1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444 |
- /* -*- 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/. */
- #include "mozilla/Likely.h"
- #include "mozilla/MathAlgorithms.h"
- #include "mozilla/IntegerRange.h"
- #include "mozilla/WritingModes.h"
- #include "nsCOMPtr.h"
- #include "nsTableFrame.h"
- #include "nsRenderingContext.h"
- #include "nsStyleContext.h"
- #include "nsStyleConsts.h"
- #include "nsIContent.h"
- #include "nsCellMap.h"
- #include "nsTableCellFrame.h"
- #include "nsHTMLParts.h"
- #include "nsTableColFrame.h"
- #include "nsTableColGroupFrame.h"
- #include "nsTableRowFrame.h"
- #include "nsTableRowGroupFrame.h"
- #include "nsTableWrapperFrame.h"
- #include "BasicTableLayoutStrategy.h"
- #include "FixedTableLayoutStrategy.h"
- #include "nsPresContext.h"
- #include "nsContentUtils.h"
- #include "nsCSSRendering.h"
- #include "nsGkAtoms.h"
- #include "nsCSSAnonBoxes.h"
- #include "nsIPresShell.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMHTMLElement.h"
- #include "nsIScriptError.h"
- #include "nsFrameManager.h"
- #include "nsError.h"
- #include "nsCSSFrameConstructor.h"
- #include "mozilla/StyleSetHandle.h"
- #include "mozilla/StyleSetHandleInlines.h"
- #include "mozilla/gfx/Helpers.h"
- #include "nsDisplayList.h"
- #include "nsIScrollableFrame.h"
- #include "nsCSSProps.h"
- #include "RestyleTracker.h"
- #include <algorithm>
- using namespace mozilla;
- using namespace mozilla::gfx;
- using namespace mozilla::image;
- using namespace mozilla::layout;
- /********************************************************************************
- ** TableReflowInput **
- ********************************************************************************/
- namespace mozilla {
- struct TableReflowInput {
- // the real reflow state
- const ReflowInput& reflowInput;
- // The table's available size (in reflowInput's writing mode)
- LogicalSize availSize;
- // Stationary inline-offset
- nscoord iCoord;
- // Running block-offset
- nscoord bCoord;
- TableReflowInput(const ReflowInput& aReflowInput,
- const LogicalSize& aAvailSize)
- : reflowInput(aReflowInput)
- , availSize(aAvailSize)
- {
- MOZ_ASSERT(reflowInput.mFrame->GetType() == nsGkAtoms::tableFrame,
- "TableReflowInput should only be created for nsTableFrame");
- nsTableFrame* table =
- static_cast<nsTableFrame*>(reflowInput.mFrame->FirstInFlow());
- WritingMode wm = aReflowInput.GetWritingMode();
- LogicalMargin borderPadding = table->GetChildAreaOffset(wm, &reflowInput);
- iCoord = borderPadding.IStart(wm) + table->GetColSpacing(-1);
- bCoord = borderPadding.BStart(wm); //cellspacing added during reflow
- // XXX do we actually need to check for unconstrained inline-size here?
- if (NS_UNCONSTRAINEDSIZE != availSize.ISize(wm)) {
- int32_t colCount = table->GetColCount();
- availSize.ISize(wm) -= borderPadding.IStartEnd(wm) +
- table->GetColSpacing(-1) +
- table->GetColSpacing(colCount);
- availSize.ISize(wm) = std::max(0, availSize.ISize(wm));
- }
- if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
- availSize.BSize(wm) -= borderPadding.BStartEnd(wm) +
- table->GetRowSpacing(-1) +
- table->GetRowSpacing(table->GetRowCount());
- availSize.BSize(wm) = std::max(0, availSize.BSize(wm));
- }
- }
- void ReduceAvailableBSizeBy(WritingMode aWM, nscoord aAmount) {
- if (availSize.BSize(aWM) == NS_UNCONSTRAINEDSIZE) {
- return;
- }
- availSize.BSize(aWM) -= aAmount;
- availSize.BSize(aWM) = std::max(0, availSize.BSize(aWM));
- }
- };
- } // namespace mozilla
- /********************************************************************************
- ** nsTableFrame **
- ********************************************************************************/
- struct BCPropertyData
- {
- BCPropertyData() : mBStartBorderWidth(0), mIEndBorderWidth(0),
- mBEndBorderWidth(0), mIStartBorderWidth(0),
- mIStartCellBorderWidth(0), mIEndCellBorderWidth(0) {}
- TableArea mDamageArea;
- BCPixelSize mBStartBorderWidth;
- BCPixelSize mIEndBorderWidth;
- BCPixelSize mBEndBorderWidth;
- BCPixelSize mIStartBorderWidth;
- BCPixelSize mIStartCellBorderWidth;
- BCPixelSize mIEndCellBorderWidth;
- };
- nsStyleContext*
- nsTableFrame::GetParentStyleContext(nsIFrame** aProviderFrame) const
- {
- // Since our parent, the table wrapper frame, returned this frame, we
- // must return whatever our parent would normally have returned.
- NS_PRECONDITION(GetParent(), "table constructed without table wrapper");
- if (!mContent->GetParent() && !StyleContext()->GetPseudo()) {
- // We're the root. We have no style context parent.
- *aProviderFrame = nullptr;
- return nullptr;
- }
- return GetParent()->DoGetParentStyleContext(aProviderFrame);
- }
- nsIAtom*
- nsTableFrame::GetType() const
- {
- return nsGkAtoms::tableFrame;
- }
- nsTableFrame::nsTableFrame(nsStyleContext* aContext)
- : nsContainerFrame(aContext),
- mCellMap(nullptr),
- mTableLayoutStrategy(nullptr)
- {
- memset(&mBits, 0, sizeof(mBits));
- }
- void
- nsTableFrame::Init(nsIContent* aContent,
- nsContainerFrame* aParent,
- nsIFrame* aPrevInFlow)
- {
- NS_PRECONDITION(!mCellMap, "Init called twice");
- NS_PRECONDITION(!mTableLayoutStrategy, "Init called twice");
- NS_PRECONDITION(!aPrevInFlow ||
- aPrevInFlow->GetType() == nsGkAtoms::tableFrame,
- "prev-in-flow must be of same type");
- // Let the base class do its processing
- nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
- // see if border collapse is on, if so set it
- const nsStyleTableBorder* tableStyle = StyleTableBorder();
- bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
- SetBorderCollapse(borderCollapse);
- if (borderCollapse) {
- SetNeedToCalcHasBCBorders(true);
- }
- if (!aPrevInFlow) {
- // If we're the first-in-flow, we manage the cell map & layout strategy that
- // get used by our continuation chain:
- mCellMap = new nsTableCellMap(*this, borderCollapse);
- if (IsAutoLayout()) {
- mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
- } else {
- mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
- }
- } else {
- // Set my isize, because all frames in a table flow are the same isize and
- // code in nsTableWrapperFrame depends on this being set.
- WritingMode wm = GetWritingMode();
- SetSize(LogicalSize(wm, aPrevInFlow->ISize(wm), BSize(wm)));
- }
- }
- nsTableFrame::~nsTableFrame()
- {
- delete mCellMap;
- delete mTableLayoutStrategy;
- }
- void
- nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot)
- {
- mColGroups.DestroyFramesFrom(aDestructRoot);
- nsContainerFrame::DestroyFrom(aDestructRoot);
- }
- // Make sure any views are positioned properly
- void
- nsTableFrame::RePositionViews(nsIFrame* aFrame)
- {
- nsContainerFrame::PositionFrameView(aFrame);
- nsContainerFrame::PositionChildViews(aFrame);
- }
- static bool
- IsRepeatedFrame(nsIFrame* kidFrame)
- {
- return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
- kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
- kidFrame->HasAnyStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
- }
- bool
- nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
- nsIFrame* aNextFrame)
- {
- const nsStyleDisplay* display = aSourceFrame->StyleDisplay();
- nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
- // don't allow a page break after a repeated element ...
- if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) &&
- !IsRepeatedFrame(aSourceFrame)) {
- return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
- }
- if (aNextFrame) {
- display = aNextFrame->StyleDisplay();
- // don't allow a page break before a repeated element ...
- nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
- if ((display->mBreakBefore ||
- (nextRg && nextRg->HasInternalBreakBefore())) &&
- !IsRepeatedFrame(aNextFrame)) {
- return !IsRepeatedFrame(aSourceFrame); // or after
- }
- }
- return false;
- }
- /* static */ void
- nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
- {
- // Supporting relative positioning for table parts other than table cells has
- // the potential to break sites that apply 'position: relative' to those
- // parts, expecting nothing to happen. We warn at the console to make tracking
- // down the issue easy.
- if (!IS_TABLE_CELL(aFrame->GetType())) {
- nsIContent* content = aFrame->GetContent();
- nsPresContext* presContext = aFrame->PresContext();
- if (content && !presContext->HasWarnedAboutPositionedTableParts()) {
- presContext->SetHasWarnedAboutPositionedTableParts();
- nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("Layout: Tables"),
- content->OwnerDoc(),
- nsContentUtils::eLAYOUT_PROPERTIES,
- "TablePartRelPosWarning");
- }
- }
- nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
- MOZ_ASSERT(tableFrame, "Should have a table frame here");
- tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
- // Retrieve the positioned parts array for this table.
- FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
- // Lazily create the array if it doesn't exist yet.
- if (!positionedParts) {
- positionedParts = new FrameTArray;
- tableFrame->SetProperty(PositionedTablePartArray(), positionedParts);
- }
- // Add this frame to the list.
- positionedParts->AppendElement(aFrame);
- }
- /* static */ void
- nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
- nsIFrame* aDestructRoot)
- {
- // Retrieve the table frame, and check if we hit aDestructRoot on the way.
- bool didPassThrough;
- nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame,
- &didPassThrough);
- if (!didPassThrough && !tableFrame->GetPrevContinuation()) {
- // The table frame will be destroyed, and it's the first im flow (and thus
- // owning the PositionedTablePartArray), so we don't need to do
- // anything.
- return;
- }
- tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
- // Retrieve the positioned parts array for this table.
- FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
- // Remove the frame.
- MOZ_ASSERT(positionedParts && positionedParts->Contains(aFrame),
- "Asked to unregister a positioned table part that wasn't registered");
- if (positionedParts) {
- positionedParts->RemoveElement(aFrame);
- }
- }
- // XXX this needs to be cleaned up so that the frame constructor breaks out col group
- // frames into a separate child list, bug 343048.
- void
- nsTableFrame::SetInitialChildList(ChildListID aListID,
- nsFrameList& aChildList)
- {
- if (aListID != kPrincipalList) {
- nsContainerFrame::SetInitialChildList(aListID, aChildList);
- return;
- }
- MOZ_ASSERT(mFrames.IsEmpty() && mColGroups.IsEmpty(),
- "unexpected second call to SetInitialChildList");
- // XXXbz the below code is an icky cesspit that's only needed in its current
- // form for two reasons:
- // 1) Both rowgroups and column groups come in on the principal child list.
- while (aChildList.NotEmpty()) {
- nsIFrame* childFrame = aChildList.FirstChild();
- aChildList.RemoveFirstChild();
- const nsStyleDisplay* childDisplay = childFrame->StyleDisplay();
- if (mozilla::StyleDisplay::TableColumnGroup == childDisplay->mDisplay) {
- NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(),
- "This is not a colgroup");
- mColGroups.AppendFrame(nullptr, childFrame);
- }
- else { // row groups and unknown frames go on the main list for now
- mFrames.AppendFrame(nullptr, childFrame);
- }
- }
- // If we have a prev-in-flow, then we're a table that has been split and
- // so don't treat this like an append
- if (!GetPrevInFlow()) {
- // process col groups first so that real cols get constructed before
- // anonymous ones due to cells in rows.
- InsertColGroups(0, mColGroups);
- InsertRowGroups(mFrames);
- // calc collapsing borders
- if (IsBorderCollapse()) {
- SetFullBCDamageArea();
- }
- }
- }
- void
- nsTableFrame::AttributeChangedFor(nsIFrame* aFrame,
- nsIContent* aContent,
- nsIAtom* aAttribute)
- {
- nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
- if (cellFrame) {
- if ((nsGkAtoms::rowspan == aAttribute) ||
- (nsGkAtoms::colspan == aAttribute)) {
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- // for now just remove the cell from the map and reinsert it
- uint32_t rowIndex = cellFrame->RowIndex();
- uint32_t colIndex = cellFrame->ColIndex();
- RemoveCell(cellFrame, rowIndex);
- AutoTArray<nsTableCellFrame*, 1> cells;
- cells.AppendElement(cellFrame);
- InsertCells(cells, rowIndex, colIndex - 1);
- // XXX Should this use eStyleChange? It currently doesn't need
- // to, but it might given more optimization.
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
- }
- }
- }
- }
- /* ****** CellMap methods ******* */
- /* return the effective col count */
- int32_t
- nsTableFrame::GetEffectiveColCount() const
- {
- int32_t colCount = GetColCount();
- if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
- nsTableCellMap* cellMap = GetCellMap();
- if (!cellMap) {
- return 0;
- }
- // don't count cols at the end that don't have originating cells
- for (int32_t colIdx = colCount - 1; colIdx >= 0; colIdx--) {
- if (cellMap->GetNumCellsOriginatingInCol(colIdx) > 0) {
- break;
- }
- colCount--;
- }
- }
- return colCount;
- }
- int32_t
- nsTableFrame::GetIndexOfLastRealCol()
- {
- int32_t numCols = mColFrames.Length();
- if (numCols > 0) {
- for (int32_t colIdx = numCols - 1; colIdx >= 0; colIdx--) {
- nsTableColFrame* colFrame = GetColFrame(colIdx);
- if (colFrame) {
- if (eColAnonymousCell != colFrame->GetColType()) {
- return colIdx;
- }
- }
- }
- }
- return -1;
- }
- nsTableColFrame*
- nsTableFrame::GetColFrame(int32_t aColIndex) const
- {
- NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
- int32_t numCols = mColFrames.Length();
- if ((aColIndex >= 0) && (aColIndex < numCols)) {
- return mColFrames.ElementAt(aColIndex);
- }
- else {
- NS_ERROR("invalid col index");
- return nullptr;
- }
- }
- int32_t
- nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex,
- const nsTableCellFrame& aCell) const
- {
- nsTableCellMap* cellMap = GetCellMap();
- NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated.");
- return cellMap->GetEffectiveRowSpan(aRowIndex, aCell.ColIndex());
- }
- int32_t
- nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
- nsCellMap* aCellMap)
- {
- nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
- uint32_t colIndex = aCell.ColIndex();
- uint32_t rowIndex = aCell.RowIndex();
- if (aCellMap)
- return aCellMap->GetRowSpan(rowIndex, colIndex, true);
- else
- return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
- }
- int32_t
- nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
- nsCellMap* aCellMap) const
- {
- nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
- uint32_t colIndex = aCell.ColIndex();
- uint32_t rowIndex = aCell.RowIndex();
- if (aCellMap)
- return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex);
- else
- return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
- }
- bool
- nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const
- {
- nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
- return tableCellMap->HasMoreThanOneCell(aRowIndex);
- }
- void
- nsTableFrame::AdjustRowIndices(int32_t aRowIndex,
- int32_t aAdjustment)
- {
- // Iterate over the row groups and adjust the row indices of all rows
- // whose index is >= aRowIndex.
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- rowGroups[rgIdx]->AdjustRowIndices(aRowIndex, aAdjustment);
- }
- }
- void
- nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
- {
- // Iterate over the row groups and adjust the row indices of all rows
- // omit the rowgroups that will be inserted later
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- int32_t rowIndex = 0;
- nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
- nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
- while (!excludeRowGroupsEnumerator.AtEnd()) {
- excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
- excludeRowGroupsEnumerator.Next();
- }
- for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- if (!excludeRowGroups.GetEntry(rgFrame)) {
- const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
- for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
- if (mozilla::StyleDisplay::TableRow == rows.get()->StyleDisplay()->mDisplay) {
- ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex);
- rowIndex++;
- }
- }
- }
- }
- }
- void
- nsTableFrame::InsertColGroups(int32_t aStartColIndex,
- const nsFrameList::Slice& aColGroups)
- {
- int32_t colIndex = aStartColIndex;
- nsFrameList::Enumerator colGroups(aColGroups);
- for (; !colGroups.AtEnd(); colGroups.Next()) {
- MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame);
- nsTableColGroupFrame* cgFrame =
- static_cast<nsTableColGroupFrame*>(colGroups.get());
- cgFrame->SetStartColumnIndex(colIndex);
- // XXXbz this sucks. AddColsToTable will actually remove colgroups from
- // the list we're traversing! Need to fix things here. :( I guess this is
- // why the old code used pointer-to-last-frame as opposed to
- // pointer-to-frame-after-last....
- // How about dealing with this by storing a const reference to the
- // mNextSibling of the framelist's last frame, instead of storing a pointer
- // to the first-after-next frame? Will involve making nsFrameList friend
- // of nsIFrame, but it's time for that anyway.
- cgFrame->AddColsToTable(colIndex, false,
- colGroups.get()->PrincipalChildList());
- int32_t numCols = cgFrame->GetColCount();
- colIndex += numCols;
- }
- nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator();
- if (!remainingColgroups.AtEnd()) {
- nsTableColGroupFrame::ResetColIndices(
- static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex);
- }
- }
- void
- nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
- int32_t aColIndex)
- {
- mColFrames.InsertElementAt(aColIndex, &aColFrame);
- nsTableColType insertedColType = aColFrame.GetColType();
- int32_t numCacheCols = mColFrames.Length();
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- int32_t numMapCols = cellMap->GetColCount();
- if (numCacheCols > numMapCols) {
- bool removedFromCache = false;
- if (eColAnonymousCell != insertedColType) {
- nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
- if (lastCol) {
- nsTableColType lastColType = lastCol->GetColType();
- if (eColAnonymousCell == lastColType) {
- // remove the col from the cache
- mColFrames.RemoveElementAt(numCacheCols - 1);
- // remove the col from the eColGroupAnonymousCell col group
- nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
- if (lastColGroup) {
- lastColGroup->RemoveChild(*lastCol, false);
- // remove the col group if it is empty
- if (lastColGroup->GetColCount() <= 0) {
- mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
- }
- }
- removedFromCache = true;
- }
- }
- }
- if (!removedFromCache) {
- cellMap->AddColsAtEnd(1);
- }
- }
- }
- // for now, just bail and recalc all of the collapsing borders
- if (IsBorderCollapse()) {
- TableArea damageArea(aColIndex, 0, 1, GetRowCount());
- AddBCDamageArea(damageArea);
- }
- }
- void
- nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
- int32_t aColIndex,
- bool aRemoveFromCache,
- bool aRemoveFromCellMap)
- {
- if (aRemoveFromCache) {
- mColFrames.RemoveElementAt(aColIndex);
- }
- if (aRemoveFromCellMap) {
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- // If we have some anonymous cols at the end already, we just
- // add a new anonymous col.
- if (!mColFrames.IsEmpty() &&
- mColFrames.LastElement() && // XXXbz is this ever null?
- mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
- AppendAnonymousColFrames(1);
- } else {
- // All of our colframes correspond to actual <col> tags. It's possible
- // that we still have at least as many <col> tags as we have logical
- // columns from cells, but we might have one less. Handle the latter
- // case as follows: First ask the cellmap to drop its last col if it
- // doesn't have any actual cells in it. Then call
- // MatchCellMapToColCache to append an anonymous column if it's needed;
- // this needs to be after RemoveColsAtEnd, since it will determine the
- // need for a new column frame based on the width of the cell map.
- cellMap->RemoveColsAtEnd();
- MatchCellMapToColCache(cellMap);
- }
- }
- }
- // for now, just bail and recalc all of the collapsing borders
- if (IsBorderCollapse()) {
- TableArea damageArea(0, 0, GetColCount(), GetRowCount());
- AddBCDamageArea(damageArea);
- }
- }
- /** Get the cell map for this table frame. It is not always mCellMap.
- * Only the first-in-flow has a legit cell map.
- */
- nsTableCellMap*
- nsTableFrame::GetCellMap() const
- {
- return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap;
- }
- // XXX this needs to be moved to nsCSSFrameConstructor
- nsTableColGroupFrame*
- nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
- {
- nsIContent* colGroupContent = GetContent();
- nsPresContext* presContext = PresContext();
- nsIPresShell *shell = presContext->PresShell();
- RefPtr<nsStyleContext> colGroupStyle;
- colGroupStyle = shell->StyleSet()->
- ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext);
- // Create a col group frame
- nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
- ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
- newFrame->Init(colGroupContent, this, nullptr);
- return (nsTableColGroupFrame *)newFrame;
- }
- void
- nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd)
- {
- // get the last col group frame
- nsTableColGroupFrame* colGroupFrame =
- static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
- if (!colGroupFrame ||
- (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
- int32_t colIndex = (colGroupFrame) ?
- colGroupFrame->GetStartColumnIndex() +
- colGroupFrame->GetColCount() : 0;
- colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
- if (!colGroupFrame) {
- return;
- }
- // add the new frame to the child list
- mColGroups.AppendFrame(this, colGroupFrame);
- colGroupFrame->SetStartColumnIndex(colIndex);
- }
- AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
- true);
- }
- // XXX this needs to be moved to nsCSSFrameConstructor
- // Right now it only creates the col frames at the end
- void
- nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
- int32_t aNumColsToAdd,
- nsTableColType aColType,
- bool aAddToTable)
- {
- NS_PRECONDITION(aColGroupFrame, "null frame");
- NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
- nsIPresShell *shell = PresContext()->PresShell();
- // Get the last col frame
- nsFrameList newColFrames;
- int32_t startIndex = mColFrames.Length();
- int32_t lastIndex = startIndex + aNumColsToAdd - 1;
- for (int32_t childX = startIndex; childX <= lastIndex; childX++) {
- nsIContent* iContent;
- RefPtr<nsStyleContext> styleContext;
- nsStyleContext* parentStyleContext;
- // all anonymous cols that we create here use a pseudo style context of the
- // col group
- iContent = aColGroupFrame->GetContent();
- parentStyleContext = aColGroupFrame->StyleContext();
- styleContext = shell->StyleSet()->
- ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext);
- // ASSERTION to check for bug 54454 sneaking back in...
- NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
- // create the new col frame
- nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
- ((nsTableColFrame *) colFrame)->SetColType(aColType);
- colFrame->Init(iContent, aColGroupFrame, nullptr);
- newColFrames.AppendFrame(nullptr, colFrame);
- }
- nsFrameList& cols = aColGroupFrame->GetWritableChildList();
- nsIFrame* oldLastCol = cols.LastChild();
- const nsFrameList::Slice& newCols =
- cols.InsertFrames(nullptr, oldLastCol, newColFrames);
- if (aAddToTable) {
- // get the starting col index in the cache
- int32_t startColIndex;
- if (oldLastCol) {
- startColIndex =
- static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
- } else {
- startColIndex = aColGroupFrame->GetStartColumnIndex();
- }
- aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
- }
- }
- void
- nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
- {
- int32_t numColsInMap = GetColCount();
- int32_t numColsInCache = mColFrames.Length();
- int32_t numColsToAdd = numColsInMap - numColsInCache;
- if (numColsToAdd > 0) {
- // this sets the child list, updates the col cache and cell map
- AppendAnonymousColFrames(numColsToAdd);
- }
- if (numColsToAdd < 0) {
- int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
- // if the cell map has fewer cols than the cache, correct it
- if (numColsNotRemoved > 0) {
- aCellMap->AddColsAtEnd(numColsNotRemoved);
- }
- }
- }
- void
- nsTableFrame::DidResizeColumns()
- {
- NS_PRECONDITION(!GetPrevInFlow(),
- "should only be called on first-in-flow");
- if (mBits.mResizedColumns)
- return; // already marked
- for (nsTableFrame *f = this; f;
- f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
- f->mBits.mResizedColumns = true;
- }
- void
- nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
- int32_t aRowIndex)
- {
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- TableArea damageArea(0, 0, 0, 0);
- cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
- MatchCellMapToColCache(cellMap);
- if (IsBorderCollapse()) {
- AddBCDamageArea(damageArea);
- }
- }
- }
- void
- nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
- int32_t aRowIndex,
- int32_t aColIndexBefore)
- {
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- TableArea damageArea(0, 0, 0, 0);
- cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
- MatchCellMapToColCache(cellMap);
- if (IsBorderCollapse()) {
- AddBCDamageArea(damageArea);
- }
- }
- }
- // this removes the frames from the col group and table, but not the cell map
- int32_t
- nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames)
- {
- // only remove cols that are of type eTypeAnonymous cell (they are at the end)
- int32_t endIndex = mColFrames.Length() - 1;
- int32_t startIndex = (endIndex - aNumFrames) + 1;
- int32_t numColsRemoved = 0;
- for (int32_t colIdx = endIndex; colIdx >= startIndex; colIdx--) {
- nsTableColFrame* colFrame = GetColFrame(colIdx);
- if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
- nsTableColGroupFrame* cgFrame =
- static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
- // remove the frame from the colgroup
- cgFrame->RemoveChild(*colFrame, false);
- // remove the frame from the cache, but not the cell map
- RemoveCol(nullptr, colIdx, true, false);
- numColsRemoved++;
- }
- else {
- break;
- }
- }
- return (aNumFrames - numColsRemoved);
- }
- void
- nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
- int32_t aRowIndex)
- {
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- TableArea damageArea(0, 0, 0, 0);
- cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
- MatchCellMapToColCache(cellMap);
- if (IsBorderCollapse()) {
- AddBCDamageArea(damageArea);
- }
- }
- }
- int32_t
- nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame)
- {
- RowGroupArray orderedRowGroups;
- OrderRowGroups(orderedRowGroups);
- int32_t rowIndex = 0;
- for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
- nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
- if (rgFrame == aRowGroupFrame) {
- break;
- }
- int32_t numRows = rgFrame->GetRowCount();
- rowIndex += numRows;
- }
- return rowIndex;
- }
- // this cannot extend beyond a single row group
- void
- nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame,
- int32_t aRowIndex,
- nsTArray<nsTableRowFrame*>& aRowFrames)
- {
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
- InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
- }
- }
- // this cannot extend beyond a single row group
- int32_t
- nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame,
- nsTArray<nsTableRowFrame*>& aRowFrames,
- int32_t aRowIndex,
- bool aConsiderSpans)
- {
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
- Dump(true, false, true);
- #endif
- int32_t numColsToAdd = 0;
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- TableArea damageArea(0, 0, 0, 0);
- int32_t origNumRows = cellMap->GetRowCount();
- int32_t numNewRows = aRowFrames.Length();
- cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
- MatchCellMapToColCache(cellMap);
- if (aRowIndex < origNumRows) {
- AdjustRowIndices(aRowIndex, numNewRows);
- }
- // assign the correct row indices to the new rows. If they were adjusted above
- // it may not have been done correctly because each row is constructed with index 0
- for (int32_t rowB = 0; rowB < numNewRows; rowB++) {
- nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowB);
- rowFrame->SetRowIndex(aRowIndex + rowB);
- }
- if (IsBorderCollapse()) {
- AddBCDamageArea(damageArea);
- }
- }
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== insertRowsAfter \n");
- Dump(true, false, true);
- #endif
- return numColsToAdd;
- }
- // this cannot extend beyond a single row group
- void
- nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
- int32_t aNumRowsToRemove,
- bool aConsiderSpans)
- {
- #ifdef TBD_OPTIMIZATION
- // decide if we need to rebalance. we have to do this here because the row group
- // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
- bool stopTelling = false;
- for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
- kidFrame = kidFrame->GetNextSibling()) {
- nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
- if (cellFrame) {
- stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
- cellFrame->GetMaximumWidth(), true);
- }
- }
- // XXX need to consider what happens if there are cells that have rowspans
- // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
- #endif
- int32_t firstRowIndex = aFirstRowFrame.GetRowIndex();
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
- Dump(true, false, true);
- #endif
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- TableArea damageArea(0, 0, 0, 0);
- cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
- MatchCellMapToColCache(cellMap);
- if (IsBorderCollapse()) {
- AddBCDamageArea(damageArea);
- }
- }
- AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== removeRowsAfter\n");
- Dump(true, true, true);
- #endif
- }
- // collect the rows ancestors of aFrame
- int32_t
- nsTableFrame::CollectRows(nsIFrame* aFrame,
- nsTArray<nsTableRowFrame*>& aCollection)
- {
- NS_PRECONDITION(aFrame, "null frame");
- int32_t numRows = 0;
- for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
- aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
- numRows++;
- }
- return numRows;
- }
- void
- nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups)
- {
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== insertRowGroupsBefore\n");
- Dump(true, false, true);
- #endif
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- RowGroupArray orderedRowGroups;
- OrderRowGroups(orderedRowGroups);
- AutoTArray<nsTableRowFrame*, 8> rows;
- // Loop over the rowgroups and check if some of them are new, if they are
- // insert cellmaps in the order that is predefined by OrderRowGroups,
- // XXXbz this code is O(N*M) where N is number of new rowgroups
- // and M is number of rowgroups we have!
- uint32_t rgIndex;
- for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
- for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
- rowgroups.Next()) {
- if (orderedRowGroups[rgIndex] == rowgroups.get()) {
- nsTableRowGroupFrame* priorRG =
- (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
- // create and add the cell map for the row group
- cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
- break;
- }
- }
- }
- cellMap->Synchronize(this);
- ResetRowIndices(aRowGroups);
- //now that the cellmaps are reordered too insert the rows
- for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
- for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
- rowgroups.Next()) {
- if (orderedRowGroups[rgIndex] == rowgroups.get()) {
- nsTableRowGroupFrame* priorRG =
- (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
- // collect the new row frames in an array and add them to the table
- int32_t numRows = CollectRows(rowgroups.get(), rows);
- if (numRows > 0) {
- int32_t rowIndex = 0;
- if (priorRG) {
- int32_t priorNumRows = priorRG->GetRowCount();
- rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
- }
- InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
- rows.Clear();
- }
- break;
- }
- }
- }
- }
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== insertRowGroupsAfter\n");
- Dump(true, true, true);
- #endif
- }
- /////////////////////////////////////////////////////////////////////////////
- // Child frame enumeration
- const nsFrameList&
- nsTableFrame::GetChildList(ChildListID aListID) const
- {
- if (aListID == kColGroupList) {
- return mColGroups;
- }
- return nsContainerFrame::GetChildList(aListID);
- }
- void
- nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const
- {
- nsContainerFrame::GetChildLists(aLists);
- mColGroups.AppendIfNonempty(aLists, kColGroupList);
- }
- nsRect
- nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
- *aSnap = false;
- return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
- }
- void
- nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
- {
- nsStyleContext *bgSC;
- if (!nsCSSRendering::FindBackground(aFrame, &bgSC))
- return;
- if (!bgSC->StyleBackground()->HasFixedBackground(aFrame))
- return;
- mPartHasFixedBackground = true;
- }
- nsDisplayItemGeometry*
- nsDisplayTableItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
- {
- return new nsDisplayTableItemGeometry(this, aBuilder,
- mFrame->GetOffsetTo(mFrame->PresContext()->PresShell()->GetRootFrame()));
- }
- void
- nsDisplayTableItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
- const nsDisplayItemGeometry* aGeometry,
- nsRegion *aInvalidRegion)
- {
- auto geometry =
- static_cast<const nsDisplayTableItemGeometry*>(aGeometry);
- bool invalidateForAttachmentFixed = false;
- if (mDrawsBackground && mPartHasFixedBackground) {
- nsPoint frameOffsetToViewport = mFrame->GetOffsetTo(
- mFrame->PresContext()->PresShell()->GetRootFrame());
- invalidateForAttachmentFixed =
- frameOffsetToViewport != geometry->mFrameOffsetToViewport;
- }
- if (invalidateForAttachmentFixed ||
- (aBuilder->ShouldSyncDecodeImages() &&
- geometry->ShouldInvalidateToSyncDecodeImages())) {
- bool snap;
- aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
- }
- nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
- }
- // A display item that draws all collapsed borders for a table.
- class nsDisplayTableBorderCollapse : public nsDisplayTableItem {
- public:
- nsDisplayTableBorderCollapse(nsDisplayListBuilder* aBuilder,
- nsTableFrame* aFrame)
- : nsDisplayTableItem(aBuilder, aFrame) {
- MOZ_COUNT_CTOR(nsDisplayTableBorderCollapse);
- }
- #ifdef NS_BUILD_REFCNT_LOGGING
- virtual ~nsDisplayTableBorderCollapse() {
- MOZ_COUNT_DTOR(nsDisplayTableBorderCollapse);
- }
- #endif
- virtual void Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx) override;
- NS_DISPLAY_DECL_NAME("TableBorderCollapse", TYPE_TABLE_BORDER_COLLAPSE)
- };
- void
- nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx)
- {
- nsPoint pt = ToReferenceFrame();
- DrawTarget* drawTarget = aCtx->GetDrawTarget();
- gfxPoint devPixelOffset =
- nsLayoutUtils::PointToGfxPoint(pt, mFrame->PresContext()->AppUnitsPerDevPixel());
- AutoRestoreTransform autoRestoreTransform(drawTarget);
- drawTarget->SetTransform(
- drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
- static_cast<nsTableFrame*>(mFrame)->PaintBCBorders(*drawTarget, mVisibleRect - pt);
- }
- static inline bool FrameHasBorder(nsIFrame* f) {
- if (!f->StyleVisibility()->IsVisible()) {
- return false;
- }
- if (f->StyleBorder()->HasBorder()) {
- return true;
- }
- return false;
- }
- void nsTableFrame::CalcHasBCBorders() {
- if (!IsBorderCollapse()) {
- SetHasBCBorders(false);
- return;
- }
- if (FrameHasBorder(this)) {
- SetHasBCBorders(true);
- return;
- }
- // Check col and col group has borders.
- for (nsIFrame* f : this->GetChildList(kColGroupList)) {
- if (FrameHasBorder(f)) {
- SetHasBCBorders(true);
- return;
- }
- nsTableColGroupFrame* colGroup = static_cast<nsTableColGroupFrame*>(f);
- for (nsTableColFrame* col = colGroup->GetFirstColumn(); col;
- col = col->GetNextCol()) {
- if (FrameHasBorder(col)) {
- SetHasBCBorders(true);
- return;
- }
- }
- }
- // check row group, row and cell has borders.
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- for (nsTableRowGroupFrame* rowGroup : rowGroups) {
- if (FrameHasBorder(rowGroup)) {
- SetHasBCBorders(true);
- return;
- }
- for (nsTableRowFrame* row = rowGroup->GetFirstRow(); row;
- row = row->GetNextRow()) {
- if (FrameHasBorder(row)) {
- SetHasBCBorders(true);
- return;
- }
- for (nsTableCellFrame* cell = row->GetFirstCell(); cell;
- cell = cell->GetNextCell()) {
- if (FrameHasBorder(cell)) {
- SetHasBCBorders(true);
- return;
- }
- }
- }
- }
- SetHasBCBorders(false);
- }
- // table paint code is concerned primarily with borders and bg color
- // SEC: TODO: adjust the rect for captions
- void
- nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsDisplayListSet& aLists)
- {
- DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
- DisplayBorderBackgroundOutline(aBuilder, aLists);
- nsDisplayTableBackgroundSet tableBGs(aBuilder, this);
- nsDisplayListCollection lists(aBuilder);
- // This is similar to what
- // nsContainerFrame::BuildDisplayListForNonBlockChildren does, except that we
- // allow the children's background and borders to go in our BorderBackground
- // list. This doesn't really affect background painting --- the children won't
- // actually draw their own backgrounds because the nsTableFrame already drew
- // them, unless a child has its own stacking context, in which case the child
- // won't use its passed-in BorderBackground list anyway. It does affect cell
- // borders though; this lets us get cell borders into the nsTableFrame's
- // BorderBackground list.
- for (nsIFrame* colGroup : FirstContinuation()->GetChildList(kColGroupList)) {
- for (nsIFrame* col : colGroup->PrincipalChildList()) {
- tableBGs.AddColumn((nsTableColFrame*)col);
- }
- }
- for (nsIFrame* kid : PrincipalChildList()) {
- BuildDisplayListForChild(aBuilder, kid, lists);
- }
- tableBGs.MoveTo(aLists);
- lists.MoveTo(aLists);
- if (IsVisibleForPainting(aBuilder)) {
- // In the collapsed border model, overlay all collapsed borders.
- if (IsBorderCollapse()) {
- if (HasBCBorders()) {
- aLists.BorderBackground()->AppendNewToTop(
- new (aBuilder) nsDisplayTableBorderCollapse
- (aBuilder, this));
- }
- } else {
- const nsStyleBorder* borderStyle = StyleBorder();
- if (borderStyle->HasBorder()) {
- aLists.BorderBackground()->AppendNewToTop(
- new (aBuilder) nsDisplayBorder
- (aBuilder, this));
- }
- }
- }
- }
- nsMargin
- nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
- {
- if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
- !IsBorderCollapse())
- return nsMargin(0,0,0,0);
- WritingMode wm = GetWritingMode();
- return GetOuterBCBorder(wm).GetPhysicalMargin(wm);
- }
- nsIFrame::LogicalSides
- nsTableFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
- {
- if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
- StyleBoxDecorationBreak::Clone)) {
- return LogicalSides();
- }
- LogicalSides skip;
- // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
- // account for pagination
- if (nullptr != GetPrevInFlow()) {
- skip |= eLogicalSideBitsBStart;
- }
- if (nullptr != GetNextInFlow()) {
- skip |= eLogicalSideBitsBEnd;
- }
- return skip;
- }
- void
- nsTableFrame::SetColumnDimensions(nscoord aBSize, WritingMode aWM,
- const LogicalMargin& aBorderPadding,
- const nsSize& aContainerSize)
- {
- const nscoord colBSize = aBSize - (aBorderPadding.BStartEnd(aWM) +
- GetRowSpacing(-1) + GetRowSpacing(GetRowCount()));
- int32_t colIdx = 0;
- LogicalPoint colGroupOrigin(aWM,
- aBorderPadding.IStart(aWM) + GetColSpacing(-1),
- aBorderPadding.BStart(aWM) + GetRowSpacing(-1));
- nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
- for (nsIFrame* colGroupFrame : mColGroups) {
- MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame);
- // first we need to figure out the size of the colgroup
- int32_t groupFirstCol = colIdx;
- nscoord colGroupISize = 0;
- nscoord cellSpacingI = 0;
- const nsFrameList& columnList = colGroupFrame->PrincipalChildList();
- for (nsIFrame* colFrame : columnList) {
- if (mozilla::StyleDisplay::TableColumn ==
- colFrame->StyleDisplay()->mDisplay) {
- NS_ASSERTION(colIdx < GetColCount(), "invalid number of columns");
- cellSpacingI = GetColSpacing(colIdx);
- colGroupISize += fif->GetColumnISizeFromFirstInFlow(colIdx) +
- cellSpacingI;
- ++colIdx;
- }
- }
- if (colGroupISize) {
- colGroupISize -= cellSpacingI;
- }
- LogicalRect colGroupRect(aWM, colGroupOrigin.I(aWM), colGroupOrigin.B(aWM),
- colGroupISize, colBSize);
- colGroupFrame->SetRect(aWM, colGroupRect, aContainerSize);
- nsSize colGroupSize = colGroupFrame->GetSize();
- // then we can place the columns correctly within the group
- colIdx = groupFirstCol;
- LogicalPoint colOrigin(aWM);
- for (nsIFrame* colFrame : columnList) {
- if (mozilla::StyleDisplay::TableColumn ==
- colFrame->StyleDisplay()->mDisplay) {
- nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
- LogicalRect colRect(aWM, colOrigin.I(aWM), colOrigin.B(aWM),
- colISize, colBSize);
- colFrame->SetRect(aWM, colRect, colGroupSize);
- cellSpacingI = GetColSpacing(colIdx);
- colOrigin.I(aWM) += colISize + cellSpacingI;
- ++colIdx;
- }
- }
- colGroupOrigin.I(aWM) += colGroupISize + cellSpacingI;
- }
- }
- // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
- // XXX this could be made more general to handle row modifications that change the
- // table bsize, but first we need to scrutinize every Invalidate
- void
- nsTableFrame::ProcessRowInserted(nscoord aNewBSize)
- {
- SetRowInserted(false); // reset the bit that got us here
- nsTableFrame::RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- // find the row group containing the inserted row
- for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- NS_ASSERTION(rgFrame, "Must have rgFrame here");
- // find the row that was inserted first
- for (nsIFrame* childFrame : rgFrame->PrincipalChildList()) {
- nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
- if (rowFrame) {
- if (rowFrame->IsFirstInserted()) {
- rowFrame->SetFirstInserted(false);
- // damage the table from the 1st row inserted to the end of the table
- nsIFrame::InvalidateFrame();
- // XXXbz didn't we do this up front? Why do we need to do it again?
- SetRowInserted(false);
- return; // found it, so leave
- }
- }
- }
- }
- }
- /* virtual */ void
- nsTableFrame::MarkIntrinsicISizesDirty()
- {
- nsITableLayoutStrategy* tls = LayoutStrategy();
- if (MOZ_UNLIKELY(!tls)) {
- // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
- // walking up the ancestor chain in a table next-in-flow. In this case
- // our original first-in-flow (which owns the TableLayoutStrategy) has
- // already been destroyed and unhooked from the flow chain and thusly
- // LayoutStrategy() returns null. All the frames in the flow will be
- // destroyed so no need to mark anything dirty here. See bug 595758.
- return;
- }
- tls->MarkIntrinsicISizesDirty();
- // XXXldb Call SetBCDamageArea?
- nsContainerFrame::MarkIntrinsicISizesDirty();
- }
- /* virtual */ nscoord
- nsTableFrame::GetMinISize(nsRenderingContext *aRenderingContext)
- {
- if (NeedToCalcBCBorders())
- CalcBCBorders();
- ReflowColGroups(aRenderingContext);
- return LayoutStrategy()->GetMinISize(aRenderingContext);
- }
- /* virtual */ nscoord
- nsTableFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
- {
- if (NeedToCalcBCBorders())
- CalcBCBorders();
- ReflowColGroups(aRenderingContext);
- return LayoutStrategy()->GetPrefISize(aRenderingContext, false);
- }
- /* virtual */ nsIFrame::IntrinsicISizeOffsetData
- nsTableFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis)
- {
- IntrinsicISizeOffsetData result =
- nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
- result.hMargin = 0;
- if (IsBorderCollapse()) {
- result.hPadding = 0;
- WritingMode wm = GetWritingMode();
- LogicalMargin outerBC = GetIncludedOuterBCBorder(wm);
- result.hBorder = outerBC.IStartEnd(wm);
- }
- return result;
- }
- /* virtual */
- LogicalSize
- nsTableFrame::ComputeSize(nsRenderingContext* aRenderingContext,
- WritingMode aWM,
- const LogicalSize& aCBSize,
- nscoord aAvailableISize,
- const LogicalSize& aMargin,
- const LogicalSize& aBorder,
- const LogicalSize& aPadding,
- ComputeSizeFlags aFlags)
- {
- LogicalSize result =
- nsContainerFrame::ComputeSize(aRenderingContext, aWM,
- aCBSize, aAvailableISize,
- aMargin, aBorder, aPadding, aFlags);
- // XXX The code below doesn't make sense if the caller's writing mode
- // is orthogonal to this frame's. Not sure yet what should happen then;
- // for now, just bail out.
- if (aWM.IsVertical() != GetWritingMode().IsVertical()) {
- return result;
- }
- // If we're a container for font size inflation, then shrink
- // wrapping inside of us should not apply font size inflation.
- AutoMaybeDisableFontInflation an(this);
- // Tables never shrink below their min inline-size.
- nscoord minISize = GetMinISize(aRenderingContext);
- if (minISize > result.ISize(aWM)) {
- result.ISize(aWM) = minISize;
- }
- return result;
- }
- nscoord
- nsTableFrame::TableShrinkISizeToFit(nsRenderingContext *aRenderingContext,
- nscoord aISizeInCB)
- {
- // If we're a container for font size inflation, then shrink
- // wrapping inside of us should not apply font size inflation.
- AutoMaybeDisableFontInflation an(this);
- nscoord result;
- nscoord minISize = GetMinISize(aRenderingContext);
- if (minISize > aISizeInCB) {
- result = minISize;
- } else {
- // Tables shrink inline-size to fit with a slightly different algorithm
- // from the one they use for their intrinsic isize (the difference
- // relates to handling of percentage isizes on columns). So this
- // function differs from nsFrame::ShrinkWidthToFit by only the
- // following line.
- // Since we've already called GetMinISize, we don't need to do any
- // of the other stuff GetPrefISize does.
- nscoord prefISize =
- LayoutStrategy()->GetPrefISize(aRenderingContext, true);
- if (prefISize > aISizeInCB) {
- result = aISizeInCB;
- } else {
- result = prefISize;
- }
- }
- return result;
- }
- /* virtual */
- LogicalSize
- nsTableFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
- WritingMode aWM,
- const LogicalSize& aCBSize,
- nscoord aAvailableISize,
- const LogicalSize& aMargin,
- const LogicalSize& aBorder,
- const LogicalSize& aPadding,
- ComputeSizeFlags aFlags)
- {
- // Tables always shrink-wrap.
- nscoord cbBased = aAvailableISize - aMargin.ISize(aWM) - aBorder.ISize(aWM) -
- aPadding.ISize(aWM);
- return LogicalSize(aWM, TableShrinkISizeToFit(aRenderingContext, cbBased),
- NS_UNCONSTRAINEDSIZE);
- }
- // Return true if aParentReflowInput.frame or any of its ancestors within
- // the containing table have non-auto bsize. (e.g. pct or fixed bsize)
- bool
- nsTableFrame::AncestorsHaveStyleBSize(const ReflowInput& aParentReflowInput)
- {
- WritingMode wm = aParentReflowInput.GetWritingMode();
- for (const ReflowInput* rs = &aParentReflowInput;
- rs && rs->mFrame; rs = rs->mParentReflowInput) {
- nsIAtom* frameType = rs->mFrame->GetType();
- if (IS_TABLE_CELL(frameType) ||
- (nsGkAtoms::tableRowFrame == frameType) ||
- (nsGkAtoms::tableRowGroupFrame == frameType)) {
- const nsStyleCoord &bsize = rs->mStylePosition->BSize(wm);
- // calc() with percentages treated like 'auto' on internal table elements
- if (bsize.GetUnit() != eStyleUnit_Auto &&
- (!bsize.IsCalcUnit() || !bsize.HasPercent())) {
- return true;
- }
- }
- else if (nsGkAtoms::tableFrame == frameType) {
- // we reached the containing table, so always return
- return rs->mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto;
- }
- }
- return false;
- }
- // See if a special block-size reflow needs to occur and if so,
- // call RequestSpecialBSizeReflow
- void
- nsTableFrame::CheckRequestSpecialBSizeReflow(const ReflowInput& aReflowInput)
- {
- NS_ASSERTION(IS_TABLE_CELL(aReflowInput.mFrame->GetType()) ||
- aReflowInput.mFrame->GetType() == nsGkAtoms::tableRowFrame ||
- aReflowInput.mFrame->GetType() == nsGkAtoms::tableRowGroupFrame ||
- aReflowInput.mFrame->GetType() == nsGkAtoms::tableFrame,
- "unexpected frame type");
- WritingMode wm = aReflowInput.GetWritingMode();
- if (!aReflowInput.mFrame->GetPrevInFlow() && // 1st in flow
- (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedBSize() || // no computed bsize
- 0 == aReflowInput.ComputedBSize()) &&
- eStyleUnit_Percent == aReflowInput.mStylePosition->BSize(wm).GetUnit() && // pct bsize
- nsTableFrame::AncestorsHaveStyleBSize(*aReflowInput.mParentReflowInput)) {
- nsTableFrame::RequestSpecialBSizeReflow(aReflowInput);
- }
- }
- // Notify the frame and its ancestors (up to the containing table) that a special
- // bsize reflow will occur. During a special bsize reflow, a table, row group,
- // row, or cell returns the last size it was reflowed at. However, the table may
- // change the bsize of row groups, rows, cells in DistributeBSizeToRows after.
- // And the row group can change the bsize of rows, cells in CalculateRowBSizes.
- void
- nsTableFrame::RequestSpecialBSizeReflow(const ReflowInput& aReflowInput)
- {
- // notify the frame and its ancestors of the special reflow, stopping at the containing table
- for (const ReflowInput* rs = &aReflowInput; rs && rs->mFrame; rs = rs->mParentReflowInput) {
- nsIAtom* frameType = rs->mFrame->GetType();
- NS_ASSERTION(IS_TABLE_CELL(frameType) ||
- nsGkAtoms::tableRowFrame == frameType ||
- nsGkAtoms::tableRowGroupFrame == frameType ||
- nsGkAtoms::tableFrame == frameType,
- "unexpected frame type");
- rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
- if (nsGkAtoms::tableFrame == frameType) {
- NS_ASSERTION(rs != &aReflowInput,
- "should not request special bsize reflow for table");
- // always stop when we reach a table
- break;
- }
- }
- }
- /******************************************************************************************
- * Before reflow, intrinsic inline-size calculation is done using GetMinISize
- * and GetPrefISize. This used to be known as pass 1 reflow.
- *
- * After the intrinsic isize calculation, the table determines the
- * column widths using BalanceColumnISizes() and
- * then reflows each child again with a constrained avail isize. This reflow is referred to
- * as the pass 2 reflow.
- *
- * A special bsize reflow (pass 3 reflow) can occur during an initial or resize reflow
- * if (a) a row group, row, cell, or a frame inside a cell has a percent bsize but no computed
- * bsize or (b) in paginated mode, a table has a bsize. (a) supports percent nested tables
- * contained inside cells whose bsizes aren't known until after the pass 2 reflow. (b) is
- * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
- * the special bsize reflow (variety a) are as follows:
- *
- * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
- * to indicate that it should get the reflow. It does this when it has a percent bsize but
- * no computed bsize by calling CheckRequestSpecialBSizeReflow(). This method calls
- * RequestSpecialBSizeReflow() which calls SetNeedSpecialReflow() on its ancestors until
- * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
- * percent bsize frames inside cells, during DidReflow(), the cell's NotifyPercentBSize()
- * is called (the cell is the reflow state's mPercentBSizeObserver in this case).
- * NotifyPercentBSize() calls RequestSpecialBSizeReflow().
- *
- * XXX (jfkthame) This comment appears to be out of date; it refers to methods/flags
- * that are no longer present in the code.
- * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
- * will do the special bsize reflow, setting the reflow state's mFlags.mSpecialBSizeReflow
- * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
- * returns true because in that case another special bsize reflow will be coming along with the
- * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
- * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
- * appropriate bsizes will not be known.
- *
- * 3) Since the bsizes of the table, row groups, rows, and cells was determined during the pass 2
- * reflow, they return their last desired sizes during the special bsize reflow. The reflow only
- * permits percent bsize frames inside the cells to resize based on the cells bsize and that bsize
- * was determined during the pass 2 reflow.
- *
- * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
- * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
- * until the current initiator is its containing table. Since these reflows are only received by
- * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
- *
- * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
- * that all frames will receive the reflow even if they don't really need them.
- *
- * Open issues with the special bsize reflow:
- *
- * 1) At some point there should be 2 kinds of special bsize reflows because (a) and (b) above are
- * really quite different. This would avoid unnecessary reflows during printing.
- * 2) When a cell contains frames whose percent bsizes > 100%, there is data loss (see bug 115245).
- * However, this can also occur if a cell has a fixed bsize and there is no special bsize reflow.
- *
- * XXXldb Special bsize reflow should really be its own method, not
- * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on
- * the contents of the cells to do the necessary block-axis resizing.
- *
- ******************************************************************************************/
- /* Layout the entire inner table. */
- void
- nsTableFrame::Reflow(nsPresContext* aPresContext,
- ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput,
- nsReflowStatus& aStatus)
- {
- MarkInReflow();
- DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
- DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
- bool isPaginated = aPresContext->IsPaginated();
- WritingMode wm = aReflowInput.GetWritingMode();
- aStatus = NS_FRAME_COMPLETE;
- if (!GetPrevInFlow() && !mTableLayoutStrategy) {
- NS_ERROR("strategy should have been created in Init");
- return;
- }
- // see if collapsing borders need to be calculated
- if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
- CalcBCBorders();
- }
- aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
- // Check for an overflow list, and append any row group frames being pushed
- MoveOverflowToChildList();
- bool haveDesiredBSize = false;
- SetHaveReflowedColGroups(false);
- // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
- // constrained initial reflow and other reflows which require either a strategy init or balance.
- // This isn't done during an unconstrained reflow, because it will occur later when the parent
- // reflows with a constrained isize.
- bool fixupKidPositions = false;
- if (NS_SUBTREE_DIRTY(this) ||
- aReflowInput.ShouldReflowAllKids() ||
- IsGeometryDirty() ||
- aReflowInput.IsBResize()) {
- if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
- // Also check IsBResize(), to handle the first Reflow preceding a
- // special bsize Reflow, when we've already had a special bsize
- // Reflow (where ComputedBSize() would not be
- // NS_UNCONSTRAINEDSIZE, but without a style change in between).
- aReflowInput.IsBResize()) {
- // XXX Eventually, we should modify DistributeBSizeToRows to use
- // nsTableRowFrame::GetInitialBSize instead of nsIFrame::BSize().
- // That way, it will make its calculations based on internal table
- // frame bsizes as they are before they ever had any extra bsize
- // distributed to them. In the meantime, this reflows all the
- // internal table frames, which restores them to their state before
- // DistributeBSizeToRows was called.
- SetGeometryDirty();
- }
- bool needToInitiateSpecialReflow =
- HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
- // see if an extra reflow will be necessary in pagination mode
- // when there is a specified table bsize
- if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize())) {
- nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
- if ((tableSpecifiedBSize > 0) &&
- (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE)) {
- needToInitiateSpecialReflow = true;
- }
- }
- nsIFrame* lastChildReflowed = nullptr;
- NS_ASSERTION(!aReflowInput.mFlags.mSpecialBSizeReflow,
- "Shouldn't be in special bsize reflow here!");
- // do the pass 2 reflow unless this is a special bsize reflow and we will be
- // initiating a special bsize reflow
- // XXXldb I changed this. Should I change it back?
- // if we need to initiate a special bsize reflow, then don't constrain the
- // bsize of the reflow before that
- nscoord availBSize = needToInitiateSpecialReflow
- ? NS_UNCONSTRAINEDSIZE
- : aReflowInput.AvailableBSize();
- ReflowTable(aDesiredSize, aReflowInput, availBSize,
- lastChildReflowed, aStatus);
- // If ComputedWidth is unconstrained, we may need to fix child positions
- // later (in vertical-rl mode) due to use of 0 as a dummy
- // containerSize.width during ReflowChildren.
- fixupKidPositions = wm.IsVerticalRL() &&
- aReflowInput.ComputedWidth() == NS_UNCONSTRAINEDSIZE;
- // reevaluate special bsize reflow conditions
- if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
- needToInitiateSpecialReflow = true;
- }
- // XXXldb Are all these conditions correct?
- if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) {
- // XXXldb Do we need to set the IsBResize flag on any reflow states?
- ReflowInput &mutable_rs =
- const_cast<ReflowInput&>(aReflowInput);
- // distribute extra block-direction space to rows
- CalcDesiredBSize(aReflowInput, aDesiredSize);
- mutable_rs.mFlags.mSpecialBSizeReflow = true;
- ReflowTable(aDesiredSize, aReflowInput, aReflowInput.AvailableBSize(),
- lastChildReflowed, aStatus);
- if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
- // if there is an incomplete child, then set the desired bsize
- // to include it but not the next one
- LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
- aDesiredSize.BSize(wm) =
- borderPadding.BEnd(wm) + GetRowSpacing(GetRowCount()) +
- lastChildReflowed->GetNormalRect().YMost(); // XXX YMost should be B-flavored
- }
- haveDesiredBSize = true;
- mutable_rs.mFlags.mSpecialBSizeReflow = false;
- }
- }
- aDesiredSize.ISize(wm) = aReflowInput.ComputedISize() +
- aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
- if (!haveDesiredBSize) {
- CalcDesiredBSize(aReflowInput, aDesiredSize);
- }
- if (IsRowInserted()) {
- ProcessRowInserted(aDesiredSize.BSize(wm));
- }
- if (fixupKidPositions) {
- // If we didn't already know the containerSize (and so used zero during
- // ReflowChildren), then we need to update the block-position of our kids.
- for (nsIFrame* kid : mFrames) {
- kid->MovePositionBy(nsPoint(aDesiredSize.Width(), 0));
- RePositionViews(kid);
- }
- }
- // Calculate the overflow area contribution from our children. We couldn't
- // do this on the fly during ReflowChildren(), because in vertical-rl mode
- // with unconstrained width, we weren't placing them in their final positions
- // until the fixupKidPositions loop just above.
- for (nsIFrame* kid : mFrames) {
- ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
- }
- LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
- SetColumnDimensions(aDesiredSize.BSize(wm), wm, borderPadding,
- aDesiredSize.PhysicalSize());
- if (NeedToCollapse() &&
- (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableISize())) {
- AdjustForCollapsingRowsCols(aDesiredSize, wm, borderPadding);
- }
- // If there are any relatively-positioned table parts, we need to reflow their
- // absolutely-positioned descendants now that their dimensions are final.
- FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowInput);
- // make sure the table overflow area does include the table rect.
- nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
- if (!ShouldApplyOverflowClipping(this, aReflowInput.mStyleDisplay)) {
- // collapsed border may leak out
- LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
- tableRect.Inflate(bcMargin.GetPhysicalMargin(wm));
- }
- aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
- if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW) ||
- nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
- nsIFrame::InvalidateFrame();
- }
- FinishAndStoreOverflow(&aDesiredSize);
- NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
- }
- void
- nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext,
- ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput)
- {
- FrameTArray* positionedParts = GetProperty(PositionedTablePartArray());
- if (!positionedParts) {
- return;
- }
- OverflowChangedTracker overflowTracker;
- overflowTracker.SetSubtreeRoot(this);
- for (size_t i = 0; i < positionedParts->Length(); ++i) {
- nsIFrame* positionedPart = positionedParts->ElementAt(i);
- // As we've already finished reflow, positionedParts's size and overflow
- // areas have already been assigned, so we just pull them back out.
- nsSize size(positionedPart->GetSize());
- ReflowOutput desiredSize(aReflowInput.GetWritingMode());
- desiredSize.Width() = size.width;
- desiredSize.Height() = size.height;
- desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
- // Construct a dummy reflow state and reflow status.
- // XXX(seth): Note that the dummy reflow state doesn't have a correct
- // chain of parent reflow states. It also doesn't necessarily have a
- // correct containing block.
- WritingMode wm = positionedPart->GetWritingMode();
- LogicalSize availSize(wm, size);
- availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
- ReflowInput reflowInput(aPresContext, positionedPart,
- aReflowInput.mRenderingContext, availSize,
- ReflowInput::DUMMY_PARENT_REFLOW_STATE);
- nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
- // Reflow absolutely-positioned descendants of the positioned part.
- // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the bsize and
- // ignoring any change to the reflow status aren't correct. We'll never
- // paginate absolutely positioned frames.
- nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
- positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
- desiredSize,
- reflowInput,
- reflowStatus,
- true);
- // FinishReflowWithAbsoluteFrames has updated overflow on
- // |positionedPart|. We need to make sure that update propagates
- // through the intermediate frames between it and this frame.
- nsIFrame* positionedFrameParent = positionedPart->GetParent();
- if (positionedFrameParent != this) {
- overflowTracker.AddFrame(positionedFrameParent,
- OverflowChangedTracker::CHILDREN_CHANGED);
- }
- }
- // Propagate updated overflow areas up the tree.
- overflowTracker.Flush();
- // Update our own overflow areas. (OverflowChangedTracker doesn't update the
- // subtree root itself.)
- aDesiredSize.SetOverflowAreasToDesiredBounds();
- nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas);
- }
- bool
- nsTableFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
- {
- // As above in Reflow, make sure the table overflow area includes the table
- // rect, and check for collapsed borders leaking out.
- if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
- nsRect bounds(nsPoint(0, 0), GetSize());
- WritingMode wm = GetWritingMode();
- LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
- bounds.Inflate(bcMargin.GetPhysicalMargin(wm));
- aOverflowAreas.UnionAllWith(bounds);
- }
- return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
- }
- void
- nsTableFrame::ReflowTable(ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput,
- nscoord aAvailBSize,
- nsIFrame*& aLastChildReflowed,
- nsReflowStatus& aStatus)
- {
- aLastChildReflowed = nullptr;
- if (!GetPrevInFlow()) {
- mTableLayoutStrategy->ComputeColumnISizes(aReflowInput);
- }
- // Constrain our reflow isize to the computed table isize (of the 1st in flow).
- // and our reflow bsize to our avail bsize minus border, padding, cellspacing
- WritingMode wm = aReflowInput.GetWritingMode();
- aDesiredSize.ISize(wm) = aReflowInput.ComputedISize() +
- aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
- TableReflowInput reflowInput(aReflowInput,
- LogicalSize(wm, aDesiredSize.ISize(wm),
- aAvailBSize));
- ReflowChildren(reflowInput, aStatus, aLastChildReflowed,
- aDesiredSize.mOverflowAreas);
- ReflowColGroups(aReflowInput.mRenderingContext);
- }
- nsIFrame*
- nsTableFrame::GetFirstBodyRowGroupFrame()
- {
- nsIFrame* headerFrame = nullptr;
- nsIFrame* footerFrame = nullptr;
- for (nsIFrame* kidFrame : mFrames) {
- const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay();
- // We expect the header and footer row group frames to be first, and we only
- // allow one header and one footer
- if (mozilla::StyleDisplay::TableHeaderGroup == childDisplay->mDisplay) {
- if (headerFrame) {
- // We already have a header frame and so this header frame is treated
- // like an ordinary body row group frame
- return kidFrame;
- }
- headerFrame = kidFrame;
- } else if (mozilla::StyleDisplay::TableFooterGroup == childDisplay->mDisplay) {
- if (footerFrame) {
- // We already have a footer frame and so this footer frame is treated
- // like an ordinary body row group frame
- return kidFrame;
- }
- footerFrame = kidFrame;
- } else if (mozilla::StyleDisplay::TableRowGroup == childDisplay->mDisplay) {
- return kidFrame;
- }
- }
- return nullptr;
- }
- // Table specific version that takes into account repeated header and footer
- // frames when continuing table frames
- void
- nsTableFrame::PushChildren(const RowGroupArray& aRowGroups,
- int32_t aPushFrom)
- {
- NS_PRECONDITION(aPushFrom > 0, "pushing first child");
- // extract the frames from the array into a sibling list
- nsFrameList frames;
- uint32_t childX;
- for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
- nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
- if (!rgFrame->IsRepeatable()) {
- mFrames.RemoveFrame(rgFrame);
- frames.AppendFrame(nullptr, rgFrame);
- }
- }
- if (frames.IsEmpty()) {
- return;
- }
- nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
- if (nextInFlow) {
- // Insert the frames after any repeated header and footer frames.
- nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
- nsIFrame* prevSibling = nullptr;
- if (firstBodyFrame) {
- prevSibling = firstBodyFrame->GetPrevSibling();
- }
- // When pushing and pulling frames we need to check for whether any
- // views need to be reparented.
- ReparentFrameViewList(frames, this, nextInFlow);
- nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling,
- frames);
- }
- else {
- // Add the frames to our overflow list.
- SetOverflowFrames(frames);
- }
- }
- // collapsing row groups, rows, col groups and cols are accounted for after both passes of
- // reflow so that it has no effect on the calculations of reflow.
- void
- nsTableFrame::AdjustForCollapsingRowsCols(ReflowOutput& aDesiredSize,
- const WritingMode aWM,
- const LogicalMargin& aBorderPadding)
- {
- nscoord bTotalOffset = 0; // total offset among all rows in all row groups
- // reset the bit, it will be set again if row/rowgroup or col/colgroup are
- // collapsed
- SetNeedToCollapse(false);
- // collapse the rows and/or row groups as necessary
- // Get the ordered children
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
- nscoord iSize = firstInFlow->GetCollapsedISize(aWM, aBorderPadding);
- nscoord rgISize = iSize - GetColSpacing(-1) -
- GetColSpacing(GetColCount());
- nsOverflowAreas overflow;
- // Walk the list of children
- for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[childX];
- NS_ASSERTION(rgFrame, "Must have row group frame here");
- bTotalOffset += rgFrame->CollapseRowGroupIfNecessary(bTotalOffset, rgISize,
- aWM);
- ConsiderChildOverflow(overflow, rgFrame);
- }
- aDesiredSize.BSize(aWM) -= bTotalOffset;
- aDesiredSize.ISize(aWM) = iSize;
- overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
- FinishAndStoreOverflow(overflow,
- nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
- }
- nscoord
- nsTableFrame::GetCollapsedISize(const WritingMode aWM,
- const LogicalMargin& aBorderPadding)
- {
- NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedISize called on next in flow");
- nscoord iSize = GetColSpacing(GetColCount());
- iSize += aBorderPadding.IStartEnd(aWM);
- nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
- for (nsIFrame* groupFrame : mColGroups) {
- const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
- bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
- nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
- for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
- colFrame = colFrame->GetNextCol()) {
- const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
- nscoord colIdx = colFrame->GetColIndex();
- if (mozilla::StyleDisplay::TableColumn == colDisplay->mDisplay) {
- const nsStyleVisibility* colVis = colFrame->StyleVisibility();
- bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
- nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
- if (!collapseGroup && !collapseCol) {
- iSize += colISize;
- if (ColumnHasCellSpacingBefore(colIdx)) {
- iSize += GetColSpacing(colIdx - 1);
- }
- }
- else {
- SetNeedToCollapse(true);
- }
- }
- }
- }
- return iSize;
- }
- /* virtual */ void
- nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
- {
- nsContainerFrame::DidSetStyleContext(aOldStyleContext);
- if (!aOldStyleContext) //avoid this on init
- return;
- if (IsBorderCollapse() &&
- BCRecalcNeeded(aOldStyleContext, StyleContext())) {
- SetFullBCDamageArea();
- }
- //avoid this on init or nextinflow
- if (!mTableLayoutStrategy || GetPrevInFlow())
- return;
- bool isAuto = IsAutoLayout();
- if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
- nsITableLayoutStrategy* temp;
- if (isAuto)
- temp = new BasicTableLayoutStrategy(this);
- else
- temp = new FixedTableLayoutStrategy(this);
- if (temp) {
- delete mTableLayoutStrategy;
- mTableLayoutStrategy = temp;
- }
- }
- }
- void
- nsTableFrame::AppendFrames(ChildListID aListID,
- nsFrameList& aFrameList)
- {
- NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
- "unexpected child list");
- // Because we actually have two child lists, one for col group frames and one
- // for everything else, we need to look at each frame individually
- // XXX The frame construction code should be separating out child frames
- // based on the type, bug 343048.
- while (!aFrameList.IsEmpty()) {
- nsIFrame* f = aFrameList.FirstChild();
- aFrameList.RemoveFrame(f);
- // See what kind of frame we have
- const nsStyleDisplay* display = f->StyleDisplay();
- if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
- if (MOZ_UNLIKELY(GetPrevInFlow())) {
- nsFrameList colgroupFrame(f, f);
- auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
- firstInFlow->AppendFrames(aListID, colgroupFrame);
- continue;
- }
- nsTableColGroupFrame* lastColGroup =
- nsTableColGroupFrame::GetLastRealColGroup(this);
- int32_t startColIndex = (lastColGroup)
- ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
- mColGroups.InsertFrame(this, lastColGroup, f);
- // Insert the colgroup and its cols into the table
- InsertColGroups(startColIndex,
- nsFrameList::Slice(mColGroups, f, f->GetNextSibling()));
- } else if (IsRowGroup(display->mDisplay)) {
- DrainSelfOverflowList(); // ensure the last frame is in mFrames
- // Append the new row group frame to the sibling chain
- mFrames.AppendFrame(nullptr, f);
- // insert the row group and its rows into the table
- InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr));
- } else {
- // Nothing special to do, just add the frame to our child list
- NS_NOTREACHED("How did we get here? Frame construction screwed up");
- mFrames.AppendFrame(nullptr, f);
- }
- }
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== TableFrame::AppendFrames\n");
- Dump(true, true, true);
- #endif
- PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- SetGeometryDirty();
- }
- // Needs to be at file scope or ArrayLength fails to compile.
- struct ChildListInsertions {
- nsIFrame::ChildListID mID;
- nsFrameList mList;
- };
- void
- nsTableFrame::InsertFrames(ChildListID aListID,
- nsIFrame* aPrevFrame,
- nsFrameList& aFrameList)
- {
- // The frames in aFrameList can be a mix of row group frames and col group
- // frames. The problem is that they should go in separate child lists so
- // we need to deal with that here...
- // XXX The frame construction code should be separating out child frames
- // based on the type, bug 343048.
- NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
- "inserting after sibling frame with different parent");
- if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
- (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
- // Treat this like an append; still a workaround for bug 343048.
- AppendFrames(aListID, aFrameList);
- return;
- }
- // Collect ColGroupFrames into a separate list and insert those separately
- // from the other frames (bug 759249).
- ChildListInsertions insertions[2]; // ColGroup, other
- const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
- nsFrameList::FrameLinkEnumerator e(aFrameList);
- for (; !aFrameList.IsEmpty(); e.Next()) {
- nsIFrame* next = e.NextFrame();
- if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) {
- nsFrameList head = aFrameList.ExtractHead(e);
- if (display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) {
- insertions[0].mID = kColGroupList;
- insertions[0].mList.AppendFrames(nullptr, head);
- } else {
- insertions[1].mID = kPrincipalList;
- insertions[1].mList.AppendFrames(nullptr, head);
- }
- if (!next) {
- break;
- }
- display = next->StyleDisplay();
- }
- }
- for (uint32_t i = 0; i < ArrayLength(insertions); ++i) {
- // We pass aPrevFrame for both ColGroup and other frames since
- // HomogenousInsertFrames will only use it if it's a suitable
- // prev-sibling for the frames in the frame list.
- if (!insertions[i].mList.IsEmpty()) {
- HomogenousInsertFrames(insertions[i].mID, aPrevFrame,
- insertions[i].mList);
- }
- }
- }
- void
- nsTableFrame::HomogenousInsertFrames(ChildListID aListID,
- nsIFrame* aPrevFrame,
- nsFrameList& aFrameList)
- {
- // See what kind of frame we have
- const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
- bool isColGroup = mozilla::StyleDisplay::TableColumnGroup == display->mDisplay;
- #ifdef DEBUG
- // Verify that either all siblings have display:table-column-group, or they
- // all have display values different from table-column-group.
- for (nsIFrame* frame : aFrameList) {
- auto nextDisplay = frame->StyleDisplay()->mDisplay;
- MOZ_ASSERT(isColGroup ==
- (nextDisplay == mozilla::StyleDisplay::TableColumnGroup),
- "heterogenous childlist");
- }
- #endif
- if (MOZ_UNLIKELY(isColGroup && GetPrevInFlow())) {
- auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
- firstInFlow->AppendFrames(aListID, aFrameList);
- return;
- }
- if (aPrevFrame) {
- const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay();
- // Make sure they belong on the same frame list
- if ((display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) !=
- (prevDisplay->mDisplay == mozilla::StyleDisplay::TableColumnGroup)) {
- // the previous frame is not valid, see comment at ::AppendFrames
- // XXXbz Using content indices here means XBL will get screwed
- // over... Oh, well.
- nsIFrame* pseudoFrame = aFrameList.FirstChild();
- nsIContent* parentContent = GetContent();
- nsIContent* content = nullptr;
- aPrevFrame = nullptr;
- while (pseudoFrame && (parentContent ==
- (content = pseudoFrame->GetContent()))) {
- pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
- }
- nsCOMPtr<nsIContent> container = content->GetParent();
- if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823.
- int32_t newIndex = container->IndexOf(content);
- nsIFrame* kidFrame;
- nsTableColGroupFrame* lastColGroup = nullptr;
- if (isColGroup) {
- kidFrame = mColGroups.FirstChild();
- lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
- }
- else {
- kidFrame = mFrames.FirstChild();
- }
- // Important: need to start at a value smaller than all valid indices
- int32_t lastIndex = -1;
- while (kidFrame) {
- if (isColGroup) {
- if (kidFrame == lastColGroup) {
- aPrevFrame = kidFrame; // there is no real colgroup after this one
- break;
- }
- }
- pseudoFrame = kidFrame;
- while (pseudoFrame && (parentContent ==
- (content = pseudoFrame->GetContent()))) {
- pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
- }
- int32_t index = container->IndexOf(content);
- if (index > lastIndex && index < newIndex) {
- lastIndex = index;
- aPrevFrame = kidFrame;
- }
- kidFrame = kidFrame->GetNextSibling();
- }
- }
- }
- }
- if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
- NS_ASSERTION(aListID == kColGroupList, "unexpected child list");
- // Insert the column group frames
- const nsFrameList::Slice& newColgroups =
- mColGroups.InsertFrames(this, aPrevFrame, aFrameList);
- // find the starting col index for the first new col group
- int32_t startColIndex = 0;
- if (aPrevFrame) {
- nsTableColGroupFrame* prevColGroup =
- (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
- nsGkAtoms::tableColGroupFrame);
- if (prevColGroup) {
- startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
- }
- }
- InsertColGroups(startColIndex, newColgroups);
- } else if (IsRowGroup(display->mDisplay)) {
- NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
- DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
- // Insert the frames in the sibling chain
- const nsFrameList::Slice& newRowGroups =
- mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
- InsertRowGroups(newRowGroups);
- } else {
- NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
- NS_NOTREACHED("How did we even get here?");
- // Just insert the frame and don't worry about reflowing it
- mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
- return;
- }
- PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- SetGeometryDirty();
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== TableFrame::InsertFrames\n");
- Dump(true, true, true);
- #endif
- return;
- }
- void
- nsTableFrame::DoRemoveFrame(ChildListID aListID,
- nsIFrame* aOldFrame)
- {
- if (aListID == kColGroupList) {
- nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
- nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
- int32_t firstColIndex = colGroup->GetStartColumnIndex();
- int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1;
- mColGroups.DestroyFrame(aOldFrame);
- nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
- // remove the cols from the table
- int32_t colIdx;
- for (colIdx = lastColIndex; colIdx >= firstColIndex; colIdx--) {
- nsTableColFrame* colFrame = mColFrames.SafeElementAt(colIdx);
- if (colFrame) {
- RemoveCol(colGroup, colIdx, true, false);
- }
- }
- // If we have some anonymous cols at the end already, we just
- // add more of them.
- if (!mColFrames.IsEmpty() &&
- mColFrames.LastElement() && // XXXbz is this ever null?
- mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
- int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
- if (numAnonymousColsToAdd > 0) {
- // this sets the child list, updates the col cache and cell map
- AppendAnonymousColFrames(numAnonymousColsToAdd);
- }
- } else {
- // All of our colframes correspond to actual <col> tags. It's possible
- // that we still have at least as many <col> tags as we have logical
- // columns from cells, but we might have one less. Handle the latter case
- // as follows: First ask the cellmap to drop its last col if it doesn't
- // have any actual cells in it. Then call MatchCellMapToColCache to
- // append an anonymous column if it's needed; this needs to be after
- // RemoveColsAtEnd, since it will determine the need for a new column
- // frame based on the width of the cell map.
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) { // XXXbz is this ever null?
- cellMap->RemoveColsAtEnd();
- MatchCellMapToColCache(cellMap);
- }
- }
- } else {
- NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
- nsTableRowGroupFrame* rgFrame =
- static_cast<nsTableRowGroupFrame*>(aOldFrame);
- // remove the row group from the cell map
- nsTableCellMap* cellMap = GetCellMap();
- if (cellMap) {
- cellMap->RemoveGroupCellMap(rgFrame);
- }
- // remove the row group frame from the sibling chain
- mFrames.DestroyFrame(aOldFrame);
- // the removal of a row group changes the cellmap, the columns might change
- if (cellMap) {
- cellMap->Synchronize(this);
- // Create an empty slice
- ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
- TableArea damageArea;
- cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea);
- static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap);
- }
- }
- }
- void
- nsTableFrame::RemoveFrame(ChildListID aListID,
- nsIFrame* aOldFrame)
- {
- NS_ASSERTION(aListID == kColGroupList ||
- mozilla::StyleDisplay::TableColumnGroup !=
- aOldFrame->StyleDisplay()->mDisplay,
- "Wrong list name; use kColGroupList iff colgroup");
- nsIPresShell* shell = PresContext()->PresShell();
- nsTableFrame* lastParent = nullptr;
- while (aOldFrame) {
- nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
- nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent());
- if (parent != lastParent) {
- parent->DrainSelfOverflowList();
- }
- parent->DoRemoveFrame(aListID, aOldFrame);
- aOldFrame = oldFrameNextContinuation;
- if (parent != lastParent) {
- // for now, just bail and recalc all of the collapsing borders
- // as the cellmap changes we need to recalc
- if (parent->IsBorderCollapse()) {
- parent->SetFullBCDamageArea();
- }
- parent->SetGeometryDirty();
- shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- lastParent = parent;
- }
- }
- #ifdef DEBUG_TABLE_CELLMAP
- printf("=== TableFrame::RemoveFrame\n");
- Dump(true, true, true);
- #endif
- }
- /* virtual */ nsMargin
- nsTableFrame::GetUsedBorder() const
- {
- if (!IsBorderCollapse())
- return nsContainerFrame::GetUsedBorder();
- WritingMode wm = GetWritingMode();
- return GetIncludedOuterBCBorder(wm).GetPhysicalMargin(wm);
- }
- /* virtual */ nsMargin
- nsTableFrame::GetUsedPadding() const
- {
- if (!IsBorderCollapse())
- return nsContainerFrame::GetUsedPadding();
- return nsMargin(0,0,0,0);
- }
- /* virtual */ nsMargin
- nsTableFrame::GetUsedMargin() const
- {
- // The margin is inherited to the table wrapper frame via
- // the ::-moz-table-wrapper rule in ua.css.
- return nsMargin(0, 0, 0, 0);
- }
- NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCProperty, BCPropertyData)
- BCPropertyData*
- nsTableFrame::GetBCProperty() const
- {
- return GetProperty(TableBCProperty());
- }
- BCPropertyData*
- nsTableFrame::GetOrCreateBCProperty()
- {
- BCPropertyData* value = GetProperty(TableBCProperty());
- if (!value) {
- value = new BCPropertyData();
- SetProperty(TableBCProperty(), value);
- }
- return value;
- }
- static void
- DivideBCBorderSize(BCPixelSize aPixelSize,
- BCPixelSize& aSmallHalf,
- BCPixelSize& aLargeHalf)
- {
- aSmallHalf = aPixelSize / 2;
- aLargeHalf = aPixelSize - aSmallHalf;
- }
- LogicalMargin
- nsTableFrame::GetOuterBCBorder(const WritingMode aWM) const
- {
- if (NeedToCalcBCBorders()) {
- const_cast<nsTableFrame*>(this)->CalcBCBorders();
- }
- int32_t d2a = PresContext()->AppUnitsPerDevPixel();
- BCPropertyData* propData = GetBCProperty();
- if (propData) {
- return LogicalMargin(aWM,
- BC_BORDER_START_HALF_COORD(d2a, propData->mBStartBorderWidth),
- BC_BORDER_END_HALF_COORD(d2a, propData->mIEndBorderWidth),
- BC_BORDER_END_HALF_COORD(d2a, propData->mBEndBorderWidth),
- BC_BORDER_START_HALF_COORD(d2a, propData->mIStartBorderWidth));
- }
- return LogicalMargin(aWM);
- }
- LogicalMargin
- nsTableFrame::GetIncludedOuterBCBorder(const WritingMode aWM) const
- {
- if (NeedToCalcBCBorders()) {
- const_cast<nsTableFrame*>(this)->CalcBCBorders();
- }
- int32_t d2a = PresContext()->AppUnitsPerDevPixel();
- BCPropertyData* propData = GetBCProperty();
- if (propData) {
- return LogicalMargin(aWM,
- BC_BORDER_START_HALF_COORD(d2a, propData->mBStartBorderWidth),
- BC_BORDER_END_HALF_COORD(d2a, propData->mIEndCellBorderWidth),
- BC_BORDER_END_HALF_COORD(d2a, propData->mBEndBorderWidth),
- BC_BORDER_START_HALF_COORD(d2a, propData->mIStartCellBorderWidth));
- }
- return LogicalMargin(aWM);
- }
- LogicalMargin
- nsTableFrame::GetExcludedOuterBCBorder(const WritingMode aWM) const
- {
- return GetOuterBCBorder(aWM) - GetIncludedOuterBCBorder(aWM);
- }
- static LogicalMargin
- GetSeparateModelBorderPadding(const WritingMode aWM,
- const ReflowInput* aReflowInput,
- nsStyleContext* aStyleContext)
- {
- // XXXbz Either we _do_ have a reflow state and then we can use its
- // mComputedBorderPadding or we don't and then we get the padding
- // wrong!
- const nsStyleBorder* border = aStyleContext->StyleBorder();
- LogicalMargin borderPadding(aWM, border->GetComputedBorder());
- if (aReflowInput) {
- borderPadding += aReflowInput->ComputedLogicalPadding();
- }
- return borderPadding;
- }
- LogicalMargin
- nsTableFrame::GetChildAreaOffset(const WritingMode aWM,
- const ReflowInput* aReflowInput) const
- {
- return IsBorderCollapse() ? GetIncludedOuterBCBorder(aWM) :
- GetSeparateModelBorderPadding(aWM, aReflowInput, mStyleContext);
- }
- void
- nsTableFrame::InitChildReflowInput(ReflowInput& aReflowInput)
- {
- nsMargin collapseBorder;
- nsMargin padding(0,0,0,0);
- nsMargin* pCollapseBorder = nullptr;
- nsPresContext* presContext = PresContext();
- if (IsBorderCollapse()) {
- nsTableRowGroupFrame* rgFrame =
- static_cast<nsTableRowGroupFrame*>(aReflowInput.mFrame);
- WritingMode wm = GetWritingMode();
- LogicalMargin border = rgFrame->GetBCBorderWidth(wm);
- collapseBorder = border.GetPhysicalMargin(wm);
- pCollapseBorder = &collapseBorder;
- }
- aReflowInput.Init(presContext, nullptr, pCollapseBorder, &padding);
- NS_ASSERTION(!mBits.mResizedColumns ||
- !aReflowInput.mParentReflowInput->mFlags.mSpecialBSizeReflow,
- "should not resize columns on special bsize reflow");
- if (mBits.mResizedColumns) {
- aReflowInput.SetIResize(true);
- }
- }
- // Position and size aKidFrame and update our reflow state. The origin of
- // aKidRect is relative to the upper-left origin of our frame
- void
- nsTableFrame::PlaceChild(TableReflowInput& aReflowInput,
- nsIFrame* aKidFrame,
- nsPoint aKidPosition,
- ReflowOutput& aKidDesiredSize,
- const nsRect& aOriginalKidRect,
- const nsRect& aOriginalKidVisualOverflow)
- {
- WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
- bool isFirstReflow =
- aKidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
- // Place and size the child
- FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
- aKidPosition.x, aKidPosition.y, 0);
- InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
- isFirstReflow);
- // Adjust the running block-offset
- aReflowInput.bCoord += aKidDesiredSize.BSize(wm);
- // If our bsize is constrained, then update the available bsize
- aReflowInput.ReduceAvailableBSizeBy(wm, aKidDesiredSize.BSize(wm));
- }
- void
- nsTableFrame::OrderRowGroups(RowGroupArray& aChildren,
- nsTableRowGroupFrame** aHead,
- nsTableRowGroupFrame** aFoot) const
- {
- aChildren.Clear();
- nsTableRowGroupFrame* head = nullptr;
- nsTableRowGroupFrame* foot = nullptr;
- nsIFrame* kidFrame = mFrames.FirstChild();
- while (kidFrame) {
- const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay();
- nsTableRowGroupFrame* rowGroup =
- static_cast<nsTableRowGroupFrame*>(kidFrame);
- switch (kidDisplay->mDisplay) {
- case mozilla::StyleDisplay::TableHeaderGroup:
- if (head) { // treat additional thead like tbody
- aChildren.AppendElement(rowGroup);
- }
- else {
- head = rowGroup;
- }
- break;
- case mozilla::StyleDisplay::TableFooterGroup:
- if (foot) { // treat additional tfoot like tbody
- aChildren.AppendElement(rowGroup);
- }
- else {
- foot = rowGroup;
- }
- break;
- case mozilla::StyleDisplay::TableRowGroup:
- aChildren.AppendElement(rowGroup);
- break;
- default:
- NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
- // Just ignore it
- break;
- }
- // Get the next sibling but skip it if it's also the next-in-flow, since
- // a next-in-flow will not be part of the current table.
- while (kidFrame) {
- nsIFrame* nif = kidFrame->GetNextInFlow();
- kidFrame = kidFrame->GetNextSibling();
- if (kidFrame != nif)
- break;
- }
- }
- // put the thead first
- if (head) {
- aChildren.InsertElementAt(0, head);
- }
- if (aHead)
- *aHead = head;
- // put the tfoot after the last tbody
- if (foot) {
- aChildren.AppendElement(foot);
- }
- if (aFoot)
- *aFoot = foot;
- }
- nsTableRowGroupFrame*
- nsTableFrame::GetTHead() const
- {
- nsIFrame* kidFrame = mFrames.FirstChild();
- while (kidFrame) {
- if (kidFrame->StyleDisplay()->mDisplay ==
- mozilla::StyleDisplay::TableHeaderGroup) {
- return static_cast<nsTableRowGroupFrame*>(kidFrame);
- }
- // Get the next sibling but skip it if it's also the next-in-flow, since
- // a next-in-flow will not be part of the current table.
- while (kidFrame) {
- nsIFrame* nif = kidFrame->GetNextInFlow();
- kidFrame = kidFrame->GetNextSibling();
- if (kidFrame != nif)
- break;
- }
- }
- return nullptr;
- }
- nsTableRowGroupFrame*
- nsTableFrame::GetTFoot() const
- {
- nsIFrame* kidFrame = mFrames.FirstChild();
- while (kidFrame) {
- if (kidFrame->StyleDisplay()->mDisplay ==
- mozilla::StyleDisplay::TableFooterGroup) {
- return static_cast<nsTableRowGroupFrame*>(kidFrame);
- }
- // Get the next sibling but skip it if it's also the next-in-flow, since
- // a next-in-flow will not be part of the current table.
- while (kidFrame) {
- nsIFrame* nif = kidFrame->GetNextInFlow();
- kidFrame = kidFrame->GetNextSibling();
- if (kidFrame != nif)
- break;
- }
- }
- return nullptr;
- }
- static bool
- IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
- {
- return aFrameHeight < (aPageHeight / 4);
- }
- nsresult
- nsTableFrame::SetupHeaderFooterChild(const TableReflowInput& aReflowInput,
- nsTableRowGroupFrame* aFrame,
- nscoord* aDesiredHeight)
- {
- nsPresContext* presContext = PresContext();
- nscoord pageHeight = presContext->GetPageSize().height;
- // Reflow the child with unconstrained height
- WritingMode wm = aFrame->GetWritingMode();
- LogicalSize availSize = aReflowInput.reflowInput.AvailableSize(wm);
- nsSize containerSize = availSize.GetPhysicalSize(wm);
- // XXX check for containerSize.* == NS_UNCONSTRAINEDSIZE
- availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
- ReflowInput kidReflowInput(presContext, aReflowInput.reflowInput,
- aFrame, availSize, nullptr,
- ReflowInput::CALLER_WILL_INIT);
- InitChildReflowInput(kidReflowInput);
- kidReflowInput.mFlags.mIsTopOfPage = true;
- ReflowOutput desiredSize(aReflowInput.reflowInput);
- desiredSize.ClearSize();
- nsReflowStatus status;
- ReflowChild(aFrame, presContext, desiredSize, kidReflowInput,
- wm, LogicalPoint(wm, aReflowInput.iCoord, aReflowInput.bCoord),
- containerSize, 0, status);
- // The child will be reflowed again "for real" so no need to place it now
- aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight));
- *aDesiredHeight = desiredSize.Height();
- return NS_OK;
- }
- void
- nsTableFrame::PlaceRepeatedFooter(TableReflowInput& aReflowInput,
- nsTableRowGroupFrame *aTfoot,
- nscoord aFooterHeight)
- {
- nsPresContext* presContext = PresContext();
- WritingMode wm = aTfoot->GetWritingMode();
- LogicalSize kidAvailSize = aReflowInput.availSize;
- nsSize containerSize = kidAvailSize.GetPhysicalSize(wm);
- // XXX check for containerSize.* == NS_UNCONSTRAINEDSIZE
- kidAvailSize.BSize(wm) = aFooterHeight;
- ReflowInput footerReflowInput(presContext,
- aReflowInput.reflowInput,
- aTfoot, kidAvailSize,
- nullptr,
- ReflowInput::CALLER_WILL_INIT);
- InitChildReflowInput(footerReflowInput);
- aReflowInput.bCoord += GetRowSpacing(GetRowCount());
- nsRect origTfootRect = aTfoot->GetRect();
- nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
- nsReflowStatus footerStatus;
- ReflowOutput desiredSize(aReflowInput.reflowInput);
- desiredSize.ClearSize();
- LogicalPoint kidPosition(wm, aReflowInput.iCoord, aReflowInput.bCoord);
- ReflowChild(aTfoot, presContext, desiredSize, footerReflowInput,
- wm, kidPosition, containerSize, 0, footerStatus);
- footerReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
- PlaceChild(aReflowInput, aTfoot,
- // We subtract desiredSize.PhysicalSize() from containerSize here
- // to account for the fact that in RTL modes, the origin is
- // on the right-hand side so we're not simply converting a
- // point, we're also swapping the child's origin side.
- kidPosition.GetPhysicalPoint(wm, containerSize -
- desiredSize.PhysicalSize()),
- desiredSize, origTfootRect, origTfootVisualOverflow);
- }
- // Reflow the children based on the avail size and reason in aReflowInput
- void
- nsTableFrame::ReflowChildren(TableReflowInput& aReflowInput,
- nsReflowStatus& aStatus,
- nsIFrame*& aLastChildReflowed,
- nsOverflowAreas& aOverflowAreas)
- {
- aStatus = NS_FRAME_COMPLETE;
- aLastChildReflowed = nullptr;
- nsIFrame* prevKidFrame = nullptr;
- WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
- NS_WARNING_ASSERTION(
- wm.IsVertical() ||
- NS_UNCONSTRAINEDSIZE != aReflowInput.reflowInput.ComputedWidth(),
- "shouldn't have unconstrained width in horizontal mode");
- nsSize containerSize =
- aReflowInput.reflowInput.ComputedSizeAsContainerIfConstrained();
- nsPresContext* presContext = PresContext();
- // XXXldb Should we be checking constrained height instead?
- // tables are not able to pull back children from its next inflow, so even
- // under paginated contexts tables are should not paginate if they are inside
- // column set
- bool isPaginated = presContext->IsPaginated() &&
- NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm) &&
- aReflowInput.reflowInput.mFlags.mTableIsSplittable;
- aOverflowAreas.Clear();
- bool reflowAllKids = aReflowInput.reflowInput.ShouldReflowAllKids() ||
- mBits.mResizedColumns ||
- IsGeometryDirty();
- RowGroupArray rowGroups;
- nsTableRowGroupFrame *thead, *tfoot;
- OrderRowGroups(rowGroups, &thead, &tfoot);
- bool pageBreak = false;
- nscoord footerHeight = 0;
- // Determine the repeatablility of headers and footers, and also the desired
- // height of any repeatable footer.
- // The repeatability of headers on continued tables is handled
- // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
- // We handle the repeatability of footers again here because we need to
- // determine the footer's height anyway. We could perhaps optimize by
- // using the footer's prev-in-flow's height instead of reflowing it again,
- // but there's no real need.
- if (isPaginated) {
- bool reorder = false;
- if (thead && !GetPrevInFlow()) {
- if (thead->GetNextInFlow()) {
- reorder = true;
- }
- nscoord desiredHeight;
- nsresult rv = SetupHeaderFooterChild(aReflowInput, thead, &desiredHeight);
- if (NS_FAILED(rv))
- return;
- }
- if (tfoot) {
- if (tfoot->GetNextInFlow()) {
- reorder = true;
- }
- nsresult rv = SetupHeaderFooterChild(aReflowInput, tfoot, &footerHeight);
- if (NS_FAILED(rv))
- return;
- }
- if (reorder) {
- // Reorder row groups, because the reflow may have changed what's next-in-flow.
- OrderRowGroups(rowGroups, &thead, &tfoot);
- }
- }
- // if the child is a tbody in paginated mode reduce the height by a repeated footer
- bool allowRepeatedFooter = false;
- for (size_t childX = 0; childX < rowGroups.Length(); childX++) {
- nsIFrame* kidFrame = rowGroups[childX];
- nsTableRowGroupFrame* rowGroupFrame = rowGroups[childX];
- nscoord cellSpacingB = GetRowSpacing(rowGroupFrame->GetStartRowIndex()+
- rowGroupFrame->GetRowCount());
- // Get the frame state bits
- // See if we should only reflow the dirty child frames
- if (reflowAllKids ||
- NS_SUBTREE_DIRTY(kidFrame) ||
- (aReflowInput.reflowInput.mFlags.mSpecialBSizeReflow &&
- (isPaginated || kidFrame->HasAnyStateBits(
- NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) {
- if (pageBreak) {
- if (allowRepeatedFooter) {
- PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
- }
- else if (tfoot && tfoot->IsRepeatable()) {
- tfoot->SetRepeatable(false);
- }
- PushChildren(rowGroups, childX);
- aStatus = NS_FRAME_NOT_COMPLETE;
- break;
- }
- LogicalSize kidAvailSize(aReflowInput.availSize);
- allowRepeatedFooter = false;
- if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.BSize(wm))) {
- nsTableRowGroupFrame* kidRG =
- static_cast<nsTableRowGroupFrame*>(kidFrame);
- if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
- // the child is a tbody and there is a repeatable footer
- NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
- if (footerHeight + cellSpacingB < kidAvailSize.BSize(wm)) {
- allowRepeatedFooter = true;
- kidAvailSize.BSize(wm) -= footerHeight + cellSpacingB;
- }
- }
- }
- nsRect oldKidRect = kidFrame->GetRect();
- nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
- ReflowOutput desiredSize(aReflowInput.reflowInput);
- desiredSize.ClearSize();
- // Reflow the child into the available space
- ReflowInput kidReflowInput(presContext, aReflowInput.reflowInput,
- kidFrame,
- kidAvailSize,
- nullptr,
- ReflowInput::CALLER_WILL_INIT);
- InitChildReflowInput(kidReflowInput);
- // If this isn't the first row group, and the previous row group has a
- // nonzero YMost, then we can't be at the top of the page.
- // We ignore a repeated head row group in this check to avoid causing
- // infinite loops in some circumstances - see bug 344883.
- if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
- (rowGroups[childX - 1]->GetNormalRect().YMost() > 0)) {
- kidReflowInput.mFlags.mIsTopOfPage = false;
- }
- aReflowInput.bCoord += cellSpacingB;
- aReflowInput.ReduceAvailableBSizeBy(wm, cellSpacingB);
- // record the presence of a next in flow, it might get destroyed so we
- // need to reorder the row group array
- bool reorder = false;
- if (kidFrame->GetNextInFlow()) {
- reorder = true;
- }
- LogicalPoint kidPosition(wm, aReflowInput.iCoord, aReflowInput.bCoord);
- ReflowChild(kidFrame, presContext, desiredSize, kidReflowInput,
- wm, kidPosition, containerSize, 0, aStatus);
- kidReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
- if (reorder) {
- // Reorder row groups, because the reflow may have changed what's next-in-flow.
- OrderRowGroups(rowGroups, &thead, &tfoot);
- childX = rowGroups.IndexOf(kidFrame);
- if (childX == RowGroupArray::NoIndex) {
- // XXXbz can this happen?
- childX = rowGroups.Length();
- }
- }
- if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
- ShouldAvoidBreakInside(aReflowInput.reflowInput)) {
- aStatus = NS_INLINE_LINE_BREAK_BEFORE();
- break;
- }
- // see if the rowgroup did not fit on this page might be pushed on
- // the next page
- if (isPaginated &&
- (NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
- (NS_FRAME_IS_COMPLETE(aStatus) &&
- (NS_UNCONSTRAINEDSIZE != kidReflowInput.AvailableHeight()) &&
- kidReflowInput.AvailableHeight() < desiredSize.Height()))) {
- if (ShouldAvoidBreakInside(aReflowInput.reflowInput)) {
- aStatus = NS_INLINE_LINE_BREAK_BEFORE();
- break;
- }
- // if we are on top of the page place with dataloss
- if (kidReflowInput.mFlags.mIsTopOfPage) {
- if (childX+1 < rowGroups.Length()) {
- nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
- if (nextRowGroupFrame) {
- PlaceChild(aReflowInput, kidFrame,
- kidPosition.GetPhysicalPoint(wm,
- containerSize - desiredSize.PhysicalSize()),
- desiredSize, oldKidRect, oldKidVisualOverflow);
- if (allowRepeatedFooter) {
- PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
- }
- else if (tfoot && tfoot->IsRepeatable()) {
- tfoot->SetRepeatable(false);
- }
- aStatus = NS_FRAME_NOT_COMPLETE;
- PushChildren(rowGroups, childX + 1);
- aLastChildReflowed = kidFrame;
- break;
- }
- }
- }
- else { // we are not on top, push this rowgroup onto the next page
- if (prevKidFrame) { // we had a rowgroup before so push this
- if (allowRepeatedFooter) {
- PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
- }
- else if (tfoot && tfoot->IsRepeatable()) {
- tfoot->SetRepeatable(false);
- }
- aStatus = NS_FRAME_NOT_COMPLETE;
- PushChildren(rowGroups, childX);
- aLastChildReflowed = prevKidFrame;
- break;
- }
- else { // we can't push so lets make clear how much space we need
- PlaceChild(aReflowInput, kidFrame,
- kidPosition.GetPhysicalPoint(wm,
- containerSize - desiredSize.PhysicalSize()),
- desiredSize, oldKidRect, oldKidVisualOverflow);
- aLastChildReflowed = kidFrame;
- if (allowRepeatedFooter) {
- PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
- aLastChildReflowed = tfoot;
- }
- break;
- }
- }
- }
- aLastChildReflowed = kidFrame;
- pageBreak = false;
- // see if there is a page break after this row group or before the next one
- if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
- (NS_UNCONSTRAINEDSIZE != kidReflowInput.AvailableHeight())) {
- nsIFrame* nextKid =
- (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
- pageBreak = PageBreakAfter(kidFrame, nextKid);
- }
- // Place the child
- PlaceChild(aReflowInput, kidFrame,
- kidPosition.GetPhysicalPoint(wm, containerSize -
- desiredSize.PhysicalSize()),
- desiredSize, oldKidRect, oldKidVisualOverflow);
- // Remember where we just were in case we end up pushing children
- prevKidFrame = kidFrame;
- MOZ_ASSERT(!NS_FRAME_IS_NOT_COMPLETE(aStatus) || isPaginated,
- "Table contents should only fragment in paginated contexts");
- // Special handling for incomplete children
- if (isPaginated && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
- nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
- if (!kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- kidNextInFlow = presContext->PresShell()->FrameConstructor()->
- CreateContinuingFrame(presContext, kidFrame, this);
- // Insert the kid's new next-in-flow into our sibling list...
- mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow);
- // and in rowGroups after childX so that it will get pushed below.
- rowGroups.InsertElementAt(childX + 1,
- static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
- } else if (kidNextInFlow == kidFrame->GetNextSibling()) {
- // OrderRowGroups excludes NIFs in the child list from 'rowGroups'
- // so we deal with that here to make sure they get pushed.
- MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow),
- "OrderRowGroups must not put our NIF in 'rowGroups'");
- rowGroups.InsertElementAt(childX + 1,
- static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
- }
- // We've used up all of our available space so push the remaining
- // children.
- if (allowRepeatedFooter) {
- PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
- }
- else if (tfoot && tfoot->IsRepeatable()) {
- tfoot->SetRepeatable(false);
- }
- nsIFrame* nextSibling = kidFrame->GetNextSibling();
- if (nextSibling) {
- PushChildren(rowGroups, childX + 1);
- }
- break;
- }
- }
- else { // it isn't being reflowed
- aReflowInput.bCoord += cellSpacingB;
- LogicalRect kidRect(wm, kidFrame->GetNormalRect(), containerSize);
- if (kidRect.BStart(wm) != aReflowInput.bCoord) {
- // invalidate the old position
- kidFrame->InvalidateFrameSubtree();
- // move to the new position
- kidFrame->MovePositionBy(wm, LogicalPoint(wm, 0, aReflowInput.bCoord -
- kidRect.BStart(wm)));
- RePositionViews(kidFrame);
- // invalidate the new position
- kidFrame->InvalidateFrameSubtree();
- }
- aReflowInput.bCoord += kidRect.BSize(wm);
- // If our bsize is constrained then update the available bsize.
- aReflowInput.ReduceAvailableBSizeBy(wm, cellSpacingB + kidRect.BSize(wm));
- }
- }
- // We've now propagated the column resizes and geometry changes to all
- // the children.
- mBits.mResizedColumns = false;
- ClearGeometryDirty();
- }
- void
- nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext)
- {
- if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
- ReflowOutput kidMet(GetWritingMode());
- nsPresContext *presContext = PresContext();
- for (nsIFrame* kidFrame : mColGroups) {
- if (NS_SUBTREE_DIRTY(kidFrame)) {
- // The column groups don't care about dimensions or reflow states.
- ReflowInput
- kidReflowInput(presContext, kidFrame, aRenderingContext,
- LogicalSize(kidFrame->GetWritingMode()));
- nsReflowStatus cgStatus;
- ReflowChild(kidFrame, presContext, kidMet, kidReflowInput, 0, 0, 0,
- cgStatus);
- FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0);
- }
- }
- SetHaveReflowedColGroups(true);
- }
- }
- void
- nsTableFrame::CalcDesiredBSize(const ReflowInput& aReflowInput,
- ReflowOutput& aDesiredSize)
- {
- WritingMode wm = aReflowInput.GetWritingMode();
- nsTableCellMap* cellMap = GetCellMap();
- if (!cellMap) {
- NS_ERROR("never ever call me until the cell map is built!");
- aDesiredSize.BSize(wm) = 0;
- return;
- }
- LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
- // get the natural bsize based on the last child's (row group) rect
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- if (rowGroups.IsEmpty()) {
- // tables can be used as rectangular items without content
- nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
- if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedBSize) &&
- (tableSpecifiedBSize > 0) &&
- eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
- // empty tables should not have a size in quirks mode
- aDesiredSize.BSize(wm) = tableSpecifiedBSize;
- } else {
- aDesiredSize.BSize(wm) = 0;
- }
- return;
- }
- int32_t rowCount = cellMap->GetRowCount();
- int32_t colCount = cellMap->GetColCount();
- nscoord desiredBSize = borderPadding.BStartEnd(wm);
- if (rowCount > 0 && colCount > 0) {
- desiredBSize += GetRowSpacing(-1);
- for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- desiredBSize += rowGroups[rgIdx]->BSize(wm) +
- GetRowSpacing(rowGroups[rgIdx]->GetRowCount() +
- rowGroups[rgIdx]->GetStartRowIndex());
- }
- }
- // see if a specified table bsize requires dividing additional space to rows
- if (!GetPrevInFlow()) {
- nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
- if ((tableSpecifiedBSize > 0) &&
- (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE) &&
- (tableSpecifiedBSize > desiredBSize)) {
- // proportionately distribute the excess bsize to unconstrained rows in each
- // unconstrained row group.
- DistributeBSizeToRows(aReflowInput, tableSpecifiedBSize - desiredBSize);
- // this might have changed the overflow area incorporate the childframe overflow area.
- for (nsIFrame* kidFrame : mFrames) {
- ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
- }
- desiredBSize = tableSpecifiedBSize;
- }
- }
- aDesiredSize.BSize(wm) = desiredBSize;
- }
- static
- void ResizeCells(nsTableFrame& aTableFrame)
- {
- nsTableFrame::RowGroupArray rowGroups;
- aTableFrame.OrderRowGroups(rowGroups);
- WritingMode wm = aTableFrame.GetWritingMode();
- ReflowOutput tableDesiredSize(wm);
- tableDesiredSize.SetSize(wm, aTableFrame.GetLogicalSize(wm));
- tableDesiredSize.SetOverflowAreasToDesiredBounds();
- for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- ReflowOutput groupDesiredSize(wm);
- groupDesiredSize.SetSize(wm, rgFrame->GetLogicalSize(wm));
- groupDesiredSize.SetOverflowAreasToDesiredBounds();
- nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
- while (rowFrame) {
- rowFrame->DidResize();
- rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
- rowFrame = rowFrame->GetNextRow();
- }
- rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
- tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
- rgFrame->GetPosition());
- }
- aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
- }
- void
- nsTableFrame::DistributeBSizeToRows(const ReflowInput& aReflowInput,
- nscoord aAmount)
- {
- WritingMode wm = aReflowInput.GetWritingMode();
- LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
- nsSize containerSize =
- aReflowInput.ComputedSizeAsContainerIfConstrained();
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- nscoord amountUsed = 0;
- // distribute space to each pct bsize row whose row group doesn't have a computed
- // bsize, and base the pct on the table bsize. If the row group had a computed
- // bsize, then this was already done in nsTableRowGroupFrame::CalculateRowBSizes
- nscoord pctBasis = aReflowInput.ComputedBSize() - GetRowSpacing(-1, GetRowCount());
- nscoord bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(0);
- nscoord bEndRG = bOriginRG;
- uint32_t rgIdx;
- for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- nscoord amountUsedByRG = 0;
- nscoord bOriginRow = 0;
- LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), containerSize);
- if (!rgFrame->HasStyleBSize()) {
- nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
- while (rowFrame) {
- // We don't know the final width of the rowGroupFrame yet, so use 0,0
- // as a dummy containerSize here; we'll adjust the row positions at
- // the end, after the rowGroup size is finalized.
- const nsSize dummyContainerSize;
- LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(),
- dummyContainerSize);
- nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex());
- if ((amountUsed < aAmount) && rowFrame->HasPctBSize()) {
- nscoord pctBSize = rowFrame->GetInitialBSize(pctBasis);
- nscoord amountForRow = std::min(aAmount - amountUsed,
- pctBSize - rowNormalRect.BSize(wm));
- if (amountForRow > 0) {
- // XXXbz we don't need to move the row's b-position to bOriginRow?
- nsRect origRowRect = rowFrame->GetRect();
- nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
- rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm),
- newRowBSize));
- bOriginRow += newRowBSize + cellSpacingB;
- bEndRG += newRowBSize + cellSpacingB;
- amountUsed += amountForRow;
- amountUsedByRG += amountForRow;
- //rowFrame->DidResize();
- nsTableFrame::RePositionViews(rowFrame);
- rgFrame->InvalidateFrameWithRect(origRowRect);
- rgFrame->InvalidateFrame();
- }
- }
- else {
- if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm) &&
- !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
- rowFrame->InvalidateFrameSubtree();
- rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
- rowNormalRect.BStart(wm)));
- nsTableFrame::RePositionViews(rowFrame);
- rowFrame->InvalidateFrameSubtree();
- }
- bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB;
- bEndRG += rowNormalRect.BSize(wm) + cellSpacingB;
- }
- rowFrame = rowFrame->GetNextRow();
- }
- if (amountUsed > 0) {
- if (rgNormalRect.BStart(wm) != bOriginRG) {
- rgFrame->InvalidateFrameSubtree();
- }
- nsRect origRgNormalRect = rgFrame->GetRect();
- nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
- rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
- rgNormalRect.BStart(wm)));
- rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm),
- rgNormalRect.BSize(wm) + amountUsedByRG));
- nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
- origRgVisualOverflow, false);
- }
- }
- else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
- rgFrame->InvalidateFrameSubtree();
- rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
- rgNormalRect.BStart(wm)));
- // Make sure child views are properly positioned
- nsTableFrame::RePositionViews(rgFrame);
- rgFrame->InvalidateFrameSubtree();
- }
- bOriginRG = bEndRG;
- }
- if (amountUsed >= aAmount) {
- ResizeCells(*this);
- return;
- }
- // get the first row without a style bsize where its row group has an
- // unconstrained bsize
- nsTableRowGroupFrame* firstUnStyledRG = nullptr;
- nsTableRowFrame* firstUnStyledRow = nullptr;
- for (rgIdx = 0; rgIdx < rowGroups.Length() && !firstUnStyledRG; rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- if (!rgFrame->HasStyleBSize()) {
- nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
- while (rowFrame) {
- if (!rowFrame->HasStyleBSize()) {
- firstUnStyledRG = rgFrame;
- firstUnStyledRow = rowFrame;
- break;
- }
- rowFrame = rowFrame->GetNextRow();
- }
- }
- }
- nsTableRowFrame* lastEligibleRow = nullptr;
- // Accumulate the correct divisor. This will be the total bsize of all
- // unstyled rows inside unstyled row groups, unless there are none, in which
- // case, it will be number of all rows. If the unstyled rows don't have a
- // bsize, divide the space equally among them.
- nscoord divisor = 0;
- int32_t eligibleRows = 0;
- bool expandEmptyRows = false;
- if (!firstUnStyledRow) {
- // there is no unstyled row
- divisor = GetRowCount();
- }
- else {
- for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- if (!firstUnStyledRG || !rgFrame->HasStyleBSize()) {
- nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
- while (rowFrame) {
- if (!firstUnStyledRG || !rowFrame->HasStyleBSize()) {
- NS_ASSERTION(rowFrame->BSize(wm) >= 0,
- "negative row frame block-size");
- divisor += rowFrame->BSize(wm);
- eligibleRows++;
- lastEligibleRow = rowFrame;
- }
- rowFrame = rowFrame->GetNextRow();
- }
- }
- }
- if (divisor <= 0) {
- if (eligibleRows > 0) {
- expandEmptyRows = true;
- }
- else {
- NS_ERROR("invalid divisor");
- return;
- }
- }
- }
- // allocate the extra bsize to the unstyled row groups and rows
- nscoord bSizeToDistribute = aAmount - amountUsed;
- bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(-1);
- bEndRG = bOriginRG;
- for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- nscoord amountUsedByRG = 0;
- nscoord bOriginRow = 0;
- LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), containerSize);
- nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
- // see if there is an eligible row group or we distribute to all rows
- if (!firstUnStyledRG || !rgFrame->HasStyleBSize() || !eligibleRows) {
- for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
- rowFrame; rowFrame = rowFrame->GetNextRow()) {
- nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex());
- // We don't know the final width of the rowGroupFrame yet, so use 0,0
- // as a dummy containerSize here; we'll adjust the row positions at
- // the end, after the rowGroup size is finalized.
- const nsSize dummyContainerSize;
- LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(),
- dummyContainerSize);
- nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
- // see if there is an eligible row or we distribute to all rows
- if (!firstUnStyledRow || !rowFrame->HasStyleBSize() || !eligibleRows) {
- float ratio;
- if (eligibleRows) {
- if (!expandEmptyRows) {
- // The amount of additional space each row gets is proportional
- // to its bsize
- ratio = float(rowNormalRect.BSize(wm)) / float(divisor);
- } else {
- // empty rows get all the same additional space
- ratio = 1.0f / float(eligibleRows);
- }
- }
- else {
- // all rows get the same additional space
- ratio = 1.0f / float(divisor);
- }
- // give rows their additional space, except for the last row which
- // gets the remainder
- nscoord amountForRow =
- (rowFrame == lastEligibleRow)
- ? aAmount - amountUsed
- : NSToCoordRound(((float)(bSizeToDistribute)) * ratio);
- amountForRow = std::min(amountForRow, aAmount - amountUsed);
- if (bOriginRow != rowNormalRect.BStart(wm)) {
- rowFrame->InvalidateFrameSubtree();
- }
- // update the row bsize
- nsRect origRowRect = rowFrame->GetRect();
- nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
- rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
- rowNormalRect.BStart(wm)));
- rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm),
- newRowBSize));
- bOriginRow += newRowBSize + cellSpacingB;
- bEndRG += newRowBSize + cellSpacingB;
- amountUsed += amountForRow;
- amountUsedByRG += amountForRow;
- NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
- //rowFrame->DidResize();
- nsTableFrame::RePositionViews(rowFrame);
- nsTableFrame::InvalidateTableFrame(rowFrame, origRowRect,
- rowVisualOverflow, false);
- }
- else {
- if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm)) {
- rowFrame->InvalidateFrameSubtree();
- rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
- rowNormalRect.BStart(wm)));
- nsTableFrame::RePositionViews(rowFrame);
- rowFrame->InvalidateFrameSubtree();
- }
- bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB;
- bEndRG += rowNormalRect.BSize(wm) + cellSpacingB;
- }
- }
- if (amountUsed > 0) {
- if (rgNormalRect.BStart(wm) != bOriginRG) {
- rgFrame->InvalidateFrameSubtree();
- }
- nsRect origRgNormalRect = rgFrame->GetRect();
- rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
- rgNormalRect.BStart(wm)));
- rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm),
- rgNormalRect.BSize(wm) + amountUsedByRG));
- nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
- rgVisualOverflow, false);
- }
- // For vertical-rl mode, we needed to position the rows relative to the
- // right-hand (block-start) side of the group; but we couldn't do that
- // above, as we didn't know the rowGroupFrame's final block size yet.
- // So we used a dummyContainerSize of 0,0 earlier, placing the rows to
- // the left of the rowGroupFrame's (physical) origin. Now we move them
- // all rightwards by its final width.
- if (wm.IsVerticalRL()) {
- nscoord rgWidth = rgFrame->GetSize().width;
- for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
- rowFrame; rowFrame = rowFrame->GetNextRow()) {
- rowFrame->InvalidateFrameSubtree();
- rowFrame->MovePositionBy(nsPoint(rgWidth, 0));
- nsTableFrame::RePositionViews(rowFrame);
- rowFrame->InvalidateFrameSubtree();
- }
- }
- }
- else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
- rgFrame->InvalidateFrameSubtree();
- rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
- rgNormalRect.BStart(wm)));
- // Make sure child views are properly positioned
- nsTableFrame::RePositionViews(rgFrame);
- rgFrame->InvalidateFrameSubtree();
- }
- bOriginRG = bEndRG;
- }
- ResizeCells(*this);
- }
- nscoord
- nsTableFrame::GetColumnISizeFromFirstInFlow(int32_t aColIndex)
- {
- MOZ_ASSERT(this == FirstInFlow());
- nsTableColFrame* colFrame = GetColFrame(aColIndex);
- return colFrame ? colFrame->GetFinalISize() : 0;
- }
- nscoord
- nsTableFrame::GetColSpacing()
- {
- if (IsBorderCollapse())
- return 0;
- return StyleTableBorder()->mBorderSpacingCol;
- }
- // XXX: could cache this. But be sure to check style changes if you do!
- nscoord
- nsTableFrame::GetColSpacing(int32_t aColIndex)
- {
- NS_ASSERTION(aColIndex >= -1 && aColIndex <= GetColCount(),
- "Column index exceeds the bounds of the table");
- // Index is irrelevant for ordinary tables. We check that it falls within
- // appropriate bounds to increase confidence of correctness in situations
- // where it does matter.
- return GetColSpacing();
- }
- nscoord
- nsTableFrame::GetColSpacing(int32_t aStartColIndex,
- int32_t aEndColIndex)
- {
- NS_ASSERTION(aStartColIndex >= -1 && aStartColIndex <= GetColCount(),
- "Start column index exceeds the bounds of the table");
- NS_ASSERTION(aEndColIndex >= -1 && aEndColIndex <= GetColCount(),
- "End column index exceeds the bounds of the table");
- NS_ASSERTION(aStartColIndex <= aEndColIndex,
- "End index must not be less than start index");
- // Only one possible value so just multiply it out. Tables where index
- // matters will override this function
- return GetColSpacing() * (aEndColIndex - aStartColIndex);
- }
- nscoord
- nsTableFrame::GetRowSpacing()
- {
- if (IsBorderCollapse())
- return 0;
- return StyleTableBorder()->mBorderSpacingRow;
- }
- // XXX: could cache this. But be sure to check style changes if you do!
- nscoord
- nsTableFrame::GetRowSpacing(int32_t aRowIndex)
- {
- NS_ASSERTION(aRowIndex >= -1 && aRowIndex <= GetRowCount(),
- "Row index exceeds the bounds of the table");
- // Index is irrelevant for ordinary tables. We check that it falls within
- // appropriate bounds to increase confidence of correctness in situations
- // where it does matter.
- return GetRowSpacing();
- }
- nscoord
- nsTableFrame::GetRowSpacing(int32_t aStartRowIndex,
- int32_t aEndRowIndex)
- {
- NS_ASSERTION(aStartRowIndex >= -1 && aStartRowIndex <= GetRowCount(),
- "Start row index exceeds the bounds of the table");
- NS_ASSERTION(aEndRowIndex >= -1 && aEndRowIndex <= GetRowCount(),
- "End row index exceeds the bounds of the table");
- NS_ASSERTION(aStartRowIndex <= aEndRowIndex,
- "End index must not be less than start index");
- // Only one possible value so just multiply it out. Tables where index
- // matters will override this function
- return GetRowSpacing() * (aEndRowIndex - aStartRowIndex);
- }
- /* virtual */ nscoord
- nsTableFrame::GetLogicalBaseline(WritingMode aWM) const
- {
- nscoord baseline;
- if (!GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, &baseline)) {
- baseline = BSize(aWM);
- }
- return baseline;
- }
- /* virtual */ bool
- nsTableFrame::GetNaturalBaselineBOffset(WritingMode aWM,
- BaselineSharingGroup aBaselineGroup,
- nscoord* aBaseline) const
- {
- RowGroupArray orderedRowGroups;
- OrderRowGroups(orderedRowGroups);
- // XXX not sure if this should be the size of the containing block instead.
- nsSize containerSize = mRect.Size();
- auto TableBaseline = [aWM, containerSize] (nsTableRowGroupFrame* aRowGroup,
- nsTableRowFrame* aRow) {
- nscoord rgBStart = LogicalRect(aWM, aRowGroup->GetNormalRect(),
- containerSize).BStart(aWM);
- nscoord rowBStart = LogicalRect(aWM, aRow->GetNormalRect(),
- containerSize).BStart(aWM);
- return rgBStart + rowBStart + aRow->GetRowBaseline(aWM);
- };
- if (aBaselineGroup == BaselineSharingGroup::eFirst) {
- for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
- nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
- nsTableRowFrame* row = rgFrame->GetFirstRow();
- if (row) {
- *aBaseline = TableBaseline(rgFrame, row);
- return true;
- }
- }
- } else {
- for (uint32_t rgIndex = orderedRowGroups.Length(); rgIndex-- > 0;) {
- nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
- nsTableRowFrame* row = rgFrame->GetLastRow();
- if (row) {
- *aBaseline = BSize(aWM) - TableBaseline(rgFrame, row);
- return true;
- }
- }
- }
- return false;
- }
- /* ----- global methods ----- */
- nsTableFrame*
- NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
- {
- return new (aPresShell) nsTableFrame(aContext);
- }
- NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)
- nsTableFrame*
- nsTableFrame::GetTableFrame(nsIFrame* aFrame)
- {
- for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
- ancestor = ancestor->GetParent()) {
- if (nsGkAtoms::tableFrame == ancestor->GetType()) {
- return static_cast<nsTableFrame*>(ancestor);
- }
- }
- NS_RUNTIMEABORT("unable to find table parent");
- return nullptr;
- }
- nsTableFrame*
- nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
- nsIFrame* aFrame,
- bool* aDidPassThrough)
- {
- MOZ_ASSERT(aMustPassThrough == aFrame ||
- nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
- "aMustPassThrough should be an ancestor");
- // Retrieve the table frame, and check if we hit aMustPassThrough on the
- // way.
- *aDidPassThrough = false;
- nsTableFrame* tableFrame = nullptr;
- for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
- if (ancestor == aMustPassThrough) {
- *aDidPassThrough = true;
- }
- if (nsGkAtoms::tableFrame == ancestor->GetType()) {
- tableFrame = static_cast<nsTableFrame*>(ancestor);
- break;
- }
- }
- MOZ_ASSERT(tableFrame, "Should have a table frame here");
- return tableFrame;
- }
- bool
- nsTableFrame::IsAutoBSize(WritingMode aWM)
- {
- const nsStyleCoord &bsize = StylePosition()->BSize(aWM);
- // Don't consider calc() here like this quirk for percent.
- return bsize.GetUnit() == eStyleUnit_Auto ||
- (bsize.GetUnit() == eStyleUnit_Percent &&
- bsize.GetPercentValue() <= 0.0f);
- }
- nscoord
- nsTableFrame::CalcBorderBoxBSize(const ReflowInput& aState)
- {
- nscoord bSize = aState.ComputedBSize();
- if (NS_AUTOHEIGHT != bSize) {
- WritingMode wm = aState.GetWritingMode();
- LogicalMargin borderPadding = GetChildAreaOffset(wm, &aState);
- bSize += borderPadding.BStartEnd(wm);
- }
- bSize = std::max(0, bSize);
- return bSize;
- }
- bool
- nsTableFrame::IsAutoLayout()
- {
- if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
- return true;
- // a fixed-layout inline-table must have a inline size
- // and tables with inline size set to '-moz-max-content' must be
- // auto-layout (at least as long as
- // FixedTableLayoutStrategy::GetPrefISize returns nscoord_MAX)
- const nsStyleCoord &iSize = StylePosition()->ISize(GetWritingMode());
- return (iSize.GetUnit() == eStyleUnit_Auto) ||
- (iSize.GetUnit() == eStyleUnit_Enumerated &&
- iSize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
- }
- #ifdef DEBUG_FRAME_DUMP
- nsresult
- nsTableFrame::GetFrameName(nsAString& aResult) const
- {
- return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
- }
- #endif
- // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
- // is of type aChildType
- nsIFrame*
- nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
- nsIFrame* aPriorChildFrame,
- nsIAtom* aChildType)
- {
- nsIFrame* result = nullptr;
- if (!aPriorChildFrame) {
- return result;
- }
- if (aChildType == aPriorChildFrame->GetType()) {
- return aPriorChildFrame;
- }
- // aPriorChildFrame is not of type aChildType, so we need start from
- // the beginnng and find the closest one
- nsIFrame* lastMatchingFrame = nullptr;
- nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild();
- while (childFrame && (childFrame != aPriorChildFrame)) {
- if (aChildType == childFrame->GetType()) {
- lastMatchingFrame = childFrame;
- }
- childFrame = childFrame->GetNextSibling();
- }
- return lastMatchingFrame;
- }
- #ifdef DEBUG
- void
- nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
- {
- if (!aKidFrame)
- return;
- for (nsIFrame* cFrame : aKidFrame->PrincipalChildList()) {
- nsTableRowFrame *rowFrame = do_QueryFrame(cFrame);
- if (rowFrame) {
- printf("row(%d)=%p ", rowFrame->GetRowIndex(),
- static_cast<void*>(rowFrame));
- for (nsIFrame* childFrame : cFrame->PrincipalChildList()) {
- nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
- if (cellFrame) {
- uint32_t colIndex = cellFrame->ColIndex();
- printf("cell(%u)=%p ", colIndex, static_cast<void*>(childFrame));
- }
- }
- printf("\n");
- }
- else {
- DumpRowGroup(rowFrame);
- }
- }
- }
- void
- nsTableFrame::Dump(bool aDumpRows,
- bool aDumpCols,
- bool aDumpCellMap)
- {
- printf("***START TABLE DUMP*** \n");
- // dump the columns widths array
- printf("mColWidths=");
- int32_t numCols = GetColCount();
- int32_t colIdx;
- nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
- for (colIdx = 0; colIdx < numCols; colIdx++) {
- printf("%d ", fif->GetColumnISizeFromFirstInFlow(colIdx));
- }
- printf("\n");
- if (aDumpRows) {
- nsIFrame* kidFrame = mFrames.FirstChild();
- while (kidFrame) {
- DumpRowGroup(kidFrame);
- kidFrame = kidFrame->GetNextSibling();
- }
- }
- if (aDumpCols) {
- // output col frame cache
- printf("\n col frame cache ->");
- for (colIdx = 0; colIdx < numCols; colIdx++) {
- nsTableColFrame* colFrame = mColFrames.ElementAt(colIdx);
- if (0 == (colIdx % 8)) {
- printf("\n");
- }
- printf ("%d=%p ", colIdx, static_cast<void*>(colFrame));
- nsTableColType colType = colFrame->GetColType();
- switch (colType) {
- case eColContent:
- printf(" content ");
- break;
- case eColAnonymousCol:
- printf(" anonymous-column ");
- break;
- case eColAnonymousColGroup:
- printf(" anonymous-colgroup ");
- break;
- case eColAnonymousCell:
- printf(" anonymous-cell ");
- break;
- }
- }
- printf("\n colgroups->");
- for (nsIFrame* childFrame : mColGroups) {
- if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
- nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
- colGroupFrame->Dump(1);
- }
- }
- for (colIdx = 0; colIdx < numCols; colIdx++) {
- printf("\n");
- nsTableColFrame* colFrame = GetColFrame(colIdx);
- colFrame->Dump(1);
- }
- }
- if (aDumpCellMap) {
- nsTableCellMap* cellMap = GetCellMap();
- cellMap->Dump();
- }
- printf(" ***END TABLE DUMP*** \n");
- }
- #endif
- bool
- nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const
- {
- // Since fixed-layout tables should not have their column sizes change
- // as they load, we assume that all columns are significant.
- if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
- return true;
- // the first column is always significant
- if (aColIndex == 0)
- return true;
- nsTableCellMap* cellMap = GetCellMap();
- if (!cellMap)
- return false;
- return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
- }
- /********************************************************************************
- * Collapsing Borders
- *
- * The CSS spec says to resolve border conflicts in this order:
- * 1) any border with the style HIDDEN wins
- * 2) the widest border with a style that is not NONE wins
- * 3) the border styles are ranked in this order, highest to lowest precedence:
- * double, solid, dashed, dotted, ridge, outset, groove, inset
- * 4) borders that are of equal width and style (differ only in color) have this precedence:
- * cell, row, rowgroup, col, colgroup, table
- * 5) if all border styles are NONE, then that's the computed border style.
- *******************************************************************************/
- #ifdef DEBUG
- #define VerifyNonNegativeDamageRect(r) \
- NS_ASSERTION((r).StartCol() >= 0, "negative col index"); \
- NS_ASSERTION((r).StartRow() >= 0, "negative row index"); \
- NS_ASSERTION((r).ColCount() >= 0, "negative cols damage"); \
- NS_ASSERTION((r).RowCount() >= 0, "negative rows damage");
- #define VerifyDamageRect(r) \
- VerifyNonNegativeDamageRect(r); \
- NS_ASSERTION((r).EndCol() <= GetColCount(), \
- "cols damage extends outside table"); \
- NS_ASSERTION((r).EndRow() <= GetRowCount(), \
- "rows damage extends outside table");
- #endif
- void
- nsTableFrame::AddBCDamageArea(const TableArea& aValue)
- {
- NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
- #ifdef DEBUG
- VerifyDamageRect(aValue);
- #endif
- SetNeedToCalcBCBorders(true);
- SetNeedToCalcHasBCBorders(true);
- // Get the property
- BCPropertyData* value = GetOrCreateBCProperty();
- if (value) {
- #ifdef DEBUG
- VerifyNonNegativeDamageRect(value->mDamageArea);
- #endif
- // Clamp the old damage area to the current table area in case it shrunk.
- int32_t cols = GetColCount();
- if (value->mDamageArea.EndCol() > cols) {
- if (value->mDamageArea.StartCol() > cols) {
- value->mDamageArea.StartCol() = cols;
- value->mDamageArea.ColCount() = 0;
- }
- else {
- value->mDamageArea.ColCount() = cols - value->mDamageArea.StartCol();
- }
- }
- int32_t rows = GetRowCount();
- if (value->mDamageArea.EndRow() > rows) {
- if (value->mDamageArea.StartRow() > rows) {
- value->mDamageArea.StartRow() = rows;
- value->mDamageArea.RowCount() = 0;
- }
- else {
- value->mDamageArea.RowCount() = rows - value->mDamageArea.StartRow();
- }
- }
- // Construct a union of the new and old damage areas.
- value->mDamageArea.UnionArea(value->mDamageArea, aValue);
- }
- }
- void
- nsTableFrame::SetFullBCDamageArea()
- {
- NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
- SetNeedToCalcBCBorders(true);
- SetNeedToCalcHasBCBorders(true);
- BCPropertyData* value = GetOrCreateBCProperty();
- if (value) {
- value->mDamageArea = TableArea(0, 0, GetColCount(), GetRowCount());
- }
- }
- /* BCCellBorder represents a border segment which can be either an inline-dir
- * or a block-dir segment. For each segment we need to know the color, width,
- * style, who owns it and how long it is in cellmap coordinates.
- * Ownership of these segments is important to calculate which corners should
- * be bevelled. This structure has dual use, its used first to compute the
- * dominant border for inline-dir and block-dir segments and to store the
- * preliminary computed border results in the BCCellBorders structure.
- * This temporary storage is not symmetric with respect to inline-dir and
- * block-dir border segments, its always column oriented. For each column in
- * the cellmap there is a temporary stored block-dir and inline-dir segment.
- * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
- */
- struct BCCellBorder
- {
- BCCellBorder() { Reset(0, 1); }
- void Reset(uint32_t aRowIndex, uint32_t aRowSpan);
- nscolor color; // border segment color
- BCPixelSize width; // border segment width in pixel coordinates !!
- uint8_t style; // border segment style, possible values are defined
- // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
- BCBorderOwner owner; // border segment owner, possible values are defined
- // in celldata.h. In the cellmap for each border
- // segment we store the owner and later when
- // painting we know the owner and can retrieve the
- // style info from the corresponding frame
- int32_t rowIndex; // rowIndex of temporary stored inline-dir border
- // segments relative to the table
- int32_t rowSpan; // row span of temporary stored inline-dir border
- // segments
- };
- void
- BCCellBorder::Reset(uint32_t aRowIndex,
- uint32_t aRowSpan)
- {
- style = NS_STYLE_BORDER_STYLE_NONE;
- color = 0;
- width = 0;
- owner = eTableOwner;
- rowIndex = aRowIndex;
- rowSpan = aRowSpan;
- }
- class BCMapCellIterator;
- /*****************************************************************
- * BCMapCellInfo
- * This structure stores information about the cellmap and all involved
- * table related frames that are used during the computation of winning borders
- * in CalcBCBorders so that they do need to be looked up again and again when
- * iterating over the cells.
- ****************************************************************/
- struct BCMapCellInfo
- {
- explicit BCMapCellInfo(nsTableFrame* aTableFrame);
- void ResetCellInfo();
- void SetInfo(nsTableRowFrame* aNewRow,
- int32_t aColIndex,
- BCCellData* aCellData,
- BCMapCellIterator* aIter,
- nsCellMap* aCellMap = nullptr);
- // The BCMapCellInfo has functions to set the continous
- // border widths (see nsTablePainter.cpp for a description of the continous
- // borders concept). The widths are computed inside these functions based on
- // the current position inside the table and the cached frames that correspond
- // to this position. The widths are stored in member variables of the internal
- // table frames.
- void SetTableBStartIStartContBCBorder();
- void SetRowGroupIStartContBCBorder();
- void SetRowGroupIEndContBCBorder();
- void SetRowGroupBEndContBCBorder();
- void SetRowIStartContBCBorder();
- void SetRowIEndContBCBorder();
- void SetColumnBStartIEndContBCBorder();
- void SetColumnBEndContBCBorder();
- void SetColGroupBEndContBCBorder();
- void SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup,
- nsTableRowFrame* aNextRow);
- // functions to set the border widths on the table related frames, where the
- // knowledge about the current position in the table is used.
- void SetTableBStartBorderWidth(BCPixelSize aWidth);
- void SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth);
- void SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth);
- void SetTableBEndBorderWidth(BCPixelSize aWidth);
- void SetIStartBorderWidths(BCPixelSize aWidth);
- void SetIEndBorderWidths(BCPixelSize aWidth);
- void SetBStartBorderWidths(BCPixelSize aWidth);
- void SetBEndBorderWidths(BCPixelSize aWidth);
- // functions to compute the borders; they depend on the
- // knowledge about the current position in the table. The edge functions
- // should be called if a table edge is involved, otherwise the internal
- // functions should be called.
- BCCellBorder GetBStartEdgeBorder();
- BCCellBorder GetBEndEdgeBorder();
- BCCellBorder GetIStartEdgeBorder();
- BCCellBorder GetIEndEdgeBorder();
- BCCellBorder GetIEndInternalBorder();
- BCCellBorder GetIStartInternalBorder();
- BCCellBorder GetBStartInternalBorder();
- BCCellBorder GetBEndInternalBorder();
- // functions to set the internal position information
- void SetColumn(int32_t aColX);
- // Increment the row as we loop over the rows of a rowspan
- void IncrementRow(bool aResetToBStartRowOfCell = false);
- // Helper functions to get extent of the cell
- int32_t GetCellEndRowIndex() const;
- int32_t GetCellEndColIndex() const;
- // storage of table information
- nsTableFrame* mTableFrame;
- int32_t mNumTableRows;
- int32_t mNumTableCols;
- BCPropertyData* mTableBCData;
- WritingMode mTableWM;
- // a cell can only belong to one rowgroup
- nsTableRowGroupFrame* mRowGroup;
- // a cell with a rowspan has a bstart and a bend row, and rows in between
- nsTableRowFrame* mStartRow;
- nsTableRowFrame* mEndRow;
- nsTableRowFrame* mCurrentRowFrame;
- // a cell with a colspan has an istart and iend column and columns in between
- // they can belong to different colgroups
- nsTableColGroupFrame* mColGroup;
- nsTableColGroupFrame* mCurrentColGroupFrame;
- nsTableColFrame* mStartCol;
- nsTableColFrame* mEndCol;
- nsTableColFrame* mCurrentColFrame;
- // cell information
- BCCellData* mCellData;
- nsBCTableCellFrame* mCell;
- int32_t mRowIndex;
- int32_t mRowSpan;
- int32_t mColIndex;
- int32_t mColSpan;
- // flags to describe the position of the cell with respect to the row- and
- // colgroups, for instance mRgAtStart documents that the bStart cell border hits
- // a rowgroup border
- bool mRgAtStart;
- bool mRgAtEnd;
- bool mCgAtStart;
- bool mCgAtEnd;
- };
- BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
- : mTableFrame(aTableFrame)
- , mNumTableRows(aTableFrame->GetRowCount())
- , mNumTableCols(aTableFrame->GetColCount())
- , mTableBCData(mTableFrame->GetProperty(TableBCProperty()))
- , mTableWM(aTableFrame->StyleContext())
- {
- ResetCellInfo();
- }
- void
- BCMapCellInfo::ResetCellInfo()
- {
- mCellData = nullptr;
- mRowGroup = nullptr;
- mStartRow = nullptr;
- mEndRow = nullptr;
- mColGroup = nullptr;
- mStartCol = nullptr;
- mEndCol = nullptr;
- mCell = nullptr;
- mRowIndex = mRowSpan = mColIndex = mColSpan = 0;
- mRgAtStart = mRgAtEnd = mCgAtStart = mCgAtEnd = false;
- }
- inline int32_t
- BCMapCellInfo::GetCellEndRowIndex() const
- {
- return mRowIndex + mRowSpan - 1;
- }
- inline int32_t
- BCMapCellInfo::GetCellEndColIndex() const
- {
- return mColIndex + mColSpan - 1;
- }
- class BCMapCellIterator
- {
- public:
- BCMapCellIterator(nsTableFrame* aTableFrame,
- const TableArea& aDamageArea);
- void First(BCMapCellInfo& aMapCellInfo);
- void Next(BCMapCellInfo& aMapCellInfo);
- void PeekIEnd(BCMapCellInfo& aRefInfo,
- uint32_t aRowIndex,
- BCMapCellInfo& aAjaInfo);
- void PeekBEnd(BCMapCellInfo& aRefInfo,
- uint32_t aColIndex,
- BCMapCellInfo& aAjaInfo);
- bool IsNewRow() { return mIsNewRow; }
- nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
- nsTableRowFrame* GetCurrentRow() const { return mRow; }
- nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup; }
- int32_t mRowGroupStart;
- int32_t mRowGroupEnd;
- bool mAtEnd;
- nsCellMap* mCellMap;
- private:
- bool SetNewRow(nsTableRowFrame* row = nullptr);
- bool SetNewRowGroup(bool aFindFirstDamagedRow);
- nsTableFrame* mTableFrame;
- nsTableCellMap* mTableCellMap;
- nsTableFrame::RowGroupArray mRowGroups;
- nsTableRowGroupFrame* mRowGroup;
- int32_t mRowGroupIndex;
- uint32_t mNumTableRows;
- nsTableRowFrame* mRow;
- nsTableRowFrame* mPrevRow;
- bool mIsNewRow;
- int32_t mRowIndex;
- uint32_t mNumTableCols;
- int32_t mColIndex;
- nsPoint mAreaStart; // These are not really points in the usual
- nsPoint mAreaEnd; // sense; they're column/row coordinates
- // in the cell map.
- };
- BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
- const TableArea& aDamageArea)
- : mTableFrame(aTableFrame)
- {
- mTableCellMap = aTableFrame->GetCellMap();
- mAreaStart.x = aDamageArea.StartCol();
- mAreaStart.y = aDamageArea.StartRow();
- mAreaEnd.x = aDamageArea.EndCol() - 1;
- mAreaEnd.y = aDamageArea.EndRow() - 1;
- mNumTableRows = mTableFrame->GetRowCount();
- mRow = nullptr;
- mRowIndex = 0;
- mNumTableCols = mTableFrame->GetColCount();
- mColIndex = 0;
- mRowGroupIndex = -1;
- // Get the ordered row groups
- aTableFrame->OrderRowGroups(mRowGroups);
- mAtEnd = true; // gets reset when First() is called
- }
- // fill fields that we need for border collapse computation on a given cell
- void
- BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow,
- int32_t aColIndex,
- BCCellData* aCellData,
- BCMapCellIterator* aIter,
- nsCellMap* aCellMap)
- {
- // fill the cell information
- mCellData = aCellData;
- mColIndex = aColIndex;
- // initialize the row information if it was not previously set for cells in
- // this row
- mRowIndex = 0;
- if (aNewRow) {
- mStartRow = aNewRow;
- mRowIndex = aNewRow->GetRowIndex();
- }
- // fill cell frame info and row information
- mCell = nullptr;
- mRowSpan = 1;
- mColSpan = 1;
- if (aCellData) {
- mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
- if (mCell) {
- if (!mStartRow) {
- mStartRow = mCell->GetTableRowFrame();
- if (!mStartRow) ABORT0();
- mRowIndex = mStartRow->GetRowIndex();
- }
- mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
- mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
- }
- }
- if (!mStartRow) {
- mStartRow = aIter->GetCurrentRow();
- }
- if (1 == mRowSpan) {
- mEndRow = mStartRow;
- }
- else {
- mEndRow = mStartRow->GetNextRow();
- if (mEndRow) {
- for (int32_t span = 2; mEndRow && span < mRowSpan; span++) {
- mEndRow = mEndRow->GetNextRow();
- }
- NS_ASSERTION(mEndRow, "spanned row not found");
- }
- else {
- NS_ERROR("error in cell map");
- mRowSpan = 1;
- mEndRow = mStartRow;
- }
- }
- // row group frame info
- // try to reuse the rgStart and rgEnd from the iterator as calls to
- // GetRowCount() are computationally expensive and should be avoided if
- // possible
- uint32_t rgStart = aIter->mRowGroupStart;
- uint32_t rgEnd = aIter->mRowGroupEnd;
- mRowGroup = mStartRow->GetTableRowGroupFrame();
- if (mRowGroup != aIter->GetCurrentRowGroup()) {
- rgStart = mRowGroup->GetStartRowIndex();
- rgEnd = rgStart + mRowGroup->GetRowCount() - 1;
- }
- uint32_t rowIndex = mStartRow->GetRowIndex();
- mRgAtStart = rgStart == rowIndex;
- mRgAtEnd = rgEnd == rowIndex + mRowSpan - 1;
- // col frame info
- mStartCol = mTableFrame->GetColFrame(aColIndex);
- if (!mStartCol) ABORT0();
- mEndCol = mStartCol;
- if (mColSpan > 1) {
- nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex +
- mColSpan -1);
- if (!colFrame) ABORT0();
- mEndCol = colFrame;
- }
- // col group frame info
- mColGroup = mStartCol->GetTableColGroupFrame();
- int32_t cgStart = mColGroup->GetStartColumnIndex();
- int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1);
- mCgAtStart = cgStart == aColIndex;
- mCgAtEnd = cgEnd == aColIndex + mColSpan - 1;
- }
- bool
- BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
- {
- mAtEnd = true;
- mPrevRow = mRow;
- if (aRow) {
- mRow = aRow;
- }
- else if (mRow) {
- mRow = mRow->GetNextRow();
- }
- if (mRow) {
- mRowIndex = mRow->GetRowIndex();
- // get to the first entry with an originating cell
- int32_t rgRowIndex = mRowIndex - mRowGroupStart;
- if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length())
- ABORT1(false);
- const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
- for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
- CellData* cellData = row.SafeElementAt(mColIndex);
- if (!cellData) { // add a dead cell data
- TableArea damageArea;
- cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex,
- false, 0, damageArea);
- if (!cellData) ABORT1(false);
- }
- if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
- break;
- }
- }
- mIsNewRow = true;
- mAtEnd = false;
- }
- else ABORT1(false);
- return !mAtEnd;
- }
- bool
- BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow)
- {
- mAtEnd = true;
- int32_t numRowGroups = mRowGroups.Length();
- mCellMap = nullptr;
- for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
- mRowGroup = mRowGroups[mRowGroupIndex];
- int32_t rowCount = mRowGroup->GetRowCount();
- mRowGroupStart = mRowGroup->GetStartRowIndex();
- mRowGroupEnd = mRowGroupStart + rowCount - 1;
- if (rowCount > 0) {
- mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
- if (!mCellMap) ABORT1(false);
- nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
- if (aFindFirstDamagedRow) {
- if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
- // the damage area starts in the row group
- if (aFindFirstDamagedRow) {
- // find the correct first damaged row
- int32_t numRows = mAreaStart.y - mRowGroupStart;
- for (int32_t i = 0; i < numRows; i++) {
- firstRow = firstRow->GetNextRow();
- if (!firstRow) ABORT1(false);
- }
- }
- }
- else {
- continue;
- }
- }
- if (SetNewRow(firstRow)) { // sets mAtEnd
- break;
- }
- }
- }
- return !mAtEnd;
- }
- void
- BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
- {
- aMapInfo.ResetCellInfo();
- SetNewRowGroup(true); // sets mAtEnd
- while (!mAtEnd) {
- if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
- BCCellData* cellData =
- static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y -
- mRowGroupStart,
- mAreaStart.x));
- if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
- aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
- return;
- }
- else {
- NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) ,
- "damage area expanded incorrectly");
- }
- }
- SetNewRowGroup(true); // sets mAtEnd
- }
- }
- void
- BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
- {
- if (mAtEnd) ABORT0();
- aMapInfo.ResetCellInfo();
- mIsNewRow = false;
- mColIndex++;
- while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
- for (; mColIndex <= mAreaEnd.x; mColIndex++) {
- int32_t rgRowIndex = mRowIndex - mRowGroupStart;
- BCCellData* cellData =
- static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
- if (!cellData) { // add a dead cell data
- TableArea damageArea;
- cellData =
- static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
- rgRowIndex, false, 0,
- damageArea));
- if (!cellData) ABORT0();
- }
- if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
- aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
- return;
- }
- }
- if (mRowIndex >= mRowGroupEnd) {
- SetNewRowGroup(false); // could set mAtEnd
- }
- else {
- SetNewRow(); // could set mAtEnd
- }
- }
- mAtEnd = true;
- }
- void
- BCMapCellIterator::PeekIEnd(BCMapCellInfo& aRefInfo,
- uint32_t aRowIndex,
- BCMapCellInfo& aAjaInfo)
- {
- aAjaInfo.ResetCellInfo();
- int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan;
- uint32_t rgRowIndex = aRowIndex - mRowGroupStart;
- BCCellData* cellData =
- static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
- if (!cellData) { // add a dead cell data
- NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
- TableArea damageArea;
- cellData =
- static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
- rgRowIndex, false, 0,
- damageArea));
- if (!cellData) ABORT0();
- }
- nsTableRowFrame* row = nullptr;
- if (cellData->IsRowSpan()) {
- rgRowIndex -= cellData->GetRowSpanOffset();
- cellData =
- static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
- if (!cellData)
- ABORT0();
- }
- else {
- row = mRow;
- }
- aAjaInfo.SetInfo(row, colIndex, cellData, this);
- }
- void
- BCMapCellIterator::PeekBEnd(BCMapCellInfo& aRefInfo,
- uint32_t aColIndex,
- BCMapCellInfo& aAjaInfo)
- {
- aAjaInfo.ResetCellInfo();
- int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
- int32_t rgRowIndex = rowIndex - mRowGroupStart;
- nsTableRowGroupFrame* rg = mRowGroup;
- nsCellMap* cellMap = mCellMap;
- nsTableRowFrame* nextRow = nullptr;
- if (rowIndex > mRowGroupEnd) {
- int32_t nextRgIndex = mRowGroupIndex;
- do {
- nextRgIndex++;
- rg = mRowGroups.SafeElementAt(nextRgIndex);
- if (rg) {
- cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
- rgRowIndex = 0;
- nextRow = rg->GetFirstRow();
- }
- }
- while (rg && !nextRow);
- if(!rg) return;
- }
- else {
- // get the row within the same row group
- nextRow = mRow;
- for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) {
- nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
- }
- }
- BCCellData* cellData =
- static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
- if (!cellData) { // add a dead cell data
- NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
- TableArea damageArea;
- cellData =
- static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr,
- rgRowIndex, false, 0,
- damageArea));
- if (!cellData) ABORT0();
- }
- if (cellData->IsColSpan()) {
- aColIndex -= cellData->GetColSpanOffset();
- cellData =
- static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
- }
- aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
- }
- // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
- // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
- static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE
- 2, // NS_STYLE_BORDER_STYLE_GROOVE
- 4, // NS_STYLE_BORDER_STYLE_RIDGE
- 5, // NS_STYLE_BORDER_STYLE_DOTTED
- 6, // NS_STYLE_BORDER_STYLE_DASHED
- 7, // NS_STYLE_BORDER_STYLE_SOLID
- 8, // NS_STYLE_BORDER_STYLE_DOUBLE
- 1, // NS_STYLE_BORDER_STYLE_INSET
- 3, // NS_STYLE_BORDER_STYLE_OUTSET
- 9 };// NS_STYLE_BORDER_STYLE_HIDDEN
- // priority rules follow CSS 2.1 spec
- // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
- // and the lowest: 'inset'. none is even weaker
- #define CELL_CORNER true
- /** return the border style, border color and optionally the width in
- * pixel for a given frame and side
- * @param aFrame - query the info for this frame
- * @param aTableWM - the writing-mode of the frame
- * @param aSide - the side of the frame
- * @param aStyle - the border style
- * @param aColor - the border color
- * @param aWidth - the border width in px
- */
- static void
- GetColorAndStyle(const nsIFrame* aFrame,
- WritingMode aTableWM,
- LogicalSide aSide,
- uint8_t* aStyle,
- nscolor* aColor,
- BCPixelSize* aWidth = nullptr)
- {
- NS_PRECONDITION(aFrame, "null frame");
- NS_PRECONDITION(aStyle && aColor, "null argument");
- // initialize out arg
- *aColor = 0;
- if (aWidth) {
- *aWidth = 0;
- }
- const nsStyleBorder* styleData = aFrame->StyleBorder();
- mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide);
- *aStyle = styleData->GetBorderStyle(physicalSide);
- if ((NS_STYLE_BORDER_STYLE_NONE == *aStyle) ||
- (NS_STYLE_BORDER_STYLE_HIDDEN == *aStyle)) {
- return;
- }
- *aColor = aFrame->StyleContext()->GetVisitedDependentColor(
- nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[physicalSide]);
- if (aWidth) {
- nscoord width = styleData->GetComputedBorderWidth(physicalSide);
- *aWidth = aFrame->PresContext()->AppUnitsToDevPixels(width);
- }
- }
- /** coerce the paint style as required by CSS2.1
- * @param aFrame - query the info for this frame
- * @param aTableWM - the writing mode of the frame
- * @param aSide - the side of the frame
- * @param aStyle - the border style
- * @param aColor - the border color
- */
- static void
- GetPaintStyleInfo(const nsIFrame* aFrame,
- WritingMode aTableWM,
- LogicalSide aSide,
- uint8_t* aStyle,
- nscolor* aColor)
- {
- GetColorAndStyle(aFrame, aTableWM, aSide, aStyle, aColor);
- if (NS_STYLE_BORDER_STYLE_INSET == *aStyle) {
- *aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
- } else if (NS_STYLE_BORDER_STYLE_OUTSET == *aStyle) {
- *aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
- }
- }
- class nsDelayedCalcBCBorders : public Runnable {
- public:
- explicit nsDelayedCalcBCBorders(nsIFrame* aFrame) :
- mFrame(aFrame) {}
- NS_IMETHOD Run() override {
- if (mFrame) {
- nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
- if (tableFrame->NeedToCalcBCBorders()) {
- tableFrame->CalcBCBorders();
- }
- }
- return NS_OK;
- }
- private:
- nsWeakFrame mFrame;
- };
- bool
- nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
- nsStyleContext* aNewStyleContext)
- {
- // Attention: the old style context is the one we're forgetting,
- // and hence possibly completely bogus for GetStyle* purposes.
- // We use PeekStyleData instead.
- const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder();
- if (!oldStyleData)
- return false;
- const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder();
- nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
- if (!change)
- return false;
- if (change & nsChangeHint_NeedReflow)
- return true; // the caller only needs to mark the bc damage area
- if (change & nsChangeHint_RepaintFrame) {
- // we need to recompute the borders and the caller needs to mark
- // the bc damage area
- // XXX In principle this should only be necessary for border style changes
- // However the bc painting code tries to maximize the drawn border segments
- // so it stores in the cellmap where a new border segment starts and this
- // introduces a unwanted cellmap data dependence on color
- nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
- NS_DispatchToCurrentThread(evt);
- return true;
- }
- return false;
- }
- // Compare two border segments, this comparison depends whether the two
- // segments meet at a corner and whether the second segment is inline-dir.
- // The return value is whichever of aBorder1 or aBorder2 dominates.
- static const BCCellBorder&
- CompareBorders(bool aIsCorner, // Pass true for corner calculations
- const BCCellBorder& aBorder1,
- const BCCellBorder& aBorder2,
- bool aSecondIsInlineDir,
- bool* aFirstDominates = nullptr)
- {
- bool firstDominates = true;
- if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
- firstDominates = (aIsCorner) ? false : true;
- }
- else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
- firstDominates = (aIsCorner) ? true : false;
- }
- else if (aBorder1.width < aBorder2.width) {
- firstDominates = false;
- }
- else if (aBorder1.width == aBorder2.width) {
- if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
- firstDominates = false;
- }
- else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
- if (aBorder1.owner == aBorder2.owner) {
- firstDominates = !aSecondIsInlineDir;
- }
- else if (aBorder1.owner < aBorder2.owner) {
- firstDominates = false;
- }
- }
- }
- if (aFirstDominates)
- *aFirstDominates = firstDominates;
- if (firstDominates)
- return aBorder1;
- return aBorder2;
- }
- /** calc the dominant border by considering the table, row/col group, row/col,
- * cell.
- * Depending on whether the side is block-dir or inline-dir and whether
- * adjacent frames are taken into account the ownership of a single border
- * segment is defined. The return value is the dominating border
- * The cellmap stores only bstart and istart borders for each cellmap position.
- * If the cell border is owned by the cell that is istart-wards of the border
- * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
- * scenarios with a adjacent owner.
- * @param xxxFrame - the frame for style information, might be zero if
- * it should not be considered
- * @param aTableWM - the writing mode of the frame
- * @param aSide - side of the frames that should be considered
- * @param aAja - the border comparison takes place from the point of
- * a frame that is adjacent to the cellmap entry, for
- * when a cell owns its lower border it will be the
- * adjacent owner as in the cellmap only bstart and
- * istart borders are stored.
- */
- static BCCellBorder
- CompareBorders(const nsIFrame* aTableFrame,
- const nsIFrame* aColGroupFrame,
- const nsIFrame* aColFrame,
- const nsIFrame* aRowGroupFrame,
- const nsIFrame* aRowFrame,
- const nsIFrame* aCellFrame,
- WritingMode aTableWM,
- LogicalSide aSide,
- bool aAja)
- {
- BCCellBorder border, tempBorder;
- bool inlineAxis = IsBlock(aSide);
- // start with the table as dominant if present
- if (aTableFrame) {
- GetColorAndStyle(aTableFrame, aTableWM, aSide,
- &border.style, &border.color, &border.width);
- border.owner = eTableOwner;
- if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
- return border;
- }
- }
- // see if the colgroup is dominant
- if (aColGroupFrame) {
- GetColorAndStyle(aColGroupFrame, aTableWM, aSide,
- &tempBorder.style, &tempBorder.color, &tempBorder.width);
- tempBorder.owner = aAja && !inlineAxis ? eAjaColGroupOwner : eColGroupOwner;
- // pass here and below false for aSecondIsInlineDir as it is only used for corner calculations.
- border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
- if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
- return border;
- }
- }
- // see if the col is dominant
- if (aColFrame) {
- GetColorAndStyle(aColFrame, aTableWM, aSide,
- &tempBorder.style, &tempBorder.color, &tempBorder.width);
- tempBorder.owner = aAja && !inlineAxis ? eAjaColOwner : eColOwner;
- border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
- if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
- return border;
- }
- }
- // see if the rowgroup is dominant
- if (aRowGroupFrame) {
- GetColorAndStyle(aRowGroupFrame, aTableWM, aSide,
- &tempBorder.style, &tempBorder.color, &tempBorder.width);
- tempBorder.owner = aAja && inlineAxis ? eAjaRowGroupOwner : eRowGroupOwner;
- border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
- if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
- return border;
- }
- }
- // see if the row is dominant
- if (aRowFrame) {
- GetColorAndStyle(aRowFrame, aTableWM, aSide,
- &tempBorder.style, &tempBorder.color, &tempBorder.width);
- tempBorder.owner = aAja && inlineAxis ? eAjaRowOwner : eRowOwner;
- border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
- if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
- return border;
- }
- }
- // see if the cell is dominant
- if (aCellFrame) {
- GetColorAndStyle(aCellFrame, aTableWM, aSide,
- &tempBorder.style, &tempBorder.color, &tempBorder.width);
- tempBorder.owner = aAja ? eAjaCellOwner : eCellOwner;
- border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
- }
- return border;
- }
- static bool
- Perpendicular(mozilla::LogicalSide aSide1,
- mozilla::LogicalSide aSide2)
- {
- return IsInline(aSide1) != IsInline(aSide2);
- }
- // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
- struct BCCornerInfo
- {
- BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide =
- subElem = hasDashDot = numSegs = bevel = 0; ownerSide = eLogicalSideBStart;
- ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; }
- void Set(mozilla::LogicalSide aSide,
- BCCellBorder border);
- void Update(mozilla::LogicalSide aSide,
- BCCellBorder border);
- nscolor ownerColor; // color of borderOwner
- uint16_t ownerWidth; // pixel width of borderOwner
- uint16_t subWidth; // pixel width of the largest border intersecting the border perpendicular
- // to ownerSide
- uint32_t ownerSide:2; // LogicalSide (e.g eLogicalSideBStart, etc) of the border
- // owning the corner relative to the corner
- uint32_t ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner
- uint32_t ownerStyle:8; // border style of ownerElem
- uint32_t subSide:2; // side of border with subWidth relative to the corner
- uint32_t subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner
- uint32_t subStyle:8; // border style of subElem
- uint32_t hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled
- uint32_t numSegs:3; // number of segments entering corner
- uint32_t bevel:1; // is the corner beveled (uses the above two fields together with subWidth)
- uint32_t unused:1;
- };
- void
- BCCornerInfo::Set(mozilla::LogicalSide aSide,
- BCCellBorder aBorder)
- {
- ownerElem = aBorder.owner;
- ownerStyle = aBorder.style;
- ownerWidth = aBorder.width;
- ownerColor = aBorder.color;
- ownerSide = aSide;
- hasDashDot = 0;
- numSegs = 0;
- if (aBorder.width > 0) {
- numSegs++;
- hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
- (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
- }
- bevel = 0;
- subWidth = 0;
- // the following will get set later
- subSide = IsInline(aSide) ? eLogicalSideBStart : eLogicalSideIStart;
- subElem = eTableOwner;
- subStyle = NS_STYLE_BORDER_STYLE_SOLID;
- }
- void
- BCCornerInfo::Update(mozilla::LogicalSide aSide,
- BCCellBorder aBorder)
- {
- bool existingWins = false;
- if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
- Set(aSide, aBorder);
- }
- else {
- bool isInline = IsInline(aSide); // relative to the corner
- BCCellBorder oldBorder, tempBorder;
- oldBorder.owner = (BCBorderOwner) ownerElem;
- oldBorder.style = ownerStyle;
- oldBorder.width = ownerWidth;
- oldBorder.color = ownerColor;
- LogicalSide oldSide = LogicalSide(ownerSide);
- tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, isInline, &existingWins);
- ownerElem = tempBorder.owner;
- ownerStyle = tempBorder.style;
- ownerWidth = tempBorder.width;
- ownerColor = tempBorder.color;
- if (existingWins) { // existing corner is dominant
- if (::Perpendicular(LogicalSide(ownerSide), aSide)) {
- // see if the new sub info replaces the old
- BCCellBorder subBorder;
- subBorder.owner = (BCBorderOwner) subElem;
- subBorder.style = subStyle;
- subBorder.width = subWidth;
- subBorder.color = 0; // we are not interested in subBorder color
- bool firstWins;
- tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, isInline, &firstWins);
- subElem = tempBorder.owner;
- subStyle = tempBorder.style;
- subWidth = tempBorder.width;
- if (!firstWins) {
- subSide = aSide;
- }
- }
- }
- else { // input args are dominant
- ownerSide = aSide;
- if (::Perpendicular(oldSide, LogicalSide(ownerSide))) {
- subElem = oldBorder.owner;
- subStyle = oldBorder.style;
- subWidth = oldBorder.width;
- subSide = oldSide;
- }
- }
- if (aBorder.width > 0) {
- numSegs++;
- if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
- (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
- hasDashDot = 1;
- }
- }
- // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
- bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
- }
- }
- struct BCCorners
- {
- BCCorners(int32_t aNumCorners,
- int32_t aStartIndex);
- ~BCCorners() { delete [] corners; }
- BCCornerInfo& operator [](int32_t i) const
- { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
- return corners[clamped(i, startIndex, endIndex) - startIndex]; }
- int32_t startIndex;
- int32_t endIndex;
- BCCornerInfo* corners;
- };
- BCCorners::BCCorners(int32_t aNumCorners,
- int32_t aStartIndex)
- {
- NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
- startIndex = aStartIndex;
- endIndex = aStartIndex + aNumCorners - 1;
- corners = new BCCornerInfo[aNumCorners];
- }
- struct BCCellBorders
- {
- BCCellBorders(int32_t aNumBorders,
- int32_t aStartIndex);
- ~BCCellBorders() { delete [] borders; }
- BCCellBorder& operator [](int32_t i) const
- { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
- return borders[clamped(i, startIndex, endIndex) - startIndex]; }
- int32_t startIndex;
- int32_t endIndex;
- BCCellBorder* borders;
- };
- BCCellBorders::BCCellBorders(int32_t aNumBorders,
- int32_t aStartIndex)
- {
- NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
- startIndex = aStartIndex;
- endIndex = aStartIndex + aNumBorders - 1;
- borders = new BCCellBorder[aNumBorders];
- }
- // this function sets the new border properties and returns true if the border
- // segment will start a new segment and not be accumulated into the previous
- // segment.
- static bool
- SetBorder(const BCCellBorder& aNewBorder,
- BCCellBorder& aBorder)
- {
- bool changed = (aNewBorder.style != aBorder.style) ||
- (aNewBorder.width != aBorder.width) ||
- (aNewBorder.color != aBorder.color);
- aBorder.color = aNewBorder.color;
- aBorder.width = aNewBorder.width;
- aBorder.style = aNewBorder.style;
- aBorder.owner = aNewBorder.owner;
- return changed;
- }
- // this function will set the inline-dir border. It will return true if the
- // existing segment will not be continued. Having a block-dir owner of a corner
- // should also start a new segment.
- static bool
- SetInlineDirBorder(const BCCellBorder& aNewBorder,
- const BCCornerInfo& aCorner,
- BCCellBorder& aBorder)
- {
- bool startSeg = ::SetBorder(aNewBorder, aBorder);
- if (!startSeg) {
- startSeg = !IsInline(LogicalSide(aCorner.ownerSide));
- }
- return startSeg;
- }
- // Make the damage area larger on the top and bottom by at least one row and on the left and right
- // at least one column. This is done so that adjacent elements are part of the border calculations.
- // The extra segments and borders outside the actual damage area will not be updated in the cell map,
- // because they in turn would need info from adjacent segments outside the damage area to be accurate.
- void
- nsTableFrame::ExpandBCDamageArea(TableArea& aArea) const
- {
- int32_t numRows = GetRowCount();
- int32_t numCols = GetColCount();
- int32_t dStartX = aArea.StartCol();
- int32_t dEndX = aArea.EndCol() - 1;
- int32_t dStartY = aArea.StartRow();
- int32_t dEndY = aArea.EndRow() - 1;
- // expand the damage area in each direction
- if (dStartX > 0) {
- dStartX--;
- }
- if (dEndX < (numCols - 1)) {
- dEndX++;
- }
- if (dStartY > 0) {
- dStartY--;
- }
- if (dEndY < (numRows - 1)) {
- dEndY++;
- }
- // Check the damage area so that there are no cells spanning in or out. If there are any then
- // make the damage area as big as the table, similarly to the way the cell map decides whether
- // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
- // no spanners, but it may not be worth the effort in general, and it would need to be done in the
- // cell map as well.
- bool haveSpanner = false;
- if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
- nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
- // Get the ordered row groups
- RowGroupArray rowGroups;
- OrderRowGroups(rowGroups);
- // Scope outside loop to be used as hint.
- nsCellMap* cellMap = nullptr;
- for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
- nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
- int32_t rgStartY = rgFrame->GetStartRowIndex();
- int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1;
- if (dEndY < rgStartY)
- break;
- cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
- if (!cellMap) ABORT0();
- // check for spanners from above and below
- if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
- if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length())
- ABORT0();
- const nsCellMap::CellDataArray& row =
- cellMap->mRows[dStartY - rgStartY];
- for (int32_t x = dStartX; x <= dEndX; x++) {
- CellData* cellData = row.SafeElementAt(x);
- if (cellData && (cellData->IsRowSpan())) {
- haveSpanner = true;
- break;
- }
- }
- if (dEndY < rgEndY) {
- if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
- ABORT0();
- const nsCellMap::CellDataArray& row2 =
- cellMap->mRows[dEndY + 1 - rgStartY];
- for (int32_t x = dStartX; x <= dEndX; x++) {
- CellData* cellData = row2.SafeElementAt(x);
- if (cellData && (cellData->IsRowSpan())) {
- haveSpanner = true;
- break;
- }
- }
- }
- }
- // check for spanners on the left and right
- int32_t iterStartY = -1;
- int32_t iterEndY = -1;
- if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
- // the damage area starts in the row group
- iterStartY = dStartY;
- iterEndY = std::min(dEndY, rgEndY);
- }
- else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
- // the damage area ends in the row group
- iterStartY = rgStartY;
- iterEndY = dEndY;
- }
- else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
- // the damage area contains the row group
- iterStartY = rgStartY;
- iterEndY = rgEndY;
- }
- if ((iterStartY >= 0) && (iterEndY >= 0)) {
- for (int32_t y = iterStartY; y <= iterEndY; y++) {
- if (uint32_t(y - rgStartY) >= cellMap->mRows.Length())
- ABORT0();
- const nsCellMap::CellDataArray& row =
- cellMap->mRows[y - rgStartY];
- CellData* cellData = row.SafeElementAt(dStartX);
- if (cellData && (cellData->IsColSpan())) {
- haveSpanner = true;
- break;
- }
- if (dEndX < (numCols - 1)) {
- cellData = row.SafeElementAt(dEndX + 1);
- if (cellData && (cellData->IsColSpan())) {
- haveSpanner = true;
- break;
- }
- }
- }
- }
- }
- }
- if (haveSpanner) {
- // make the damage area the whole table
- aArea.StartCol() = 0;
- aArea.StartRow() = 0;
- aArea.ColCount() = numCols;
- aArea.RowCount() = numRows;
- }
- else {
- aArea.StartCol() = dStartX;
- aArea.StartRow() = dStartY;
- aArea.ColCount() = 1 + dEndX - dStartX;
- aArea.RowCount() = 1 + dEndY - dStartY;
- }
- }
- #define ADJACENT true
- #define INLINE_DIR true
- void
- BCMapCellInfo::SetTableBStartIStartContBCBorder()
- {
- BCCellBorder currentBorder;
- //calculate continuous top first row & rowgroup border: special case
- //because it must include the table in the collapse
- if (mStartRow) {
- currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
- mStartRow, nullptr, mTableWM,
- eLogicalSideBStart, !ADJACENT);
- mStartRow->SetContinuousBCBorderWidth(eLogicalSideBStart,
- currentBorder.width);
- }
- if (mCgAtEnd && mColGroup) {
- //calculate continuous top colgroup border once per colgroup
- currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
- mStartRow, nullptr, mTableWM,
- eLogicalSideBStart, !ADJACENT);
- mColGroup->SetContinuousBCBorderWidth(eLogicalSideBStart,
- currentBorder.width);
- }
- if (0 == mColIndex) {
- currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol, nullptr,
- nullptr, nullptr, mTableWM,
- eLogicalSideIStart, !ADJACENT);
- mTableFrame->SetContinuousIStartBCBorderWidth(currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetRowGroupIStartContBCBorder()
- {
- BCCellBorder currentBorder;
- //get row group continuous borders
- if (mRgAtEnd && mRowGroup) { //once per row group, so check for bottom
- currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol,
- mRowGroup, nullptr, nullptr, mTableWM,
- eLogicalSideIStart, !ADJACENT);
- mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIStart,
- currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetRowGroupIEndContBCBorder()
- {
- BCCellBorder currentBorder;
- //get row group continuous borders
- if (mRgAtEnd && mRowGroup) { //once per mRowGroup, so check for bottom
- currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
- nullptr, nullptr, mTableWM, eLogicalSideIEnd,
- ADJACENT);
- mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIEnd,
- currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetColumnBStartIEndContBCBorder()
- {
- BCCellBorder currentBorder;
- //calculate column continuous borders
- //we only need to do this once, so we'll do it only on the first row
- currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
- mCurrentColFrame, mRowGroup, mStartRow,
- nullptr, mTableWM, eLogicalSideBStart,
- !ADJACENT);
- mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBStart,
- currentBorder.width);
- if (mNumTableCols == GetCellEndColIndex() + 1) {
- currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
- mCurrentColFrame, nullptr, nullptr, nullptr,
- mTableWM, eLogicalSideIEnd, !ADJACENT);
- }
- else {
- currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame,
- mCurrentColFrame, nullptr,nullptr, nullptr,
- mTableWM, eLogicalSideIEnd, !ADJACENT);
- }
- mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd,
- currentBorder.width);
- }
- void
- BCMapCellInfo::SetColumnBEndContBCBorder()
- {
- BCCellBorder currentBorder;
- //get col continuous border
- currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
- mCurrentColFrame, mRowGroup, mEndRow,
- nullptr, mTableWM, eLogicalSideBEnd, ADJACENT);
- mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBEnd,
- currentBorder.width);
- }
- void
- BCMapCellInfo::SetColGroupBEndContBCBorder()
- {
- BCCellBorder currentBorder;
- if (mColGroup) {
- currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
- mEndRow, nullptr, mTableWM,
- eLogicalSideBEnd, ADJACENT);
- mColGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetRowGroupBEndContBCBorder()
- {
- BCCellBorder currentBorder;
- if (mRowGroup) {
- currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
- mEndRow, nullptr, mTableWM,
- eLogicalSideBEnd, ADJACENT);
- mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup,
- nsTableRowFrame* aNextRow)
- {
- BCCellBorder currentBorder, adjacentBorder;
- const nsIFrame* rowgroup = mRgAtEnd ? mRowGroup : nullptr;
- currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mEndRow,
- nullptr, mTableWM, eLogicalSideBEnd, ADJACENT);
- adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup,
- aNextRow, nullptr, mTableWM, eLogicalSideBStart,
- !ADJACENT);
- currentBorder = CompareBorders(false, currentBorder, adjacentBorder,
- INLINE_DIR);
- if (aNextRow) {
- aNextRow->SetContinuousBCBorderWidth(eLogicalSideBStart,
- currentBorder.width);
- }
- if (mRgAtEnd && mRowGroup) {
- mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetRowIStartContBCBorder()
- {
- //get row continuous borders
- if (mCurrentRowFrame) {
- BCCellBorder currentBorder;
- currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol,
- mRowGroup, mCurrentRowFrame, nullptr,
- mTableWM, eLogicalSideIStart, !ADJACENT);
- mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIStart,
- currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetRowIEndContBCBorder()
- {
- if (mCurrentRowFrame) {
- BCCellBorder currentBorder;
- currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
- mCurrentRowFrame, nullptr, mTableWM,
- eLogicalSideIEnd, ADJACENT);
- mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd,
- currentBorder.width);
- }
- }
- void
- BCMapCellInfo::SetTableBStartBorderWidth(BCPixelSize aWidth)
- {
- mTableBCData->mBStartBorderWidth = std::max(mTableBCData->mBStartBorderWidth,
- aWidth);
- }
- void
- BCMapCellInfo::SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth)
- {
- // update the iStart first cell border
- if (aRowB == 0) {
- mTableBCData->mIStartCellBorderWidth = aWidth;
- }
- mTableBCData->mIStartBorderWidth = std::max(mTableBCData->mIStartBorderWidth,
- aWidth);
- }
- void
- BCMapCellInfo::SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth)
- {
- // update the iEnd first cell border
- if (aRowB == 0) {
- mTableBCData->mIEndCellBorderWidth = aWidth;
- }
- mTableBCData->mIEndBorderWidth = std::max(mTableBCData->mIEndBorderWidth,
- aWidth);
- }
- void
- BCMapCellInfo::SetIEndBorderWidths(BCPixelSize aWidth)
- {
- // update the borders of the cells and cols affected
- if (mCell) {
- mCell->SetBorderWidth(eLogicalSideIEnd, std::max(aWidth,
- mCell->GetBorderWidth(eLogicalSideIEnd)));
- }
- if (mEndCol) {
- BCPixelSize half = BC_BORDER_START_HALF(aWidth);
- mEndCol->SetIEndBorderWidth(
- std::max(nscoord(half), mEndCol->GetIEndBorderWidth()));
- }
- }
- void
- BCMapCellInfo::SetBEndBorderWidths(BCPixelSize aWidth)
- {
- // update the borders of the affected cells and rows
- if (mCell) {
- mCell->SetBorderWidth(eLogicalSideBEnd, std::max(aWidth,
- mCell->GetBorderWidth(eLogicalSideBEnd)));
- }
- if (mEndRow) {
- BCPixelSize half = BC_BORDER_START_HALF(aWidth);
- mEndRow->SetBEndBCBorderWidth(
- std::max(nscoord(half), mEndRow->GetBEndBCBorderWidth()));
- }
- }
- void
- BCMapCellInfo::SetBStartBorderWidths(BCPixelSize aWidth)
- {
- if (mCell) {
- mCell->SetBorderWidth(eLogicalSideBStart, std::max(aWidth,
- mCell->GetBorderWidth(eLogicalSideBStart)));
- }
- if (mStartRow) {
- BCPixelSize half = BC_BORDER_END_HALF(aWidth);
- mStartRow->SetBStartBCBorderWidth(
- std::max(nscoord(half), mStartRow->GetBStartBCBorderWidth()));
- }
- }
- void
- BCMapCellInfo::SetIStartBorderWidths(BCPixelSize aWidth)
- {
- if (mCell) {
- mCell->SetBorderWidth(eLogicalSideIStart, std::max(aWidth,
- mCell->GetBorderWidth(eLogicalSideIStart)));
- }
- if (mStartCol) {
- BCPixelSize half = BC_BORDER_END_HALF(aWidth);
- mStartCol->SetIStartBorderWidth(
- std::max(nscoord(half), mStartCol->GetIStartBorderWidth()));
- }
- }
- void
- BCMapCellInfo::SetTableBEndBorderWidth(BCPixelSize aWidth)
- {
- mTableBCData->mBEndBorderWidth = std::max(mTableBCData->mBEndBorderWidth,
- aWidth);
- }
- void
- BCMapCellInfo::SetColumn(int32_t aColX)
- {
- mCurrentColFrame = mTableFrame->GetColFrame(aColX);
- if (!mCurrentColFrame) {
- NS_ERROR("null mCurrentColFrame");
- }
- mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*>
- (mCurrentColFrame->GetParent());
- if (!mCurrentColGroupFrame) {
- NS_ERROR("null mCurrentColGroupFrame");
- }
- }
- void
- BCMapCellInfo::IncrementRow(bool aResetToBStartRowOfCell)
- {
- mCurrentRowFrame =
- aResetToBStartRowOfCell ? mStartRow : mCurrentRowFrame->GetNextRow();
- }
- BCCellBorder
- BCMapCellInfo::GetBStartEdgeBorder()
- {
- return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
- mRowGroup, mStartRow, mCell, mTableWM,
- eLogicalSideBStart, !ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetBEndEdgeBorder()
- {
- return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
- mRowGroup, mEndRow, mCell, mTableWM,
- eLogicalSideBEnd, ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetIStartEdgeBorder()
- {
- return CompareBorders(mTableFrame, mColGroup, mStartCol, mRowGroup,
- mCurrentRowFrame, mCell, mTableWM, eLogicalSideIStart,
- !ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetIEndEdgeBorder()
- {
- return CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
- mCurrentRowFrame, mCell, mTableWM, eLogicalSideIEnd,
- ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetIEndInternalBorder()
- {
- const nsIFrame* cg = mCgAtEnd ? mColGroup : nullptr;
- return CompareBorders(nullptr, cg, mEndCol, nullptr, nullptr, mCell,
- mTableWM, eLogicalSideIEnd, ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetIStartInternalBorder()
- {
- const nsIFrame* cg = mCgAtStart ? mColGroup : nullptr;
- return CompareBorders(nullptr, cg, mStartCol, nullptr, nullptr, mCell,
- mTableWM, eLogicalSideIStart, !ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetBEndInternalBorder()
- {
- const nsIFrame* rg = mRgAtEnd ? mRowGroup : nullptr;
- return CompareBorders(nullptr, nullptr, nullptr, rg, mEndRow, mCell,
- mTableWM, eLogicalSideBEnd, ADJACENT);
- }
- BCCellBorder
- BCMapCellInfo::GetBStartInternalBorder()
- {
- const nsIFrame* rg = mRgAtStart ? mRowGroup : nullptr;
- return CompareBorders(nullptr, nullptr, nullptr, rg, mStartRow, mCell,
- mTableWM, eLogicalSideBStart, !ADJACENT);
- }
- /* XXX This comment is still written in physical (horizontal-tb) terms.
- Here is the order for storing border edges in the cell map as a cell is processed. There are
- n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
- 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
- a bottom edge from a cell above will take care of it.
- 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
- a right edge from a cell to the left will take care of it.
- 3) Store the right edge (or edges if a row span)
- 4) Store the bottom edge (or edges if a col span)
- Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
- calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
- would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
- borders already have about an extra 8 byte per cell map entry overhead (this could be
- reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
- rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
- left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
- edges per cell and n=rowspan left and right border edges per cell.
- 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
- Never store the top-right corner, since it will get stored as a right-top corner.
- 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
- since it will get stored as a bottom-left corner.
- 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
- the top edge of the table. Never store the right-bottom corner since it will get stored as a
- bottom-right corner.
- 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
- otherwise, since it will get stored as either a right-top corner by a cell below or
- a bottom-left corner from a cell to the right.
- 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
- the top side of a colspan in its interior. Never store the corner otherwise, since it will
- get stored as a right-top corner by a cell from below.
- XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
- In order to draw borders in rtl conditions somehow correct, the existing structure which relies
- heavily on the assumption that the next cell sibling will be on the right side, has been modified.
- We flip the border during painting and during style lookup. Look for tableIsLTR for places where
- the flipping is done.
- */
- // Calc the dominant border at every cell edge and corner within the current damage area
- void
- nsTableFrame::CalcBCBorders()
- {
- NS_ASSERTION(IsBorderCollapse(),
- "calling CalcBCBorders on separated-border table");
- nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
- int32_t numRows = GetRowCount();
- int32_t numCols = GetColCount();
- if (!numRows || !numCols)
- return; // nothing to do
- // Get the property holding the table damage area and border widths
- BCPropertyData* propData = GetBCProperty();
- if (!propData) ABORT0();
- // calculate an expanded damage area
- TableArea damageArea(propData->mDamageArea);
- ExpandBCDamageArea(damageArea);
- // segments that are on the table border edges need
- // to be initialized only once
- bool tableBorderReset[4];
- for (uint32_t sideX = 0; sideX < ArrayLength(tableBorderReset); sideX++) {
- tableBorderReset[sideX] = false;
- }
- // block-dir borders indexed in inline-direction (cols)
- BCCellBorders lastBlockDirBorders(damageArea.ColCount() + 1,
- damageArea.StartCol());
- if (!lastBlockDirBorders.borders) ABORT0();
- BCCellBorder lastBStartBorder, lastBEndBorder;
- // inline-dir borders indexed in inline-direction (cols)
- BCCellBorders lastBEndBorders(damageArea.ColCount() + 1,
- damageArea.StartCol());
- if (!lastBEndBorders.borders) ABORT0();
- bool startSeg;
- bool gotRowBorder = false;
- BCMapCellInfo info(this), ajaInfo(this);
- BCCellBorder currentBorder, adjacentBorder;
- BCCorners bStartCorners(damageArea.ColCount() + 1, damageArea.StartCol());
- if (!bStartCorners.corners) ABORT0();
- BCCorners bEndCorners(damageArea.ColCount() + 1, damageArea.StartCol());
- if (!bEndCorners.corners) ABORT0();
- BCMapCellIterator iter(this, damageArea);
- for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
- // see if lastBStartBorder, lastBEndBorder need to be reset
- if (iter.IsNewRow()) {
- gotRowBorder = false;
- lastBStartBorder.Reset(info.mRowIndex, info.mRowSpan);
- lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
- }
- else if (info.mColIndex > damageArea.StartCol()) {
- lastBEndBorder = lastBEndBorders[info.mColIndex - 1];
- if (info.mRowIndex >
- (lastBEndBorder.rowIndex - lastBEndBorder.rowSpan)) {
- // the bStart border's iStart edge butts against the middle of a rowspan
- lastBStartBorder.Reset(info.mRowIndex, info.mRowSpan);
- }
- if (lastBEndBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
- // the bEnd border's iStart edge butts against the middle of a rowspan
- lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
- }
- }
- // find the dominant border considering the cell's bStart border and the table,
- // row group, row if the border is at the bStart of the table, otherwise it was
- // processed in a previous row
- if (0 == info.mRowIndex) {
- if (!tableBorderReset[eLogicalSideBStart]) {
- propData->mBStartBorderWidth = 0;
- tableBorderReset[eLogicalSideBStart] = true;
- }
- for (int32_t colIdx = info.mColIndex;
- colIdx <= info.GetCellEndColIndex(); colIdx++) {
- info.SetColumn(colIdx);
- currentBorder = info.GetBStartEdgeBorder();
- // update/store the bStart-iStart & bStart-iEnd corners of the seg
- BCCornerInfo& tlCorner = bStartCorners[colIdx]; // bStart-iStart
- if (0 == colIdx) {
- // we are on the iEnd side of the corner
- tlCorner.Set(eLogicalSideIEnd, currentBorder);
- }
- else {
- tlCorner.Update(eLogicalSideIEnd, currentBorder);
- tableCellMap->SetBCBorderCorner(eBStartIStart, *iter.mCellMap, 0, 0, colIdx,
- LogicalSide(tlCorner.ownerSide),
- tlCorner.subWidth,
- tlCorner.bevel);
- }
- bStartCorners[colIdx + 1].Set(eLogicalSideIStart, currentBorder); // bStart-iEnd
- // update lastBStartBorder and see if a new segment starts
- startSeg = SetInlineDirBorder(currentBorder, tlCorner, lastBStartBorder);
- // store the border segment in the cell map
- tableCellMap->SetBCBorderEdge(eLogicalSideBStart, *iter.mCellMap, 0, 0, colIdx,
- 1, currentBorder.owner,
- currentBorder.width, startSeg);
- info.SetTableBStartBorderWidth(currentBorder.width);
- info.SetBStartBorderWidths(currentBorder.width);
- info.SetColumnBStartIEndContBCBorder();
- }
- info.SetTableBStartIStartContBCBorder();
- }
- else {
- // see if the bStart border needs to be the start of a segment due to a
- // block-dir border owning the corner
- if (info.mColIndex > 0) {
- BCData& data = info.mCellData->mData;
- if (!data.IsBStartStart()) {
- LogicalSide cornerSide;
- bool bevel;
- data.GetCorner(cornerSide, bevel);
- if (IsBlock(cornerSide)) {
- data.SetBStartStart(true);
- }
- }
- }
- }
- // find the dominant border considering the cell's iStart border and the
- // table, col group, col if the border is at the iStart of the table,
- // otherwise it was processed in a previous col
- if (0 == info.mColIndex) {
- if (!tableBorderReset[eLogicalSideIStart]) {
- propData->mIStartBorderWidth = 0;
- tableBorderReset[eLogicalSideIStart] = true;
- }
- info.mCurrentRowFrame = nullptr;
- for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
- rowB++) {
- info.IncrementRow(rowB == info.mRowIndex);
- currentBorder = info.GetIStartEdgeBorder();
- BCCornerInfo& tlCorner = (0 == rowB) ? bStartCorners[0] : bEndCorners[0];
- tlCorner.Update(eLogicalSideBEnd, currentBorder);
- tableCellMap->SetBCBorderCorner(eBStartIStart, *iter.mCellMap,
- iter.mRowGroupStart, rowB, 0,
- LogicalSide(tlCorner.ownerSide),
- tlCorner.subWidth,
- tlCorner.bevel);
- bEndCorners[0].Set(eLogicalSideBStart, currentBorder); // bEnd-iStart
- // update lastBlockDirBorders and see if a new segment starts
- startSeg = SetBorder(currentBorder, lastBlockDirBorders[0]);
- // store the border segment in the cell map
- tableCellMap->SetBCBorderEdge(eLogicalSideIStart, *iter.mCellMap,
- iter.mRowGroupStart, rowB, info.mColIndex,
- 1, currentBorder.owner,
- currentBorder.width, startSeg);
- info.SetTableIStartBorderWidth(rowB , currentBorder.width);
- info.SetIStartBorderWidths(currentBorder.width);
- info.SetRowIStartContBCBorder();
- }
- info.SetRowGroupIStartContBCBorder();
- }
- // find the dominant border considering the cell's iEnd border, adjacent
- // cells and the table, row group, row
- if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
- // touches iEnd edge of table
- if (!tableBorderReset[eLogicalSideIEnd]) {
- propData->mIEndBorderWidth = 0;
- tableBorderReset[eLogicalSideIEnd] = true;
- }
- info.mCurrentRowFrame = nullptr;
- for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
- rowB++) {
- info.IncrementRow(rowB == info.mRowIndex);
- currentBorder = info.GetIEndEdgeBorder();
- // update/store the bStart-iEnd & bEnd-iEnd corners
- BCCornerInfo& trCorner = (0 == rowB) ?
- bStartCorners[info.GetCellEndColIndex() + 1] :
- bEndCorners[info.GetCellEndColIndex() + 1];
- trCorner.Update(eLogicalSideBEnd, currentBorder); // bStart-iEnd
- tableCellMap->SetBCBorderCorner(eBStartIEnd, *iter.mCellMap,
- iter.mRowGroupStart, rowB,
- info.GetCellEndColIndex(),
- LogicalSide(trCorner.ownerSide),
- trCorner.subWidth,
- trCorner.bevel);
- BCCornerInfo& brCorner = bEndCorners[info.GetCellEndColIndex() + 1];
- brCorner.Set(eLogicalSideBStart, currentBorder); // bEnd-iEnd
- tableCellMap->SetBCBorderCorner(eBEndIEnd, *iter.mCellMap,
- iter.mRowGroupStart, rowB,
- info.GetCellEndColIndex(),
- LogicalSide(brCorner.ownerSide),
- brCorner.subWidth,
- brCorner.bevel);
- // update lastBlockDirBorders and see if a new segment starts
- startSeg = SetBorder(currentBorder,
- lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
- // store the border segment in the cell map and update cellBorders
- tableCellMap->SetBCBorderEdge(eLogicalSideIEnd, *iter.mCellMap,
- iter.mRowGroupStart, rowB,
- info.GetCellEndColIndex(), 1,
- currentBorder.owner, currentBorder.width,
- startSeg);
- info.SetTableIEndBorderWidth(rowB, currentBorder.width);
- info.SetIEndBorderWidths(currentBorder.width);
- info.SetRowIEndContBCBorder();
- }
- info.SetRowGroupIEndContBCBorder();
- }
- else {
- int32_t segLength = 0;
- BCMapCellInfo priorAjaInfo(this);
- for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
- rowB += segLength) {
- iter.PeekIEnd(info, rowB, ajaInfo);
- currentBorder = info.GetIEndInternalBorder();
- adjacentBorder = ajaInfo.GetIStartInternalBorder();
- currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
- adjacentBorder, !INLINE_DIR);
- segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowB);
- segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowB);
- // update lastBlockDirBorders and see if a new segment starts
- startSeg = SetBorder(currentBorder,
- lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
- // store the border segment in the cell map and update cellBorders
- if (info.GetCellEndColIndex() < damageArea.EndCol() &&
- rowB >= damageArea.StartRow() && rowB < damageArea.EndRow()) {
- tableCellMap->SetBCBorderEdge(eLogicalSideIEnd, *iter.mCellMap,
- iter.mRowGroupStart, rowB,
- info.GetCellEndColIndex(), segLength,
- currentBorder.owner,
- currentBorder.width, startSeg);
- info.SetIEndBorderWidths(currentBorder.width);
- ajaInfo.SetIStartBorderWidths(currentBorder.width);
- }
- // update the bStart-iEnd corner
- bool hitsSpanOnIEnd = (rowB > ajaInfo.mRowIndex) &&
- (rowB < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
- BCCornerInfo* trCorner = ((0 == rowB) || hitsSpanOnIEnd) ?
- &bStartCorners[info.GetCellEndColIndex() + 1] :
- &bEndCorners[info.GetCellEndColIndex() + 1];
- trCorner->Update(eLogicalSideBEnd, currentBorder);
- // if this is not the first time through,
- // consider the segment to the iEnd side
- if (rowB != info.mRowIndex) {
- currentBorder = priorAjaInfo.GetBEndInternalBorder();
- adjacentBorder = ajaInfo.GetBStartInternalBorder();
- currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
- adjacentBorder, INLINE_DIR);
- trCorner->Update(eLogicalSideIEnd, currentBorder);
- }
- // store the bStart-iEnd corner in the cell map
- if (info.GetCellEndColIndex() < damageArea.EndCol() &&
- rowB >= damageArea.StartRow()) {
- if (0 != rowB) {
- tableCellMap->SetBCBorderCorner(eBStartIEnd, *iter.mCellMap,
- iter.mRowGroupStart, rowB,
- info.GetCellEndColIndex(),
- LogicalSide(trCorner->ownerSide),
- trCorner->subWidth,
- trCorner->bevel);
- }
- // store any corners this cell spans together with the aja cell
- for (int32_t rX = rowB + 1; rX < rowB + segLength; rX++) {
- tableCellMap->SetBCBorderCorner(eBEndIEnd, *iter.mCellMap,
- iter.mRowGroupStart, rX,
- info.GetCellEndColIndex(),
- LogicalSide(trCorner->ownerSide),
- trCorner->subWidth, false);
- }
- }
- // update bEnd-iEnd corner, bStartCorners, bEndCorners
- hitsSpanOnIEnd = (rowB + segLength <
- ajaInfo.mRowIndex + ajaInfo.mRowSpan);
- BCCornerInfo& brCorner = (hitsSpanOnIEnd) ?
- bStartCorners[info.GetCellEndColIndex() + 1] :
- bEndCorners[info.GetCellEndColIndex() + 1];
- brCorner.Set(eLogicalSideBStart, currentBorder);
- priorAjaInfo = ajaInfo;
- }
- }
- for (int32_t colIdx = info.mColIndex + 1;
- colIdx <= info.GetCellEndColIndex(); colIdx++) {
- lastBlockDirBorders[colIdx].Reset(0,1);
- }
- // find the dominant border considering the cell's bEnd border, adjacent
- // cells and the table, row group, row
- if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
- // touches bEnd edge of table
- if (!tableBorderReset[eLogicalSideBEnd]) {
- propData->mBEndBorderWidth = 0;
- tableBorderReset[eLogicalSideBEnd] = true;
- }
- for (int32_t colIdx = info.mColIndex;
- colIdx <= info.GetCellEndColIndex(); colIdx++) {
- info.SetColumn(colIdx);
- currentBorder = info.GetBEndEdgeBorder();
- // update/store the bEnd-iStart & bEnd-IEnd corners
- BCCornerInfo& blCorner = bEndCorners[colIdx]; // bEnd-iStart
- blCorner.Update(eLogicalSideIEnd, currentBorder);
- tableCellMap->SetBCBorderCorner(eBEndIStart, *iter.mCellMap,
- iter.mRowGroupStart,
- info.GetCellEndRowIndex(),
- colIdx,
- LogicalSide(blCorner.ownerSide),
- blCorner.subWidth, blCorner.bevel);
- BCCornerInfo& brCorner = bEndCorners[colIdx + 1]; // bEnd-iEnd
- brCorner.Update(eLogicalSideIStart, currentBorder);
- if (info.mNumTableCols == colIdx + 1) { // bEnd-IEnd corner of the table
- tableCellMap->SetBCBorderCorner(eBEndIEnd, *iter.mCellMap,
- iter.mRowGroupStart,
- info.GetCellEndRowIndex(), colIdx,
- LogicalSide(brCorner.ownerSide),
- brCorner.subWidth,
- brCorner.bevel, true);
- }
- // update lastBEndBorder and see if a new segment starts
- startSeg = SetInlineDirBorder(currentBorder, blCorner, lastBEndBorder);
- if (!startSeg) {
- // make sure that we did not compare apples to oranges i.e. the
- // current border should be a continuation of the lastBEndBorder,
- // as it is a bEnd border
- // add 1 to the info.GetCellEndRowIndex()
- startSeg = (lastBEndBorder.rowIndex !=
- (info.GetCellEndRowIndex() + 1));
- }
- // store the border segment in the cell map and update cellBorders
- tableCellMap->SetBCBorderEdge(eLogicalSideBEnd, *iter.mCellMap,
- iter.mRowGroupStart,
- info.GetCellEndRowIndex(),
- colIdx, 1, currentBorder.owner,
- currentBorder.width, startSeg);
- // update lastBEndBorders
- lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
- lastBEndBorder.rowSpan = info.mRowSpan;
- lastBEndBorders[colIdx] = lastBEndBorder;
- info.SetBEndBorderWidths(currentBorder.width);
- info.SetTableBEndBorderWidth(currentBorder.width);
- info.SetColumnBEndContBCBorder();
- }
- info.SetRowGroupBEndContBCBorder();
- info.SetColGroupBEndContBCBorder();
- }
- else {
- int32_t segLength = 0;
- for (int32_t colIdx = info.mColIndex;
- colIdx <= info.GetCellEndColIndex(); colIdx += segLength) {
- iter.PeekBEnd(info, colIdx, ajaInfo);
- currentBorder = info.GetBEndInternalBorder();
- adjacentBorder = ajaInfo.GetBStartInternalBorder();
- currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
- adjacentBorder, INLINE_DIR);
- segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colIdx);
- segLength = std::min(segLength, info.mColIndex + info.mColSpan - colIdx);
- // update, store the bEnd-iStart corner
- BCCornerInfo& blCorner = bEndCorners[colIdx]; // bEnd-iStart
- bool hitsSpanBelow = (colIdx > ajaInfo.mColIndex) &&
- (colIdx < ajaInfo.mColIndex + ajaInfo.mColSpan);
- bool update = true;
- if (colIdx == info.mColIndex && colIdx > damageArea.StartCol()) {
- int32_t prevRowIndex = lastBEndBorders[colIdx - 1].rowIndex;
- if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
- // hits a rowspan on the iEnd side
- update = false;
- // the corner was taken care of during the cell on the iStart side
- }
- else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
- // spans below the cell to the iStart side
- bStartCorners[colIdx] = blCorner;
- blCorner.Set(eLogicalSideIEnd, currentBorder);
- update = false;
- }
- }
- if (update) {
- blCorner.Update(eLogicalSideIEnd, currentBorder);
- }
- if (info.GetCellEndRowIndex() < damageArea.EndRow() &&
- colIdx >= damageArea.StartCol()) {
- if (hitsSpanBelow) {
- tableCellMap->SetBCBorderCorner(eBEndIStart, *iter.mCellMap,
- iter.mRowGroupStart,
- info.GetCellEndRowIndex(), colIdx,
- LogicalSide(blCorner.ownerSide),
- blCorner.subWidth, blCorner.bevel);
- }
- // store any corners this cell spans together with the aja cell
- for (int32_t c = colIdx + 1; c < colIdx + segLength; c++) {
- BCCornerInfo& corner = bEndCorners[c];
- corner.Set(eLogicalSideIEnd, currentBorder);
- tableCellMap->SetBCBorderCorner(eBEndIStart, *iter.mCellMap,
- iter.mRowGroupStart,
- info.GetCellEndRowIndex(), c,
- LogicalSide(corner.ownerSide),
- corner.subWidth,
- false);
- }
- }
- // update lastBEndBorders and see if a new segment starts
- startSeg = SetInlineDirBorder(currentBorder, blCorner, lastBEndBorder);
- if (!startSeg) {
- // make sure that we did not compare apples to oranges i.e. the
- // current border should be a continuation of the lastBEndBorder,
- // as it is a bEnd border
- // add 1 to the info.GetCellEndRowIndex()
- startSeg = (lastBEndBorder.rowIndex !=
- info.GetCellEndRowIndex() + 1);
- }
- lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
- lastBEndBorder.rowSpan = info.mRowSpan;
- for (int32_t c = colIdx; c < colIdx + segLength; c++) {
- lastBEndBorders[c] = lastBEndBorder;
- }
- // store the border segment the cell map and update cellBorders
- if (info.GetCellEndRowIndex() < damageArea.EndRow() &&
- colIdx >= damageArea.StartCol() && colIdx < damageArea.EndCol()) {
- tableCellMap->SetBCBorderEdge(eLogicalSideBEnd, *iter.mCellMap,
- iter.mRowGroupStart,
- info.GetCellEndRowIndex(),
- colIdx, segLength, currentBorder.owner,
- currentBorder.width, startSeg);
- info.SetBEndBorderWidths(currentBorder.width);
- ajaInfo.SetBStartBorderWidths(currentBorder.width);
- }
- // update bEnd-iEnd corner
- BCCornerInfo& brCorner = bEndCorners[colIdx + segLength];
- brCorner.Update(eLogicalSideIStart, currentBorder);
- }
- if (!gotRowBorder && 1 == info.mRowSpan &&
- (ajaInfo.mStartRow || info.mRgAtEnd)) {
- //get continuous row/row group border
- //we need to check the row group's bEnd border if this is
- //the last row in the row group, but only a cell with rowspan=1
- //will know whether *this* row is at the bEnd
- const nsIFrame* nextRowGroup =
- ajaInfo.mRgAtStart ? ajaInfo.mRowGroup : nullptr;
- info.SetInnerRowGroupBEndContBCBorder(nextRowGroup, ajaInfo.mStartRow);
- gotRowBorder = true;
- }
- }
- // see if the cell to the iEnd side had a rowspan and its bEnd-iStart border
- // needs be joined with this one's bEnd
- // if there is a cell to the iEnd and the cell to iEnd side was a rowspan
- if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) &&
- (lastBEndBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) {
- BCCornerInfo& corner = bEndCorners[info.GetCellEndColIndex() + 1];
- if (!IsBlock(LogicalSide(corner.ownerSide))) {
- // not a block-dir owner
- BCCellBorder& thisBorder = lastBEndBorder;
- BCCellBorder& nextBorder = lastBEndBorders[info.mColIndex + 1];
- if ((thisBorder.color == nextBorder.color) &&
- (thisBorder.width == nextBorder.width) &&
- (thisBorder.style == nextBorder.style)) {
- // set the flag on the next border indicating it is not the start of a
- // new segment
- if (iter.mCellMap) {
- tableCellMap->ResetBStartStart(eLogicalSideBEnd, *iter.mCellMap,
- info.GetCellEndRowIndex(),
- info.GetCellEndColIndex() + 1);
- }
- }
- }
- }
- } // for (iter.First(info); info.mCell; iter.Next(info)) {
- // reset the bc flag and damage area
- SetNeedToCalcBCBorders(false);
- propData->mDamageArea = TableArea(0, 0, 0, 0);
- #ifdef DEBUG_TABLE_CELLMAP
- mCellMap->Dump();
- #endif
- }
- class BCPaintBorderIterator;
- struct BCBlockDirSeg
- {
- BCBlockDirSeg();
- void Start(BCPaintBorderIterator& aIter,
- BCBorderOwner aBorderOwner,
- BCPixelSize aBlockSegISize,
- BCPixelSize aInlineSegBSize);
- void Initialize(BCPaintBorderIterator& aIter);
- void GetBEndCorner(BCPaintBorderIterator& aIter,
- BCPixelSize aInlineSegBSize);
- void Paint(BCPaintBorderIterator& aIter,
- DrawTarget& aDrawTarget,
- BCPixelSize aInlineSegBSize);
- void AdvanceOffsetB();
- void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
- union {
- nsTableColFrame* mCol;
- int32_t mColWidth;
- };
- nscoord mOffsetI; // i-offset with respect to the table edge
- nscoord mOffsetB; // b-offset with respect to the table edge
- nscoord mLength; // block-dir length including corners
- BCPixelSize mWidth; // thickness in pixels
- nsTableCellFrame* mAjaCell; // previous sibling to the first cell
- // where the segment starts, it can be
- // the owner of a segment
- nsTableCellFrame* mFirstCell; // cell at the start of the segment
- nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment
- nsTableRowFrame* mFirstRow; // row at the start of the segment
- nsTableCellFrame* mLastCell; // cell at the current end of the
- // segment
- uint8_t mOwner; // owner of the border, defines the
- // style
- LogicalSide mBStartBevelSide; // direction to bevel at the bStart
- nscoord mBStartBevelOffset; // how much to bevel at the bStart
- BCPixelSize mBEndInlineSegBSize; // bSize of the crossing
- // inline-dir border
- nscoord mBEndOffset; // how much longer is the segment due
- // to the inline-dir border, by this
- // amount the next segment needs to be
- // shifted.
- bool mIsBEndBevel; // should we bevel at the bEnd
- };
- struct BCInlineDirSeg
- {
- BCInlineDirSeg();
- void Start(BCPaintBorderIterator& aIter,
- BCBorderOwner aBorderOwner,
- BCPixelSize aBEndBlockSegISize,
- BCPixelSize aInlineSegBSize);
- void GetIEndCorner(BCPaintBorderIterator& aIter,
- BCPixelSize aIStartSegISize);
- void AdvanceOffsetI();
- void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
- void Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget);
- nscoord mOffsetI; // i-offset with respect to the table edge
- nscoord mOffsetB; // b-offset with respect to the table edge
- nscoord mLength; // inline-dir length including corners
- BCPixelSize mWidth; // border thickness in pixels
- nscoord mIStartBevelOffset; // how much to bevel at the iStart
- LogicalSide mIStartBevelSide; // direction to bevel at the iStart
- bool mIsIEndBevel; // should we bevel at the iEnd end
- nscoord mIEndBevelOffset; // how much to bevel at the iEnd
- LogicalSide mIEndBevelSide; // direction to bevel at the iEnd
- nscoord mEndOffset; // how much longer is the segment due
- // to the block-dir border, by this
- // amount the next segment needs to be
- // shifted.
- uint8_t mOwner; // owner of the border, defines the
- // style
- nsTableCellFrame* mFirstCell; // cell at the start of the segment
- nsTableCellFrame* mAjaCell; // neighboring cell to the first cell
- // where the segment starts, it can be
- // the owner of a segment
- };
- // Iterates over borders (iStart border, corner, bStart border) in the cell map within a damage area
- // from iStart to iEnd, bStart to bEnd. All members are in terms of the 1st in flow frames, except
- // where suffixed by InFlow.
- class BCPaintBorderIterator
- {
- public:
- explicit BCPaintBorderIterator(nsTableFrame* aTable);
- ~BCPaintBorderIterator() { if (mBlockDirInfo) {
- delete [] mBlockDirInfo;
- }}
- void Reset();
- /**
- * Determine the damage area in terms of rows and columns and finalize
- * mInitialOffsetI and mInitialOffsetB.
- * @param aDirtyRect - dirty rect in table coordinates
- * @return - true if we need to paint something given dirty rect
- */
- bool SetDamageArea(const nsRect& aDamageRect);
- void First();
- void Next();
- void AccumulateOrPaintInlineDirSegment(DrawTarget& aDrawTarget);
- void AccumulateOrPaintBlockDirSegment(DrawTarget& aDrawTarget);
- void ResetVerInfo();
- void StoreColumnWidth(int32_t aIndex);
- bool BlockDirSegmentOwnsCorner();
- nsTableFrame* mTable;
- nsTableFrame* mTableFirstInFlow;
- nsTableCellMap* mTableCellMap;
- nsCellMap* mCellMap;
- WritingMode mTableWM;
- const nsStyleBackground* mTableBgColor;
- nsTableFrame::RowGroupArray mRowGroups;
- nsTableRowGroupFrame* mPrevRg;
- nsTableRowGroupFrame* mRg;
- bool mIsRepeatedHeader;
- bool mIsRepeatedFooter;
- nsTableRowGroupFrame* mStartRg; // first row group in the damagearea
- int32_t mRgIndex; // current row group index in the
- // mRowgroups array
- int32_t mFifRgFirstRowIndex; // start row index of the first in
- // flow of the row group
- int32_t mRgFirstRowIndex; // row index of the first row in the
- // row group
- int32_t mRgLastRowIndex; // row index of the last row in the row
- // group
- int32_t mNumTableRows; // number of rows in the table and all
- // continuations
- int32_t mNumTableCols; // number of columns in the table
- int32_t mColIndex; // with respect to the table
- int32_t mRowIndex; // with respect to the table
- int32_t mRepeatedHeaderRowIndex; // row index in a repeated
- //header, it's equivalent to
- // mRowIndex when we're in a repeated
- // header, and set to the last row
- // index of a repeated header when
- // we're not
- bool mIsNewRow;
- bool mAtEnd; // the iterator cycled over all
- // borders
- nsTableRowFrame* mPrevRow;
- nsTableRowFrame* mRow;
- nsTableRowFrame* mStartRow; //first row in a inside the damagearea
- // cell properties
- nsTableCellFrame* mPrevCell;
- nsTableCellFrame* mCell;
- BCCellData* mPrevCellData;
- BCCellData* mCellData;
- BCData* mBCData;
- bool IsTableBStartMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();}
- bool IsTableIEndMost() {return (mColIndex >= mNumTableCols);}
- bool IsTableBEndMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();}
- bool IsTableIStartMost() {return (mColIndex == 0);}
- bool IsDamageAreaBStartMost() const
- { return mRowIndex == mDamageArea.StartRow(); }
- bool IsDamageAreaIEndMost() const
- { return mColIndex >= mDamageArea.EndCol(); }
- bool IsDamageAreaBEndMost() const
- { return mRowIndex >= mDamageArea.EndRow(); }
- bool IsDamageAreaIStartMost() const
- { return mColIndex == mDamageArea.StartCol(); }
- int32_t GetRelativeColIndex() const
- { return mColIndex - mDamageArea.StartCol(); }
- TableArea mDamageArea; // damageArea in cellmap coordinates
- bool IsAfterRepeatedHeader()
- { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1)); }
- bool StartRepeatedFooter() const
- {
- return mIsRepeatedFooter && mRowIndex == mRgFirstRowIndex &&
- mRowIndex != mDamageArea.StartRow();
- }
- nscoord mInitialOffsetI; // offsetI of the first border with
- // respect to the table
- nscoord mInitialOffsetB; // offsetB of the first border with
- // respect to the table
- nscoord mNextOffsetB; // offsetB of the next segment
- BCBlockDirSeg* mBlockDirInfo; // this array is used differently when
- // inline-dir and block-dir borders are drawn
- // When inline-dir border are drawn we cache
- // the column widths and the width of the
- // block-dir borders that arrive from bStart
- // When we draw block-dir borders we store
- // lengths and width for block-dir borders
- // before they are drawn while we move over
- // the columns in the damage area
- // It has one more elements than columns are
- // in the table.
- BCInlineDirSeg mInlineSeg; // the inline-dir segment while we
- // move over the colums
- BCPixelSize mPrevInlineSegBSize; // the bSize of the previous
- // inline-dir border
- private:
- bool SetNewRow(nsTableRowFrame* aRow = nullptr);
- bool SetNewRowGroup();
- void SetNewData(int32_t aRowIndex, int32_t aColIndex);
- };
- BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
- : mTable(aTable)
- , mTableFirstInFlow(static_cast<nsTableFrame*>(aTable->FirstInFlow()))
- , mTableCellMap(aTable->GetCellMap())
- , mTableWM(aTable->StyleContext())
- {
- mBlockDirInfo = nullptr;
- LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
- // y position of first row in damage area
- mInitialOffsetB =
- mTable->GetPrevInFlow() ? 0 : childAreaOffset.BStart(mTableWM);
- mNumTableRows = mTable->GetRowCount();
- mNumTableCols = mTable->GetColCount();
- // Get the ordered row groups
- mTable->OrderRowGroups(mRowGroups);
- // initialize to a non existing index
- mRepeatedHeaderRowIndex = -99;
- nsIFrame* bgFrame =
- nsCSSRendering::FindNonTransparentBackgroundFrame(aTable);
- mTableBgColor = bgFrame->StyleBackground();
- }
- bool
- BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect)
- {
- nsSize containerSize = mTable->GetSize();
- LogicalRect dirtyRect(mTableWM, aDirtyRect, containerSize);
- uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex;
- startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
- bool done = false;
- bool haveIntersect = false;
- // find startRowIndex, endRowIndex
- nscoord rowB = mInitialOffsetB;
- for (uint32_t rgIdx = 0; rgIdx < mRowGroups.Length() && !done; rgIdx++) {
- nsTableRowGroupFrame* rgFrame = mRowGroups[rgIdx];
- for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
- rowFrame = rowFrame->GetNextRow()) {
- // get the row rect relative to the table rather than the row group
- nscoord rowBSize = rowFrame->BSize(mTableWM);
- if (haveIntersect) {
- // conservatively estimate the half border widths outside the row
- nscoord borderHalf = mTable->GetPrevInFlow() ? 0 :
- mTable->PresContext()->DevPixelsToAppUnits(rowFrame->GetBStartBCBorderWidth() + 1);
- if (dirtyRect.BEnd(mTableWM) >= rowB - borderHalf) {
- nsTableRowFrame* fifRow =
- static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
- endRowIndex = fifRow->GetRowIndex();
- }
- else done = true;
- }
- else {
- // conservatively estimate the half border widths outside the row
- nscoord borderHalf = mTable->GetNextInFlow() ? 0 :
- mTable->PresContext()->DevPixelsToAppUnits(rowFrame->GetBEndBCBorderWidth() + 1);
- if (rowB + rowBSize + borderHalf >= dirtyRect.BStart(mTableWM)) {
- mStartRg = rgFrame;
- mStartRow = rowFrame;
- nsTableRowFrame* fifRow =
- static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
- startRowIndex = endRowIndex = fifRow->GetRowIndex();
- haveIntersect = true;
- }
- else {
- mInitialOffsetB += rowBSize;
- }
- }
- rowB += rowBSize;
- }
- }
- mNextOffsetB = mInitialOffsetB;
- // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
- // XXX but I don't understand it, so not changing it for now
- // table wrapper borders overflow the table, so the table might be
- // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
- // on the table
- if (!haveIntersect)
- return false;
- // find startColIndex, endColIndex, startColX
- haveIntersect = false;
- if (0 == mNumTableCols)
- return false;
- LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
- // inline position of first col in damage area
- mInitialOffsetI = childAreaOffset.IStart(mTableWM);
- nscoord x = 0;
- int32_t colIdx;
- for (colIdx = 0; colIdx != mNumTableCols; colIdx++) {
- nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colIdx);
- if (!colFrame) ABORT1(false);
- // get the col rect relative to the table rather than the col group
- nscoord colISize = colFrame->ISize(mTableWM);
- if (haveIntersect) {
- // conservatively estimate the iStart half border width outside the col
- nscoord iStartBorderHalf =
- mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetIStartBorderWidth() + 1);
- if (dirtyRect.IEnd(mTableWM) >= x - iStartBorderHalf) {
- endColIndex = colIdx;
- }
- else break;
- }
- else {
- // conservatively estimate the iEnd half border width outside the col
- nscoord iEndBorderHalf =
- mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetIEndBorderWidth() + 1);
- if (x + colISize + iEndBorderHalf >= dirtyRect.IStart(mTableWM)) {
- startColIndex = endColIndex = colIdx;
- haveIntersect = true;
- }
- else {
- mInitialOffsetI += colISize;
- }
- }
- x += colISize;
- }
- if (!haveIntersect)
- return false;
- mDamageArea = TableArea(startColIndex, startRowIndex,
- 1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex),
- 1 + endRowIndex - startRowIndex);
- Reset();
- mBlockDirInfo = new BCBlockDirSeg[mDamageArea.ColCount() + 1];
- if (!mBlockDirInfo)
- return false;
- return true;
- }
- void
- BCPaintBorderIterator::Reset()
- {
- mAtEnd = true; // gets reset when First() is called
- mRg = mStartRg;
- mPrevRow = nullptr;
- mRow = mStartRow;
- mRowIndex = 0;
- mColIndex = 0;
- mRgIndex = -1;
- mPrevCell = nullptr;
- mCell = nullptr;
- mPrevCellData = nullptr;
- mCellData = nullptr;
- mBCData = nullptr;
- ResetVerInfo();
- }
- /**
- * Set the iterator data to a new cellmap coordinate
- * @param aRowIndex - the row index
- * @param aColIndex - the col index
- */
- void
- BCPaintBorderIterator::SetNewData(int32_t aY,
- int32_t aX)
- {
- if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0();
- mColIndex = aX;
- mRowIndex = aY;
- mPrevCellData = mCellData;
- if (IsTableIEndMost() && IsTableBEndMost()) {
- mCell = nullptr;
- mBCData = &mTableCellMap->mBCInfo->mBEndIEndCorner;
- }
- else if (IsTableIEndMost()) {
- mCellData = nullptr;
- mBCData = &mTableCellMap->mBCInfo->mIEndBorders.ElementAt(aY);
- }
- else if (IsTableBEndMost()) {
- mCellData = nullptr;
- mBCData = &mTableCellMap->mBCInfo->mBEndBorders.ElementAt(aX);
- }
- else {
- if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) {
- mBCData = nullptr;
- mCellData =
- (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex);
- if (mCellData) {
- mBCData = &mCellData->mData;
- if (!mCellData->IsOrig()) {
- if (mCellData->IsRowSpan()) {
- aY -= mCellData->GetRowSpanOffset();
- }
- if (mCellData->IsColSpan()) {
- aX -= mCellData->GetColSpanOffset();
- }
- if ((aX >= 0) && (aY >= 0)) {
- mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX];
- }
- }
- if (mCellData->IsOrig()) {
- mPrevCell = mCell;
- mCell = mCellData->GetCellFrame();
- }
- }
- }
- }
- }
- /**
- * Set the iterator to a new row
- * @param aRow - the new row frame, if null the iterator will advance to the
- * next row
- */
- bool
- BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow)
- {
- mPrevRow = mRow;
- mRow = (aRow) ? aRow : mRow->GetNextRow();
- if (mRow) {
- mIsNewRow = true;
- mRowIndex = mRow->GetRowIndex();
- mColIndex = mDamageArea.StartCol();
- mPrevInlineSegBSize = 0;
- if (mIsRepeatedHeader) {
- mRepeatedHeaderRowIndex = mRowIndex;
- }
- }
- else {
- mAtEnd = true;
- }
- return !mAtEnd;
- }
- /**
- * Advance the iterator to the next row group
- */
- bool
- BCPaintBorderIterator::SetNewRowGroup()
- {
- mRgIndex++;
- mIsRepeatedHeader = false;
- mIsRepeatedFooter = false;
- NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds");
- if (uint32_t(mRgIndex) < mRowGroups.Length()) {
- mPrevRg = mRg;
- mRg = mRowGroups[mRgIndex];
- nsTableRowGroupFrame* fifRg =
- static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow());
- mFifRgFirstRowIndex = fifRg->GetStartRowIndex();
- mRgFirstRowIndex = mRg->GetStartRowIndex();
- mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1;
- if (SetNewRow(mRg->GetFirstRow())) {
- mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr);
- if (!mCellMap) ABORT1(false);
- }
- if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) {
- // if mRowGroup doesn't have a prev in flow, then it may be a repeated
- // header or footer
- const nsStyleDisplay* display = mRg->StyleDisplay();
- if (mRowIndex == mDamageArea.StartRow()) {
- mIsRepeatedHeader = (mozilla::StyleDisplay::TableHeaderGroup == display->mDisplay);
- } else {
- mIsRepeatedFooter = (mozilla::StyleDisplay::TableFooterGroup == display->mDisplay);
- }
- }
- }
- else {
- mAtEnd = true;
- }
- return !mAtEnd;
- }
- /**
- * Move the iterator to the first position in the damageArea
- */
- void
- BCPaintBorderIterator::First()
- {
- if (!mTable || mDamageArea.StartCol() >= mNumTableCols ||
- mDamageArea.StartRow() >= mNumTableRows) ABORT0();
- mAtEnd = false;
- uint32_t numRowGroups = mRowGroups.Length();
- for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) {
- nsTableRowGroupFrame* rowG = mRowGroups[rgY];
- int32_t start = rowG->GetStartRowIndex();
- int32_t end = start + rowG->GetRowCount() - 1;
- if (mDamageArea.StartRow() >= start && mDamageArea.StartRow() <= end) {
- mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex
- if (SetNewRowGroup()) {
- while (mRowIndex < mDamageArea.StartRow() && !mAtEnd) {
- SetNewRow();
- }
- if (!mAtEnd) {
- SetNewData(mDamageArea.StartRow(), mDamageArea.StartCol());
- }
- }
- return;
- }
- }
- mAtEnd = true;
- }
- /**
- * Advance the iterator to the next position
- */
- void
- BCPaintBorderIterator::Next()
- {
- if (mAtEnd) ABORT0();
- mIsNewRow = false;
- mColIndex++;
- if (mColIndex > mDamageArea.EndCol()) {
- mRowIndex++;
- if (mRowIndex == mDamageArea.EndRow()) {
- mColIndex = mDamageArea.StartCol();
- }
- else if (mRowIndex < mDamageArea.EndRow()) {
- if (mRowIndex <= mRgLastRowIndex) {
- SetNewRow();
- }
- else {
- SetNewRowGroup();
- }
- }
- else {
- mAtEnd = true;
- }
- }
- if (!mAtEnd) {
- SetNewData(mRowIndex, mColIndex);
- }
- }
- // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine
- // them
- // XXX Update terminology from physical to logical
- /** Compute the vertical offset of a vertical border segment
- * @param aCornerOwnerSide - which side owns the corner
- * @param aCornerSubWidth - how wide is the nonwinning side of the corner
- * @param aHorWidth - how wide is the horizontal edge of the corner
- * @param aIsStartOfSeg - does this corner start a new segment
- * @param aIsBevel - is this corner beveled
- * @return - offset in twips
- */
- static nscoord
- CalcVerCornerOffset(LogicalSide aCornerOwnerSide,
- BCPixelSize aCornerSubWidth,
- BCPixelSize aHorWidth,
- bool aIsStartOfSeg,
- bool aIsBevel,
- nsPresContext* aPresContext)
- {
- nscoord offset = 0;
- // XXX These should be replaced with appropriate side-specific macros (which?)
- BCPixelSize smallHalf, largeHalf;
- if (IsBlock(aCornerOwnerSide)) {
- DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
- if (aIsBevel) {
- offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
- }
- else {
- offset = (eLogicalSideBStart == aCornerOwnerSide) ? smallHalf : -largeHalf;
- }
- }
- else {
- DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
- if (aIsBevel) {
- offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
- }
- else {
- offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
- }
- }
- return aPresContext->DevPixelsToAppUnits(offset);
- }
- /** Compute the horizontal offset of a horizontal border segment
- * @param aCornerOwnerSide - which side owns the corner
- * @param aCornerSubWidth - how wide is the nonwinning side of the corner
- * @param aVerWidth - how wide is the vertical edge of the corner
- * @param aIsStartOfSeg - does this corner start a new segment
- * @param aIsBevel - is this corner beveled
- * @return - offset in twips
- */
- static nscoord
- CalcHorCornerOffset(LogicalSide aCornerOwnerSide,
- BCPixelSize aCornerSubWidth,
- BCPixelSize aVerWidth,
- bool aIsStartOfSeg,
- bool aIsBevel,
- nsPresContext* aPresContext)
- {
- nscoord offset = 0;
- // XXX These should be replaced with appropriate side-specific macros (which?)
- BCPixelSize smallHalf, largeHalf;
- if (IsInline(aCornerOwnerSide)) {
- DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
- if (aIsBevel) {
- offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
- }
- else {
- offset = (eLogicalSideIStart == aCornerOwnerSide) ? smallHalf : -largeHalf;
- }
- }
- else {
- DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
- if (aIsBevel) {
- offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
- }
- else {
- offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
- }
- }
- return aPresContext->DevPixelsToAppUnits(offset);
- }
- BCBlockDirSeg::BCBlockDirSeg()
- {
- mCol = nullptr;
- mFirstCell = mLastCell = mAjaCell = nullptr;
- mOffsetI = mOffsetB = mLength = mWidth = mBStartBevelOffset = 0;
- mBStartBevelSide = eLogicalSideBStart;
- mOwner = eCellOwner;
- }
- /**
- * Start a new block-direction segment
- * @param aIter - iterator containing the structural information
- * @param aBorderOwner - determines the border style
- * @param aBlockSegISize - the width of segment in pixel
- * @param aInlineSegBSize - the width of the inline-dir segment joining the corner
- * at the start
- */
- void
- BCBlockDirSeg::Start(BCPaintBorderIterator& aIter,
- BCBorderOwner aBorderOwner,
- BCPixelSize aBlockSegISize,
- BCPixelSize aInlineSegBSize)
- {
- LogicalSide ownerSide = eLogicalSideBStart;
- bool bevel = false;
- nscoord cornerSubWidth = (aIter.mBCData) ?
- aIter.mBCData->GetCorner(ownerSide, bevel) : 0;
- bool bStartBevel = (aBlockSegISize > 0) ? bevel : false;
- BCPixelSize maxInlineSegBSize = std::max(aIter.mPrevInlineSegBSize, aInlineSegBSize);
- nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
- maxInlineSegBSize, true,
- bStartBevel, aIter.mTable->PresContext());
- mBStartBevelOffset = bStartBevel ?
- aIter.mTable->PresContext()->DevPixelsToAppUnits(maxInlineSegBSize): 0;
- // XXX this assumes that only corners where 2 segments join can be beveled
- mBStartBevelSide = (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart;
- mOffsetB += offset;
- mLength = -offset;
- mWidth = aBlockSegISize;
- mOwner = aBorderOwner;
- mFirstCell = aIter.mCell;
- mFirstRowGroup = aIter.mRg;
- mFirstRow = aIter.mRow;
- if (aIter.GetRelativeColIndex() > 0) {
- mAjaCell = aIter.mBlockDirInfo[aIter.GetRelativeColIndex() - 1].mLastCell;
- }
- }
- /**
- * Initialize the block-dir segments with information that will persist for any
- * block-dir segment in this column
- * @param aIter - iterator containing the structural information
- */
- void
- BCBlockDirSeg::Initialize(BCPaintBorderIterator& aIter)
- {
- int32_t relColIndex = aIter.GetRelativeColIndex();
- mCol = aIter.IsTableIEndMost() ? aIter.mBlockDirInfo[relColIndex - 1].mCol :
- aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex);
- if (!mCol) ABORT0();
- if (0 == relColIndex) {
- mOffsetI = aIter.mInitialOffsetI;
- }
- // set mOffsetI for the next column
- if (!aIter.IsDamageAreaIEndMost()) {
- aIter.mBlockDirInfo[relColIndex + 1].mOffsetI =
- mOffsetI + mCol->ISize(aIter.mTableWM);
- }
- mOffsetB = aIter.mInitialOffsetB;
- mLastCell = aIter.mCell;
- }
- /**
- * Compute the offsets for the bEnd corner of a block-dir segment
- * @param aIter - iterator containing the structural information
- * @param aInlineSegBSize - the width of the inline-dir segment joining the corner
- * at the start
- */
- void
- BCBlockDirSeg::GetBEndCorner(BCPaintBorderIterator& aIter,
- BCPixelSize aInlineSegBSize)
- {
- LogicalSide ownerSide = eLogicalSideBStart;
- nscoord cornerSubWidth = 0;
- bool bevel = false;
- if (aIter.mBCData) {
- cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
- }
- mIsBEndBevel = (mWidth > 0) ? bevel : false;
- mBEndInlineSegBSize = std::max(aIter.mPrevInlineSegBSize, aInlineSegBSize);
- mBEndOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
- mBEndInlineSegBSize, false,
- mIsBEndBevel, aIter.mTable->PresContext());
- mLength += mBEndOffset;
- }
- /**
- * Paint the block-dir segment
- * @param aIter - iterator containing the structural information
- * @param aDrawTarget - the draw target
- * @param aInlineSegBSize - the width of the inline-dir segment joining the
- * corner at the start
- */
- void
- BCBlockDirSeg::Paint(BCPaintBorderIterator& aIter,
- DrawTarget& aDrawTarget,
- BCPixelSize aInlineSegBSize)
- {
- // get the border style, color and paint the segment
- LogicalSide side =
- aIter.IsDamageAreaIEndMost() ? eLogicalSideIEnd : eLogicalSideIStart;
- int32_t relColIndex = aIter.GetRelativeColIndex();
- nsTableColFrame* col = mCol; if (!col) ABORT0();
- nsTableCellFrame* cell = mFirstCell; // ???
- nsIFrame* owner = nullptr;
- uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
- nscolor color = 0xFFFFFFFF;
- // All the tables frames have the same presContext, so we just use any one
- // that exists here:
- int32_t appUnitsPerDevPixel = col->PresContext()->AppUnitsPerDevPixel();
- switch (mOwner) {
- case eTableOwner:
- owner = aIter.mTable;
- break;
- case eAjaColGroupOwner:
- side = eLogicalSideIEnd;
- if (!aIter.IsTableIEndMost() && (relColIndex > 0)) {
- col = aIter.mBlockDirInfo[relColIndex - 1].mCol;
- }
- MOZ_FALLTHROUGH;
- case eColGroupOwner:
- if (col) {
- owner = col->GetParent();
- }
- break;
- case eAjaColOwner:
- side = eLogicalSideIEnd;
- if (!aIter.IsTableIEndMost() && (relColIndex > 0)) {
- col = aIter.mBlockDirInfo[relColIndex - 1].mCol;
- }
- MOZ_FALLTHROUGH;
- case eColOwner:
- owner = col;
- break;
- case eAjaRowGroupOwner:
- NS_ERROR("a neighboring rowgroup can never own a vertical border");
- MOZ_FALLTHROUGH;
- case eRowGroupOwner:
- NS_ASSERTION(aIter.IsTableIStartMost() || aIter.IsTableIEndMost(),
- "row group can own border only at table edge");
- owner = mFirstRowGroup;
- break;
- case eAjaRowOwner:
- NS_ERROR("program error");
- MOZ_FALLTHROUGH;
- case eRowOwner:
- NS_ASSERTION(aIter.IsTableIStartMost() || aIter.IsTableIEndMost(),
- "row can own border only at table edge");
- owner = mFirstRow;
- break;
- case eAjaCellOwner:
- side = eLogicalSideIEnd;
- cell = mAjaCell;
- MOZ_FALLTHROUGH;
- case eCellOwner:
- owner = cell;
- break;
- }
- if (owner) {
- ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &style, &color);
- }
- BCPixelSize smallHalf, largeHalf;
- DivideBCBorderSize(mWidth, smallHalf, largeHalf);
- LogicalRect segRect(aIter.mTableWM,
- mOffsetI - aIter.mTable->PresContext()->DevPixelsToAppUnits(largeHalf),
- mOffsetB,
- aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth), mLength);
- nscoord bEndBevelOffset = (mIsBEndBevel) ?
- aIter.mTable->PresContext()->DevPixelsToAppUnits(mBEndInlineSegBSize) : 0;
- LogicalSide bEndBevelSide =
- (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart;
- // Convert logical to physical sides/coordinates for DrawTableBorderSegment.
- nsRect physicalRect = segRect.GetPhysicalRect(aIter.mTableWM,
- aIter.mTable->GetSize());
- // XXX For reversed vertical writing-modes (with direction:rtl), we need to
- // invert physicalRect's y-position here, with respect to the table.
- // However, it's not worth fixing the border positions here until the
- // ordering of the table columns themselves is also fixed (bug 1180528).
- uint8_t startBevelSide = aIter.mTableWM.PhysicalSide(mBStartBevelSide);
- uint8_t endBevelSide = aIter.mTableWM.PhysicalSide(bEndBevelSide);
- nscoord startBevelOffset = mBStartBevelOffset;
- nscoord endBevelOffset = bEndBevelOffset;
- // In vertical-rl mode, the 'start' and 'end' of the block-dir (horizontal)
- // border segment need to be swapped because DrawTableBorderSegment will
- // apply the 'start' bevel at the left edge, and 'end' at the right.
- // (Note: In this case, startBevelSide/endBevelSide will usually both be
- // "top" or "bottom". DrawTableBorderSegment works purely with physical
- // coordinates, so it expects startBevelOffset to be the indentation-from-
- // the-left for the "start" (left) end of the border-segment, and
- // endBevelOffset is the indentation-from-the-right for the "end" (right)
- // end of the border-segment. We've got them reversed, since our block dir
- // is RTL, so we have to swap them here.)
- if (aIter.mTableWM.IsVerticalRL()) {
- Swap(startBevelSide, endBevelSide);
- Swap(startBevelOffset, endBevelOffset);
- }
- nsCSSRendering::DrawTableBorderSegment(aDrawTarget, style, color,
- aIter.mTableBgColor, physicalRect,
- appUnitsPerDevPixel,
- aIter.mTable->PresContext()->AppUnitsPerDevPixel(),
- startBevelSide, startBevelOffset,
- endBevelSide, endBevelOffset);
- }
- /**
- * Advance the start point of a segment
- */
- void
- BCBlockDirSeg::AdvanceOffsetB()
- {
- mOffsetB += mLength - mBEndOffset;
- }
- /**
- * Accumulate the current segment
- */
- void
- BCBlockDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
- {
- mLastCell = aIter.mCell;
- mLength += aIter.mRow->BSize(aIter.mTableWM);
- }
- BCInlineDirSeg::BCInlineDirSeg()
- {
- mOffsetI = mOffsetB = mLength = mWidth = mIStartBevelOffset = 0;
- mIStartBevelSide = eLogicalSideBStart;
- mFirstCell = mAjaCell = nullptr;
- }
- /** Initialize an inline-dir border segment for painting
- * @param aIter - iterator storing the current and adjacent frames
- * @param aBorderOwner - which frame owns the border
- * @param aBEndBlockSegISize - block-dir segment width coming from up
- * @param aInlineSegBSize - the thickness of the segment
- + */
- void
- BCInlineDirSeg::Start(BCPaintBorderIterator& aIter,
- BCBorderOwner aBorderOwner,
- BCPixelSize aBEndBlockSegISize,
- BCPixelSize aInlineSegBSize)
- {
- LogicalSide cornerOwnerSide = eLogicalSideBStart;
- bool bevel = false;
- mOwner = aBorderOwner;
- nscoord cornerSubWidth = (aIter.mBCData) ?
- aIter.mBCData->GetCorner(cornerOwnerSide,
- bevel) : 0;
- bool iStartBevel = (aInlineSegBSize > 0) ? bevel : false;
- int32_t relColIndex = aIter.GetRelativeColIndex();
- nscoord maxBlockSegISize = std::max(aIter.mBlockDirInfo[relColIndex].mWidth,
- aBEndBlockSegISize);
- nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth,
- maxBlockSegISize, true, iStartBevel,
- aIter.mTable->PresContext());
- mIStartBevelOffset = (iStartBevel && (aInlineSegBSize > 0)) ? maxBlockSegISize : 0;
- // XXX this assumes that only corners where 2 segments join can be beveled
- mIStartBevelSide = (aBEndBlockSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart;
- mOffsetI += offset;
- mLength = -offset;
- mWidth = aInlineSegBSize;
- mFirstCell = aIter.mCell;
- mAjaCell = (aIter.IsDamageAreaBStartMost()) ? nullptr :
- aIter.mBlockDirInfo[relColIndex].mLastCell;
- }
- /**
- * Compute the offsets for the iEnd corner of an inline-dir segment
- * @param aIter - iterator containing the structural information
- * @param aIStartSegISize - the iSize of the block-dir segment joining the corner
- * at the start
- */
- void
- BCInlineDirSeg::GetIEndCorner(BCPaintBorderIterator& aIter,
- BCPixelSize aIStartSegISize)
- {
- LogicalSide ownerSide = eLogicalSideBStart;
- nscoord cornerSubWidth = 0;
- bool bevel = false;
- if (aIter.mBCData) {
- cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
- }
- mIsIEndBevel = (mWidth > 0) ? bevel : 0;
- int32_t relColIndex = aIter.GetRelativeColIndex();
- nscoord verWidth = std::max(aIter.mBlockDirInfo[relColIndex].mWidth,
- aIStartSegISize);
- mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth,
- false, mIsIEndBevel, aIter.mTable->PresContext());
- mLength += mEndOffset;
- mIEndBevelOffset = (mIsIEndBevel) ?
- aIter.mTable->PresContext()->DevPixelsToAppUnits(verWidth) : 0;
- mIEndBevelSide = (aIStartSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart;
- }
- /**
- * Paint the inline-dir segment
- * @param aIter - iterator containing the structural information
- * @param aDrawTarget - the draw target
- */
- void
- BCInlineDirSeg::Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget)
- {
- // get the border style, color and paint the segment
- LogicalSide side =
- aIter.IsDamageAreaBEndMost() ? eLogicalSideBEnd : eLogicalSideBStart;
- nsIFrame* rg = aIter.mRg; if (!rg) ABORT0();
- nsIFrame* row = aIter.mRow; if (!row) ABORT0();
- nsIFrame* cell = mFirstCell;
- nsIFrame* col;
- nsIFrame* owner = nullptr;
- // All the tables frames have the same presContext, so we just use any one
- // that exists here:
- int32_t appUnitsPerDevPixel = row->PresContext()->AppUnitsPerDevPixel();
- uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
- nscolor color = 0xFFFFFFFF;
- switch (mOwner) {
- case eTableOwner:
- owner = aIter.mTable;
- break;
- case eAjaColGroupOwner:
- NS_ERROR("neighboring colgroups can never own an inline-dir border");
- MOZ_FALLTHROUGH;
- case eColGroupOwner:
- NS_ASSERTION(aIter.IsTableBStartMost() || aIter.IsTableBEndMost(),
- "col group can own border only at the table edge");
- col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
- if (!col) ABORT0();
- owner = col->GetParent();
- break;
- case eAjaColOwner:
- NS_ERROR("neighboring column can never own an inline-dir border");
- MOZ_FALLTHROUGH;
- case eColOwner:
- NS_ASSERTION(aIter.IsTableBStartMost() || aIter.IsTableBEndMost(),
- "col can own border only at the table edge");
- owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
- break;
- case eAjaRowGroupOwner:
- side = eLogicalSideBEnd;
- rg = (aIter.IsTableBEndMost()) ? aIter.mRg : aIter.mPrevRg;
- MOZ_FALLTHROUGH;
- case eRowGroupOwner:
- owner = rg;
- break;
- case eAjaRowOwner:
- side = eLogicalSideBEnd;
- row = (aIter.IsTableBEndMost()) ? aIter.mRow : aIter.mPrevRow;
- MOZ_FALLTHROUGH;
- case eRowOwner:
- owner = row;
- break;
- case eAjaCellOwner:
- side = eLogicalSideBEnd;
- // if this is null due to the damage area origin-y > 0, then the border
- // won't show up anyway
- cell = mAjaCell;
- MOZ_FALLTHROUGH;
- case eCellOwner:
- owner = cell;
- break;
- }
- if (owner) {
- ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &style, &color);
- }
- BCPixelSize smallHalf, largeHalf;
- DivideBCBorderSize(mWidth, smallHalf, largeHalf);
- LogicalRect segRect(aIter.mTableWM, mOffsetI,
- mOffsetB - aIter.mTable->PresContext()->DevPixelsToAppUnits(largeHalf),
- mLength,
- aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth));
- // Convert logical to physical sides/coordinates for DrawTableBorderSegment.
- nsRect physicalRect = segRect.GetPhysicalRect(aIter.mTableWM,
- aIter.mTable->GetSize());
- uint8_t startBevelSide = aIter.mTableWM.PhysicalSide(mIStartBevelSide);
- uint8_t endBevelSide = aIter.mTableWM.PhysicalSide(mIEndBevelSide);
- nscoord startBevelOffset =
- aIter.mTable->PresContext()->DevPixelsToAppUnits(mIStartBevelOffset);
- nscoord endBevelOffset = mIEndBevelOffset;
- // With inline-RTL directionality, the 'start' and 'end' of the inline-dir
- // border segment need to be swapped because DrawTableBorderSegment will
- // apply the 'start' bevel physically at the left or top edge, and 'end' at
- // the right or bottom.
- // (Note: startBevelSide/endBevelSide will be "top" or "bottom" in horizontal
- // writing mode, or "left" or "right" in vertical mode.
- // DrawTableBorderSegment works purely with physical coordinates, so it
- // expects startBevelOffset to be the indentation-from-the-left or top end
- // of the border-segment, and endBevelOffset is the indentation-from-the-
- // right or bottom end. If the writing mode is inline-RTL, our "start" and
- // "end" will be reversed from this physical-coord view, so we have to swap
- // them here.
- if (!aIter.mTableWM.IsBidiLTR()) {
- Swap(startBevelSide, endBevelSide);
- Swap(startBevelOffset, endBevelOffset);
- }
- nsCSSRendering::DrawTableBorderSegment(aDrawTarget, style, color,
- aIter.mTableBgColor, physicalRect,
- appUnitsPerDevPixel,
- aIter.mTable->PresContext()->AppUnitsPerDevPixel(),
- startBevelSide, startBevelOffset,
- endBevelSide, endBevelOffset);
- }
- /**
- * Advance the start point of a segment
- */
- void
- BCInlineDirSeg::AdvanceOffsetI()
- {
- mOffsetI += (mLength - mEndOffset);
- }
- /**
- * Accumulate the current segment
- */
- void
- BCInlineDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
- {
- mLength += aIter.mBlockDirInfo[aIter.GetRelativeColIndex()].mColWidth;
- }
- /**
- * store the column width information while painting inline-dir segment
- */
- void
- BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex)
- {
- if (IsTableIEndMost()) {
- mBlockDirInfo[aIndex].mColWidth = mBlockDirInfo[aIndex - 1].mColWidth;
- }
- else {
- nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex);
- if (!col) ABORT0();
- mBlockDirInfo[aIndex].mColWidth = col->ISize(mTableWM);
- }
- }
- /**
- * Determine if a block-dir segment owns the corner
- */
- bool
- BCPaintBorderIterator::BlockDirSegmentOwnsCorner()
- {
- LogicalSide cornerOwnerSide = eLogicalSideBStart;
- bool bevel = false;
- if (mBCData) {
- mBCData->GetCorner(cornerOwnerSide, bevel);
- }
- // unitialized ownerside, bevel
- return (eLogicalSideBStart == cornerOwnerSide) ||
- (eLogicalSideBEnd == cornerOwnerSide);
- }
- /**
- * Paint if necessary an inline-dir segment, otherwise accumulate it
- * @param aDrawTarget - the draw target
- */
- void
- BCPaintBorderIterator::AccumulateOrPaintInlineDirSegment(DrawTarget& aDrawTarget)
- {
- int32_t relColIndex = GetRelativeColIndex();
- // store the current col width if it hasn't been already
- if (mBlockDirInfo[relColIndex].mColWidth < 0) {
- StoreColumnWidth(relColIndex);
- }
- BCBorderOwner borderOwner = eCellOwner;
- BCBorderOwner ignoreBorderOwner;
- bool isSegStart = true;
- bool ignoreSegStart;
- nscoord iStartSegISize =
- mBCData ? mBCData->GetIStartEdge(ignoreBorderOwner, ignoreSegStart) : 0;
- nscoord bStartSegBSize =
- mBCData ? mBCData->GetBStartEdge(borderOwner, isSegStart) : 0;
- if (mIsNewRow || (IsDamageAreaIStartMost() && IsDamageAreaBEndMost())) {
- // reset for every new row and on the bottom of the last row
- mInlineSeg.mOffsetB = mNextOffsetB;
- mNextOffsetB = mNextOffsetB + mRow->BSize(mTableWM);
- mInlineSeg.mOffsetI = mInitialOffsetI;
- mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
- }
- if (!IsDamageAreaIStartMost() && (isSegStart || IsDamageAreaIEndMost() ||
- BlockDirSegmentOwnsCorner())) {
- // paint the previous seg or the current one if IsDamageAreaIEndMost()
- if (mInlineSeg.mLength > 0) {
- mInlineSeg.GetIEndCorner(*this, iStartSegISize);
- if (mInlineSeg.mWidth > 0) {
- mInlineSeg.Paint(*this, aDrawTarget);
- }
- mInlineSeg.AdvanceOffsetI();
- }
- mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
- }
- mInlineSeg.IncludeCurrentBorder(*this);
- mBlockDirInfo[relColIndex].mWidth = iStartSegISize;
- mBlockDirInfo[relColIndex].mLastCell = mCell;
- }
- /**
- * Paint if necessary a block-dir segment, otherwise accumulate it
- * @param aDrawTarget - the draw target
- */
- void
- BCPaintBorderIterator::AccumulateOrPaintBlockDirSegment(DrawTarget& aDrawTarget)
- {
- BCBorderOwner borderOwner = eCellOwner;
- BCBorderOwner ignoreBorderOwner;
- bool isSegStart = true;
- bool ignoreSegStart;
- nscoord blockSegISize =
- mBCData ? mBCData->GetIStartEdge(borderOwner, isSegStart) : 0;
- nscoord inlineSegBSize =
- mBCData ? mBCData->GetBStartEdge(ignoreBorderOwner, ignoreSegStart) : 0;
- int32_t relColIndex = GetRelativeColIndex();
- BCBlockDirSeg& blockDirSeg = mBlockDirInfo[relColIndex];
- if (!blockDirSeg.mCol) { // on the first damaged row and the first segment in the
- // col
- blockDirSeg.Initialize(*this);
- blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
- }
- if (!IsDamageAreaBStartMost() && (isSegStart || IsDamageAreaBEndMost() ||
- IsAfterRepeatedHeader() ||
- StartRepeatedFooter())) {
- // paint the previous seg or the current one if IsDamageAreaBEndMost()
- if (blockDirSeg.mLength > 0) {
- blockDirSeg.GetBEndCorner(*this, inlineSegBSize);
- if (blockDirSeg.mWidth > 0) {
- blockDirSeg.Paint(*this, aDrawTarget, inlineSegBSize);
- }
- blockDirSeg.AdvanceOffsetB();
- }
- blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
- }
- blockDirSeg.IncludeCurrentBorder(*this);
- mPrevInlineSegBSize = inlineSegBSize;
- }
- /**
- * Reset the block-dir information cache
- */
- void
- BCPaintBorderIterator::ResetVerInfo()
- {
- if (mBlockDirInfo) {
- memset(mBlockDirInfo, 0, mDamageArea.ColCount() * sizeof(BCBlockDirSeg));
- // XXX reinitialize properly
- for (auto xIndex : MakeRange(mDamageArea.ColCount())) {
- mBlockDirInfo[xIndex].mColWidth = -1;
- }
- }
- }
- /**
- * Method to paint BCBorders, this does not use currently display lists although
- * it will do this in future
- * @param aDrawTarget - the rendering context
- * @param aDirtyRect - inside this rectangle the BC Borders will redrawn
- */
- void
- nsTableFrame::PaintBCBorders(DrawTarget& aDrawTarget, const nsRect& aDirtyRect)
- {
- // We first transfer the aDirtyRect into cellmap coordinates to compute which
- // cell borders need to be painted
- BCPaintBorderIterator iter(this);
- if (!iter.SetDamageArea(aDirtyRect))
- return;
- // XXX comment still has physical terminology
- // First, paint all of the vertical borders from top to bottom and left to
- // right as they become complete. They are painted first, since they are less
- // efficient to paint than horizontal segments. They were stored with as few
- // segments as possible (since horizontal borders are painted last and
- // possibly over them). For every cell in a row that fails in the damage are
- // we look up if the current border would start a new segment, if so we paint
- // the previously stored vertical segment and start a new segment. After
- // this we the now active segment with the current border. These
- // segments are stored in mBlockDirInfo to be used on the next row
- for (iter.First(); !iter.mAtEnd; iter.Next()) {
- iter.AccumulateOrPaintBlockDirSegment(aDrawTarget);
- }
- // Next, paint all of the inline-dir border segments from bStart to bEnd reuse
- // the mBlockDirInfo array to keep track of col widths and block-dir segments for
- // corner calculations
- iter.Reset();
- for (iter.First(); !iter.mAtEnd; iter.Next()) {
- iter.AccumulateOrPaintInlineDirSegment(aDrawTarget);
- }
- }
- bool
- nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols)
- {
- bool result = false;
- nsTableCellMap* cellMap = GetCellMap();
- NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
- if (cellMap) {
- result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
- }
- return result;
- }
- bool
- nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols)
- {
- bool result = false;
- nsTableCellMap* cellMap = GetCellMap();
- NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
- if (cellMap) {
- result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
- }
- return result;
- }
- /* static */
- void
- nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame,
- const nsRect& aOrigRect,
- const nsRect& aOrigVisualOverflow,
- bool aIsFirstReflow)
- {
- nsIFrame* parent = aFrame->GetParent();
- NS_ASSERTION(parent, "What happened here?");
- if (parent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
- // Don't bother; we'll invalidate the parent's overflow rect when
- // we finish reflowing it.
- return;
- }
- // The part that looks at both the rect and the overflow rect is a
- // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent
- // description of its hackishness.
- //
- // This doesn't really make sense now that we have DLBI.
- // This code can probably be simplified a fair bit.
- nsRect visualOverflow = aFrame->GetVisualOverflowRect();
- if (aIsFirstReflow ||
- aOrigRect.TopLeft() != aFrame->GetPosition() ||
- aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) {
- // Invalidate the old and new overflow rects. Note that if the
- // frame moved, we can't just use aOrigVisualOverflow, since it's in
- // coordinates relative to the old position. So invalidate via
- // aFrame's parent, and reposition that overflow rect to the right
- // place.
- // XXXbz this doesn't handle outlines, does it?
- aFrame->InvalidateFrame();
- parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft());
- } else if (aOrigRect.Size() != aFrame->GetSize() ||
- aOrigVisualOverflow.Size() != visualOverflow.Size()){
- aFrame->InvalidateFrameWithRect(aOrigVisualOverflow);
- aFrame->InvalidateFrame();
- parent->InvalidateFrameWithRect(aOrigRect);
- parent->InvalidateFrame();
- }
- }
|