12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /*
- * Implementation of selection: nsISelection,nsISelectionPrivate and nsFrameSelection
- */
- #include "mozilla/dom/Selection.h"
- #include "mozilla/Attributes.h"
- #include "mozilla/EventStates.h"
- #include "nsCOMPtr.h"
- #include "nsString.h"
- #include "nsFrameSelection.h"
- #include "nsISelectionListener.h"
- #include "nsContentCID.h"
- #include "nsDeviceContext.h"
- #include "nsIContent.h"
- #include "nsIDOMNode.h"
- #include "nsRange.h"
- #include "nsCOMArray.h"
- #include "nsITableCellLayout.h"
- #include "nsTArray.h"
- #include "nsTableWrapperFrame.h"
- #include "nsTableCellFrame.h"
- #include "nsIScrollableFrame.h"
- #include "nsCCUncollectableMarker.h"
- #include "nsIContentIterator.h"
- #include "nsIDocumentEncoder.h"
- #include "nsTextFragment.h"
- #include <algorithm>
- #include "nsContentUtils.h"
- #include "nsGkAtoms.h"
- #include "nsIFrameTraversal.h"
- #include "nsLayoutUtils.h"
- #include "nsLayoutCID.h"
- #include "nsBidiPresUtils.h"
- static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
- #include "nsTextFrame.h"
- #include "nsIDOMText.h"
- #include "nsContentUtils.h"
- #include "nsThreadUtils.h"
- #include "mozilla/Preferences.h"
- #include "nsDOMClassInfoID.h"
- #include "nsPresContext.h"
- #include "nsIPresShell.h"
- #include "nsCaret.h"
- #include "AccessibleCaretEventHub.h"
- #include "mozilla/MouseEvents.h"
- #include "mozilla/TextEvents.h"
- #include "nsITimer.h"
- #include "nsFrameManager.h"
- // notifications
- #include "nsIDOMDocument.h"
- #include "nsIDocument.h"
- #include "nsISelectionController.h"//for the enums
- #include "nsAutoCopyListener.h"
- #include "SelectionChangeListener.h"
- #include "nsCopySupport.h"
- #include "nsIClipboard.h"
- #include "nsIFrameInlines.h"
- #include "nsIBidiKeyboard.h"
- #include "nsError.h"
- #include "mozilla/dom/Element.h"
- #include "mozilla/dom/ShadowRoot.h"
- #include "mozilla/ErrorResult.h"
- #include "mozilla/dom/SelectionBinding.h"
- #include "mozilla/AsyncEventDispatcher.h"
- #include "mozilla/Telemetry.h"
- #include "mozilla/layers/ScrollInputMethods.h"
- #include "nsViewManager.h"
- #include "nsIEditor.h"
- #include "nsIHTMLEditor.h"
- #include "nsFocusManager.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- using mozilla::layers::ScrollInputMethod;
- //#define DEBUG_TABLE 1
- static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
- static nsIAtom *GetTag(nsINode *aNode);
- // returns the parent
- static nsINode* ParentOffset(nsINode *aNode, int32_t *aChildOffset);
- static nsINode* GetCellParent(nsINode *aDomNode);
- #ifdef PRINT_RANGE
- static void printRange(nsRange *aDomRange);
- #define DEBUG_OUT_RANGE(x) printRange(x)
- #else
- #define DEBUG_OUT_RANGE(x)
- #endif // PRINT_RANGE
- /******************************************************************************
- * Utility methods defined in nsISelectionController.idl
- ******************************************************************************/
- namespace mozilla {
- const char*
- ToChar(SelectionType aSelectionType)
- {
- switch (aSelectionType) {
- case SelectionType::eInvalid:
- return "SelectionType::eInvalid";
- case SelectionType::eNone:
- return "SelectionType::eNone";
- case SelectionType::eNormal:
- return "SelectionType::eNormal";
- case SelectionType::eSpellCheck:
- return "SelectionType::eSpellCheck";
- case SelectionType::eIMERawClause:
- return "SelectionType::eIMERawClause";
- case SelectionType::eIMESelectedRawClause:
- return "SelectionType::eIMESelectedRawClause";
- case SelectionType::eIMEConvertedClause:
- return "SelectionType::eIMEConvertedClause";
- case SelectionType::eIMESelectedClause:
- return "SelectionType::eIMESelectedClause";
- case SelectionType::eAccessibility:
- return "SelectionType::eAccessibility";
- case SelectionType::eFind:
- return "SelectionType::eFind";
- case SelectionType::eURLSecondary:
- return "SelectionType::eURLSecondary";
- case SelectionType::eURLStrikeout:
- return "SelectionType::eURLStrikeout";
- default:
- return "Invalid SelectionType";
- }
- }
- static bool
- IsValidSelectionType(RawSelectionType aRawSelectionType)
- {
- switch (static_cast<SelectionType>(aRawSelectionType)) {
- case SelectionType::eNone:
- case SelectionType::eNormal:
- case SelectionType::eSpellCheck:
- case SelectionType::eIMERawClause:
- case SelectionType::eIMESelectedRawClause:
- case SelectionType::eIMEConvertedClause:
- case SelectionType::eIMESelectedClause:
- case SelectionType::eAccessibility:
- case SelectionType::eFind:
- case SelectionType::eURLSecondary:
- case SelectionType::eURLStrikeout:
- return true;
- default:
- return false;
- }
- }
- SelectionType
- ToSelectionType(RawSelectionType aRawSelectionType)
- {
- if (!IsValidSelectionType(aRawSelectionType)) {
- return SelectionType::eInvalid;
- }
- return static_cast<SelectionType>(aRawSelectionType);
- }
- RawSelectionType
- ToRawSelectionType(SelectionType aSelectionType)
- {
- return static_cast<RawSelectionType>(aSelectionType);
- }
- bool operator &(SelectionType aSelectionType,
- RawSelectionType aRawSelectionTypes)
- {
- return (ToRawSelectionType(aSelectionType) & aRawSelectionTypes) != 0;
- }
- } // namespace mozilla
- /******************************************************************************
- * nsPeekOffsetStruct
- ******************************************************************************/
- //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
- //#define DEBUG_NAVIGATION
- //#define DEBUG_TABLE_SELECTION 1
- nsPeekOffsetStruct::nsPeekOffsetStruct(nsSelectionAmount aAmount,
- nsDirection aDirection,
- int32_t aStartOffset,
- nsPoint aDesiredPos,
- bool aJumpLines,
- bool aScrollViewStop,
- bool aIsKeyboardSelect,
- bool aVisual,
- bool aExtend,
- EWordMovementType aWordMovementType)
- : mAmount(aAmount)
- , mDirection(aDirection)
- , mStartOffset(aStartOffset)
- , mDesiredPos(aDesiredPos)
- , mWordMovementType(aWordMovementType)
- , mJumpLines(aJumpLines)
- , mScrollViewStop(aScrollViewStop)
- , mIsKeyboardSelect(aIsKeyboardSelect)
- , mVisual(aVisual)
- , mExtend(aExtend)
- , mResultContent()
- , mResultFrame(nullptr)
- , mContentOffset(0)
- , mAttach(CARET_ASSOCIATE_BEFORE)
- {
- }
- struct CachedOffsetForFrame {
- CachedOffsetForFrame()
- : mCachedFrameOffset(0, 0) // nsPoint ctor
- , mLastCaretFrame(nullptr)
- , mLastContentOffset(0)
- , mCanCacheFrameOffset(false)
- {}
- nsPoint mCachedFrameOffset; // cached frame offset
- nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in.
- int32_t mLastContentOffset; // store last content offset
- bool mCanCacheFrameOffset; // cached frame offset is valid?
- };
- class nsAutoScrollTimer final : public nsITimerCallback
- {
- public:
- NS_DECL_ISUPPORTS
- nsAutoScrollTimer()
- : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
- {
- }
- // aPoint is relative to aPresContext's root frame
- nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
- {
- mPoint = aPoint;
- // Store the presentation context. The timer will be
- // stopped by the selection if the prescontext is destroyed.
- mPresContext = aPresContext;
- mContent = nsIPresShell::GetCapturingContent();
- if (!mTimer)
- {
- nsresult result;
- mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
- if (NS_FAILED(result))
- return result;
- }
- return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
- }
- nsresult Stop()
- {
- if (mTimer)
- {
- mTimer->Cancel();
- mTimer = nullptr;
- }
- mContent = nullptr;
- return NS_OK;
- }
- nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection)
- {
- mFrameSelection = aFrameSelection;
- mSelection = aSelection;
- return NS_OK;
- }
- nsresult SetDelay(uint32_t aDelay)
- {
- mDelay = aDelay;
- return NS_OK;
- }
- NS_IMETHOD Notify(nsITimer *timer) override
- {
- if (mSelection && mPresContext)
- {
- nsWeakFrame frame =
- mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
- if (!frame)
- return NS_OK;
- mContent = nullptr;
- nsPoint pt = mPoint -
- frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- frameSelection->HandleDrag(frame, pt);
- if (!frame.IsAlive())
- return NS_OK;
- NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
- mSelection->DoAutoScroll(frame, pt);
- }
- return NS_OK;
- }
- protected:
- virtual ~nsAutoScrollTimer()
- {
- if (mTimer) {
- mTimer->Cancel();
- }
- }
- private:
- nsFrameSelection *mFrameSelection;
- Selection* mSelection;
- nsPresContext *mPresContext;
- // relative to mPresContext's root frame
- nsPoint mPoint;
- nsCOMPtr<nsITimer> mTimer;
- nsCOMPtr<nsIContent> mContent;
- uint32_t mDelay;
- };
- NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback)
- nsresult NS_NewDomSelection(nsISelection **aDomSelection)
- {
- Selection* rlist = new Selection;
- *aDomSelection = (nsISelection *)rlist;
- NS_ADDREF(rlist);
- return NS_OK;
- }
- static int8_t
- GetIndexFromSelectionType(SelectionType aSelectionType)
- {
- switch (aSelectionType) {
- case SelectionType::eNormal:
- return 0;
- case SelectionType::eSpellCheck:
- return 1;
- case SelectionType::eIMERawClause:
- return 2;
- case SelectionType::eIMESelectedRawClause:
- return 3;
- case SelectionType::eIMEConvertedClause:
- return 4;
- case SelectionType::eIMESelectedClause:
- return 5;
- case SelectionType::eAccessibility:
- return 6;
- case SelectionType::eFind:
- return 7;
- case SelectionType::eURLSecondary:
- return 8;
- case SelectionType::eURLStrikeout:
- return 9;
- default:
- return -1;
- }
- /* NOTREACHED */
- }
- static SelectionType
- GetSelectionTypeFromIndex(int8_t aIndex)
- {
- static const SelectionType kSelectionTypes[] = {
- SelectionType::eNormal,
- SelectionType::eSpellCheck,
- SelectionType::eIMERawClause,
- SelectionType::eIMESelectedRawClause,
- SelectionType::eIMEConvertedClause,
- SelectionType::eIMESelectedClause,
- SelectionType::eAccessibility,
- SelectionType::eFind,
- SelectionType::eURLSecondary,
- SelectionType::eURLStrikeout
- };
- if (NS_WARN_IF(aIndex < 0) ||
- NS_WARN_IF(static_cast<size_t>(aIndex) >= ArrayLength(kSelectionTypes))) {
- return SelectionType::eNormal;
- }
- return kSelectionTypes[aIndex];
- }
- /*
- The limiter is used specifically for the text areas and textfields
- In that case it is the DIV tag that is anonymously created for the text
- areas/fields. Text nodes and BR nodes fall beneath it. In the case of a
- BR node the limiter will be the parent and the offset will point before or
- after the BR node. In the case of the text node the parent content is
- the text node itself and the offset will be the exact character position.
- The offset is not important to check for validity. Simply look at the
- passed in content. If it equals the limiter then the selection point is valid.
- If its parent it the limiter then the point is also valid. In the case of
- NO limiter all points are valid since you are in a topmost iframe. (browser
- or composer)
- */
- bool
- IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
- {
- if (!aFrameSel || !aNode)
- return false;
- nsIContent *limiter = aFrameSel->GetLimiter();
- if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
- //if newfocus == the limiter. that's ok. but if not there and not parent bad
- return false; //not in the right content. tLimiter said so
- }
- limiter = aFrameSel->GetAncestorLimiter();
- return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
- }
- namespace mozilla {
- struct MOZ_RAII AutoPrepareFocusRange
- {
- AutoPrepareFocusRange(Selection* aSelection,
- bool aContinueSelection,
- bool aMultipleSelection
- MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
- {
- MOZ_GUARD_OBJECT_NOTIFIER_INIT;
- if (aSelection->mRanges.Length() <= 1) {
- return;
- }
- if (aSelection->mFrameSelection->IsUserSelectionReason()) {
- mUserSelect.emplace(aSelection);
- }
- bool userSelection = aSelection->mUserInitiated;
- nsTArray<RangeData>& ranges = aSelection->mRanges;
- if (!userSelection ||
- (!aContinueSelection && aMultipleSelection)) {
- // Scripted command or the user is starting a new explicit multi-range
- // selection.
- for (RangeData& entry : ranges) {
- entry.mRange->SetIsGenerated(false);
- }
- return;
- }
- int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
- bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
- nsISelectionListener::MOUSEDOWN_REASON |
- nsISelectionListener::MOUSEUP_REASON |
- nsISelectionListener::COLLAPSETOSTART_REASON));
- if (!isAnchorRelativeOp) {
- return;
- }
- // This operation is against the anchor but our current mAnchorFocusRange
- // represents the focus in a multi-range selection. The anchor from a user
- // perspective is the most distant generated range on the opposite side.
- // Find that range and make it the mAnchorFocusRange.
- const size_t len = ranges.Length();
- size_t newAnchorFocusIndex = size_t(-1);
- if (aSelection->GetDirection() == eDirNext) {
- for (size_t i = 0; i < len; ++i) {
- if (ranges[i].mRange->IsGenerated()) {
- newAnchorFocusIndex = i;
- break;
- }
- }
- } else {
- size_t i = len;
- while (i--) {
- if (ranges[i].mRange->IsGenerated()) {
- newAnchorFocusIndex = i;
- break;
- }
- }
- }
- if (newAnchorFocusIndex == size_t(-1)) {
- // There are no generated ranges - that's fine.
- return;
- }
- // Setup the new mAnchorFocusRange and mark the old one as generated.
- if (aSelection->mAnchorFocusRange) {
- aSelection->mAnchorFocusRange->SetIsGenerated(true);
- }
- nsRange* range = ranges[newAnchorFocusIndex].mRange;
- range->SetIsGenerated(false);
- aSelection->mAnchorFocusRange = range;
- // Remove all generated ranges (including the old mAnchorFocusRange).
- RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
- size_t i = len;
- while (i--) {
- range = aSelection->mRanges[i].mRange;
- if (range->IsGenerated()) {
- range->SetSelection(nullptr);
- aSelection->selectFrames(presContext, range, false);
- aSelection->mRanges.RemoveElementAt(i);
- }
- }
- if (aSelection->mFrameSelection) {
- aSelection->mFrameSelection->InvalidateDesiredPos();
- }
- }
- Maybe<Selection::AutoUserInitiated> mUserSelect;
- MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
- };
- } // namespace mozilla
- ////////////BEGIN nsFrameSelection methods
- nsFrameSelection::nsFrameSelection()
- {
- for (size_t i = 0; i < kPresentSelectionTypeCount; i++){
- mDomSelections[i] = new Selection(this);
- mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
- }
- mBatching = 0;
- mChangesDuringBatching = false;
- mNotifyFrames = true;
-
- mMouseDoubleDownState = false;
-
- mHint = CARET_ASSOCIATE_BEFORE;
- mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
- mKbdBidiLevel = NSBIDI_LTR;
- mDragSelectingCells = false;
- mSelectingTableCellMode = 0;
- mSelectedCellIndex = 0;
- nsAutoCopyListener *autoCopy = nullptr;
- // Check to see if the autocopy pref is enabled
- // and add the autocopy listener if it is
- if (Preferences::GetBool("clipboard.autocopy")) {
- autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionClipboard);
- }
- if (autoCopy) {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (mDomSelections[index]) {
- autoCopy->Listen(mDomSelections[index]);
- }
- }
- mDisplaySelection = nsISelectionController::SELECTION_OFF;
- mSelectionChangeReason = nsISelectionListener::NO_REASON;
- mDelayedMouseEventValid = false;
- // These values are not used since they are only valid when
- // mDelayedMouseEventValid is true, and setting mDelayedMouseEventValid
- //alwaysoverrides these values.
- mDelayedMouseEventIsShift = false;
- mDelayedMouseEventClickCount = 0;
- }
- nsFrameSelection::~nsFrameSelection()
- {
- }
- NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
- for (size_t i = 0; i < kPresentSelectionTypeCount; ++i) {
- tmp->mDomSelections[i] = nullptr;
- }
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mCellParent)
- tmp->mSelectingTableCellMode = 0;
- tmp->mDragSelectingCells = false;
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSelectedCell)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSelectedCell)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mAppendStartSelectedCell)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnselectCellOnMouseUp)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaintainRange)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiter)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mAncestorLimiter)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
- if (tmp->mShell && tmp->mShell->GetDocument() &&
- nsCCUncollectableMarker::InGeneration(cb,
- tmp->mShell->GetDocument()->
- GetMarkedCCGeneration())) {
- return NS_SUCCESS_INTERRUPTED_TRAVERSE;
- }
- for (size_t i = 0; i < kPresentSelectionTypeCount; ++i) {
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDomSelections[i])
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellParent)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSelectedCell)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSelectedCell)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAppendStartSelectedCell)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnselectCellOnMouseUp)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMaintainRange)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiter)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAncestorLimiter)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsFrameSelection, AddRef)
- NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsFrameSelection, Release)
- // Get the x (or y, in vertical writing mode) position requested
- // by the Key Handling for line-up/down
- nsresult
- nsFrameSelection::FetchDesiredPos(nsPoint &aDesiredPos)
- {
- if (!mShell) {
- NS_ERROR("fetch desired position failed");
- return NS_ERROR_FAILURE;
- }
- if (mDesiredPosSet) {
- aDesiredPos = mDesiredPos;
- return NS_OK;
- }
- RefPtr<nsCaret> caret = mShell->GetCaret();
- if (!caret) {
- return NS_ERROR_NULL_POINTER;
- }
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- caret->SetSelection(mDomSelections[index]);
- nsRect coord;
- nsIFrame* caretFrame = caret->GetGeometry(&coord);
- if (!caretFrame) {
- return NS_ERROR_FAILURE;
- }
- nsPoint viewOffset(0, 0);
- nsView* view = nullptr;
- caretFrame->GetOffsetFromView(viewOffset, &view);
- if (view) {
- coord += viewOffset;
- }
- aDesiredPos = coord.TopLeft();
- return NS_OK;
- }
- void
- nsFrameSelection::InvalidateDesiredPos() // do not listen to mDesiredPos;
- // you must get another.
- {
- mDesiredPosSet = false;
- }
- void
- nsFrameSelection::SetDesiredPos(nsPoint aPos)
- {
- mDesiredPos = aPos;
- mDesiredPosSet = true;
- }
- nsresult
- nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame,
- nsPoint& aPoint,
- nsIFrame **aRetFrame,
- nsPoint& aRetPoint)
- {
- //
- // The whole point of this method is to return a frame and point that
- // that lie within the same valid subtree as the anchor node's frame,
- // for use with the method GetContentAndOffsetsFromPoint().
- //
- // A valid subtree is defined to be one where all the content nodes in
- // the tree have a valid parent-child relationship.
- //
- // If the anchor frame and aFrame are in the same subtree, aFrame will
- // be returned in aRetFrame. If they are in different subtrees, we
- // return the frame for the root of the subtree.
- //
- if (!aFrame || !aRetFrame)
- return NS_ERROR_NULL_POINTER;
- *aRetFrame = aFrame;
- aRetPoint = aPoint;
- //
- // Get the frame and content for the selection's anchor point!
- //
- nsresult result;
- nsCOMPtr<nsIDOMNode> anchorNode;
- int32_t anchorOffset = 0;
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
- if (NS_FAILED(result))
- return result;
- if (!anchorNode)
- return NS_OK;
- result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
- if (NS_FAILED(result))
- return result;
- nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
- if (!anchorContent)
- return NS_ERROR_FAILURE;
-
- //
- // Now find the root of the subtree containing the anchor's content.
- //
- NS_ENSURE_STATE(mShell);
- nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell);
- NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
- //
- // Now find the root of the subtree containing aFrame's content.
- //
- nsIContent* content = aFrame->GetContent();
- if (content)
- {
- nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
- NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
- if (anchorRoot == contentRoot)
- {
- // If the aFrame's content isn't the capturing content, it should be
- // a descendant. At this time, we can return simply.
- nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
- if (capturedContent != content)
- {
- return NS_OK;
- }
- // Find the frame under the mouse cursor with the root frame.
- // At this time, don't use the anchor's frame because it may not have
- // fixed positioned frames.
- nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
- nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
- nsIFrame* cursorFrame =
- nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
- // If the mouse cursor in on a frame which is descendant of same
- // selection root, we can expand the selection to the frame.
- if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
- {
- nsIContent* cursorContent = cursorFrame->GetContent();
- NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
- nsIContent* cursorContentRoot =
- cursorContent->GetSelectionRootContent(mShell);
- NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
- if (cursorContentRoot == anchorRoot)
- {
- *aRetFrame = cursorFrame;
- aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
- return NS_OK;
- }
- }
- // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
- // cursor is out of the window), we should use the frame of the anchor
- // root.
- }
- }
- //
- // When we can't find a frame which is under the mouse cursor and has a same
- // selection root as the anchor node's, we should return the selection root
- // frame.
- //
- *aRetFrame = anchorRoot->GetPrimaryFrame();
- if (!*aRetFrame)
- return NS_ERROR_FAILURE;
- //
- // Now make sure that aRetPoint is converted to the same coordinate
- // system used by aRetFrame.
- //
- aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
- return NS_OK;
- }
- void
- nsFrameSelection::SetCaretBidiLevel(nsBidiLevel aLevel)
- {
- // If the current level is undefined, we have just inserted new text.
- // In this case, we don't want to reset the keyboard language
- mCaretBidiLevel = aLevel;
- RefPtr<nsCaret> caret;
- if (mShell && (caret = mShell->GetCaret())) {
- caret->SchedulePaint();
- }
- return;
- }
- nsBidiLevel
- nsFrameSelection::GetCaretBidiLevel() const
- {
- return mCaretBidiLevel;
- }
- void
- nsFrameSelection::UndefineCaretBidiLevel()
- {
- mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
- }
- #ifdef PRINT_RANGE
- void printRange(nsRange *aDomRange)
- {
- if (!aDomRange)
- {
- printf("NULL nsIDOMRange\n");
- }
- nsINode* startNode = aDomRange->GetStartParent();
- nsINode* endNode = aDomRange->GetEndParent();
- int32_t startOffset = aDomRange->StartOffset();
- int32_t endOffset = aDomRange->EndOffset();
-
- printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
- (unsigned long)aDomRange,
- (unsigned long)startNode, (long)startOffset,
- (unsigned long)endNode, (long)endOffset);
-
- }
- #endif /* PRINT_RANGE */
- static
- nsIAtom *GetTag(nsINode *aNode)
- {
- nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
- if (!content)
- {
- NS_NOTREACHED("bad node passed to GetTag()");
- return nullptr;
- }
-
- return content->NodeInfo()->NameAtom();
- }
- // Returns the parent
- nsINode*
- ParentOffset(nsINode *aNode, int32_t *aChildOffset)
- {
- if (!aNode || !aChildOffset)
- return nullptr;
- nsIContent* parent = aNode->GetParent();
- if (parent)
- {
- *aChildOffset = parent->IndexOf(aNode);
- return parent;
- }
- return nullptr;
- }
- static nsINode*
- GetCellParent(nsINode *aDomNode)
- {
- if (!aDomNode)
- return nullptr;
- nsINode* current = aDomNode;
- // Start with current node and look for a table cell
- while (current)
- {
- nsIAtom* tag = GetTag(current);
- if (tag == nsGkAtoms::td || tag == nsGkAtoms::th)
- return current;
- current = current->GetParent();
- }
- return nullptr;
- }
- void
- nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
- {
- mShell = aShell;
- mDragState = false;
- mDesiredPosSet = false;
- mLimiter = aLimiter;
- mCaretMovementStyle =
- Preferences::GetInt("bidi.edit.caret_movement_style", 2);
- // This should only ever be initialized on the main thread, so we are OK here.
- static bool prefCachesInitialized = false;
- if (!prefCachesInitialized) {
- prefCachesInitialized = true;
- Preferences::AddBoolVarCache(&sSelectionEventsEnabled,
- "dom.select_events.enabled", false);
- Preferences::AddBoolVarCache(&sSelectionEventsOnTextControlsEnabled,
- "dom.select_events.textcontrols.enabled", false);
- }
- RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
- if (eventHub) {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (mDomSelections[index]) {
- mDomSelections[index]->AddSelectionListener(eventHub);
- }
- }
- nsIDocument* doc = aShell->GetDocument();
- if (sSelectionEventsEnabled ||
- (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (mDomSelections[index]) {
- // The Selection instance will hold a strong reference to its selectionchangelistener
- // so we don't have to worry about that!
- RefPtr<SelectionChangeListener> listener = new SelectionChangeListener;
- mDomSelections[index]->AddSelectionListener(listener);
- }
- }
- }
- bool nsFrameSelection::sSelectionEventsEnabled = false;
- bool nsFrameSelection::sSelectionEventsOnTextControlsEnabled = false;
- nsresult
- nsFrameSelection::MoveCaret(nsDirection aDirection,
- bool aContinueSelection,
- nsSelectionAmount aAmount,
- CaretMovementStyle aMovementStyle)
- {
- bool visualMovement = aMovementStyle == eVisual ||
- (aMovementStyle == eUsePrefStyle &&
- (mCaretMovementStyle == 1 ||
- (mCaretMovementStyle == 2 && !aContinueSelection)));
- NS_ENSURE_STATE(mShell);
- // Flush out layout, since we need it to be up to date to do caret
- // positioning.
- mShell->FlushPendingNotifications(Flush_Layout);
- if (!mShell) {
- return NS_OK;
- }
- nsPresContext *context = mShell->GetPresContext();
- if (!context)
- return NS_ERROR_FAILURE;
- bool isCollapsed;
- nsPoint desiredPos(0, 0); //we must keep this around and revalidate it when its just UP/DOWN
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- RefPtr<Selection> sel = mDomSelections[index];
- if (!sel)
- return NS_ERROR_NULL_POINTER;
- int32_t scrollFlags = Selection::SCROLL_FOR_CARET_MOVE;
- nsINode* focusNode = sel->GetFocusNode();
- if (focusNode &&
- (focusNode->IsEditable() ||
- (focusNode->IsElement() &&
- focusNode->AsElement()->State().
- HasState(NS_EVENT_STATE_MOZ_READWRITE)))) {
- // If caret moves in editor, it should cause scrolling even if it's in
- // overflow: hidden;.
- scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
- }
- nsresult result = sel->GetIsCollapsed(&isCollapsed);
- if (NS_FAILED(result)) {
- return result;
- }
- int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0);
- if (caretStyle == 0
- #ifdef XP_WIN
- && aAmount != eSelectLine
- #endif
- ) {
- // Put caret at the selection edge in the |aDirection| direction.
- caretStyle = 2;
- }
- bool doCollapse = !isCollapsed && !aContinueSelection && caretStyle == 2 &&
- aAmount <= eSelectLine;
- if (doCollapse) {
- if (aDirection == eDirPrevious) {
- PostReason(nsISelectionListener::COLLAPSETOSTART_REASON);
- mHint = CARET_ASSOCIATE_AFTER;
- } else {
- PostReason(nsISelectionListener::COLLAPSETOEND_REASON);
- mHint = CARET_ASSOCIATE_BEFORE;
- }
- } else {
- PostReason(nsISelectionListener::KEYPRESS_REASON);
- }
- AutoPrepareFocusRange prep(sel, aContinueSelection, false);
- if (aAmount == eSelectLine) {
- result = FetchDesiredPos(desiredPos);
- if (NS_FAILED(result)) {
- return result;
- }
- SetDesiredPos(desiredPos);
- }
- if (doCollapse) {
- const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
- if (anchorFocusRange) {
- nsINode* node;
- int32_t offset;
- if (aDirection == eDirPrevious) {
- node = anchorFocusRange->GetStartParent();
- offset = anchorFocusRange->StartOffset();
- } else {
- node = anchorFocusRange->GetEndParent();
- offset = anchorFocusRange->EndOffset();
- }
- sel->Collapse(node, offset);
- }
- sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
- nsIPresShell::ScrollAxis(),
- nsIPresShell::ScrollAxis(), scrollFlags);
- return NS_OK;
- }
- nsIFrame *frame;
- int32_t offsetused = 0;
- result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
- visualMovement);
- if (NS_FAILED(result) || !frame)
- return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
- //set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking
- //when we hit scrollable views. If no limiter then just let it go ahead
- nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredPos,
- true, mLimiter != nullptr, true, visualMovement,
- aContinueSelection);
- nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
- CaretAssociateHint tHint(mHint); //temporary variable so we dont set mHint until it is necessary
- switch (aAmount){
- case eSelectCharacter:
- case eSelectCluster:
- case eSelectWord:
- case eSelectWordNoSpace:
- InvalidateDesiredPos();
- pos.mAmount = aAmount;
- pos.mDirection = (visualMovement && paraDir == NSBIDI_RTL)
- ? nsDirection(1 - aDirection) : aDirection;
- break;
- case eSelectLine:
- pos.mAmount = aAmount;
- pos.mDirection = aDirection;
- break;
- case eSelectBeginLine:
- case eSelectEndLine:
- InvalidateDesiredPos();
- pos.mAmount = aAmount;
- pos.mDirection = (visualMovement && paraDir == NSBIDI_RTL)
- ? nsDirection(1 - aDirection) : aDirection;
- break;
- default:
- return NS_ERROR_FAILURE;
- }
- if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent)
- {
- nsIFrame *theFrame;
- int32_t currentOffset, frameStart, frameEnd;
- if (aAmount <= eSelectWordNoSpace)
- {
- // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward,
- // so determine the hint here based on the result frame and offset:
- // If we're at the end of a text frame, set the hint to ASSOCIATE_BEFORE to indicate that we
- // want the caret displayed at the end of this frame, not at the beginning of the next one.
- theFrame = pos.mResultFrame;
- theFrame->GetOffsets(frameStart, frameEnd);
- currentOffset = pos.mContentOffset;
- if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0))
- tHint = CARET_ASSOCIATE_BEFORE;
- else
- tHint = CARET_ASSOCIATE_AFTER;
- } else {
- // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all.
- // In these cases, get the frame based on the content and hint returned by PeekOffset().
- tHint = pos.mAttach;
- theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset,
- tHint, ¤tOffset);
- if (!theFrame)
- return NS_ERROR_FAILURE;
- theFrame->GetOffsets(frameStart, frameEnd);
- }
- if (context->BidiEnabled())
- {
- switch (aAmount) {
- case eSelectBeginLine:
- case eSelectEndLine: {
- // In Bidi contexts, PeekOffset calculates pos.mContentOffset
- // differently depending on whether the movement is visual or logical.
- // For visual movement, pos.mContentOffset depends on the direction-
- // ality of the first/last frame on the line (theFrame), and the caret
- // directionality must correspond.
- FrameBidiData bidiData = theFrame->GetBidiData();
- SetCaretBidiLevel(visualMovement ? bidiData.embeddingLevel
- : bidiData.baseLevel);
- break;
- }
- default:
- // If the current position is not a frame boundary, it's enough just
- // to take the Bidi level of the current frame
- if ((pos.mContentOffset != frameStart &&
- pos.mContentOffset != frameEnd) ||
- eSelectLine == aAmount) {
- SetCaretBidiLevel(theFrame->GetEmbeddingLevel());
- }
- else {
- BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset,
- aAmount, tHint);
- }
- }
- }
- result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
- tHint, aContinueSelection, false);
- } else if (aAmount <= eSelectWordNoSpace && aDirection == eDirNext &&
- !aContinueSelection) {
- // Collapse selection if PeekOffset failed, we either
- // 1. bumped into the BRFrame, bug 207623
- // 2. had select-all in a text input (DIV range), bug 352759.
- bool isBRFrame = frame->GetType() == nsGkAtoms::brFrame;
- sel->Collapse(sel->GetFocusNode(), sel->FocusOffset());
- // Note: 'frame' might be dead here.
- if (!isBRFrame) {
- mHint = CARET_ASSOCIATE_BEFORE; // We're now at the end of the frame to the left.
- }
- result = NS_OK;
- }
- if (NS_SUCCEEDED(result))
- {
- result = mDomSelections[index]->
- ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
- nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
- scrollFlags);
- }
- return result;
- }
- //END nsFrameSelection methods
- //BEGIN nsFrameSelection methods
- NS_IMETHODIMP
- Selection::ToString(nsAString& aReturn)
- {
- // We need Flush_Style here to make sure frames have been created for
- // the selected content. Use mFrameSelection->GetShell() which returns
- // null if the Selection has been disconnected (the shell is Destroyed).
- nsCOMPtr<nsIPresShell> shell =
- mFrameSelection ? mFrameSelection->GetShell() : nullptr;
- if (!shell) {
- aReturn.Truncate();
- return NS_OK;
- }
- shell->FlushPendingNotifications(Flush_Style);
- return ToStringWithFormat("text/plain",
- nsIDocumentEncoder::SkipInvisibleContent,
- 0, aReturn);
- }
- void
- Selection::Stringify(nsAString& aResult)
- {
- // Eat the error code
- ToString(aResult);
- }
- NS_IMETHODIMP
- Selection::ToStringWithFormat(const char* aFormatType, uint32_t aFlags,
- int32_t aWrapCol, nsAString& aReturn)
- {
- ErrorResult result;
- NS_ConvertUTF8toUTF16 format(aFormatType);
- ToStringWithFormat(format, aFlags, aWrapCol, aReturn, result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- return NS_OK;
- }
- void
- Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
- int32_t aWrapCol, nsAString& aReturn,
- ErrorResult& aRv)
- {
- nsresult rv = NS_OK;
- NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
- formatType.Append(aFormatType);
- nsCOMPtr<nsIDocumentEncoder> encoder =
- do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- nsIPresShell* shell = GetPresShell();
- if (!shell) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- nsIDocument *doc = shell->GetDocument();
- nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
- NS_ASSERTION(domDoc, "Need a document");
- // Flags should always include OutputSelectionOnly if we're coming from here:
- aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
- nsAutoString readstring;
- readstring.Assign(aFormatType);
- rv = encoder->Init(domDoc, readstring, aFlags);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- encoder->SetSelection(this);
- if (aWrapCol != 0)
- encoder->SetWrapColumn(aWrapCol);
- rv = encoder->EncodeToString(aReturn);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- }
- }
- NS_IMETHODIMP
- Selection::SetInterlinePosition(bool aHintRight)
- {
- ErrorResult result;
- SetInterlinePosition(aHintRight, result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- return NS_OK;
- }
- void
- Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv)
- {
- if (!mFrameSelection) {
- aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
- return;
- }
- mFrameSelection->SetHint(aHintRight ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
- }
- NS_IMETHODIMP
- Selection::GetInterlinePosition(bool* aHintRight)
- {
- ErrorResult result;
- *aHintRight = GetInterlinePosition(result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- return NS_OK;
- }
- bool
- Selection::GetInterlinePosition(ErrorResult& aRv)
- {
- if (!mFrameSelection) {
- aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
- return false;
- }
- return mFrameSelection->GetHint() == CARET_ASSOCIATE_AFTER;
- }
- Nullable<int16_t>
- Selection::GetCaretBidiLevel(mozilla::ErrorResult& aRv) const
- {
- if (!mFrameSelection) {
- aRv.Throw(NS_ERROR_NOT_INITIALIZED);
- return Nullable<int16_t>();
- }
- nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
- return (caretBidiLevel & BIDI_LEVEL_UNDEFINED) ?
- Nullable<int16_t>() : Nullable<int16_t>(caretBidiLevel);
- }
- void
- Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv)
- {
- if (!mFrameSelection) {
- aRv.Throw(NS_ERROR_NOT_INITIALIZED);
- return;
- }
- if (aCaretBidiLevel.IsNull()) {
- mFrameSelection->UndefineCaretBidiLevel();
- } else {
- mFrameSelection->SetCaretBidiLevel(aCaretBidiLevel.Value());
- }
- }
- nsPrevNextBidiLevels
- nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
- uint32_t aContentOffset,
- bool aJumpLines) const
- {
- return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
- }
- nsPrevNextBidiLevels
- nsFrameSelection::GetPrevNextBidiLevels(nsIContent* aNode,
- uint32_t aContentOffset,
- CaretAssociateHint aHint,
- bool aJumpLines) const
- {
- // Get the level of the frames on each side
- nsIFrame *currentFrame;
- int32_t currentOffset;
- int32_t frameStart, frameEnd;
- nsDirection direction;
-
- nsPrevNextBidiLevels levels;
- levels.SetData(nullptr, nullptr, 0, 0);
- currentFrame = GetFrameForNodeOffset(aNode, aContentOffset,
- aHint, ¤tOffset);
- if (!currentFrame)
- return levels;
- currentFrame->GetOffsets(frameStart, frameEnd);
- if (0 == frameStart && 0 == frameEnd)
- direction = eDirPrevious;
- else if (frameStart == currentOffset)
- direction = eDirPrevious;
- else if (frameEnd == currentOffset)
- direction = eDirNext;
- else {
- // we are neither at the beginning nor at the end of the frame, so we have no worries
- nsBidiLevel currentLevel = currentFrame->GetEmbeddingLevel();
- levels.SetData(currentFrame, currentFrame, currentLevel, currentLevel);
- return levels;
- }
- nsIFrame *newFrame;
- int32_t offset;
- bool jumpedLine, movedOverNonSelectableText;
- nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
- aJumpLines, true,
- &newFrame, &offset, &jumpedLine,
- &movedOverNonSelectableText);
- if (NS_FAILED(rv))
- newFrame = nullptr;
- FrameBidiData currentBidi = currentFrame->GetBidiData();
- nsBidiLevel currentLevel = currentBidi.embeddingLevel;
- nsBidiLevel newLevel = newFrame ? newFrame->GetEmbeddingLevel()
- : currentBidi.baseLevel;
-
- // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
- // XXX This could be removed once bug 339786 is fixed.
- if (!aJumpLines) {
- if (currentFrame->GetType() == nsGkAtoms::brFrame) {
- currentFrame = nullptr;
- currentLevel = currentBidi.baseLevel;
- }
- if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) {
- newFrame = nullptr;
- newLevel = currentBidi.baseLevel;
- }
- }
-
- if (direction == eDirNext)
- levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
- else
- levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
- return levels;
- }
- nsresult
- nsFrameSelection::GetFrameFromLevel(nsIFrame *aFrameIn,
- nsDirection aDirection,
- nsBidiLevel aBidiLevel,
- nsIFrame **aFrameOut) const
- {
- NS_ENSURE_STATE(mShell);
- nsBidiLevel foundLevel = 0;
- nsIFrame *foundFrame = aFrameIn;
- nsCOMPtr<nsIFrameEnumerator> frameTraversal;
- nsresult result;
- nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
- if (NS_FAILED(result))
- return result;
- result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
- mShell->GetPresContext(), aFrameIn,
- eLeaf,
- false, // aVisual
- false, // aLockInScrollView
- false, // aFollowOOFs
- false // aSkipPopupChecks
- );
- if (NS_FAILED(result))
- return result;
- do {
- *aFrameOut = foundFrame;
- if (aDirection == eDirNext)
- frameTraversal->Next();
- else
- frameTraversal->Prev();
- foundFrame = frameTraversal->CurrentItem();
- if (!foundFrame)
- return NS_ERROR_FAILURE;
- foundLevel = foundFrame->GetEmbeddingLevel();
- } while (foundLevel > aBidiLevel);
- return NS_OK;
- }
- nsresult
- nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- mMaintainedAmount = aAmount;
- const nsRange* anchorFocusRange =
- mDomSelections[index]->GetAnchorFocusRange();
- if (anchorFocusRange && aAmount != eSelectNoAmount) {
- mMaintainRange = anchorFocusRange->CloneRange();
- return NS_OK;
- }
- mMaintainRange = nullptr;
- return NS_OK;
- }
- /** After moving the caret, its Bidi level is set according to the following rules:
- *
- * After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
- * After Home and End, set to the paragraph embedding level.
- * After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
- * After mouse click, set to the level of the current frame.
- *
- * The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
- * BidiLevelFromMove is called when the caret is moved in response to a keyboard event
- *
- * @param aPresShell is the presentation shell
- * @param aNode is the content node
- * @param aContentOffset is the new caret position, as an offset into aNode
- * @param aAmount is the amount of the move that gave the caret its new position
- * @param aHint is the hint indicating in what logical direction the caret moved
- */
- void nsFrameSelection::BidiLevelFromMove(nsIPresShell* aPresShell,
- nsIContent* aNode,
- uint32_t aContentOffset,
- nsSelectionAmount aAmount,
- CaretAssociateHint aHint)
- {
- switch (aAmount) {
- // Movement within the line: the new cursor Bidi level is the level of the
- // last character moved over
- case eSelectCharacter:
- case eSelectCluster:
- case eSelectWord:
- case eSelectWordNoSpace:
- case eSelectBeginLine:
- case eSelectEndLine:
- case eSelectNoAmount:
- {
- nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset,
- aHint, false);
- SetCaretBidiLevel(aHint == CARET_ASSOCIATE_BEFORE ?
- levels.mLevelBefore : levels.mLevelAfter);
- break;
- }
- /*
- // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters
- case eSelectLine:
- case eSelectParagraph:
- GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
- aPresShell->SetCaretBidiLevel(std::min(firstLevel, secondLevel));
- break;
- */
- default:
- UndefineCaretBidiLevel();
- }
- }
- /**
- * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
- *
- * @param aNode is the content node
- * @param aContentOffset is the new caret position, as an offset into aNode
- */
- void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
- uint32_t aContentOffset)
- {
- nsIFrame* clickInFrame=nullptr;
- int32_t OffsetNotUsed;
- clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
- if (!clickInFrame)
- return;
- SetCaretBidiLevel(clickInFrame->GetEmbeddingLevel());
- }
- bool
- nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
- int32_t aOffset)
- {
- if (!mMaintainRange)
- return false;
- if (!aContent) {
- return false;
- }
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return false;
- nsINode* rangeStartNode = mMaintainRange->GetStartParent();
- nsINode* rangeEndNode = mMaintainRange->GetEndParent();
- int32_t rangeStartOffset = mMaintainRange->StartOffset();
- int32_t rangeEndOffset = mMaintainRange->EndOffset();
- int32_t relToStart =
- nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset,
- aContent, aOffset);
- int32_t relToEnd =
- nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset,
- aContent, aOffset);
- // If aContent/aOffset is inside the maintained selection, or if it is on the
- // "anchor" side of the maintained selection, we need to do something.
- if ((relToStart < 0 && relToEnd > 0) ||
- (relToStart > 0 &&
- mDomSelections[index]->GetDirection() == eDirNext) ||
- (relToEnd < 0 &&
- mDomSelections[index]->GetDirection() == eDirPrevious)) {
- // Set the current range to the maintained range.
- mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange);
- if (relToStart < 0 && relToEnd > 0) {
- // We're inside the maintained selection, just keep it selected.
- return true;
- }
- // Reverse the direction of the selection so that the anchor will be on the
- // far side of the maintained selection, relative to aContent/aOffset.
- mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext);
- }
- return false;
- }
- nsresult
- nsFrameSelection::HandleClick(nsIContent* aNewFocus,
- uint32_t aContentOffset,
- uint32_t aContentEndOffset,
- bool aContinueSelection,
- bool aMultipleSelection,
- CaretAssociateHint aHint)
- {
- if (!aNewFocus)
- return NS_ERROR_INVALID_ARG;
- InvalidateDesiredPos();
- if (!aContinueSelection) {
- mMaintainRange = nullptr;
- if (!IsValidSelectionPoint(this, aNewFocus)) {
- mAncestorLimiter = nullptr;
- }
- }
- // Don't take focus when dragging off of a table
- if (!mDragSelectingCells)
- {
- BidiLevelFromClick(aNewFocus, aContentOffset);
- PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
- if (aContinueSelection &&
- AdjustForMaintainedSelection(aNewFocus, aContentOffset))
- return NS_OK; //shift clicked to maintained selection. rejected.
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- AutoPrepareFocusRange prep(mDomSelections[index], aContinueSelection, aMultipleSelection);
- return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aHint,
- aContinueSelection, aMultipleSelection);
- }
-
- return NS_OK;
- }
- void
- nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
- {
- if (!aFrame || !mShell)
- return;
- nsresult result;
- nsIFrame *newFrame = 0;
- nsPoint newPoint;
- result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
- if (NS_FAILED(result))
- return;
- if (!newFrame)
- return;
- nsIFrame::ContentOffsets offsets =
- newFrame->GetContentOffsetsFromPoint(newPoint);
- if (!offsets.content)
- return;
- if (newFrame->IsSelected() &&
- AdjustForMaintainedSelection(offsets.content, offsets.offset))
- return;
- // Adjust offsets according to maintained amount
- if (mMaintainRange &&
- mMaintainedAmount != eSelectNoAmount) {
-
- nsINode* rangenode = mMaintainRange->GetStartParent();
- int32_t rangeOffset = mMaintainRange->StartOffset();
- int32_t relativePosition =
- nsContentUtils::ComparePoints(rangenode, rangeOffset,
- offsets.content, offsets.offset);
- nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext;
- nsSelectionAmount amount = mMaintainedAmount;
- if (amount == eSelectBeginLine && direction == eDirNext)
- amount = eSelectEndLine;
- int32_t offset;
- nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset,
- CARET_ASSOCIATE_AFTER, &offset);
- if (frame && amount == eSelectWord && direction == eDirPrevious) {
- // To avoid selecting the previous word when at start of word,
- // first move one character forward.
- nsPeekOffsetStruct charPos(eSelectCharacter, eDirNext, offset,
- nsPoint(0, 0), false, mLimiter != nullptr,
- false, false, false);
- if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) {
- frame = charPos.mResultFrame;
- offset = charPos.mContentOffset;
- }
- }
- nsPeekOffsetStruct pos(amount, direction, offset, nsPoint(0, 0),
- false, mLimiter != nullptr, false, false, false);
- if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) {
- offsets.content = pos.mResultContent;
- offsets.offset = pos.mContentOffset;
- }
- }
-
- HandleClick(offsets.content, offsets.offset, offsets.offset,
- true, false, offsets.associate);
- }
- nsresult
- nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
- nsPoint aPoint,
- uint32_t aDelay)
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
- }
- void
- nsFrameSelection::StopAutoScrollTimer()
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return;
- mDomSelections[index]->StopAutoScrollTimer();
- }
- /**
- hard to go from nodes to frames, easy the other way!
- */
- nsresult
- nsFrameSelection::TakeFocus(nsIContent* aNewFocus,
- uint32_t aContentOffset,
- uint32_t aContentEndOffset,
- CaretAssociateHint aHint,
- bool aContinueSelection,
- bool aMultipleSelection)
- {
- if (!aNewFocus)
- return NS_ERROR_NULL_POINTER;
- NS_ENSURE_STATE(mShell);
- if (!IsValidSelectionPoint(this,aNewFocus))
- return NS_ERROR_FAILURE;
- // Clear all table selection data
- mSelectingTableCellMode = 0;
- mDragSelectingCells = false;
- mStartSelectedCell = nullptr;
- mEndSelectedCell = nullptr;
- mAppendStartSelectedCell = nullptr;
- mHint = aHint;
-
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- Maybe<Selection::AutoUserInitiated> userSelect;
- if (IsUserSelectionReason()) {
- userSelect.emplace(mDomSelections[index]);
- }
- //traverse through document and unselect crap here
- if (!aContinueSelection) {//single click? setting cursor down
- uint32_t batching = mBatching;//hack to use the collapse code.
- bool changes = mChangesDuringBatching;
- mBatching = 1;
- if (aMultipleSelection) {
- // Remove existing collapsed ranges as there's no point in having
- // non-anchor/focus collapsed ranges.
- mDomSelections[index]->RemoveCollapsedRanges();
- RefPtr<nsRange> newRange = new nsRange(aNewFocus);
- newRange->CollapseTo(aNewFocus, aContentOffset);
- mDomSelections[index]->AddRange(newRange);
- mBatching = batching;
- mChangesDuringBatching = changes;
- } else {
- bool oldDesiredPosSet = mDesiredPosSet; //need to keep old desired position if it was set.
- mDomSelections[index]->Collapse(aNewFocus, aContentOffset);
- mDesiredPosSet = oldDesiredPosSet; //now reset desired pos back.
- mBatching = batching;
- mChangesDuringBatching = changes;
- }
- if (aContentEndOffset != aContentOffset) {
- mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);
- }
- //find out if we are inside a table. if so, find out which one and which cell
- //once we do that, the next time we get a takefocus, check the parent tree.
- //if we are no longer inside same table ,cell then switch to table selection mode.
- // BUT only do this in an editor
- NS_ENSURE_STATE(mShell);
- bool editableCell = false;
- RefPtr<nsPresContext> context = mShell->GetPresContext();
- if (context) {
- nsCOMPtr<nsIHTMLEditor> editor = do_QueryInterface(nsContentUtils::GetHTMLEditor(context));
- if (editor) {
- nsINode* cellparent = GetCellParent(aNewFocus);
- nsCOMPtr<nsINode> editorHostNode = editor->GetActiveEditingHost();
- editableCell = cellparent && editorHostNode &&
- nsContentUtils::ContentIsDescendantOf(cellparent, editorHostNode);
- if (editableCell) {
- mCellParent = cellparent;
- #ifdef DEBUG_TABLE_SELECTION
- printf(" * TakeFocus - Collapsing into new cell\n");
- #endif
- }
- }
- }
- }
- else {
- // Now update the range list:
- if (aContinueSelection && aNewFocus)
- {
- int32_t offset;
- nsINode *cellparent = GetCellParent(aNewFocus);
- if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf(" * TakeFocus - moving into new cell\n");
- #endif
- WidgetMouseEvent event(false, eVoidEvent, nullptr,
- WidgetMouseEvent::eReal);
- // Start selecting in the cell we were in before
- nsINode* parent = ParentOffset(mCellParent, &offset);
- if (parent)
- HandleTableSelection(parent, offset,
- nsISelectionPrivate::TABLESELECTION_CELL, &event);
- // Find the parent of this new cell and extend selection to it
- parent = ParentOffset(cellparent, &offset);
- // XXXX We need to REALLY get the current key shift state
- // (we'd need to add event listener -- let's not bother for now)
- event.mModifiers &= ~MODIFIER_SHIFT; //aContinueSelection;
- if (parent)
- {
- mCellParent = cellparent;
- // Continue selection into next cell
- HandleTableSelection(parent, offset,
- nsISelectionPrivate::TABLESELECTION_CELL, &event);
- }
- }
- else
- {
- // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
- // is this the place to erase seleced cells ?????
- if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough
- {
- mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff
- }
- else
- mDomSelections[index]->Extend(aNewFocus, aContentOffset);
- }
- }
- }
- // Don't notify selection listeners if batching is on:
- if (GetBatching())
- return NS_OK;
- return NotifySelectionListeners(SelectionType::eNormal);
- }
- SelectionDetails*
- nsFrameSelection::LookUpSelection(nsIContent *aContent,
- int32_t aContentOffset,
- int32_t aContentLength,
- bool aSlowCheck) const
- {
- if (!aContent || !mShell)
- return nullptr;
- SelectionDetails* details = nullptr;
- for (size_t j = 0; j < kPresentSelectionTypeCount; j++) {
- if (mDomSelections[j]) {
- mDomSelections[j]->LookUpSelection(aContent, aContentOffset,
- aContentLength, &details,
- ToSelectionType(1 << j),
- aSlowCheck);
- }
- }
- return details;
- }
- void
- nsFrameSelection::SetDragState(bool aState)
- {
- if (mDragState == aState)
- return;
- mDragState = aState;
-
- if (!mDragState)
- {
- mDragSelectingCells = false;
- // Notify that reason is mouse up.
- PostReason(nsISelectionListener::MOUSEUP_REASON);
- NotifySelectionListeners(SelectionType::eNormal);
- }
- }
- Selection*
- nsFrameSelection::GetSelection(SelectionType aSelectionType) const
- {
- int8_t index = GetIndexFromSelectionType(aSelectionType);
- if (index < 0)
- return nullptr;
- return mDomSelections[index];
- }
- nsresult
- nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,
- SelectionRegion aRegion,
- int16_t aFlags) const
- {
- int8_t index = GetIndexFromSelectionType(aSelectionType);
- if (index < 0)
- return NS_ERROR_INVALID_ARG;
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- nsIPresShell::ScrollAxis verticalScroll = nsIPresShell::ScrollAxis();
- int32_t flags = Selection::SCROLL_DO_FLUSH;
- if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
- flags |= Selection::SCROLL_SYNCHRONOUS;
- } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
- flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY;
- }
- if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) {
- flags |= Selection::SCROLL_OVERFLOW_HIDDEN;
- }
- if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) {
- verticalScroll = nsIPresShell::ScrollAxis(
- nsIPresShell::SCROLL_CENTER, nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE);
- }
- if (aFlags & nsISelectionController::SCROLL_FOR_CARET_MOVE) {
- flags |= Selection::SCROLL_FOR_CARET_MOVE;
- }
- // After ScrollSelectionIntoView(), the pending notifications might be
- // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
- RefPtr<Selection> sel = mDomSelections[index];
- return sel->ScrollIntoView(aRegion, verticalScroll,
- nsIPresShell::ScrollAxis(), flags);
- }
- nsresult
- nsFrameSelection::RepaintSelection(SelectionType aSelectionType)
- {
- int8_t index = GetIndexFromSelectionType(aSelectionType);
- if (index < 0)
- return NS_ERROR_INVALID_ARG;
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- NS_ENSURE_STATE(mShell);
- return mDomSelections[index]->Repaint(mShell->GetPresContext());
- }
- nsIFrame*
- nsFrameSelection::GetFrameForNodeOffset(nsIContent* aNode,
- int32_t aOffset,
- CaretAssociateHint aHint,
- int32_t* aReturnOffset) const
- {
- if (!aNode || !aReturnOffset || !mShell)
- return nullptr;
- if (aOffset < 0)
- return nullptr;
- if (!aNode->GetPrimaryFrame() &&
- !mShell->FrameManager()->GetDisplayContentsStyleFor(aNode)) {
- return nullptr;
- }
- nsIFrame* returnFrame = nullptr;
- nsCOMPtr<nsIContent> theNode;
- while (true) {
- *aReturnOffset = aOffset;
- theNode = aNode;
- if (aNode->IsElement()) {
- int32_t childIndex = 0;
- int32_t numChildren = theNode->GetChildCount();
- if (aHint == CARET_ASSOCIATE_BEFORE) {
- if (aOffset > 0) {
- childIndex = aOffset - 1;
- } else {
- childIndex = aOffset;
- }
- } else {
- NS_ASSERTION(aHint == CARET_ASSOCIATE_AFTER, "unknown direction");
- if (aOffset >= numChildren) {
- if (numChildren > 0) {
- childIndex = numChildren - 1;
- } else {
- childIndex = 0;
- }
- } else {
- childIndex = aOffset;
- }
- }
-
- if (childIndex > 0 || numChildren > 0) {
- nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
- if (!childNode) {
- break;
- }
- theNode = childNode;
- }
- // Now that we have the child node, check if it too
- // can contain children. If so, descend into child.
- if (theNode->IsElement() &&
- theNode->GetChildCount() &&
- !theNode->HasIndependentSelection()) {
- aNode = theNode;
- aOffset = aOffset > childIndex ? theNode->GetChildCount() : 0;
- continue;
- } else {
- // Check to see if theNode is a text node. If it is, translate
- // aOffset into an offset into the text node.
- nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
- if (textNode) {
- if (theNode->GetPrimaryFrame()) {
- if (aOffset > childIndex) {
- uint32_t textLength = 0;
- nsresult rv = textNode->GetLength(&textLength);
- if (NS_FAILED(rv)) {
- break;
- }
- *aReturnOffset = (int32_t)textLength;
- } else {
- *aReturnOffset = 0;
- }
- } else {
- int32_t numChildren = aNode->GetChildCount();
- int32_t newChildIndex =
- aHint == CARET_ASSOCIATE_BEFORE ? childIndex - 1 : childIndex + 1;
- if (newChildIndex >= 0 && newChildIndex < numChildren) {
- nsCOMPtr<nsIContent> newChildNode = aNode->GetChildAt(newChildIndex);
- if (!newChildNode) {
- return nullptr;
- }
- aNode = newChildNode;
- aOffset = aHint == CARET_ASSOCIATE_BEFORE ? aNode->GetChildCount() : 0;
- continue;
- } else {
- // newChildIndex is illegal which means we're at first or last
- // child. Just use original node to get the frame.
- theNode = aNode;
- }
- }
- }
- }
- }
- // If the node is a ShadowRoot, the frame needs to be adjusted,
- // because a ShadowRoot does not get a frame. Its children are rendered
- // as children of the host.
- mozilla::dom::ShadowRoot* shadowRoot =
- mozilla::dom::ShadowRoot::FromNode(theNode);
- if (shadowRoot) {
- theNode = shadowRoot->GetHost();
- }
- returnFrame = theNode->GetPrimaryFrame();
- if (!returnFrame) {
- if (aHint == CARET_ASSOCIATE_BEFORE) {
- if (aOffset > 0) {
- --aOffset;
- continue;
- } else {
- break;
- }
- } else {
- int32_t end = theNode->GetChildCount();
- if (aOffset < end) {
- ++aOffset;
- continue;
- } else {
- break;
- }
- }
- }
- break;
- } // end while
- if (!returnFrame)
- return nullptr;
- // If we ended up here and were asked to position the caret after a visible
- // break, let's return the frame on the next line instead if it exists.
- if (aOffset > 0 && (uint32_t) aOffset >= aNode->Length() &&
- theNode == aNode->GetLastChild()) {
- nsIFrame* newFrame;
- nsLayoutUtils::IsInvisibleBreak(theNode, &newFrame);
- if (newFrame) {
- returnFrame = newFrame;
- *aReturnOffset = 0;
- }
- }
- // find the child frame containing the offset we want
- returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint == CARET_ASSOCIATE_AFTER,
- &aOffset, &returnFrame);
- return returnFrame;
- }
- void
- nsFrameSelection::CommonPageMove(bool aForward,
- bool aExtend,
- nsIScrollableFrame* aScrollableFrame)
- {
- // expected behavior for PageMove is to scroll AND move the caret
- // and remain relative position of the caret in view. see Bug 4302.
- //get the frame from the scrollable view
- nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
- if (!scrolledFrame)
- return;
- // find out where the caret is.
- // we should know mDesiredPos value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
- nsISelection* domSel = GetSelection(SelectionType::eNormal);
- if (!domSel) {
- return;
- }
- nsRect caretPos;
- nsIFrame* caretFrame = nsCaret::GetGeometry(domSel, &caretPos);
- if (!caretFrame)
- return;
-
- //need to adjust caret jump by percentage scroll
- nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount();
- if (aForward)
- caretPos.y += scrollDelta.height;
- else
- caretPos.y -= scrollDelta.height;
- caretPos += caretFrame->GetOffsetTo(scrolledFrame);
-
- // get a content at desired location
- nsPoint desiredPoint;
- desiredPoint.x = caretPos.x;
- desiredPoint.y = caretPos.y + caretPos.height/2;
- nsIFrame::ContentOffsets offsets =
- scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
- if (!offsets.content)
- return;
- // scroll one page
- aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
- nsIScrollableFrame::PAGES,
- nsIScrollableFrame::SMOOTH);
- // place the caret
- HandleClick(offsets.content, offsets.offset,
- offsets.offset, aExtend, false, CARET_ASSOCIATE_AFTER);
- }
- nsresult
- nsFrameSelection::PhysicalMove(int16_t aDirection, int16_t aAmount,
- bool aExtend)
- {
- NS_ENSURE_STATE(mShell);
- // Flush out layout, since we need it to be up to date to do caret
- // positioning.
- mShell->FlushPendingNotifications(Flush_Layout);
- if (!mShell) {
- return NS_OK;
- }
- // Check that parameters are safe
- if (aDirection < 0 || aDirection > 3 || aAmount < 0 || aAmount > 1) {
- return NS_ERROR_FAILURE;
- }
- nsPresContext *context = mShell->GetPresContext();
- if (!context) {
- return NS_ERROR_FAILURE;
- }
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- RefPtr<Selection> sel = mDomSelections[index];
- if (!sel) {
- return NS_ERROR_NULL_POINTER;
- }
- // Map the abstract movement amounts (0-1) to direction-specific
- // selection units.
- static const nsSelectionAmount inlineAmount[] =
- { eSelectCluster, eSelectWord };
- static const nsSelectionAmount blockPrevAmount[] =
- { eSelectLine, eSelectBeginLine };
- static const nsSelectionAmount blockNextAmount[] =
- { eSelectLine, eSelectEndLine };
- struct PhysicalToLogicalMapping {
- nsDirection direction;
- const nsSelectionAmount *amounts;
- };
- static const PhysicalToLogicalMapping verticalLR[4] = {
- { eDirPrevious, blockPrevAmount }, // left
- { eDirNext, blockNextAmount }, // right
- { eDirPrevious, inlineAmount }, // up
- { eDirNext, inlineAmount } // down
- };
- static const PhysicalToLogicalMapping verticalRL[4] = {
- { eDirNext, blockNextAmount },
- { eDirPrevious, blockPrevAmount },
- { eDirPrevious, inlineAmount },
- { eDirNext, inlineAmount }
- };
- static const PhysicalToLogicalMapping horizontal[4] = {
- { eDirPrevious, inlineAmount },
- { eDirNext, inlineAmount },
- { eDirPrevious, blockPrevAmount },
- { eDirNext, blockNextAmount }
- };
- WritingMode wm;
- nsIFrame *frame = nullptr;
- int32_t offsetused = 0;
- if (NS_SUCCEEDED(sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
- true))) {
- if (frame) {
- if (!frame->StyleContext()->IsTextCombined()) {
- wm = frame->GetWritingMode();
- } else {
- // Using different direction for horizontal-in-vertical would
- // make it hard to navigate via keyboard. Inherit the moving
- // direction from its parent.
- MOZ_ASSERT(frame->GetType() == nsGkAtoms::textFrame);
- wm = frame->GetParent()->GetWritingMode();
- MOZ_ASSERT(wm.IsVertical(), "Text combined "
- "can only appear in vertical text");
- }
- }
- }
- const PhysicalToLogicalMapping& mapping =
- wm.IsVertical()
- ? wm.IsVerticalLR() ? verticalLR[aDirection] : verticalRL[aDirection]
- : horizontal[aDirection];
- nsresult rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount],
- eVisual);
- if (NS_FAILED(rv)) {
- // If we tried to do a line move, but couldn't move in the given direction,
- // then we'll "promote" this to a line-edge move instead.
- if (mapping.amounts[aAmount] == eSelectLine) {
- rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount + 1],
- eVisual);
- }
- // And if it was a next-word move that failed (which can happen when
- // eat_space_to_next_word is true, see bug 1153237), then just move forward
- // to the line-edge.
- else if (mapping.amounts[aAmount] == eSelectWord &&
- mapping.direction == eDirNext) {
- rv = MoveCaret(eDirNext, aExtend, eSelectEndLine, eVisual);
- }
- }
- return rv;
- }
- nsresult
- nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
- {
- return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectCluster,
- eUsePrefStyle);
- }
- nsresult
- nsFrameSelection::CharacterExtendForDelete()
- {
- return MoveCaret(eDirNext, true, eSelectCluster, eLogical);
- }
- nsresult
- nsFrameSelection::CharacterExtendForBackspace()
- {
- return MoveCaret(eDirPrevious, true, eSelectCharacter, eLogical);
- }
- nsresult
- nsFrameSelection::WordMove(bool aForward, bool aExtend)
- {
- return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectWord,
- eUsePrefStyle);
- }
- nsresult
- nsFrameSelection::WordExtendForDelete(bool aForward)
- {
- return MoveCaret(aForward ? eDirNext : eDirPrevious, true, eSelectWord,
- eLogical);
- }
- nsresult
- nsFrameSelection::LineMove(bool aForward, bool aExtend)
- {
- return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectLine,
- eUsePrefStyle);
- }
- nsresult
- nsFrameSelection::IntraLineMove(bool aForward, bool aExtend)
- {
- if (aForward) {
- return MoveCaret(eDirNext, aExtend, eSelectEndLine, eLogical);
- } else {
- return MoveCaret(eDirPrevious, aExtend, eSelectBeginLine, eLogical);
- }
- }
- nsresult
- nsFrameSelection::SelectAll()
- {
- nsCOMPtr<nsIContent> rootContent;
- if (mLimiter)
- {
- rootContent = mLimiter;//addrefit
- }
- else if (mAncestorLimiter) {
- rootContent = mAncestorLimiter;
- }
- else
- {
- NS_ENSURE_STATE(mShell);
- nsIDocument *doc = mShell->GetDocument();
- if (!doc)
- return NS_ERROR_FAILURE;
- rootContent = doc->GetRootElement();
- if (!rootContent)
- return NS_ERROR_FAILURE;
- }
- int32_t numChildren = rootContent->GetChildCount();
- PostReason(nsISelectionListener::NO_REASON);
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- AutoPrepareFocusRange prep(mDomSelections[index], false, false);
- return TakeFocus(rootContent, 0, numChildren, CARET_ASSOCIATE_BEFORE, false, false);
- }
- //////////END FRAMESELECTION
- void
- nsFrameSelection::StartBatchChanges()
- {
- mBatching++;
- }
- void
- nsFrameSelection::EndBatchChanges(int16_t aReason)
- {
- mBatching--;
- NS_ASSERTION(mBatching >=0,"Bad mBatching");
- if (mBatching == 0 && mChangesDuringBatching) {
- int16_t postReason = PopReason() | aReason;
- PostReason(postReason);
- mChangesDuringBatching = false;
- NotifySelectionListeners(SelectionType::eNormal);
- }
- }
- nsresult
- nsFrameSelection::NotifySelectionListeners(SelectionType aSelectionType)
- {
- int8_t index = GetIndexFromSelectionType(aSelectionType);
- if (index >=0 && mDomSelections[index])
- {
- return mDomSelections[index]->NotifySelectionListeners();
- }
- return NS_ERROR_FAILURE;
- }
- // Start of Table Selection methods
- static bool IsCell(nsIContent *aContent)
- {
- return aContent->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
- }
- nsITableCellLayout*
- nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
- {
- NS_ENSURE_TRUE(mShell, nullptr);
- nsITableCellLayout *cellLayoutObject =
- do_QueryFrame(aCellContent->GetPrimaryFrame());
- return cellLayoutObject;
- }
- nsresult
- nsFrameSelection::ClearNormalSelection()
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- return mDomSelections[index]->RemoveAllRanges();
- }
- static nsIContent*
- GetFirstSelectedContent(nsRange* aRange)
- {
- if (!aRange) {
- return nullptr;
- }
- NS_PRECONDITION(aRange->GetStartParent(), "Must have start parent!");
- NS_PRECONDITION(aRange->GetStartParent()->IsElement(),
- "Unexpected parent");
- return aRange->GetStartParent()->GetChildAt(aRange->StartOffset());
- }
- // Table selection support.
- // TODO: Separate table methods into a separate nsITableSelection interface
- nsresult
- nsFrameSelection::HandleTableSelection(nsINode* aParentContent,
- int32_t aContentOffset,
- int32_t aTarget,
- WidgetMouseEvent* aMouseEvent)
- {
- NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
- if (mDragState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
- {
- // We were selecting cells and user drags mouse in table border or inbetween cells,
- // just do nothing
- return NS_OK;
- }
- nsresult result = NS_OK;
- nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
- // When doing table selection, always set the direction to next so
- // we can be sure that anchorNode's offset always points to the
- // selected cell
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- mDomSelections[index]->SetDirection(eDirNext);
- // Stack-class to wrap all table selection changes in
- // BeginBatchChanges() / EndBatchChanges()
- SelectionBatcher selectionBatcher(mDomSelections[index]);
- int32_t startRowIndex, startColIndex, curRowIndex, curColIndex;
- if (mDragState && mDragSelectingCells)
- {
- // We are drag-selecting
- if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
- {
- // If dragging in the same cell as last event, do nothing
- if (mEndSelectedCell == childContent)
- return NS_OK;
- #ifdef DEBUG_TABLE_SELECTION
- printf(" mStartSelectedCell = %p, mEndSelectedCell = %p, childContent = %p \n",
- mStartSelectedCell.get(), mEndSelectedCell.get(), childContent);
- #endif
- // aTarget can be any "cell mode",
- // so we can easily drag-select rows and columns
- // Once we are in row or column mode,
- // we can drift into any cell to stay in that mode
- // even if aTarget = TABLESELECTION_CELL
- if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
- mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
- {
- if (mEndSelectedCell)
- {
- // Also check if cell is in same row/col
- result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
- if (NS_FAILED(result)) return result;
- result = GetCellIndexes(childContent, curRowIndex, curColIndex);
- if (NS_FAILED(result)) return result;
-
- #ifdef DEBUG_TABLE_SELECTION
- printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
- #endif
- if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
- (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex))
- return NS_OK;
- }
- #ifdef DEBUG_TABLE_SELECTION
- printf(" Dragged into a new column or row\n");
- #endif
- // Continue dragging row or column selection
- return SelectRowOrColumn(childContent, mSelectingTableCellMode);
- }
- else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Dragged into a new cell\n");
- #endif
- // Trick for quick selection of rows and columns
- // Hold down shift, then start selecting in one direction
- // If next cell dragged into is in same row, select entire row,
- // if next cell is in same column, select entire column
- if (mStartSelectedCell && aMouseEvent->IsShift())
- {
- result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
- if (NS_FAILED(result)) return result;
- result = GetCellIndexes(childContent, curRowIndex, curColIndex);
- if (NS_FAILED(result)) return result;
-
- if (startRowIndex == curRowIndex ||
- startColIndex == curColIndex)
- {
- // Force new selection block
- mStartSelectedCell = nullptr;
- mDomSelections[index]->RemoveAllRanges();
- if (startRowIndex == curRowIndex)
- mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
- else
- mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
- return SelectRowOrColumn(childContent, mSelectingTableCellMode);
- }
- }
-
- // Reselect block of cells to new end location
- return SelectBlockOfCells(mStartSelectedCell, childContent);
- }
- }
- // Do nothing if dragging in table, but outside a cell
- return NS_OK;
- }
- else
- {
- // Not dragging -- mouse event is down or up
- if (mDragState)
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Mouse down event\n");
- #endif
- // Clear cell we stored in mouse-down
- mUnselectCellOnMouseUp = nullptr;
-
- if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
- {
- bool isSelected = false;
- // Check if we have other selected cells
- nsIContent* previousCellNode =
- GetFirstSelectedContent(GetFirstCellRange());
- if (previousCellNode)
- {
- // We have at least 1 other selected cell
- // Check if new cell is already selected
- nsIFrame *cellFrame = childContent->GetPrimaryFrame();
- if (!cellFrame) return NS_ERROR_NULL_POINTER;
- isSelected = cellFrame->IsSelected();
- }
- else
- {
- // No cells selected -- remove non-cell selection
- mDomSelections[index]->RemoveAllRanges();
- }
- mDragSelectingCells = true; // Signal to start drag-cell-selection
- mSelectingTableCellMode = aTarget;
- // Set start for new drag-selection block (not appended)
- mStartSelectedCell = childContent;
- // The initial block end is same as the start
- mEndSelectedCell = childContent;
-
- if (isSelected)
- {
- // Remember this cell to (possibly) unselect it on mouseup
- mUnselectCellOnMouseUp = childContent;
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
- #endif
- }
- else
- {
- // Select an unselected cell
- // but first remove existing selection if not in same table
- if (previousCellNode &&
- !IsInSameTable(previousCellNode, childContent))
- {
- mDomSelections[index]->RemoveAllRanges();
- // Reset selection mode that is cleared in RemoveAllRanges
- mSelectingTableCellMode = aTarget;
- }
- return SelectCellElement(childContent);
- }
- return NS_OK;
- }
- else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
- {
- //TODO: We currently select entire table when clicked between cells,
- // should we restrict to only around border?
- // *** How do we get location data for cell and click?
- mDragSelectingCells = false;
- mStartSelectedCell = nullptr;
- mEndSelectedCell = nullptr;
- // Remove existing selection and select the table
- mDomSelections[index]->RemoveAllRanges();
- return CreateAndAddRange(aParentContent, aContentOffset);
- }
- else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf("aTarget == %d\n", aTarget);
- #endif
- // Start drag-selecting mode so multiple rows/cols can be selected
- // Note: Currently, nsFrame::GetDataForTableSelection
- // will never call us for row or column selection on mouse down
- mDragSelectingCells = true;
-
- // Force new selection block
- mStartSelectedCell = nullptr;
- mDomSelections[index]->RemoveAllRanges();
- // Always do this AFTER RemoveAllRanges
- mSelectingTableCellMode = aTarget;
- return SelectRowOrColumn(childContent, aTarget);
- }
- }
- else
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%p\n",
- mDragSelectingCells, mStartSelectedCell.get());
- #endif
- // First check if we are extending a block selection
- int32_t rangeCount;
- result = mDomSelections[index]->GetRangeCount(&rangeCount);
- if (NS_FAILED(result))
- return result;
- if (rangeCount > 0 && aMouseEvent->IsShift() &&
- mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
- {
- // Shift key is down: append a block selection
- mDragSelectingCells = false;
- return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
- }
- if (mDragSelectingCells)
- mAppendStartSelectedCell = mStartSelectedCell;
-
- mDragSelectingCells = false;
- mStartSelectedCell = nullptr;
- mEndSelectedCell = nullptr;
- // Any other mouseup actions require that Ctrl or Cmd key is pressed
- // else stop table selection mode
- bool doMouseUpAction = false;
- doMouseUpAction = aMouseEvent->IsControl();
- if (!doMouseUpAction)
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%p\n",
- mAppendStartSelectedCell.get());
- #endif
- return NS_OK;
- }
- // Unselect a cell only if it wasn't
- // just selected on mousedown
- if( childContent == mUnselectCellOnMouseUp)
- {
- // Scan ranges to find the cell to unselect (the selection range to remove)
- // XXXbz it's really weird that this lives outside the loop, so once we
- // find one we keep looking at it even if we find no more cells...
- nsINode* previousCellParent = nullptr;
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
- #endif
- for( int32_t i = 0; i < rangeCount; i++)
- {
- // Strong reference, because sometimes we want to remove
- // this range, and then we might be the only owner.
- RefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i);
- if (!range) return NS_ERROR_NULL_POINTER;
- nsINode* parent = range->GetStartParent();
- if (!parent) return NS_ERROR_NULL_POINTER;
- int32_t offset = range->StartOffset();
- // Be sure previous selection is a table cell
- nsIContent* child = parent->GetChildAt(offset);
- if (child && IsCell(child))
- previousCellParent = parent;
- // We're done if we didn't find parent of a previously-selected cell
- if (!previousCellParent) break;
-
- if (previousCellParent == aParentContent && offset == aContentOffset)
- {
- // Cell is already selected
- if (rangeCount == 1)
- {
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Unselecting single selected cell\n");
- #endif
- // This was the only cell selected.
- // Collapse to "normal" selection inside the cell
- mStartSelectedCell = nullptr;
- mEndSelectedCell = nullptr;
- mAppendStartSelectedCell = nullptr;
- //TODO: We need a "Collapse to just before deepest child" routine
- // Even better, should we collapse to just after the LAST deepest child
- // (i.e., at the end of the cell's contents)?
- return mDomSelections[index]->Collapse(childContent, 0);
- }
- #ifdef DEBUG_TABLE_SELECTION
- printf("HandleTableSelection: Removing cell from multi-cell selection\n");
- #endif
- // Unselecting the start of previous block
- // XXX What do we use now!
- if (childContent == mAppendStartSelectedCell)
- mAppendStartSelectedCell = nullptr;
- // Deselect cell by removing its range from selection
- return mDomSelections[index]->RemoveRange(range);
- }
- }
- mUnselectCellOnMouseUp = nullptr;
- }
- }
- }
- return result;
- }
- nsresult
- nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
- {
- NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
- mEndSelectedCell = aEndCell;
- nsresult result = NS_OK;
- // If new end cell is in a different table, do nothing
- nsIContent* table = IsInSameTable(aStartCell, aEndCell);
- if (!table) {
- return NS_OK;
- }
- // Get starting and ending cells' location in the cellmap
- int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
- result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
- if(NS_FAILED(result)) return result;
- result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
- if(NS_FAILED(result)) return result;
- if (mDragSelectingCells)
- {
- // Drag selecting: remove selected cells outside of new block limits
- UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
- true);
- }
- // Note that we select block in the direction of user's mouse dragging,
- // which means start cell may be after the end cell in either row or column
- return AddCellsToSelection(table, startRowIndex, startColIndex,
- endRowIndex, endColIndex);
- }
- nsresult
- nsFrameSelection::UnselectCells(nsIContent *aTableContent,
- int32_t aStartRowIndex,
- int32_t aStartColumnIndex,
- int32_t aEndRowIndex,
- int32_t aEndColumnIndex,
- bool aRemoveOutsideOfCellRange)
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
- if (!tableFrame)
- return NS_ERROR_FAILURE;
- int32_t minRowIndex = std::min(aStartRowIndex, aEndRowIndex);
- int32_t maxRowIndex = std::max(aStartRowIndex, aEndRowIndex);
- int32_t minColIndex = std::min(aStartColumnIndex, aEndColumnIndex);
- int32_t maxColIndex = std::max(aStartColumnIndex, aEndColumnIndex);
- // Strong reference because we sometimes remove the range
- RefPtr<nsRange> range = GetFirstCellRange();
- nsIContent* cellNode = GetFirstSelectedContent(range);
- NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
- int32_t curRowIndex, curColIndex;
- while (cellNode)
- {
- nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
- if (NS_FAILED(result))
- return result;
- #ifdef DEBUG_TABLE_SELECTION
- if (!range)
- printf("RemoveCellsToSelection -- range is null\n");
- #endif
- if (range) {
- if (aRemoveOutsideOfCellRange) {
- if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
- curColIndex < minColIndex || curColIndex > maxColIndex) {
- mDomSelections[index]->RemoveRange(range);
- // Since we've removed the range, decrement pointer to next range
- mSelectedCellIndex--;
- }
- } else {
- // Remove cell from selection if it belongs to the given cells range or
- // it is spanned onto the cells range.
- nsTableCellFrame* cellFrame =
- tableFrame->GetCellFrameAt(curRowIndex, curColIndex);
- uint32_t origRowIndex = cellFrame->RowIndex();
- uint32_t origColIndex = cellFrame->ColIndex();
- uint32_t actualRowSpan =
- tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
- uint32_t actualColSpan =
- tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
- if (origRowIndex <= static_cast<uint32_t>(maxRowIndex) && maxRowIndex >= 0 &&
- origRowIndex + actualRowSpan - 1 >= static_cast<uint32_t>(minRowIndex) &&
- origColIndex <= static_cast<uint32_t>(maxColIndex) && maxColIndex >= 0 &&
- origColIndex + actualColSpan - 1 >= static_cast<uint32_t>(minColIndex)) {
- mDomSelections[index]->RemoveRange(range);
- // Since we've removed the range, decrement pointer to next range
- mSelectedCellIndex--;
- }
- }
- }
- range = GetNextCellRange();
- cellNode = GetFirstSelectedContent(range);
- NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
- }
- return NS_OK;
- }
- nsresult
- nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
- int32_t aStartRowIndex,
- int32_t aStartColumnIndex,
- int32_t aEndRowIndex,
- int32_t aEndColumnIndex)
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
- if (!tableFrame) // Check that |table| is a table.
- return NS_ERROR_FAILURE;
- nsresult result = NS_OK;
- uint32_t row = aStartRowIndex;
- while(true)
- {
- uint32_t col = aStartColumnIndex;
- while(true)
- {
- nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col);
- // Skip cells that are spanned from previous locations or are already selected
- if (cellFrame) {
- uint32_t origRow = cellFrame->RowIndex();
- uint32_t origCol = cellFrame->ColIndex();
- if (origRow == row && origCol == col && !cellFrame->IsSelected()) {
- result = SelectCellElement(cellFrame->GetContent());
- if (NS_FAILED(result)) return result;
- }
- }
- // Done when we reach end column
- if (col == static_cast<uint32_t>(aEndColumnIndex)) break;
- if (aStartColumnIndex < aEndColumnIndex)
- col ++;
- else
- col--;
- }
- if (row == static_cast<uint32_t>(aEndRowIndex)) break;
- if (aStartRowIndex < aEndRowIndex)
- row++;
- else
- row--;
- }
- return result;
- }
- nsresult
- nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
- int32_t aStartRowIndex,
- int32_t aStartColumnIndex,
- int32_t aEndRowIndex,
- int32_t aEndColumnIndex)
- {
- return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
- aEndRowIndex, aEndColumnIndex, false);
- }
- nsresult
- nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
- int32_t aStartRowIndex,
- int32_t aStartColumnIndex,
- int32_t aEndRowIndex,
- int32_t aEndColumnIndex)
- {
- return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
- aEndRowIndex, aEndColumnIndex, true);
- }
- nsresult
- nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, uint32_t aTarget)
- {
- if (!aCellContent) return NS_ERROR_NULL_POINTER;
- nsIContent* table = GetParentTable(aCellContent);
- if (!table) return NS_ERROR_NULL_POINTER;
- // Get table and cell layout interfaces to access
- // cell data based on cellmap location
- // Frames are not ref counted, so don't use an nsCOMPtr
- nsTableWrapperFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame());
- if (!tableFrame) return NS_ERROR_FAILURE;
- nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
- if (!cellLayout) return NS_ERROR_FAILURE;
- // Get location of target cell:
- int32_t rowIndex, colIndex;
- nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
- if (NS_FAILED(result)) return result;
- // Be sure we start at proper beginning
- // (This allows us to select row or col given ANY cell!)
- if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
- colIndex = 0;
- if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
- rowIndex = 0;
- nsCOMPtr<nsIContent> firstCell, lastCell;
- while (true) {
- // Loop through all cells in column or row to find first and last
- nsCOMPtr<nsIContent> curCellContent =
- tableFrame->GetCellAt(rowIndex, colIndex);
- if (!curCellContent)
- break;
- if (!firstCell)
- firstCell = curCellContent;
- lastCell = curCellContent.forget();
- // Move to next cell in cellmap, skipping spanned locations
- if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
- colIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
- else
- rowIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
- }
- // Use SelectBlockOfCells:
- // This will replace existing selection,
- // but allow unselecting by dragging out of selected region
- if (firstCell && lastCell)
- {
- if (!mStartSelectedCell)
- {
- // We are starting a new block, so select the first cell
- result = SelectCellElement(firstCell);
- if (NS_FAILED(result)) return result;
- mStartSelectedCell = firstCell;
- }
- nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
- result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
- // This gets set to the cell at end of row/col,
- // but we need it to be the cell under cursor
- mEndSelectedCell = aCellContent;
- return result;
- }
- #if 0
- // This is a more efficient strategy that appends row to current selection,
- // but doesn't allow dragging OFF of an existing selection to unselect!
- do {
- // Loop through all cells in column or row
- result = tableLayout->GetCellDataAt(rowIndex, colIndex,
- getter_AddRefs(cellElement),
- curRowIndex, curColIndex,
- rowSpan, colSpan,
- actualRowSpan, actualColSpan,
- isSelected);
- if (NS_FAILED(result)) return result;
- // We're done when cell is not found
- if (!cellElement) break;
- // Check spans else we infinitely loop
- NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
- NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
-
- // Skip cells that are already selected or span from outside our region
- if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
- {
- result = SelectCellElement(cellElement);
- if (NS_FAILED(result)) return result;
- }
- // Move to next row or column in cellmap, skipping spanned locations
- if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
- colIndex += actualColSpan;
- else
- rowIndex += actualRowSpan;
- }
- while (cellElement);
- #endif
- return NS_OK;
- }
- nsIContent*
- nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const
- {
- if (!aRange) return nullptr;
- nsINode* startParent = aRange->GetStartParent();
- if (!startParent)
- return nullptr;
- int32_t offset = aRange->StartOffset();
- nsIContent* childContent = startParent->GetChildAt(offset);
- if (!childContent)
- return nullptr;
- // Don't return node if not a cell
- if (!IsCell(childContent))
- return nullptr;
- return childContent;
- }
- nsRange*
- nsFrameSelection::GetFirstCellRange()
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return nullptr;
- nsRange* firstRange = mDomSelections[index]->GetRangeAt(0);
- if (!GetFirstCellNodeInRange(firstRange)) {
- return nullptr;
- }
- // Setup for next cell
- mSelectedCellIndex = 1;
- return firstRange;
- }
- nsRange*
- nsFrameSelection::GetNextCellRange()
- {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return nullptr;
- nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex);
- // Get first node in next range of selection - test if it's a cell
- if (!GetFirstCellNodeInRange(range)) {
- return nullptr;
- }
- // Setup for next cell
- mSelectedCellIndex++;
- return range;
- }
- nsresult
- nsFrameSelection::GetCellIndexes(nsIContent *aCell,
- int32_t &aRowIndex,
- int32_t &aColIndex)
- {
- if (!aCell) return NS_ERROR_NULL_POINTER;
- aColIndex=0; // initialize out params
- aRowIndex=0;
- nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
- if (!cellLayoutObject) return NS_ERROR_FAILURE;
- return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
- }
- nsIContent*
- nsFrameSelection::IsInSameTable(nsIContent *aContent1,
- nsIContent *aContent2) const
- {
- if (!aContent1 || !aContent2) return nullptr;
-
- nsIContent* tableNode1 = GetParentTable(aContent1);
- nsIContent* tableNode2 = GetParentTable(aContent2);
- // Must be in the same table. Note that we want to return false for
- // the test if both tables are null.
- return (tableNode1 == tableNode2) ? tableNode1 : nullptr;
- }
- nsIContent*
- nsFrameSelection::GetParentTable(nsIContent *aCell) const
- {
- if (!aCell) {
- return nullptr;
- }
- for (nsIContent* parent = aCell->GetParent(); parent;
- parent = parent->GetParent()) {
- if (parent->IsHTMLElement(nsGkAtoms::table)) {
- return parent;
- }
- }
- return nullptr;
- }
- nsresult
- nsFrameSelection::SelectCellElement(nsIContent *aCellElement)
- {
- nsIContent *parent = aCellElement->GetParent();
- // Get child offset
- int32_t offset = parent->IndexOf(aCellElement);
- return CreateAndAddRange(parent, offset);
- }
- nsresult
- Selection::getTableCellLocationFromRange(nsRange* aRange,
- int32_t* aSelectionType,
- int32_t* aRow, int32_t* aCol)
- {
- if (!aRange || !aSelectionType || !aRow || !aCol)
- return NS_ERROR_NULL_POINTER;
- *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
- *aRow = 0;
- *aCol = 0;
- // Must have access to frame selection to get cell info
- if (!mFrameSelection) return NS_OK;
- nsresult result = GetTableSelectionType(aRange, aSelectionType);
- if (NS_FAILED(result)) return result;
-
- // Don't fail if range does not point to a single table cell,
- // let aSelectionType tell user if we don't have a cell
- if (*aSelectionType != nsISelectionPrivate::TABLESELECTION_CELL)
- return NS_OK;
- // Get the child content (the cell) pointed to by starting node of range
- // We do minimal checking since GetTableSelectionType assures
- // us that this really is a table cell
- nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
- if (!content)
- return NS_ERROR_FAILURE;
- nsIContent *child = content->GetChildAt(aRange->StartOffset());
- if (!child)
- return NS_ERROR_FAILURE;
- //Note: This is a non-ref-counted pointer to the frame
- nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
- if (NS_FAILED(result))
- return result;
- if (!cellLayout)
- return NS_ERROR_FAILURE;
- return cellLayout->GetCellIndexes(*aRow, *aCol);
- }
- nsresult
- Selection::addTableCellRange(nsRange* aRange, bool* aDidAddRange,
- int32_t* aOutIndex)
- {
- if (!aDidAddRange || !aOutIndex)
- return NS_ERROR_NULL_POINTER;
- *aDidAddRange = false;
- *aOutIndex = -1;
- if (!mFrameSelection)
- return NS_OK;
- if (!aRange)
- return NS_ERROR_NULL_POINTER;
- nsresult result;
- // Get if we are adding a cell selection and the row, col of cell if we are
- int32_t newRow, newCol, tableMode;
- result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
- if (NS_FAILED(result)) return result;
-
- // If not adding a cell range, we are done here
- if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
- {
- mFrameSelection->mSelectingTableCellMode = tableMode;
- // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
- return NS_OK;
- }
-
- // Set frame selection mode only if not already set to a table mode
- // so we don't lose the select row and column flags (not detected by getTableCellLocation)
- if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
- mFrameSelection->mSelectingTableCellMode = tableMode;
- *aDidAddRange = true;
- return AddItem(aRange, aOutIndex);
- }
- //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
- nsresult
- Selection::GetTableSelectionType(nsIDOMRange* aDOMRange,
- int32_t* aTableSelectionType)
- {
- if (!aDOMRange || !aTableSelectionType)
- return NS_ERROR_NULL_POINTER;
- nsRange* range = static_cast<nsRange*>(aDOMRange);
-
- *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
-
- // Must have access to frame selection to get cell info
- if(!mFrameSelection) return NS_OK;
- nsINode* startNode = range->GetStartParent();
- if (!startNode) return NS_ERROR_FAILURE;
-
- nsINode* endNode = range->GetEndParent();
- if (!endNode) return NS_ERROR_FAILURE;
- // Not a single selected node
- if (startNode != endNode) return NS_OK;
- int32_t startOffset = range->StartOffset();
- int32_t endOffset = range->EndOffset();
- // Not a single selected node
- if ((endOffset - startOffset) != 1)
- return NS_OK;
- nsIContent* startContent = static_cast<nsIContent*>(startNode);
- if (!(startNode->IsElement() && startContent->IsHTMLElement())) {
- // Implies a check for being an element; if we ever make this work
- // for non-HTML, need to keep checking for elements.
- return NS_OK;
- }
- if (startContent->IsHTMLElement(nsGkAtoms::tr))
- {
- *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
- }
- else //check to see if we are selecting a table or row (column and all cells not done yet)
- {
- nsIContent *child = startNode->GetChildAt(startOffset);
- if (!child)
- return NS_ERROR_FAILURE;
- if (child->IsHTMLElement(nsGkAtoms::table))
- *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
- else if (child->IsHTMLElement(nsGkAtoms::tr))
- *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
- }
- return NS_OK;
- }
- nsresult
- nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, int32_t aOffset)
- {
- if (!aParentNode) return NS_ERROR_NULL_POINTER;
- RefPtr<nsRange> range = new nsRange(aParentNode);
- // Set range around child at given offset
- nsresult rv = range->SetStartAndEnd(aParentNode, aOffset,
- aParentNode, aOffset + 1);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- return mDomSelections[index]->AddRange(range);
- }
- // End of Table Selection
- void
- nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
- {
- if (mAncestorLimiter != aLimiter) {
- mAncestorLimiter = aLimiter;
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return;
- if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
- ClearNormalSelection();
- if (mAncestorLimiter) {
- PostReason(nsISelectionListener::NO_REASON);
- TakeFocus(mAncestorLimiter, 0, 0, CARET_ASSOCIATE_BEFORE, false, false);
- }
- }
- }
- }
- //END nsFrameSelection methods
- //BEGIN nsISelection interface implementations
- nsresult
- nsFrameSelection::DeleteFromDocument()
- {
- nsresult res;
- // If we're already collapsed, then we do nothing (bug 719503).
- bool isCollapsed;
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- if (!mDomSelections[index])
- return NS_ERROR_NULL_POINTER;
- mDomSelections[index]->GetIsCollapsed( &isCollapsed);
- if (isCollapsed)
- {
- return NS_OK;
- }
- RefPtr<Selection> selection = mDomSelections[index];
- for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
- RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
- res = range->DeleteContents();
- if (NS_FAILED(res))
- return res;
- }
- // Collapse to the new location.
- // If we deleted one character, then we move back one element.
- // FIXME We don't know how to do this past frame boundaries yet.
- if (isCollapsed)
- mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset()-1);
- else if (mDomSelections[index]->AnchorOffset() > 0)
- mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset());
- #ifdef DEBUG
- else
- printf("Don't know how to set selection back past frame boundary\n");
- #endif
- return NS_OK;
- }
- void
- nsFrameSelection::SetDelayedCaretData(WidgetMouseEvent* aMouseEvent)
- {
- if (aMouseEvent) {
- mDelayedMouseEventValid = true;
- mDelayedMouseEventIsShift = aMouseEvent->IsShift();
- mDelayedMouseEventClickCount = aMouseEvent->mClickCount;
- } else {
- mDelayedMouseEventValid = false;
- }
- }
- void
- nsFrameSelection::DisconnectFromPresShell()
- {
- RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
- if (eventHub) {
- int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
- mDomSelections[index]->RemoveSelectionListener(eventHub);
- }
- StopAutoScrollTimer();
- for (size_t i = 0; i < kPresentSelectionTypeCount; i++) {
- mDomSelections[i]->Clear(nullptr);
- }
- mShell = nullptr;
- }
- //END nsISelection interface implementations
- #if 0
- #pragma mark -
- #endif
- // mozilla::dom::Selection implementation
- // note: this can return a nil anchor node
- Selection::Selection()
- : mCachedOffsetForFrame(nullptr)
- , mDirection(eDirNext)
- , mSelectionType(SelectionType::eNormal)
- , mUserInitiated(false)
- , mSelectionChangeBlockerCount(0)
- {
- }
- Selection::Selection(nsFrameSelection* aList)
- : mFrameSelection(aList)
- , mCachedOffsetForFrame(nullptr)
- , mDirection(eDirNext)
- , mSelectionType(SelectionType::eNormal)
- , mUserInitiated(false)
- , mSelectionChangeBlockerCount(0)
- {
- }
- Selection::~Selection()
- {
- setAnchorFocusRange(-1);
- uint32_t count = mRanges.Length();
- for (uint32_t i = 0; i < count; ++i) {
- mRanges[i].mRange->SetSelection(nullptr);
- }
- if (mAutoScrollTimer) {
- mAutoScrollTimer->Stop();
- mAutoScrollTimer = nullptr;
- }
- mScrollEvent.Revoke();
- if (mCachedOffsetForFrame) {
- delete mCachedOffsetForFrame;
- mCachedOffsetForFrame = nullptr;
- }
- }
- nsIDocument*
- Selection::GetParentObject() const
- {
- nsIPresShell* shell = GetPresShell();
- if (shell) {
- return shell->GetDocument();
- }
- return nullptr;
- }
- DocGroup*
- Selection::GetDocGroup() const
- {
- nsIPresShell* shell = GetPresShell();
- if (!shell) {
- return nullptr;
- }
- nsIDocument* doc = shell->GetDocument();
- return doc ? doc->GetDocGroup() : nullptr;
- }
- NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
- // Unlink the selection listeners *before* we do RemoveAllRanges since
- // we don't want to notify the listeners during JS GC (they could be
- // in JS!).
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
- tmp->RemoveAllRanges();
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
- {
- uint32_t i, count = tmp->mRanges.Length();
- for (i = 0; i < count; ++i) {
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange)
- }
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection)
- // QueryInterface implementation for Selection
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISelection)
- NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
- NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
- NS_IMETHODIMP
- Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
- {
- nsINode* anchorNode = GetAnchorNode();
- if (anchorNode) {
- return CallQueryInterface(anchorNode, aAnchorNode);
- }
- *aAnchorNode = nullptr;
- return NS_OK;
- }
- nsINode*
- Selection::GetAnchorNode()
- {
- if (!mAnchorFocusRange)
- return nullptr;
-
- if (GetDirection() == eDirNext) {
- return mAnchorFocusRange->GetStartParent();
- }
- return mAnchorFocusRange->GetEndParent();
- }
- NS_IMETHODIMP
- Selection::GetAnchorOffset(int32_t* aAnchorOffset)
- {
- *aAnchorOffset = static_cast<int32_t>(AnchorOffset());
- return NS_OK;
- }
- // note: this can return a nil focus node
- NS_IMETHODIMP
- Selection::GetFocusNode(nsIDOMNode** aFocusNode)
- {
- nsINode* focusNode = GetFocusNode();
- if (focusNode) {
- return CallQueryInterface(focusNode, aFocusNode);
- }
- *aFocusNode = nullptr;
- return NS_OK;
- }
- nsINode*
- Selection::GetFocusNode()
- {
- if (!mAnchorFocusRange)
- return nullptr;
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->GetEndParent();
- }
- return mAnchorFocusRange->GetStartParent();
- }
- NS_IMETHODIMP
- Selection::GetFocusOffset(int32_t* aFocusOffset)
- {
- *aFocusOffset = static_cast<int32_t>(FocusOffset());
- return NS_OK;
- }
- void
- Selection::setAnchorFocusRange(int32_t indx)
- {
- if (indx >= (int32_t)mRanges.Length())
- return;
- if (indx < 0) //release all
- {
- mAnchorFocusRange = nullptr;
- }
- else{
- mAnchorFocusRange = mRanges[indx].mRange;
- }
- }
- uint32_t
- Selection::AnchorOffset()
- {
- if (!mAnchorFocusRange)
- return 0;
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->StartOffset();
- }
- return mAnchorFocusRange->EndOffset();
- }
- uint32_t
- Selection::FocusOffset()
- {
- if (!mAnchorFocusRange)
- return 0;
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->EndOffset();
- }
- return mAnchorFocusRange->StartOffset();
- }
- static nsresult
- CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
- nsRange* aRange, int32_t* aCmp)
- {
- nsINode* start = aRange->GetStartParent();
- NS_ENSURE_STATE(aCompareNode && start);
- // If the nodes that we're comparing are not in the same document,
- // assume that aCompareNode will fall at the end of the ranges.
- if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
- !start->GetComposedDoc()) {
- *aCmp = 1;
- } else {
- *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
- start, aRange->StartOffset());
- }
- return NS_OK;
- }
- static nsresult
- CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
- nsRange* aRange, int32_t* aCmp)
- {
- nsINode* end = aRange->GetEndParent();
- NS_ENSURE_STATE(aCompareNode && end);
- // If the nodes that we're comparing are not in the same document,
- // assume that aCompareNode will fall at the end of the ranges.
- if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
- !end->GetComposedDoc()) {
- *aCmp = 1;
- } else {
- *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
- end, aRange->EndOffset());
- }
- return NS_OK;
- }
- // Selection::FindInsertionPoint
- //
- // Binary searches the given sorted array of ranges for the insertion point
- // for the given node/offset. The given comparator is used, and the index
- // where the point should appear in the array is placed in *aInsertionPoint.
- //
- // If there is an item in the array equal to the input point, we will return
- // the index of this item.
- nsresult
- Selection::FindInsertionPoint(
- nsTArray<RangeData>* aElementArray,
- nsINode* aPointNode, int32_t aPointOffset,
- nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
- int32_t* aPoint)
- {
- *aPoint = 0;
- int32_t beginSearch = 0;
- int32_t endSearch = aElementArray->Length(); // one beyond what to check
- if (endSearch) {
- int32_t center = endSearch - 1; // Check last index, then binary search
- do {
- nsRange* range = (*aElementArray)[center].mRange;
- int32_t cmp;
- nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
- NS_ENSURE_SUCCESS(rv, rv);
- if (cmp < 0) { // point < cur
- endSearch = center;
- } else if (cmp > 0) { // point > cur
- beginSearch = center + 1;
- } else { // found match, done
- beginSearch = center;
- break;
- }
- center = (endSearch - beginSearch) / 2 + beginSearch;
- } while (endSearch - beginSearch > 0);
- }
- *aPoint = beginSearch;
- return NS_OK;
- }
- // Selection::SubtractRange
- //
- // A helper function that subtracts aSubtract from aRange, and adds
- // 1 or 2 RangeData objects representing the remaining non-overlapping
- // difference to aOutput. It is assumed that the caller has checked that
- // aRange and aSubtract do indeed overlap
- nsresult
- Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
- nsTArray<RangeData>* aOutput)
- {
- nsRange* range = aRange->mRange;
- // First we want to compare to the range start
- int32_t cmp;
- nsresult rv = CompareToRangeStart(range->GetStartParent(),
- range->StartOffset(),
- aSubtract, &cmp);
- NS_ENSURE_SUCCESS(rv, rv);
- // Also, make a comparison to the range end
- int32_t cmp2;
- rv = CompareToRangeEnd(range->GetEndParent(),
- range->EndOffset(),
- aSubtract, &cmp2);
- NS_ENSURE_SUCCESS(rv, rv);
- // If the existing range left overlaps the new range (aSubtract) then
- // cmp < 0, and cmp2 < 0
- // If it right overlaps the new range then cmp > 0 and cmp2 > 0
- // If it fully contains the new range, then cmp < 0 and cmp2 > 0
- if (cmp2 > 0) {
- // We need to add a new RangeData to the output, running from
- // the end of aSubtract to the end of range
- RefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndParent());
- rv = postOverlap->SetStartAndEnd(
- aSubtract->GetEndParent(), aSubtract->EndOffset(),
- range->GetEndParent(), range->EndOffset());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!postOverlap->Collapsed()) {
- if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
- return NS_ERROR_OUT_OF_MEMORY;
- (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
- }
- }
- if (cmp < 0) {
- // We need to add a new RangeData to the output, running from
- // the start of the range to the start of aSubtract
- RefPtr<nsRange> preOverlap = new nsRange(range->GetStartParent());
- rv = preOverlap->SetStartAndEnd(
- range->GetStartParent(), range->StartOffset(),
- aSubtract->GetStartParent(), aSubtract->StartOffset());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- if (!preOverlap->Collapsed()) {
- if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
- return NS_ERROR_OUT_OF_MEMORY;
- (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
- }
- }
- return NS_OK;
- }
- void
- Selection::UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange>>& aRangesToAdd)
- {
- aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
- if (aRangesToAdd.IsEmpty()) {
- ErrorResult err;
- nsINode* node = aItem->GetStartContainer(err);
- if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
- // A contenteditable node with user-select:none, for example.
- // Allow it to have a collapsed selection (for the caret).
- aItem->Collapse(GetDirection() == eDirPrevious);
- aRangesToAdd.AppendElement(aItem);
- }
- }
- }
- nsresult
- Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
- {
- if (!aItem)
- return NS_ERROR_NULL_POINTER;
- if (!aItem->IsPositioned())
- return NS_ERROR_UNEXPECTED;
- NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
- if (mUserInitiated) {
- AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
- *aOutIndex = int32_t(mRanges.Length()) - 1;
- nsIDocument* doc = GetParentObject();
- bool selectEventsEnabled =
- nsFrameSelection::sSelectionEventsEnabled ||
- (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
- if (!aNoStartSelect &&
- mSelectionType == SelectionType::eNormal &&
- selectEventsEnabled && Collapsed() &&
- !IsBlockingSelectionChangeEvents()) {
- // First, we generate the ranges to add with a scratch range, which is a
- // clone of the original range passed in. We do this seperately, because the
- // selectstart event could have caused the world to change, and required
- // ranges to be re-generated
- RefPtr<nsRange> scratchRange = aItem->CloneRange();
- UserSelectRangesToAdd(scratchRange, rangesToAdd);
- bool newRangesNonEmpty = rangesToAdd.Length() > 1 ||
- (rangesToAdd.Length() == 1 && !rangesToAdd[0]->Collapsed());
- MOZ_ASSERT(!newRangesNonEmpty || nsContentUtils::IsSafeToRunScript());
- if (newRangesNonEmpty && nsContentUtils::IsSafeToRunScript()) {
- // We consider a selection to be starting if we are currently collapsed,
- // and the selection is becoming uncollapsed, and this is caused by a user
- // initiated event.
- bool defaultAction = true;
- // The spec currently doesn't say that we should dispatch this event
- // on text controls, so for now we only support doing that under a
- // pref, disabled by default.
- // See https://github.com/w3c/selection-api/issues/53.
- bool dispatchEvent = true;
- nsCOMPtr<nsINode> target = aItem->GetStartParent();
- if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
- // Get the first element which isn't in a native anonymous subtree
- while (target && target->IsInNativeAnonymousSubtree()) {
- target = target->GetParent();
- }
- } else {
- if (target->IsInNativeAnonymousSubtree()) {
- // This is a selection under a text control, so don't dispatch the
- // event.
- dispatchEvent = false;
- }
- }
- if (dispatchEvent) {
- nsContentUtils::DispatchTrustedEvent(GetParentObject(), target,
- NS_LITERAL_STRING("selectstart"),
- true, true, &defaultAction);
- if (!defaultAction) {
- return NS_OK;
- }
- // As we just dispatched an event to the DOM, something could have
- // changed under our feet. Re-generate the rangesToAdd array, and ensure
- // that the range we are about to add is still valid.
- if (!aItem->IsPositioned()) {
- return NS_ERROR_UNEXPECTED;
- }
- }
- }
- // The scratch ranges we generated may be invalid now, throw them out
- rangesToAdd.ClearAndRetainStorage();
- }
- // Generate the ranges to add
- UserSelectRangesToAdd(aItem, rangesToAdd);
- size_t newAnchorFocusIndex =
- GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
- for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
- int32_t index;
- nsresult rv = AddItemInternal(rangesToAdd[i], &index);
- NS_ENSURE_SUCCESS(rv, rv);
- if (i == newAnchorFocusIndex) {
- *aOutIndex = index;
- rangesToAdd[i]->SetIsGenerated(false);
- } else {
- rangesToAdd[i]->SetIsGenerated(true);
- }
- }
- return NS_OK;
- }
- return AddItemInternal(aItem, aOutIndex);
- }
- nsresult
- Selection::AddItemInternal(nsRange* aItem, int32_t* aOutIndex)
- {
- MOZ_ASSERT(aItem);
- MOZ_ASSERT(aItem->IsPositioned());
- MOZ_ASSERT(aOutIndex);
- *aOutIndex = -1;
- // a common case is that we have no ranges yet
- if (mRanges.Length() == 0) {
- if (!mRanges.AppendElement(RangeData(aItem)))
- return NS_ERROR_OUT_OF_MEMORY;
- aItem->SetSelection(this);
- *aOutIndex = 0;
- return NS_OK;
- }
- int32_t startIndex, endIndex;
- nsresult rv = GetIndicesForInterval(aItem->GetStartParent(),
- aItem->StartOffset(),
- aItem->GetEndParent(),
- aItem->EndOffset(), false,
- &startIndex, &endIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- if (endIndex == -1) {
- // All ranges start after the given range. We can insert our range at
- // position 0, knowing there are no overlaps (handled below)
- startIndex = 0;
- endIndex = 0;
- } else if (startIndex == -1) {
- // All ranges end before the given range. We can insert our range at
- // the end of the array, knowing there are no overlaps (handled below)
- startIndex = mRanges.Length();
- endIndex = startIndex;
- }
- // If the range is already contained in mRanges, silently succeed
- bool sameRange = EqualsRangeAtPoint(aItem->GetStartParent(),
- aItem->StartOffset(),
- aItem->GetEndParent(),
- aItem->EndOffset(), startIndex);
- if (sameRange) {
- *aOutIndex = startIndex;
- return NS_OK;
- }
- if (startIndex == endIndex) {
- // The new range doesn't overlap any existing ranges
- if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
- return NS_ERROR_OUT_OF_MEMORY;
- aItem->SetSelection(this);
- *aOutIndex = startIndex;
- return NS_OK;
- }
- // We now know that at least 1 existing range overlaps with the range that
- // we are trying to add. In fact, the only ranges of interest are those at
- // the two end points, startIndex and endIndex - 1 (which may point to the
- // same range) as these may partially overlap the new range. Any ranges
- // between these indices are fully overlapped by the new range, and so can be
- // removed
- nsTArray<RangeData> overlaps;
- if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
- return NS_ERROR_OUT_OF_MEMORY;
- if (endIndex - 1 != startIndex) {
- if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // Remove all the overlapping ranges
- for (int32_t i = startIndex; i < endIndex; ++i) {
- mRanges[i].mRange->SetSelection(nullptr);
- }
- mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
- nsTArray<RangeData> temp;
- for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
- nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Insert the new element into our "leftovers" array
- int32_t insertionPoint;
- rv = FindInsertionPoint(&temp, aItem->GetStartParent(),
- aItem->StartOffset(), CompareToRangeStart,
- &insertionPoint);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
- return NS_ERROR_OUT_OF_MEMORY;
- // Merge the leftovers back in to mRanges
- if (!mRanges.InsertElementsAt(startIndex, temp))
- return NS_ERROR_OUT_OF_MEMORY;
- for (uint32_t i = 0; i < temp.Length(); ++i) {
- temp[i].mRange->SetSelection(this);
- }
- *aOutIndex = startIndex + insertionPoint;
- return NS_OK;
- }
- nsresult
- Selection::RemoveItem(nsRange* aItem)
- {
- if (!aItem)
- return NS_ERROR_NULL_POINTER;
- // Find the range's index & remove it. We could use FindInsertionPoint to
- // get O(log n) time, but that requires many expensive DOM comparisons.
- // For even several thousand items, this is probably faster because the
- // comparisons are so fast.
- int32_t idx = -1;
- uint32_t i;
- for (i = 0; i < mRanges.Length(); i ++) {
- if (mRanges[i].mRange == aItem) {
- idx = (int32_t)i;
- break;
- }
- }
- if (idx < 0)
- return NS_ERROR_INVALID_ARG;
- mRanges.RemoveElementAt(idx);
- aItem->SetSelection(nullptr);
- return NS_OK;
- }
- nsresult
- Selection::RemoveCollapsedRanges()
- {
- uint32_t i = 0;
- while (i < mRanges.Length()) {
- if (mRanges[i].mRange->Collapsed()) {
- nsresult rv = RemoveItem(mRanges[i].mRange);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- ++i;
- }
- }
- return NS_OK;
- }
- nsresult
- Selection::Clear(nsPresContext* aPresContext)
- {
- setAnchorFocusRange(-1);
- for (uint32_t i = 0; i < mRanges.Length(); ++i) {
- mRanges[i].mRange->SetSelection(nullptr);
- selectFrames(aPresContext, mRanges[i].mRange, false);
- }
- mRanges.Clear();
- // Reset direction so for more dependable table selection range handling
- SetDirection(eDirNext);
- // If this was an ATTENTION selection, change it back to normal now
- if (mFrameSelection &&
- mFrameSelection->GetDisplaySelection() ==
- nsISelectionController::SELECTION_ATTENTION) {
- mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::GetType(int16_t* aType)
- {
- NS_ENSURE_ARG_POINTER(aType);
- *aType = ToRawSelectionType(Type());
- return NS_OK;
- }
- // RangeMatches*Point
- //
- // Compares the range beginning or ending point, and returns true if it
- // exactly matches the given DOM point.
- static inline bool
- RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
- {
- return aRange->GetStartParent() == aNode &&
- static_cast<int32_t>(aRange->StartOffset()) == aOffset;
- }
- static inline bool
- RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
- {
- return aRange->GetEndParent() == aNode &&
- static_cast<int32_t>(aRange->EndOffset()) == aOffset;
- }
- // Selection::EqualsRangeAtPoint
- //
- // Utility method for checking equivalence of two ranges.
- bool
- Selection::EqualsRangeAtPoint(
- nsINode* aBeginNode, int32_t aBeginOffset,
- nsINode* aEndNode, int32_t aEndOffset,
- int32_t aRangeIndex)
- {
- if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) {
- nsRange* range = mRanges[aRangeIndex].mRange;
- if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
- RangeMatchesEndPoint(range, aEndNode, aEndOffset))
- return true;
- }
- return false;
- }
- // Selection::GetRangesForInterval
- //
- // XPCOM wrapper for the nsTArray version
- NS_IMETHODIMP
- Selection::GetRangesForInterval(nsIDOMNode* aBeginNode, int32_t aBeginOffset,
- nsIDOMNode* aEndNode, int32_t aEndOffset,
- bool aAllowAdjacent,
- uint32_t* aResultCount,
- nsIDOMRange*** aResults)
- {
- if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
- return NS_ERROR_NULL_POINTER;
- *aResultCount = 0;
- *aResults = nullptr;
- nsTArray<RefPtr<nsRange>> results;
- ErrorResult result;
- nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
- nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
- NS_ENSURE_TRUE(beginNode && endNode, NS_ERROR_NULL_POINTER);
- GetRangesForInterval(*beginNode, aBeginOffset, *endNode, aEndOffset,
- aAllowAdjacent, results, result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- *aResultCount = results.Length();
- if (*aResultCount == 0) {
- return NS_OK;
- }
- *aResults = static_cast<nsIDOMRange**>
- (moz_xmalloc(sizeof(nsIDOMRange*) * *aResultCount));
- NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
- for (uint32_t i = 0; i < *aResultCount; i++) {
- (*aResults)[i] = results[i].forget().take();
- }
- return NS_OK;
- }
- void
- Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
- nsINode& aEndNode, int32_t aEndOffset,
- bool aAllowAdjacent,
- nsTArray<RefPtr<nsRange>>& aReturn,
- mozilla::ErrorResult& aRv)
- {
- nsTArray<nsRange*> results;
- nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset,
- &aEndNode, aEndOffset,
- aAllowAdjacent, &results);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- aReturn.SetLength(results.Length());
- for (uint32_t i = 0; i < results.Length(); ++i) {
- aReturn[i] = results[i]; // AddRefs
- }
- }
- // Selection::GetRangesForIntervalArray
- //
- // Fills a nsTArray with the ranges overlapping the range specified by
- // the given endpoints. Ranges in the selection exactly adjacent to the
- // input range are not returned unless aAllowAdjacent is set.
- //
- // For example, if the following ranges were in the selection
- // (assume everything is within the same node)
- //
- // Start Offset: 0 2 7 9
- // End Offset: 2 5 9 10
- //
- // and passed aBeginOffset of 2 and aEndOffset of 9, then with
- // aAllowAdjacent set, all the ranges should be returned. If
- // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
- // should be returned
- //
- // Now that overlapping ranges are disallowed, there can be a maximum of
- // 2 adjacent ranges
- nsresult
- Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
- nsINode* aEndNode, int32_t aEndOffset,
- bool aAllowAdjacent,
- nsTArray<nsRange*>* aRanges)
- {
- aRanges->Clear();
- int32_t startIndex, endIndex;
- nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset,
- aEndNode, aEndOffset, aAllowAdjacent,
- &startIndex, &endIndex);
- NS_ENSURE_SUCCESS(res, res);
- if (startIndex == -1 || endIndex == -1)
- return NS_OK;
- for (int32_t i = startIndex; i < endIndex; i++) {
- if (!aRanges->AppendElement(mRanges[i].mRange))
- return NS_ERROR_OUT_OF_MEMORY;
- }
- return NS_OK;
- }
- // Selection::GetIndicesForInterval
- //
- // Works on the same principle as GetRangesForIntervalArray above, however
- // instead this returns the indices into mRanges between which the
- // overlapping ranges lie.
- nsresult
- Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
- nsINode* aEndNode, int32_t aEndOffset,
- bool aAllowAdjacent,
- int32_t* aStartIndex, int32_t* aEndIndex)
- {
- int32_t startIndex;
- int32_t endIndex;
- if (!aStartIndex)
- aStartIndex = &startIndex;
- if (!aEndIndex)
- aEndIndex = &endIndex;
- *aStartIndex = -1;
- *aEndIndex = -1;
- if (mRanges.Length() == 0)
- return NS_OK;
- bool intervalIsCollapsed = aBeginNode == aEndNode &&
- aBeginOffset == aEndOffset;
- // Ranges that end before the given interval and begin after the given
- // interval can be discarded
- int32_t endsBeforeIndex;
- if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
- &CompareToRangeStart,
- &endsBeforeIndex))) {
- return NS_OK;
- }
- if (endsBeforeIndex == 0) {
- nsRange* endRange = mRanges[endsBeforeIndex].mRange;
- // If the interval is strictly before the range at index 0, we can optimize
- // by returning now - all ranges start after the given interval
- if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
- return NS_OK;
- // We now know that the start point of mRanges[0].mRange equals the end of
- // the interval. Thus, when aAllowadjacent is true, the caller is always
- // interested in this range. However, when excluding adjacencies, we must
- // remember to include the range when both it and the given interval are
- // collapsed to the same point
- if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
- return NS_OK;
- }
- *aEndIndex = endsBeforeIndex;
- int32_t beginsAfterIndex;
- if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
- &CompareToRangeEnd,
- &beginsAfterIndex))) {
- return NS_OK;
- }
- if (beginsAfterIndex == (int32_t) mRanges.Length())
- return NS_OK; // optimization: all ranges are strictly before us
- if (aAllowAdjacent) {
- // At this point, one of the following holds:
- // endsBeforeIndex == mRanges.Length(),
- // endsBeforeIndex points to a range whose start point does not equal the
- // given interval's start point
- // endsBeforeIndex points to a range whose start point equals the given
- // interval's start point
- // In the final case, there can be two such ranges, a collapsed range, and
- // an adjacent range (they will appear in mRanges in that order). For this
- // final case, we need to increment endsBeforeIndex, until one of the
- // first two possibilites hold
- while (endsBeforeIndex < (int32_t) mRanges.Length()) {
- nsRange* endRange = mRanges[endsBeforeIndex].mRange;
- if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
- break;
- endsBeforeIndex++;
- }
- // Likewise, one of the following holds:
- // beginsAfterIndex == 0,
- // beginsAfterIndex points to a range whose end point does not equal
- // the given interval's end point
- // beginsOnOrAfter points to a range whose end point equals the given
- // interval's end point
- // In the final case, there can be two such ranges, an adjacent range, and
- // a collapsed range (they will appear in mRanges in that order). For this
- // final case, we only need to take action if both those ranges exist, and
- // we are pointing to the collapsed range - we need to point to the
- // adjacent range
- nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
- if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
- RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
- beginRange = mRanges[beginsAfterIndex - 1].mRange;
- if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
- beginsAfterIndex--;
- }
- } else {
- // See above for the possibilities at this point. The only case where we
- // need to take action is when the range at beginsAfterIndex ends on
- // the given interval's start point, but that range isn't collapsed (a
- // collapsed range should be included in the returned results).
- nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
- if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
- !beginRange->Collapsed())
- beginsAfterIndex++;
- // Again, see above for the meaning of endsBeforeIndex at this point.
- // In particular, endsBeforeIndex may point to a collaped range which
- // represents the point at the end of the interval - this range should be
- // included
- if (endsBeforeIndex < (int32_t) mRanges.Length()) {
- nsRange* endRange = mRanges[endsBeforeIndex].mRange;
- if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
- endRange->Collapsed())
- endsBeforeIndex++;
- }
- }
- NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex,
- "Is mRanges not ordered?");
- NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
- *aStartIndex = beginsAfterIndex;
- *aEndIndex = endsBeforeIndex;
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame)
- {
- if (!aReturnFrame)
- return NS_ERROR_NULL_POINTER;
-
- int32_t frameOffset = 0;
- *aReturnFrame = 0;
- nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
- if (content && mFrameSelection)
- {
- *aReturnFrame = mFrameSelection->
- GetFrameForNodeOffset(content, AnchorOffset(),
- mFrameSelection->GetHint(), &frameOffset);
- if (*aReturnFrame)
- return NS_OK;
- }
- return NS_ERROR_FAILURE;
- }
- NS_IMETHODIMP
- Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
- int32_t* aOffsetUsed,
- bool aVisual)
- {
- if (!aReturnFrame)
- return NS_ERROR_NULL_POINTER;
-
- nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
- if (!content || !mFrameSelection)
- return NS_ERROR_FAILURE;
-
- int32_t frameOffset = 0;
- *aReturnFrame = 0;
- if (!aOffsetUsed)
- aOffsetUsed = &frameOffset;
-
- CaretAssociationHint hint = mFrameSelection->GetHint();
- if (aVisual) {
- nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
- return nsCaret::GetCaretFrameForNodeOffset(mFrameSelection,
- content, FocusOffset(), hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
- }
-
- *aReturnFrame = mFrameSelection->
- GetFrameForNodeOffset(content, FocusOffset(),
- hint, aOffsetUsed);
- if (!*aReturnFrame)
- return NS_ERROR_FAILURE;
- return NS_OK;
- }
- //select all content children of aContent
- nsresult
- Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter,
- nsIContent* aContent,
- bool aSelected)
- {
- nsresult result = aInnerIter->Init(aContent);
- nsIFrame *frame;
- if (NS_SUCCEEDED(result))
- {
- // First select frame of content passed in
- frame = aContent->GetPrimaryFrame();
- if (frame && frame->GetType() == nsGkAtoms::textFrame) {
- nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
- textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(),
- aSelected, mSelectionType);
- }
- // Now iterated through the child frames and set them
- while (!aInnerIter->IsDone()) {
- nsCOMPtr<nsIContent> innercontent =
- do_QueryInterface(aInnerIter->GetCurrentNode());
- frame = innercontent->GetPrimaryFrame();
- if (frame) {
- if (frame->GetType() == nsGkAtoms::textFrame) {
- nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
- textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(),
- aSelected, mSelectionType);
- } else {
- frame->InvalidateFrameSubtree(); // frame continuations?
- }
- }
- aInnerIter->Next();
- }
- return NS_OK;
- }
- return NS_ERROR_FAILURE;
- }
- /**
- * The idea of this helper method is to select or deselect "top to bottom",
- * traversing through the frames
- */
- nsresult
- Selection::selectFrames(nsPresContext* aPresContext, nsRange* aRange,
- bool aSelect)
- {
- if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
- // nothing to do
- return NS_OK;
- }
- MOZ_ASSERT(aRange);
- if (mFrameSelection->GetTableCellSelection()) {
- nsINode* node = aRange->GetCommonAncestor();
- nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
- : aPresContext->FrameManager()->GetRootFrame();
- if (frame) {
- frame->InvalidateFrameSubtree();
- }
- return NS_OK;
- }
- nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
- iter->Init(aRange);
- // Loop through the content iterator for each content node; for each text
- // node, call SetSelected on it:
- nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
- if (!content) {
- // Don't warn, bug 1055722
- return NS_ERROR_UNEXPECTED;
- }
- // We must call first one explicitly
- if (content->IsNodeOfType(nsINode::eTEXT)) {
- nsIFrame* frame = content->GetPrimaryFrame();
- // The frame could be an SVG text frame, in which case we'll ignore it.
- if (frame && frame->GetType() == nsGkAtoms::textFrame) {
- nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
- uint32_t startOffset = aRange->StartOffset();
- uint32_t endOffset;
- if (aRange->GetEndParent() == content) {
- endOffset = aRange->EndOffset();
- } else {
- endOffset = content->Length();
- }
- textFrame->SetSelectedRange(startOffset, endOffset, aSelect,
- mSelectionType);
- }
- }
- iter->First();
- nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
- for (iter->First(); !iter->IsDone(); iter->Next()) {
- content = do_QueryInterface(iter->GetCurrentNode());
- SelectAllFramesForContent(inneriter, content, aSelect);
- }
- // We must now do the last one if it is not the same as the first
- if (aRange->GetEndParent() != aRange->GetStartParent()) {
- nsresult res;
- content = do_QueryInterface(aRange->GetEndParent(), &res);
- NS_ENSURE_SUCCESS(res, res);
- NS_ENSURE_TRUE(content, res);
- if (content->IsNodeOfType(nsINode::eTEXT)) {
- nsIFrame* frame = content->GetPrimaryFrame();
- // The frame could be an SVG text frame, in which case we'll ignore it.
- if (frame && frame->GetType() == nsGkAtoms::textFrame) {
- nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
- textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect,
- mSelectionType);
- }
- }
- }
- return NS_OK;
- }
- // Selection::LookUpSelection
- //
- // This function is called when a node wants to know where the selection is
- // over itself.
- //
- // Usually, this is called when we already know there is a selection over
- // the node in question, and we only need to find the boundaries of it on
- // that node. This is when slowCheck is false--a strict test is not needed.
- // Other times, the caller has no idea, and wants us to test everything,
- // so we are supposed to determine whether there is a selection over the
- // node at all.
- //
- // A previous version of this code used this flag to do less work when
- // inclusion was already known (slowCheck=false). However, our tree
- // structure allows us to quickly determine ranges overlapping the node,
- // so we just ignore the slowCheck flag and do the full test every time.
- //
- // PERFORMANCE: a common case is that we are doing a fast check with exactly
- // one range in the selection. In this case, this function is slower than
- // brute force because of the overhead of checking the tree. We can optimize
- // this case to make it faster by doing the same thing the previous version
- // of this function did in the case of 1 range. This would also mean that
- // the aSlowCheck flag would have meaning again.
- NS_IMETHODIMP
- Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset,
- int32_t aContentLength,
- SelectionDetails** aReturnDetails,
- SelectionType aSelectionType,
- bool aSlowCheck)
- {
- nsresult rv;
- if (!aContent || ! aReturnDetails)
- return NS_ERROR_NULL_POINTER;
- // it is common to have no ranges, to optimize that
- if (mRanges.Length() == 0)
- return NS_OK;
- nsTArray<nsRange*> overlappingRanges;
- rv = GetRangesForIntervalArray(aContent, aContentOffset,
- aContent, aContentOffset + aContentLength,
- false,
- &overlappingRanges);
- NS_ENSURE_SUCCESS(rv, rv);
- if (overlappingRanges.Length() == 0)
- return NS_OK;
- for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
- nsRange* range = overlappingRanges[i];
- nsINode* startNode = range->GetStartParent();
- nsINode* endNode = range->GetEndParent();
- int32_t startOffset = range->StartOffset();
- int32_t endOffset = range->EndOffset();
- int32_t start = -1, end = -1;
- if (startNode == aContent && endNode == aContent) {
- if (startOffset < (aContentOffset + aContentLength) &&
- endOffset > aContentOffset) {
- // this range is totally inside the requested content range
- start = std::max(0, startOffset - aContentOffset);
- end = std::min(aContentLength, endOffset - aContentOffset);
- }
- // otherwise, range is inside the requested node, but does not intersect
- // the requested content range, so ignore it
- } else if (startNode == aContent) {
- if (startOffset < (aContentOffset + aContentLength)) {
- // the beginning of the range is inside the requested node, but the
- // end is outside, select everything from there to the end
- start = std::max(0, startOffset - aContentOffset);
- end = aContentLength;
- }
- } else if (endNode == aContent) {
- if (endOffset > aContentOffset) {
- // the end of the range is inside the requested node, but the beginning
- // is outside, select everything from the beginning to there
- start = 0;
- end = std::min(aContentLength, endOffset - aContentOffset);
- }
- } else {
- // this range does not begin or end in the requested node, but since
- // GetRangesForInterval returned this range, we know it overlaps.
- // Therefore, this node is enclosed in the range, and we select all
- // of it.
- start = 0;
- end = aContentLength;
- }
- if (start < 0)
- continue; // the ranges do not overlap the input range
- SelectionDetails* details = new SelectionDetails;
- details->mNext = *aReturnDetails;
- details->mStart = start;
- details->mEnd = end;
- details->mSelectionType = aSelectionType;
- RangeData *rd = FindRangeData(range);
- if (rd) {
- details->mTextRangeStyle = rd->mTextRangeStyle;
- }
- *aReturnDetails = details;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::Repaint(nsPresContext* aPresContext)
- {
- int32_t arrCount = (int32_t)mRanges.Length();
- if (arrCount < 1)
- return NS_OK;
- int32_t i;
-
- for (i = 0; i < arrCount; i++)
- {
- nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true);
- if (NS_FAILED(rv)) {
- return rv;
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::GetCanCacheFrameOffset(bool* aCanCacheFrameOffset)
- {
- NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
- if (mCachedOffsetForFrame)
- *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
- else
- *aCanCacheFrameOffset = false;
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
- {
- if (!mCachedOffsetForFrame) {
- mCachedOffsetForFrame = new CachedOffsetForFrame;
- }
- mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
- // clean up cached frame when turn off cache
- // fix bug 207936
- if (!aCanCacheFrameOffset) {
- mCachedOffsetForFrame->mLastCaretFrame = nullptr;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
- nsPoint& aPoint)
- {
- if (!mCachedOffsetForFrame) {
- mCachedOffsetForFrame = new CachedOffsetForFrame;
- }
- nsresult rv = NS_OK;
- if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
- mCachedOffsetForFrame->mLastCaretFrame &&
- (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
- (inOffset == mCachedOffsetForFrame->mLastContentOffset))
- {
- // get cached frame offset
- aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
- }
- else
- {
- // Recalculate frame offset and cache it. Don't cache a frame offset if
- // GetPointFromOffset fails, though.
- rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
- if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
- mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
- mCachedOffsetForFrame->mLastCaretFrame = aFrame;
- mCachedOffsetForFrame->mLastContentOffset = inOffset;
- }
- }
- return rv;
- }
- NS_IMETHODIMP
- Selection::GetAncestorLimiter(nsIContent** aContent)
- {
- if (mFrameSelection) {
- nsCOMPtr<nsIContent> c = mFrameSelection->GetAncestorLimiter();
- c.forget(aContent);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::SetAncestorLimiter(nsIContent* aContent)
- {
- if (mFrameSelection) {
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- frameSelection->SetAncestorLimiter(aContent);
- }
- return NS_OK;
- }
- RangeData*
- Selection::FindRangeData(nsIDOMRange* aRange)
- {
- NS_ENSURE_TRUE(aRange, nullptr);
- for (uint32_t i = 0; i < mRanges.Length(); i++) {
- if (mRanges[i].mRange == aRange)
- return &mRanges[i];
- }
- return nullptr;
- }
- NS_IMETHODIMP
- Selection::SetTextRangeStyle(nsIDOMRange* aRange,
- const TextRangeStyle& aTextRangeStyle)
- {
- NS_ENSURE_ARG_POINTER(aRange);
- RangeData *rd = FindRangeData(aRange);
- if (rd) {
- rd->mTextRangeStyle = aTextRangeStyle;
- }
- return NS_OK;
- }
- nsresult
- Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint,
- uint32_t aDelay)
- {
- NS_PRECONDITION(aFrame, "Need a frame");
- nsresult result;
- if (!mFrameSelection)
- return NS_OK;//nothing to do
- if (!mAutoScrollTimer)
- {
- mAutoScrollTimer = new nsAutoScrollTimer();
- result = mAutoScrollTimer->Init(mFrameSelection, this);
- if (NS_FAILED(result))
- return result;
- }
- result = mAutoScrollTimer->SetDelay(aDelay);
- if (NS_FAILED(result))
- return result;
- return DoAutoScroll(aFrame, aPoint);
- }
- nsresult
- Selection::StopAutoScrollTimer()
- {
- if (mAutoScrollTimer) {
- return mAutoScrollTimer->Stop();
- }
- return NS_OK;
- }
- nsresult
- Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint)
- {
- NS_PRECONDITION(aFrame, "Need a frame");
- if (mAutoScrollTimer)
- (void)mAutoScrollTimer->Stop();
- nsPresContext* presContext = aFrame->PresContext();
- nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
- nsRootPresContext* rootPC = presContext->GetRootPresContext();
- if (!rootPC)
- return NS_OK;
- nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
- nsWeakFrame weakRootFrame(rootmostFrame);
- nsWeakFrame weakFrame(aFrame);
- // Get the point relative to the root most frame because the scroll we are
- // about to do will change the coordinates of aFrame.
- nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
- bool done = false;
- bool didScroll;
- while (true) {
- didScroll = shell->ScrollFrameRectIntoView(
- aFrame, nsRect(aPoint, nsSize(0, 0)),
- nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
- 0);
- if (!weakFrame || !weakRootFrame) {
- return NS_OK;
- }
- if (!didScroll && !done) {
- // If aPoint is at the screen edge then try to scroll anyway, once.
- RefPtr<nsDeviceContext> dx = shell->GetViewManager()->GetDeviceContext();
- nsRect screen;
- dx->GetRect(screen);
- nsPoint screenPoint = globalPoint +
- rootmostFrame->GetScreenRectInAppUnits().TopLeft();
- nscoord onePx = nsPresContext::AppUnitsPerCSSPixel();
- nscoord scrollAmount = 10 * onePx;
- if (std::abs(screen.x - screenPoint.x) <= onePx) {
- aPoint.x -= scrollAmount;
- } else if (std::abs(screen.XMost() - screenPoint.x) <= onePx) {
- aPoint.x += scrollAmount;
- } else if (std::abs(screen.y - screenPoint.y) <= onePx) {
- aPoint.y -= scrollAmount;
- } else if (std::abs(screen.YMost() - screenPoint.y) <= onePx) {
- aPoint.y += scrollAmount;
- } else {
- break;
- }
- done = true;
- continue;
- }
- break;
- }
- // Start the AutoScroll timer if necessary.
- if (didScroll && mAutoScrollTimer) {
- nsPoint presContextPoint = globalPoint -
- shell->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
- mAutoScrollTimer->Start(presContext, presContextPoint);
- }
- return NS_OK;
- }
- /** RemoveAllRanges zeroes the selection
- */
- NS_IMETHODIMP
- Selection::RemoveAllRanges()
- {
- ErrorResult result;
- RemoveAllRanges(result);
- return result.StealNSResult();
- }
- void
- Selection::RemoveAllRanges(ErrorResult& aRv)
- {
- if (!mFrameSelection)
- return; // nothing to do
- RefPtr<nsPresContext> presContext = GetPresContext();
- nsresult result = Clear(presContext);
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- return;
- }
- // Turn off signal for table selection
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- frameSelection->ClearTableCellSelection();
- result = frameSelection->NotifySelectionListeners(GetType());
- // Also need to notify the frames!
- // PresShell::CharacterDataChanged should do that on DocumentChanged
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- }
- }
- /** AddRange adds the specified range to the selection
- * @param aRange is the range to be added
- */
- NS_IMETHODIMP
- Selection::AddRange(nsIDOMRange* aDOMRange)
- {
- if (!aDOMRange) {
- return NS_ERROR_NULL_POINTER;
- }
- nsRange* range = static_cast<nsRange*>(aDOMRange);
- ErrorResult result;
- AddRange(*range, result);
- return result.StealNSResult();
- }
- void
- Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
- {
- return AddRangeInternal(aRange, GetParentObject(), aRv);
- }
- void
- Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
- ErrorResult& aRv)
- {
- nsINode* rangeRoot = aRange.GetRoot();
- if (aDocument != rangeRoot && (!rangeRoot ||
- aDocument != rangeRoot->GetComposedDoc())) {
- // http://w3c.github.io/selection-api/#dom-selection-addrange
- // "... if the root of the range's boundary points are the document
- // associated with context object. Otherwise, this method must do nothing."
- return;
- }
- // This inserts a table cell range in proper document order
- // and returns NS_OK if range doesn't contain just one table cell
- bool didAddRange;
- int32_t rangeIndex;
- nsresult result = addTableCellRange(&aRange, &didAddRange, &rangeIndex);
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- return;
- }
- if (!didAddRange) {
- result = AddItem(&aRange, &rangeIndex);
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- return;
- }
- }
- if (rangeIndex < 0) {
- return;
- }
- setAnchorFocusRange(rangeIndex);
-
- // Make sure the caret appears on the next line, if at a newline
- if (mSelectionType == SelectionType::eNormal) {
- SetInterlinePosition(true);
- }
- RefPtr<nsPresContext> presContext = GetPresContext();
- selectFrames(presContext, &aRange, true);
- if (!mFrameSelection)
- return;//nothing to do
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- result = frameSelection->NotifySelectionListeners(GetType());
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- }
- }
- // Selection::RemoveRange
- //
- // Removes the given range from the selection. The tricky part is updating
- // the flags on the frames that indicate whether they have a selection or
- // not. There could be several selection ranges on the frame, and clearing
- // the bit would cause the selection to not be drawn, even when there is
- // another range on the frame (bug 346185).
- //
- // We therefore find any ranges that intersect the same nodes as the range
- // being removed, and cause them to set the selected bits back on their
- // selected frames after we've cleared the bit from ours.
- nsresult
- Selection::RemoveRange(nsIDOMRange* aDOMRange)
- {
- if (!aDOMRange) {
- return NS_ERROR_INVALID_ARG;
- }
- nsRange* range = static_cast<nsRange*>(aDOMRange);
- ErrorResult result;
- RemoveRange(*range, result);
- return result.StealNSResult();
- }
- void
- Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
- {
- nsresult rv = RemoveItem(&aRange);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- nsINode* beginNode = aRange.GetStartParent();
- nsINode* endNode = aRange.GetEndParent();
- if (!beginNode || !endNode) {
- // Detached range; nothing else to do here.
- return;
- }
-
- // find out the length of the end node, so we can select all of it
- int32_t beginOffset, endOffset;
- if (endNode->IsNodeOfType(nsINode::eTEXT)) {
- // Get the length of the text. We can't just use the offset because
- // another range could be touching this text node but not intersect our
- // range.
- beginOffset = 0;
- endOffset = static_cast<nsIContent*>(endNode)->TextLength();
- } else {
- // For non-text nodes, the given offsets should be sufficient.
- beginOffset = aRange.StartOffset();
- endOffset = aRange.EndOffset();
- }
- // clear the selected bit from the removed range's frames
- RefPtr<nsPresContext> presContext = GetPresContext();
- selectFrames(presContext, &aRange, false);
- // add back the selected bit for each range touching our nodes
- nsTArray<nsRange*> affectedRanges;
- rv = GetRangesForIntervalArray(beginNode, beginOffset,
- endNode, endOffset,
- true, &affectedRanges);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
- selectFrames(presContext, affectedRanges[i], true);
- }
- int32_t cnt = mRanges.Length();
- if (&aRange == mAnchorFocusRange) {
- // Reset anchor to LAST range or clear it if there are no ranges.
- setAnchorFocusRange(cnt - 1);
- // When the selection is user-created it makes sense to scroll the range
- // into view. The spell-check selection, however, is created and destroyed
- // in the background. We don't want to scroll in this case or the view
- // might appear to be moving randomly (bug 337871).
- if (mSelectionType != SelectionType::eSpellCheck && cnt > 0) {
- ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
- }
- }
- if (!mFrameSelection)
- return;//nothing to do
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- rv = frameSelection->NotifySelectionListeners(GetType());
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- }
- }
- /*
- * Collapse sets the whole selection to be one point.
- */
- NS_IMETHODIMP
- Selection::Collapse(nsIDOMNode* aParentNode, int32_t aOffset)
- {
- nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
- return Collapse(parentNode, aOffset);
- }
- NS_IMETHODIMP
- Selection::CollapseNative(nsINode* aParentNode, int32_t aOffset)
- {
- return Collapse(aParentNode, aOffset);
- }
- nsresult
- Selection::Collapse(nsINode* aParentNode, int32_t aOffset)
- {
- if (!aParentNode)
- return NS_ERROR_INVALID_ARG;
- ErrorResult result;
- Collapse(*aParentNode, static_cast<uint32_t>(aOffset), result);
- return result.StealNSResult();
- }
- void
- Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
- {
- if (!mFrameSelection) {
- aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
- return;
- }
- nsCOMPtr<nsINode> parentNode = &aParentNode;
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- frameSelection->InvalidateDesiredPos();
- if (!IsValidSelectionPoint(frameSelection, parentNode)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- nsresult result;
- RefPtr<nsPresContext> presContext = GetPresContext();
- if (!presContext || presContext->Document() != parentNode->OwnerDoc()) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- // Delete all of the current ranges
- Clear(presContext);
- // Turn off signal for table selection
- frameSelection->ClearTableCellSelection();
- // Hack to display the caret on the right line (bug 1237236).
- if (frameSelection->GetHint() != CARET_ASSOCIATE_AFTER &&
- parentNode->IsContent()) {
- int32_t frameOffset;
- nsTextFrame* f =
- do_QueryFrame(nsCaret::GetFrameAndOffset(this, parentNode,
- aOffset, &frameOffset));
- if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) {
- if ((parentNode->AsContent() == f->GetContent() &&
- f->GetContentEnd() == int32_t(aOffset)) ||
- (parentNode == f->GetContent()->GetParentNode() &&
- parentNode->IndexOf(f->GetContent()) + 1 == int32_t(aOffset))) {
- frameSelection->SetHint(CARET_ASSOCIATE_AFTER);
- }
- }
- }
- RefPtr<nsRange> range = new nsRange(parentNode);
- result = range->CollapseTo(parentNode, aOffset);
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- return;
- }
- #ifdef DEBUG_SELECTION
- nsCOMPtr<nsIContent> content = do_QueryInterface(parentNode);
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(parentNode);
- printf ("Sel. Collapse to %p %s %d\n", parentNode.get(),
- content ? nsAtomCString(content->NodeInfo()->NameAtom()).get()
- : (doc ? "DOCUMENT" : "???"),
- aOffset);
- #endif
- int32_t rangeIndex = -1;
- result = AddItem(range, &rangeIndex);
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- return;
- }
- setAnchorFocusRange(0);
- selectFrames(presContext, range, true);
- result = frameSelection->NotifySelectionListeners(GetType());
- if (NS_FAILED(result)) {
- aRv.Throw(result);
- }
- }
- /*
- * Sets the whole selection to be one point
- * at the start of the current selection
- */
- NS_IMETHODIMP
- Selection::CollapseToStart()
- {
- ErrorResult result;
- CollapseToStart(result);
- return result.StealNSResult();
- }
- void
- Selection::CollapseToStart(ErrorResult& aRv)
- {
- int32_t cnt;
- nsresult rv = GetRangeCount(&cnt);
- if (NS_FAILED(rv) || cnt <= 0) {
- aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- return;
- }
- // Get the first range
- nsRange* firstRange = mRanges[0].mRange;
- if (!firstRange) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- if (mFrameSelection) {
- int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
- mFrameSelection->PostReason(reason);
- }
- nsINode* parent = firstRange->GetStartParent();
- if (!parent) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- Collapse(*parent, firstRange->StartOffset(), aRv);
- }
- /*
- * Sets the whole selection to be one point
- * at the end of the current selection
- */
- NS_IMETHODIMP
- Selection::CollapseToEnd()
- {
- ErrorResult result;
- CollapseToEnd(result);
- return result.StealNSResult();
- }
- void
- Selection::CollapseToEnd(ErrorResult& aRv)
- {
- int32_t cnt;
- nsresult rv = GetRangeCount(&cnt);
- if (NS_FAILED(rv) || cnt <= 0) {
- aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- return;
- }
- // Get the last range
- nsRange* lastRange = mRanges[cnt - 1].mRange;
- if (!lastRange) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- if (mFrameSelection) {
- int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
- mFrameSelection->PostReason(reason);
- }
- nsINode* parent = lastRange->GetEndParent();
- if (!parent) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- Collapse(*parent, lastRange->EndOffset(), aRv);
- }
- /*
- * IsCollapsed -- is the whole selection just one point, or unset?
- */
- bool
- Selection::IsCollapsed() const
- {
- uint32_t cnt = mRanges.Length();
- if (cnt == 0) {
- return true;
- }
- if (cnt != 1) {
- return false;
- }
- return mRanges[0].mRange->Collapsed();
- }
- /* virtual */
- bool
- Selection::Collapsed()
- {
- return IsCollapsed();
- }
- NS_IMETHODIMP
- Selection::GetIsCollapsed(bool* aIsCollapsed)
- {
- NS_ENSURE_TRUE(aIsCollapsed, NS_ERROR_NULL_POINTER);
- *aIsCollapsed = IsCollapsed();
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::GetRangeCount(int32_t* aRangeCount)
- {
- *aRangeCount = (int32_t)RangeCount();
- return NS_OK;
- }
- void
- Selection::GetType(nsAString& aOutType) const
- {
- if (!RangeCount()) {
- aOutType.AssignLiteral("None");
- } else if (IsCollapsed()) {
- aOutType.AssignLiteral("Caret");
- } else {
- aOutType.AssignLiteral("Range");
- }
- }
- NS_IMETHODIMP
- Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
- {
- ErrorResult result;
- *aReturn = GetRangeAt(aIndex, result);
- NS_IF_ADDREF(*aReturn);
- return result.StealNSResult();
- }
- nsRange*
- Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv)
- {
- nsRange* range = GetRangeAt(aIndex);
- if (!range) {
- aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
- return nullptr;
- }
- return range;
- }
- nsRange*
- Selection::GetRangeAt(int32_t aIndex) const
- {
- RangeData empty(nullptr);
- return mRanges.SafeElementAt(aIndex, empty).mRange;
- }
- /*
- utility function
- */
- nsresult
- Selection::SetAnchorFocusToRange(nsRange* aRange)
- {
- NS_ENSURE_STATE(mAnchorFocusRange);
- bool collapsed = Collapsed();
- nsresult res = RemoveItem(mAnchorFocusRange);
- if (NS_FAILED(res))
- return res;
- int32_t aOutIndex = -1;
- res = AddItem(aRange, &aOutIndex, !collapsed);
- if (NS_FAILED(res))
- return res;
- setAnchorFocusRange(aOutIndex);
- return NS_OK;
- }
- void
- Selection::ReplaceAnchorFocusRange(nsRange* aRange)
- {
- NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
- RefPtr<nsPresContext> presContext = GetPresContext();
- if (presContext) {
- selectFrames(presContext, mAnchorFocusRange, false);
- SetAnchorFocusToRange(aRange);
- selectFrames(presContext, mAnchorFocusRange, true);
- }
- }
- void
- Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection)
- {
- if (aDirection == mDirection) {
- return;
- }
- SetDirection(aDirection);
- if (RangeCount() <= 1) {
- return;
- }
- nsRange* firstRange = GetRangeAt(0);
- nsRange* lastRange = GetRangeAt(RangeCount() - 1);
- if (mDirection == eDirPrevious) {
- firstRange->SetIsGenerated(false);
- lastRange->SetIsGenerated(true);
- setAnchorFocusRange(0);
- } else { // aDir == eDirNext
- firstRange->SetIsGenerated(true);
- lastRange->SetIsGenerated(false);
- setAnchorFocusRange(RangeCount() - 1);
- }
- }
- /*
- Notes which might come in handy for extend:
- We can tell the direction of the selection by asking for the anchors selection
- if the begin is less than the end then we know the selection is to the "right".
- else it is a backwards selection.
- a = anchor
- 1 = old cursor
- 2 = new cursor
- if (a <= 1 && 1 <=2) a,1,2 or (a1,2)
- if (a < 2 && 1 > 2) a,2,1
- if (1 < a && a <2) 1,a,2
- if (a > 2 && 2 >1) 1,2,a
- if (2 < a && a <1) 2,a,1
- if (a > 1 && 1 >2) 2,1,a
- then execute
- a 1 2 select from 1 to 2
- a 2 1 deselect from 2 to 1
- 1 a 2 deselect from 1 to a select from a to 2
- 1 2 a deselect from 1 to 2
- 2 1 a = continue selection from 2 to 1
- */
- /*
- * Extend extends the selection away from the anchor.
- * We don't need to know the direction, because we always change the focus.
- */
- NS_IMETHODIMP
- Selection::Extend(nsIDOMNode* aParentNode, int32_t aOffset)
- {
- nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
- return Extend(parentNode, aOffset);
- }
- NS_IMETHODIMP
- Selection::ExtendNative(nsINode* aParentNode, int32_t aOffset)
- {
- return Extend(aParentNode, aOffset);
- }
- nsresult
- Selection::Extend(nsINode* aParentNode, int32_t aOffset)
- {
- if (!aParentNode)
- return NS_ERROR_INVALID_ARG;
- ErrorResult result;
- Extend(*aParentNode, static_cast<uint32_t>(aOffset), result);
- return result.StealNSResult();
- }
- void
- Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
- {
- // First, find the range containing the old focus point:
- if (!mAnchorFocusRange) {
- aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- return;
- }
- if (!mFrameSelection) {
- aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
- return;
- }
- nsresult res;
- if (!IsValidSelectionPoint(mFrameSelection, &aParentNode)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- RefPtr<nsPresContext> presContext = GetPresContext();
- if (!presContext || presContext->Document() != aParentNode.OwnerDoc()) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- #ifdef DEBUG_SELECTION
- nsDirection oldDirection = GetDirection();
- #endif
- nsINode* anchorNode = GetAnchorNode();
- nsINode* focusNode = GetFocusNode();
- uint32_t anchorOffset = AnchorOffset();
- uint32_t focusOffset = FocusOffset();
- RefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
- nsINode* startNode = range->GetStartParent();
- nsINode* endNode = range->GetEndParent();
- int32_t startOffset = range->StartOffset();
- int32_t endOffset = range->EndOffset();
- //compare anchor to old cursor.
- // We pass |disconnected| to the following ComparePoints calls in order
- // to avoid assertions. ComparePoints returns 1 in the disconnected case
- // and we can end up in various cases below, but it is assumed that in
- // any of the cases we end up, the nsRange implementation will collapse
- // the range to the new point because we can not make a valid range with
- // a disconnected point. This means that whatever range is currently
- // selected will be cleared.
- bool disconnected = false;
- bool shouldClearRange = false;
- int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
- focusNode, focusOffset,
- &disconnected);
- //compare old cursor to new cursor
- shouldClearRange |= disconnected;
- int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
- &aParentNode, aOffset,
- &disconnected);
- //compare anchor to new cursor
- shouldClearRange |= disconnected;
- int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
- &aParentNode, aOffset,
- &disconnected);
- // If the points are disconnected, the range will be collapsed below,
- // resulting in a range that selects nothing.
- if (shouldClearRange) {
- // Repaint the current range with the selection removed.
- selectFrames(presContext, range, false);
- }
- RefPtr<nsRange> difRange = new nsRange(&aParentNode);
- if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2 a,1,2
- //select from 1 to 2 unless they are collapsed
- range->SetEnd(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- SetDirection(eDirNext);
- res = difRange->SetStartAndEnd(focusNode, focusOffset,
- range->GetEndParent(), range->EndOffset());
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- selectFrames(presContext, difRange , true);
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- }
- else if (result1 == 0 && result3 > 0){//2, a1
- //select from 2 to 1a
- SetDirection(eDirPrevious);
- range->SetStart(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- selectFrames(presContext, range, true);
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- }
- else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
- //deselect from 2 to 1
- res = difRange->SetStartAndEnd(&aParentNode, aOffset,
- focusNode, focusOffset);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- range->SetEnd(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- selectFrames(presContext, difRange, false); // deselect now
- difRange->SetEnd(range->GetEndParent(), range->EndOffset());
- selectFrames(presContext, difRange, true); // must reselect last node maybe more
- }
- else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
- if (GetDirection() == eDirPrevious){
- res = range->SetStart(endNode, endOffset);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- }
- SetDirection(eDirNext);
- range->SetEnd(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
- res = difRange->SetStart(focusNode, focusOffset);
- nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset);
- if (NS_FAILED(tmp)) {
- res = tmp;
- }
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- //deselect from 1 to a
- selectFrames(presContext, difRange , false);
- }
- else
- {
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- }
- //select from a to 2
- selectFrames(presContext, range , true);
- }
- else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
- //deselect from 1 to 2
- res = difRange->SetStartAndEnd(focusNode, focusOffset,
- &aParentNode, aOffset);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- SetDirection(eDirPrevious);
- range->SetStart(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- selectFrames(presContext, difRange , false);
- difRange->SetStart(range->GetStartParent(), range->StartOffset());
- selectFrames(presContext, difRange, true);//must reselect last node
- }
- else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
- if (GetDirection() == eDirNext){
- range->SetEnd(startNode, startOffset);
- }
- SetDirection(eDirPrevious);
- range->SetStart(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- //deselect from a to 1
- if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
- res = difRange->SetStartAndEnd(anchorNode, anchorOffset,
- focusNode, focusOffset);
- nsresult tmp = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- selectFrames(presContext, difRange, false);
- }
- else
- {
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- }
- //select from 2 to a
- selectFrames(presContext, range , true);
- }
- else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
- //select from 2 to 1
- range->SetStart(aParentNode, aOffset, aRv);
- if (aRv.Failed()) {
- return;
- }
- SetDirection(eDirPrevious);
- res = difRange->SetStartAndEnd(
- range->GetStartParent(), range->StartOffset(),
- focusNode, focusOffset);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- selectFrames(presContext, difRange, true);
- res = SetAnchorFocusToRange(range);
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- return;
- }
- }
- if (mRanges.Length() > 1) {
- for (size_t i = 0; i < mRanges.Length(); ++i) {
- nsRange* range = mRanges[i].mRange;
- MOZ_ASSERT(range->IsInSelection());
- selectFrames(presContext, range, range->IsInSelection());
- }
- }
- DEBUG_OUT_RANGE(range);
- #ifdef DEBUG_SELECTION
- if (GetDirection() != oldDirection) {
- printf(" direction changed to %s\n",
- GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
- }
- nsCOMPtr<nsIContent> content = do_QueryInterface(&aParentNode);
- printf ("Sel. Extend to %p %s %d\n", content.get(),
- nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
- #endif
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- res = frameSelection->NotifySelectionListeners(GetType());
- if (NS_FAILED(res)) {
- aRv.Throw(res);
- }
- }
- NS_IMETHODIMP
- Selection::SelectAllChildren(nsIDOMNode* aParentNode)
- {
- ErrorResult result;
- nsCOMPtr<nsINode> node = do_QueryInterface(aParentNode);
- NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG);
- SelectAllChildren(*node, result);
- return result.StealNSResult();
- }
- void
- Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
- {
- if (mFrameSelection) {
- mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
- }
- SelectionBatcher batch(this);
- Collapse(aNode, 0, aRv);
- if (aRv.Failed()) {
- return;
- }
- Extend(aNode, aNode.GetChildCount(), aRv);
- }
- NS_IMETHODIMP
- Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes)
- {
- if (!aYes) {
- return NS_ERROR_NULL_POINTER;
- }
- *aYes = false;
- nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
- if (!node) {
- return NS_ERROR_NULL_POINTER;
- }
- ErrorResult result;
- *aYes = ContainsNode(*node, aAllowPartial, result);
- return result.StealNSResult();
- }
- bool
- Selection::ContainsNode(nsINode& aNode, bool aAllowPartial, ErrorResult& aRv)
- {
- nsresult rv;
- if (mRanges.Length() == 0) {
- return false;
- }
- // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
- uint32_t nodeLength;
- bool isData = aNode.IsNodeOfType(nsINode::eDATA_NODE);
- if (isData) {
- nodeLength = static_cast<nsIContent&>(aNode).TextLength();
- } else {
- nodeLength = aNode.GetChildCount();
- }
- nsTArray<nsRange*> overlappingRanges;
- rv = GetRangesForIntervalArray(&aNode, 0, &aNode, nodeLength,
- false, &overlappingRanges);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return false;
- }
- if (overlappingRanges.Length() == 0)
- return false; // no ranges overlap
- // if the caller said partial intersections are OK, we're done
- if (aAllowPartial) {
- return true;
- }
- // text nodes always count as inside
- if (isData) {
- return true;
- }
- // The caller wants to know if the node is entirely within the given range,
- // so we have to check all intersecting ranges.
- for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
- bool nodeStartsBeforeRange, nodeEndsAfterRange;
- if (NS_SUCCEEDED(nsRange::CompareNodeToRange(&aNode, overlappingRanges[i],
- &nodeStartsBeforeRange,
- &nodeEndsAfterRange))) {
- if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
- return true;
- }
- }
- }
- return false;
- }
- class PointInRectChecker : public nsLayoutUtils::RectCallback {
- public:
- explicit PointInRectChecker(const nsPoint& aPoint)
- : mPoint(aPoint)
- , mMatchFound(false)
- {
- }
- void AddRect(const nsRect& aRect) override
- {
- mMatchFound = mMatchFound || aRect.Contains(mPoint);
- }
- bool MatchFound()
- {
- return mMatchFound;
- }
- private:
- nsPoint mPoint;
- bool mMatchFound;
- };
- bool
- Selection::ContainsPoint(const nsPoint& aPoint)
- {
- if (IsCollapsed()) {
- return false;
- }
- PointInRectChecker checker(aPoint);
- for (uint32_t i = 0; i < RangeCount(); i++) {
- nsRange* range = GetRangeAt(i);
- nsRange::CollectClientRectsAndText(&checker, nullptr, range,
- range->GetStartParent(), range->StartOffset(),
- range->GetEndParent(), range->EndOffset(),
- true, false);
- if (checker.MatchFound()) {
- return true;
- }
- }
- return false;
- }
- nsPresContext*
- Selection::GetPresContext() const
- {
- nsIPresShell *shell = GetPresShell();
- if (!shell) {
- return nullptr;
- }
- return shell->GetPresContext();
- }
- nsIPresShell*
- Selection::GetPresShell() const
- {
- if (!mFrameSelection)
- return nullptr;//nothing to do
- return mFrameSelection->GetShell();
- }
- nsIFrame *
- Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
- {
- if (!mFrameSelection)
- return nullptr; // nothing to do
- NS_ENSURE_TRUE(aRect, nullptr);
- aRect->SetRect(0, 0, 0, 0);
- switch (aRegion) {
- case nsISelectionController::SELECTION_ANCHOR_REGION:
- case nsISelectionController::SELECTION_FOCUS_REGION:
- return GetSelectionEndPointGeometry(aRegion, aRect);
- case nsISelectionController::SELECTION_WHOLE_SELECTION:
- break;
- default:
- return nullptr;
- }
- NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
- "should only be SELECTION_WHOLE_SELECTION here");
- nsRect anchorRect;
- nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
- nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
- if (!anchorFrame)
- return nullptr;
- nsRect focusRect;
- nsIFrame* focusFrame = GetSelectionEndPointGeometry(
- nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
- if (!focusFrame)
- return nullptr;
- NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
- "points of selection in different documents?");
- // make focusRect relative to anchorFrame
- focusRect += focusFrame->GetOffsetTo(anchorFrame);
- aRect->UnionRectEdges(anchorRect, focusRect);
- return anchorFrame;
- }
- nsIFrame *
- Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect)
- {
- if (!mFrameSelection)
- return nullptr; // nothing to do
- NS_ENSURE_TRUE(aRect, nullptr);
- aRect->SetRect(0, 0, 0, 0);
- nsINode *node = nullptr;
- uint32_t nodeOffset = 0;
- nsIFrame *frame = nullptr;
- switch (aRegion) {
- case nsISelectionController::SELECTION_ANCHOR_REGION:
- node = GetAnchorNode();
- nodeOffset = AnchorOffset();
- break;
- case nsISelectionController::SELECTION_FOCUS_REGION:
- node = GetFocusNode();
- nodeOffset = FocusOffset();
- break;
- default:
- return nullptr;
- }
- if (!node)
- return nullptr;
- nsCOMPtr<nsIContent> content = do_QueryInterface(node);
- NS_ENSURE_TRUE(content.get(), nullptr);
- int32_t frameOffset = 0;
- frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
- mFrameSelection->GetHint(),
- &frameOffset);
- if (!frame)
- return nullptr;
- // Figure out what node type we have, then get the
- // appropriate rect for it's nodeOffset.
- bool isText = node->IsNodeOfType(nsINode::eTEXT);
- nsPoint pt(0, 0);
- if (isText) {
- nsIFrame* childFrame = nullptr;
- frameOffset = 0;
- nsresult rv =
- frame->GetChildFrameContainingOffset(nodeOffset,
- mFrameSelection->GetHint(),
- &frameOffset, &childFrame);
- if (NS_FAILED(rv))
- return nullptr;
- if (!childFrame)
- return nullptr;
- frame = childFrame;
- // Get the x coordinate of the offset into the text frame.
- rv = GetCachedFrameOffset(frame, nodeOffset, pt);
- if (NS_FAILED(rv))
- return nullptr;
- }
- // Return the rect relative to the frame, with zero width.
- if (isText) {
- aRect->x = pt.x;
- } else if (mFrameSelection->GetHint() == CARET_ASSOCIATE_BEFORE) {
- // It's the frame's right edge we're interested in.
- aRect->x = frame->GetRect().width;
- }
- aRect->height = frame->GetRect().height;
- return frame;
- }
- NS_IMETHODIMP
- Selection::ScrollSelectionIntoViewEvent::Run()
- {
- if (!mSelection)
- return NS_OK; // event revoked
- int32_t flags = Selection::SCROLL_DO_FLUSH |
- Selection::SCROLL_SYNCHRONOUS;
- Selection* sel = mSelection; // workaround to satisfy static analysis
- RefPtr<Selection> kungFuDeathGrip(sel);
- mSelection->mScrollEvent.Forget();
- mSelection->ScrollIntoView(mRegion, mVerticalScroll,
- mHorizontalScroll, mFlags | flags);
- return NS_OK;
- }
- nsresult
- Selection::PostScrollSelectionIntoViewEvent(
- SelectionRegion aRegion,
- int32_t aFlags,
- nsIPresShell::ScrollAxis aVertical,
- nsIPresShell::ScrollAxis aHorizontal)
- {
- // If we've already posted an event, revoke it and place a new one at the
- // end of the queue to make sure that any new pending reflow events are
- // processed before we scroll. This will insure that we scroll to the
- // correct place on screen.
- mScrollEvent.Revoke();
- RefPtr<ScrollSelectionIntoViewEvent> ev =
- new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
- aFlags);
- nsresult rv = NS_DispatchToCurrentThread(ev);
- NS_ENSURE_SUCCESS(rv, rv);
- mScrollEvent = ev;
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
- int16_t aVPercent, int16_t aHPercent)
- {
- ErrorResult result;
- ScrollIntoView(aRegion, aIsSynchronous, aVPercent, aHPercent, result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- return NS_OK;
- }
- void
- Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
- int16_t aVPercent, int16_t aHPercent,
- ErrorResult& aRv)
- {
- nsresult rv = ScrollIntoViewInternal(aRegion, aIsSynchronous,
- nsIPresShell::ScrollAxis(aVPercent),
- nsIPresShell::ScrollAxis(aHPercent));
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- }
- }
- NS_IMETHODIMP
- Selection::ScrollIntoViewInternal(SelectionRegion aRegion, bool aIsSynchronous,
- nsIPresShell::ScrollAxis aVertical,
- nsIPresShell::ScrollAxis aHorizontal)
- {
- return ScrollIntoView(aRegion, aVertical, aHorizontal,
- aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0);
- }
- nsresult
- Selection::ScrollIntoView(SelectionRegion aRegion,
- nsIPresShell::ScrollAxis aVertical,
- nsIPresShell::ScrollAxis aHorizontal,
- int32_t aFlags)
- {
- if (!mFrameSelection)
- return NS_OK;//nothing to do
- nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
- if (!presShell)
- return NS_OK;
- if (mFrameSelection->GetBatching())
- return NS_OK;
- if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
- return PostScrollSelectionIntoViewEvent(aRegion, aFlags,
- aVertical, aHorizontal);
- // Now that text frame character offsets are always valid (though not
- // necessarily correct), the worst that will happen if we don't flush here
- // is that some callers might scroll to the wrong place. Those should
- // either manually flush if they're in a safe position for it or use the
- // async version of this method.
- if (aFlags & Selection::SCROLL_DO_FLUSH) {
- presShell->FlushPendingNotifications(Flush_Layout);
- // Reget the presshell, since it might have been Destroy'ed.
- presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr;
- if (!presShell)
- return NS_OK;
- }
- //
- // Scroll the selection region into view.
- //
- nsRect rect;
- nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
- if (!frame)
- return NS_ERROR_FAILURE;
- // Scroll vertically to get the caret into view, but only if the container
- // is perceived to be scrollable in that direction (i.e. there is a visible
- // vertical scrollbar or the scroll range is at least one device pixel)
- aVertical.mOnlyIfPerceivedScrollableDirection = true;
- uint32_t flags = 0;
- if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) {
- flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY;
- }
- if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) {
- flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
- }
- presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal,
- flags);
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::AddSelectionListener(nsISelectionListener* aNewListener)
- {
- if (!aNewListener)
- return NS_ERROR_NULL_POINTER;
- ErrorResult result;
- AddSelectionListener(aNewListener, result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- return NS_OK;
- }
- void
- Selection::AddSelectionListener(nsISelectionListener* aNewListener,
- ErrorResult& aRv)
- {
- bool result = mSelectionListeners.AppendObject(aNewListener); // AddRefs
- if (!result) {
- aRv.Throw(NS_ERROR_FAILURE);
- }
- }
- NS_IMETHODIMP
- Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
- {
- if (!aListenerToRemove)
- return NS_ERROR_NULL_POINTER;
- ErrorResult result;
- RemoveSelectionListener(aListenerToRemove, result);
- if (result.Failed()) {
- return result.StealNSResult();
- }
- return NS_OK;
- }
- void
- Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
- ErrorResult& aRv)
- {
- bool result = mSelectionListeners.RemoveObject(aListenerToRemove); // Releases
- if (!result) {
- aRv.Throw(NS_ERROR_FAILURE);
- }
- }
- nsresult
- Selection::NotifySelectionListeners()
- {
- if (!mFrameSelection)
- return NS_OK;//nothing to do
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- if (frameSelection->GetBatching()) {
- frameSelection->SetDirty();
- return NS_OK;
- }
- nsCOMArray<nsISelectionListener> selectionListeners(mSelectionListeners);
- int32_t cnt = selectionListeners.Count();
- if (cnt != mSelectionListeners.Count()) {
- return NS_ERROR_OUT_OF_MEMORY; // nsCOMArray is fallible
- }
- nsCOMPtr<nsIDOMDocument> domdoc;
- nsIPresShell* ps = GetPresShell();
- if (ps) {
- domdoc = do_QueryInterface(ps->GetDocument());
- }
- short reason = frameSelection->PopReason();
- for (int32_t i = 0; i < cnt; i++) {
- selectionListeners[i]->NotifySelectionChanged(domdoc, this, reason);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::StartBatchChanges()
- {
- if (mFrameSelection) {
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- frameSelection->StartBatchChanges();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Selection::EndBatchChanges()
- {
- return EndBatchChangesInternal();
- }
- nsresult
- Selection::EndBatchChangesInternal(int16_t aReason)
- {
- if (mFrameSelection) {
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- frameSelection->EndBatchChanges(aReason);
- }
- return NS_OK;
- }
- void
- Selection::AddSelectionChangeBlocker()
- {
- mSelectionChangeBlockerCount++;
- }
- void
- Selection::RemoveSelectionChangeBlocker()
- {
- MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
- "mSelectionChangeBlockerCount has an invalid value - "
- "maybe you have a mismatched RemoveSelectionChangeBlocker?");
- mSelectionChangeBlockerCount--;
- }
- bool
- Selection::IsBlockingSelectionChangeEvents() const
- {
- return mSelectionChangeBlockerCount > 0;
- }
- NS_IMETHODIMP
- Selection::DeleteFromDocument()
- {
- ErrorResult result;
- DeleteFromDocument(result);
- return result.StealNSResult();
- }
- void
- Selection::DeleteFromDocument(ErrorResult& aRv)
- {
- if (!mFrameSelection)
- return;//nothing to do
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- nsresult rv = frameSelection->DeleteFromDocument();
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- }
- }
- NS_IMETHODIMP
- Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
- const nsAString& aGranularity)
- {
- ErrorResult result;
- Modify(aAlter, aDirection, aGranularity, result);
- return result.StealNSResult();
- }
- void
- Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
- const nsAString& aGranularity, ErrorResult& aRv)
- {
- // Silently exit if there's no selection or no focus node.
- if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
- return;
- }
- if (!aAlter.LowerCaseEqualsLiteral("move") &&
- !aAlter.LowerCaseEqualsLiteral("extend")) {
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
- }
- if (!aDirection.LowerCaseEqualsLiteral("forward") &&
- !aDirection.LowerCaseEqualsLiteral("backward") &&
- !aDirection.LowerCaseEqualsLiteral("left") &&
- !aDirection.LowerCaseEqualsLiteral("right")) {
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
- }
- // Line moves are always visual.
- bool visual = aDirection.LowerCaseEqualsLiteral("left") ||
- aDirection.LowerCaseEqualsLiteral("right") ||
- aGranularity.LowerCaseEqualsLiteral("line");
- bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
- aDirection.LowerCaseEqualsLiteral("right");
- bool extend = aAlter.LowerCaseEqualsLiteral("extend");
- nsSelectionAmount amount;
- if (aGranularity.LowerCaseEqualsLiteral("character")) {
- amount = eSelectCluster;
- } else if (aGranularity.LowerCaseEqualsLiteral("word")) {
- amount = eSelectWordNoSpace;
- } else if (aGranularity.LowerCaseEqualsLiteral("line")) {
- amount = eSelectLine;
- } else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
- amount = forward ? eSelectEndLine : eSelectBeginLine;
- } else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
- aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
- aGranularity.LowerCaseEqualsLiteral("paragraph") ||
- aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
- aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
- aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
- return;
- } else {
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
- }
- // If the anchor doesn't equal the focus and we try to move without first
- // collapsing the selection, MoveCaret will collapse the selection and quit.
- // To avoid this, we need to collapse the selection first.
- nsresult rv = NS_OK;
- if (!extend) {
- nsINode* focusNode = GetFocusNode();
- // We should have checked earlier that there was a focus node.
- if (!focusNode) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return;
- }
- uint32_t focusOffset = FocusOffset();
- Collapse(focusNode, focusOffset);
- }
- // If the paragraph direction of the focused frame is right-to-left,
- // we may have to swap the direction of movement.
- nsIFrame *frame;
- int32_t offset;
- rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
- if (NS_SUCCEEDED(rv) && frame) {
- nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
- if (paraDir == NSBIDI_RTL && visual) {
- if (amount == eSelectBeginLine) {
- amount = eSelectEndLine;
- forward = !forward;
- } else if (amount == eSelectEndLine) {
- amount = eSelectBeginLine;
- forward = !forward;
- }
- }
- }
- // MoveCaret will return an error if it can't move in the specified
- // direction, but we just ignore this error unless it's a line move, in which
- // case we call nsISelectionController::CompleteMove to move the cursor to
- // the beginning/end of the line.
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- rv = frameSelection->MoveCaret(forward ? eDirNext : eDirPrevious,
- extend, amount,
- visual ? nsFrameSelection::eVisual
- : nsFrameSelection::eLogical);
- if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
- nsCOMPtr<nsISelectionController> shell =
- do_QueryInterface(frameSelection->GetShell());
- if (!shell)
- return;
- shell->CompleteMove(forward, extend);
- }
- }
- /** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
- * @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
- */
- NS_IMETHODIMP
- Selection::SelectionLanguageChange(bool aLangRTL)
- {
- if (!mFrameSelection)
- return NS_ERROR_NOT_INITIALIZED; // Can't do selection
- RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
- // if the direction of the language hasn't changed, nothing to do
- nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
- if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
- return NS_OK;
- }
- frameSelection->mKbdBidiLevel = kbdBidiLevel;
- nsresult result;
- nsIFrame *focusFrame = 0;
- result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false);
- if (NS_FAILED(result)) {
- return result;
- }
- if (!focusFrame) {
- return NS_ERROR_FAILURE;
- }
- int32_t frameStart, frameEnd;
- focusFrame->GetOffsets(frameStart, frameEnd);
- RefPtr<nsPresContext> context = GetPresContext();
- nsBidiLevel levelBefore, levelAfter;
- if (!context) {
- return NS_ERROR_FAILURE;
- }
- nsBidiLevel level = focusFrame->GetEmbeddingLevel();
- int32_t focusOffset = static_cast<int32_t>(FocusOffset());
- if ((focusOffset != frameStart) && (focusOffset != frameEnd))
- // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
- // is equal to the frame level
- levelBefore = levelAfter = level;
- else {
- // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
- // before and after the cursor
- nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
- nsPrevNextBidiLevels levels = frameSelection->
- GetPrevNextBidiLevels(focusContent, focusOffset, false);
-
- levelBefore = levels.mLevelBefore;
- levelAfter = levels.mLevelAfter;
- }
- if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
- // if cursor is between two characters with the same orientation, changing the keyboard language
- // must toggle the cursor level between the level of the character with the lowest level
- // (if the new language corresponds to the orientation of that character) and this level plus 1
- // (if the new language corresponds to the opposite orientation)
- if ((level != levelBefore) && (level != levelAfter))
- level = std::min(levelBefore, levelAfter);
- if (IS_SAME_DIRECTION(level, kbdBidiLevel))
- frameSelection->SetCaretBidiLevel(level);
- else
- frameSelection->SetCaretBidiLevel(level + 1);
- }
- else {
- // if cursor is between characters with opposite orientations, changing the keyboard language must change
- // the cursor level to that of the adjacent character with the orientation corresponding to the new language.
- if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
- frameSelection->SetCaretBidiLevel(levelBefore);
- else
- frameSelection->SetCaretBidiLevel(levelAfter);
- }
-
- // The caret might have moved, so invalidate the desired position
- // for future usages of up-arrow or down-arrow
- frameSelection->InvalidateDesiredPos();
-
- return NS_OK;
- }
- NS_IMETHODIMP_(nsDirection)
- Selection::GetSelectionDirection() {
- return mDirection;
- }
- NS_IMETHODIMP_(void)
- Selection::SetSelectionDirection(nsDirection aDirection) {
- mDirection = aDirection;
- }
- JSObject*
- Selection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return mozilla::dom::SelectionBinding::Wrap(aCx, this, aGivenProto);
- }
- // AutoHideSelectionChanges
- AutoHideSelectionChanges::AutoHideSelectionChanges(const nsFrameSelection* aFrame)
- : AutoHideSelectionChanges(
- aFrame ? aFrame->GetSelection(SelectionType::eNormal) : nullptr)
- {}
- // nsAutoCopyListener
- nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr;
- NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
- /*
- * What we do now:
- * On every selection change, we copy to the clipboard anew, creating a
- * HTML buffer, a transferable, an nsISupportsString and
- * a huge mess every time. This is basically what nsPresShell::DoCopy does
- * to move the selection into the clipboard for Edit->Copy.
- *
- * What we should do, to make our end of the deal faster:
- * Create a singleton transferable with our own magic converter. When selection
- * changes (use a quick cache to detect ``real'' changes), we put the new
- * nsISelection in the transferable. Our magic converter will take care of
- * transferable->whatever-other-format when the time comes to actually
- * hand over the clipboard contents.
- *
- * Other issues:
- * - which X clipboard should we populate?
- * - should we use a different one than Edit->Copy, so that inadvertant
- * selections (or simple clicks, which currently cause a selection
- * notification, regardless of if they're in the document which currently has
- * selection!) don't lose the contents of the ``application''? Or should we
- * just put some intelligence in the ``is this a real selection?'' code to
- * protect our selection against clicks in other documents that don't create
- * selections?
- * - maybe we should just never clear the X clipboard? That would make this
- * problem just go away, which is very tempting.
- *
- * On macOS,
- * nsIClipboard::kSelectionCache is the flag for current selection cache.
- * Set the current selection cache on the parent process in
- * widget cocoa nsClipboard whenever selection changes.
- */
- NS_IMETHODIMP
- nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
- nsISelection *aSel, int16_t aReason)
- {
- if (mCachedClipboard == nsIClipboard::kSelectionCache) {
- nsFocusManager* fm = nsFocusManager::GetFocusManager();
- // If no active window, do nothing because a current selection changed
- // cannot occur unless it is in the active window.
- if (!fm->GetActiveWindow()) {
- return NS_OK;
- }
- }
- if (!(aReason & nsISelectionListener::MOUSEUP_REASON ||
- aReason & nsISelectionListener::SELECTALL_REASON ||
- aReason & nsISelectionListener::KEYPRESS_REASON))
- return NS_OK; //dont care if we are still dragging
- bool collapsed;
- if (!aDoc || !aSel ||
- NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) {
- #ifdef DEBUG_CLIPBOARD
- fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
- #endif
- // If on macOS, clear the current selection transferable cached
- // on the parent process (nsClipboard) when the selection is empty.
- if (mCachedClipboard == nsIClipboard::kSelectionCache) {
- return nsCopySupport::ClearSelectionCache();
- }
- /* clear X clipboard? */
- return NS_OK;
- }
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
- NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
- // call the copy code
- return nsCopySupport::HTMLCopy(aSel, doc,
- mCachedClipboard, false);
- }
- /**
- * See Bug 1288453.
- *
- * Update the selection cache on repaint to handle when a pre-existing
- * selection becomes active aka the current selection.
- *
- * 1. Change the current selection by click n dragging another selection.
- * - Make a selection on content page. Make a selection in a text editor.
- * - You can click n drag the content selection to make it active again.
- * 2. Change the current selection when switching to a tab with a selection.
- * - Make selection in tab.
- * - Switching tabs will make its respective selection active.
- *
- * Therefore, we only update the selection cache on a repaint
- * if the current selection being repainted is not an empty selection.
- *
- * If the current selection is empty. The current selection cache
- * would be cleared by nsAutoCopyListener::NotifySelectionChanged.
- */
- nsresult
- nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
- {
- nsIPresShell* ps = aSel->GetPresShell();
- if (!ps) {
- return NS_OK;
- }
- nsCOMPtr<nsIDocument> aDoc = ps->GetDocument();
- bool collapsed;
- if (aDoc && aSel &&
- NS_SUCCEEDED(aSel->GetIsCollapsed(&collapsed)) && !collapsed) {
- return nsCopySupport::HTMLCopy(aSel, aDoc,
- nsIClipboard::kSelectionCache, false);
- }
- return NS_OK;
- }
- // SelectionChangeListener
- SelectionChangeListener::RawRangeData::RawRangeData(const nsRange* aRange)
- {
- mozilla::ErrorResult rv;
- mStartParent = aRange->GetStartContainer(rv);
- rv.SuppressException();
- mEndParent = aRange->GetEndContainer(rv);
- rv.SuppressException();
- mStartOffset = aRange->GetStartOffset(rv);
- rv.SuppressException();
- mEndOffset = aRange->GetEndOffset(rv);
- rv.SuppressException();
- }
- bool
- SelectionChangeListener::RawRangeData::Equals(const nsRange* aRange)
- {
- mozilla::ErrorResult rv;
- bool eq = mStartParent == aRange->GetStartContainer(rv);
- rv.SuppressException();
- eq = eq && mEndParent == aRange->GetEndContainer(rv);
- rv.SuppressException();
- eq = eq && mStartOffset == aRange->GetStartOffset(rv);
- rv.SuppressException();
- eq = eq && mEndOffset == aRange->GetEndOffset(rv);
- rv.SuppressException();
- return eq;
- }
- inline void
- ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
- SelectionChangeListener::RawRangeData& aField,
- const char* aName,
- uint32_t aFlags = 0)
- {
- ImplCycleCollectionTraverse(aCallback, aField.mStartParent, "mStartParent", aFlags);
- ImplCycleCollectionTraverse(aCallback, aField.mEndParent, "mEndParent", aFlags);
- }
- NS_IMPL_CYCLE_COLLECTION_CLASS(SelectionChangeListener)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SelectionChangeListener)
- tmp->mOldRanges.Clear();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SelectionChangeListener)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOldRanges);
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SelectionChangeListener)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(SelectionChangeListener)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(SelectionChangeListener)
- NS_IMETHODIMP
- SelectionChangeListener::NotifySelectionChanged(nsIDOMDocument* aDoc,
- nsISelection* aSel, int16_t aReason)
- {
- RefPtr<Selection> sel = aSel->AsSelection();
- nsIDocument* doc = sel->GetParentObject();
- if (!(doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal())) &&
- !nsFrameSelection::sSelectionEventsEnabled) {
- return NS_OK;
- }
- // Check if the ranges have actually changed
- // Don't bother checking this if we are hiding changes.
- if (mOldRanges.Length() == sel->RangeCount() && !sel->IsBlockingSelectionChangeEvents()) {
- bool changed = false;
- for (size_t i = 0; i < mOldRanges.Length(); i++) {
- if (!mOldRanges[i].Equals(sel->GetRangeAt(i))) {
- changed = true;
- break;
- }
- }
- if (!changed) {
- return NS_OK;
- }
- }
- // The ranges have actually changed, update the mOldRanges array
- mOldRanges.ClearAndRetainStorage();
- for (size_t i = 0; i < sel->RangeCount(); i++) {
- mOldRanges.AppendElement(RawRangeData(sel->GetRangeAt(i)));
- }
- // If we are hiding changes, then don't do anything else. We do this after we
- // update mOldRanges so that changes after the changes stop being hidden don't
- // incorrectly trigger a change, even though they didn't change anything
- if (sel->IsBlockingSelectionChangeEvents()) {
- return NS_OK;
- }
- // The spec currently doesn't say that we should dispatch this event on text
- // controls, so for now we only support doing that under a pref, disabled by
- // default.
- // See https://github.com/w3c/selection-api/issues/53.
- if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
- nsCOMPtr<nsINode> target;
- // Check if we should be firing this event to a different node than the
- // document. The limiter of the nsFrameSelection will be within the native
- // anonymous subtree of the node we want to fire the event on. We need to
- // climb up the parent chain to escape the native anonymous subtree, and then
- // fire the event.
- if (const nsFrameSelection* fs = sel->GetFrameSelection()) {
- if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
- while (root && root->IsInNativeAnonymousSubtree()) {
- root = root->GetParent();
- }
- target = root.forget();
- }
- }
- // If we didn't get a target before, we can instead fire the event at the document.
- if (!target) {
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
- target = doc.forget();
- }
- if (target) {
- RefPtr<AsyncEventDispatcher> asyncDispatcher =
- new AsyncEventDispatcher(target, NS_LITERAL_STRING("selectionchange"), false);
- asyncDispatcher->PostDOMEvent();
- }
- } else {
- if (const nsFrameSelection* fs = sel->GetFrameSelection()) {
- if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
- if (root->IsInNativeAnonymousSubtree()) {
- return NS_OK;
- }
- }
- }
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
- if (doc) {
- RefPtr<AsyncEventDispatcher> asyncDispatcher =
- new AsyncEventDispatcher(doc, NS_LITERAL_STRING("selectionchange"), false);
- asyncDispatcher->PostDOMEvent();
- }
- }
- return NS_OK;
- }
|