nsTableFrame.cpp 274 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/Likely.h"
  6. #include "mozilla/MathAlgorithms.h"
  7. #include "mozilla/IntegerRange.h"
  8. #include "mozilla/WritingModes.h"
  9. #include "nsCOMPtr.h"
  10. #include "nsTableFrame.h"
  11. #include "nsRenderingContext.h"
  12. #include "nsStyleContext.h"
  13. #include "nsStyleConsts.h"
  14. #include "nsIContent.h"
  15. #include "nsCellMap.h"
  16. #include "nsTableCellFrame.h"
  17. #include "nsHTMLParts.h"
  18. #include "nsTableColFrame.h"
  19. #include "nsTableColGroupFrame.h"
  20. #include "nsTableRowFrame.h"
  21. #include "nsTableRowGroupFrame.h"
  22. #include "nsTableWrapperFrame.h"
  23. #include "BasicTableLayoutStrategy.h"
  24. #include "FixedTableLayoutStrategy.h"
  25. #include "nsPresContext.h"
  26. #include "nsContentUtils.h"
  27. #include "nsCSSRendering.h"
  28. #include "nsGkAtoms.h"
  29. #include "nsCSSAnonBoxes.h"
  30. #include "nsIPresShell.h"
  31. #include "nsIDOMElement.h"
  32. #include "nsIDOMHTMLElement.h"
  33. #include "nsIScriptError.h"
  34. #include "nsFrameManager.h"
  35. #include "nsError.h"
  36. #include "nsCSSFrameConstructor.h"
  37. #include "mozilla/StyleSetHandle.h"
  38. #include "mozilla/StyleSetHandleInlines.h"
  39. #include "mozilla/gfx/Helpers.h"
  40. #include "nsDisplayList.h"
  41. #include "nsIScrollableFrame.h"
  42. #include "nsCSSProps.h"
  43. #include "RestyleTracker.h"
  44. #include <algorithm>
  45. using namespace mozilla;
  46. using namespace mozilla::gfx;
  47. using namespace mozilla::image;
  48. using namespace mozilla::layout;
  49. /********************************************************************************
  50. ** TableReflowInput **
  51. ********************************************************************************/
  52. namespace mozilla {
  53. struct TableReflowInput {
  54. // the real reflow state
  55. const ReflowInput& reflowInput;
  56. // The table's available size (in reflowInput's writing mode)
  57. LogicalSize availSize;
  58. // Stationary inline-offset
  59. nscoord iCoord;
  60. // Running block-offset
  61. nscoord bCoord;
  62. TableReflowInput(const ReflowInput& aReflowInput,
  63. const LogicalSize& aAvailSize)
  64. : reflowInput(aReflowInput)
  65. , availSize(aAvailSize)
  66. {
  67. MOZ_ASSERT(reflowInput.mFrame->GetType() == nsGkAtoms::tableFrame,
  68. "TableReflowInput should only be created for nsTableFrame");
  69. nsTableFrame* table =
  70. static_cast<nsTableFrame*>(reflowInput.mFrame->FirstInFlow());
  71. WritingMode wm = aReflowInput.GetWritingMode();
  72. LogicalMargin borderPadding = table->GetChildAreaOffset(wm, &reflowInput);
  73. iCoord = borderPadding.IStart(wm) + table->GetColSpacing(-1);
  74. bCoord = borderPadding.BStart(wm); //cellspacing added during reflow
  75. // XXX do we actually need to check for unconstrained inline-size here?
  76. if (NS_UNCONSTRAINEDSIZE != availSize.ISize(wm)) {
  77. int32_t colCount = table->GetColCount();
  78. availSize.ISize(wm) -= borderPadding.IStartEnd(wm) +
  79. table->GetColSpacing(-1) +
  80. table->GetColSpacing(colCount);
  81. availSize.ISize(wm) = std::max(0, availSize.ISize(wm));
  82. }
  83. if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
  84. availSize.BSize(wm) -= borderPadding.BStartEnd(wm) +
  85. table->GetRowSpacing(-1) +
  86. table->GetRowSpacing(table->GetRowCount());
  87. availSize.BSize(wm) = std::max(0, availSize.BSize(wm));
  88. }
  89. }
  90. void ReduceAvailableBSizeBy(WritingMode aWM, nscoord aAmount) {
  91. if (availSize.BSize(aWM) == NS_UNCONSTRAINEDSIZE) {
  92. return;
  93. }
  94. availSize.BSize(aWM) -= aAmount;
  95. availSize.BSize(aWM) = std::max(0, availSize.BSize(aWM));
  96. }
  97. };
  98. } // namespace mozilla
  99. /********************************************************************************
  100. ** nsTableFrame **
  101. ********************************************************************************/
  102. struct BCPropertyData
  103. {
  104. BCPropertyData() : mBStartBorderWidth(0), mIEndBorderWidth(0),
  105. mBEndBorderWidth(0), mIStartBorderWidth(0),
  106. mIStartCellBorderWidth(0), mIEndCellBorderWidth(0) {}
  107. TableArea mDamageArea;
  108. BCPixelSize mBStartBorderWidth;
  109. BCPixelSize mIEndBorderWidth;
  110. BCPixelSize mBEndBorderWidth;
  111. BCPixelSize mIStartBorderWidth;
  112. BCPixelSize mIStartCellBorderWidth;
  113. BCPixelSize mIEndCellBorderWidth;
  114. };
  115. nsStyleContext*
  116. nsTableFrame::GetParentStyleContext(nsIFrame** aProviderFrame) const
  117. {
  118. // Since our parent, the table wrapper frame, returned this frame, we
  119. // must return whatever our parent would normally have returned.
  120. NS_PRECONDITION(GetParent(), "table constructed without table wrapper");
  121. if (!mContent->GetParent() && !StyleContext()->GetPseudo()) {
  122. // We're the root. We have no style context parent.
  123. *aProviderFrame = nullptr;
  124. return nullptr;
  125. }
  126. return GetParent()->DoGetParentStyleContext(aProviderFrame);
  127. }
  128. nsIAtom*
  129. nsTableFrame::GetType() const
  130. {
  131. return nsGkAtoms::tableFrame;
  132. }
  133. nsTableFrame::nsTableFrame(nsStyleContext* aContext)
  134. : nsContainerFrame(aContext),
  135. mCellMap(nullptr),
  136. mTableLayoutStrategy(nullptr)
  137. {
  138. memset(&mBits, 0, sizeof(mBits));
  139. }
  140. void
  141. nsTableFrame::Init(nsIContent* aContent,
  142. nsContainerFrame* aParent,
  143. nsIFrame* aPrevInFlow)
  144. {
  145. NS_PRECONDITION(!mCellMap, "Init called twice");
  146. NS_PRECONDITION(!mTableLayoutStrategy, "Init called twice");
  147. NS_PRECONDITION(!aPrevInFlow ||
  148. aPrevInFlow->GetType() == nsGkAtoms::tableFrame,
  149. "prev-in-flow must be of same type");
  150. // Let the base class do its processing
  151. nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
  152. // see if border collapse is on, if so set it
  153. const nsStyleTableBorder* tableStyle = StyleTableBorder();
  154. bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
  155. SetBorderCollapse(borderCollapse);
  156. if (borderCollapse) {
  157. SetNeedToCalcHasBCBorders(true);
  158. }
  159. if (!aPrevInFlow) {
  160. // If we're the first-in-flow, we manage the cell map & layout strategy that
  161. // get used by our continuation chain:
  162. mCellMap = new nsTableCellMap(*this, borderCollapse);
  163. if (IsAutoLayout()) {
  164. mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
  165. } else {
  166. mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
  167. }
  168. } else {
  169. // Set my isize, because all frames in a table flow are the same isize and
  170. // code in nsTableWrapperFrame depends on this being set.
  171. WritingMode wm = GetWritingMode();
  172. SetSize(LogicalSize(wm, aPrevInFlow->ISize(wm), BSize(wm)));
  173. }
  174. }
  175. nsTableFrame::~nsTableFrame()
  176. {
  177. delete mCellMap;
  178. delete mTableLayoutStrategy;
  179. }
  180. void
  181. nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot)
  182. {
  183. mColGroups.DestroyFramesFrom(aDestructRoot);
  184. nsContainerFrame::DestroyFrom(aDestructRoot);
  185. }
  186. // Make sure any views are positioned properly
  187. void
  188. nsTableFrame::RePositionViews(nsIFrame* aFrame)
  189. {
  190. nsContainerFrame::PositionFrameView(aFrame);
  191. nsContainerFrame::PositionChildViews(aFrame);
  192. }
  193. static bool
  194. IsRepeatedFrame(nsIFrame* kidFrame)
  195. {
  196. return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
  197. kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
  198. kidFrame->HasAnyStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
  199. }
  200. bool
  201. nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
  202. nsIFrame* aNextFrame)
  203. {
  204. const nsStyleDisplay* display = aSourceFrame->StyleDisplay();
  205. nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
  206. // don't allow a page break after a repeated element ...
  207. if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) &&
  208. !IsRepeatedFrame(aSourceFrame)) {
  209. return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
  210. }
  211. if (aNextFrame) {
  212. display = aNextFrame->StyleDisplay();
  213. // don't allow a page break before a repeated element ...
  214. nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
  215. if ((display->mBreakBefore ||
  216. (nextRg && nextRg->HasInternalBreakBefore())) &&
  217. !IsRepeatedFrame(aNextFrame)) {
  218. return !IsRepeatedFrame(aSourceFrame); // or after
  219. }
  220. }
  221. return false;
  222. }
  223. /* static */ void
  224. nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
  225. {
  226. // Supporting relative positioning for table parts other than table cells has
  227. // the potential to break sites that apply 'position: relative' to those
  228. // parts, expecting nothing to happen. We warn at the console to make tracking
  229. // down the issue easy.
  230. if (!IS_TABLE_CELL(aFrame->GetType())) {
  231. nsIContent* content = aFrame->GetContent();
  232. nsPresContext* presContext = aFrame->PresContext();
  233. if (content && !presContext->HasWarnedAboutPositionedTableParts()) {
  234. presContext->SetHasWarnedAboutPositionedTableParts();
  235. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  236. NS_LITERAL_CSTRING("Layout: Tables"),
  237. content->OwnerDoc(),
  238. nsContentUtils::eLAYOUT_PROPERTIES,
  239. "TablePartRelPosWarning");
  240. }
  241. }
  242. nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
  243. MOZ_ASSERT(tableFrame, "Should have a table frame here");
  244. tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
  245. // Retrieve the positioned parts array for this table.
  246. FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
  247. // Lazily create the array if it doesn't exist yet.
  248. if (!positionedParts) {
  249. positionedParts = new FrameTArray;
  250. tableFrame->SetProperty(PositionedTablePartArray(), positionedParts);
  251. }
  252. // Add this frame to the list.
  253. positionedParts->AppendElement(aFrame);
  254. }
  255. /* static */ void
  256. nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
  257. nsIFrame* aDestructRoot)
  258. {
  259. // Retrieve the table frame, and check if we hit aDestructRoot on the way.
  260. bool didPassThrough;
  261. nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame,
  262. &didPassThrough);
  263. if (!didPassThrough && !tableFrame->GetPrevContinuation()) {
  264. // The table frame will be destroyed, and it's the first im flow (and thus
  265. // owning the PositionedTablePartArray), so we don't need to do
  266. // anything.
  267. return;
  268. }
  269. tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
  270. // Retrieve the positioned parts array for this table.
  271. FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
  272. // Remove the frame.
  273. MOZ_ASSERT(positionedParts && positionedParts->Contains(aFrame),
  274. "Asked to unregister a positioned table part that wasn't registered");
  275. if (positionedParts) {
  276. positionedParts->RemoveElement(aFrame);
  277. }
  278. }
  279. // XXX this needs to be cleaned up so that the frame constructor breaks out col group
  280. // frames into a separate child list, bug 343048.
  281. void
  282. nsTableFrame::SetInitialChildList(ChildListID aListID,
  283. nsFrameList& aChildList)
  284. {
  285. if (aListID != kPrincipalList) {
  286. nsContainerFrame::SetInitialChildList(aListID, aChildList);
  287. return;
  288. }
  289. MOZ_ASSERT(mFrames.IsEmpty() && mColGroups.IsEmpty(),
  290. "unexpected second call to SetInitialChildList");
  291. // XXXbz the below code is an icky cesspit that's only needed in its current
  292. // form for two reasons:
  293. // 1) Both rowgroups and column groups come in on the principal child list.
  294. while (aChildList.NotEmpty()) {
  295. nsIFrame* childFrame = aChildList.FirstChild();
  296. aChildList.RemoveFirstChild();
  297. const nsStyleDisplay* childDisplay = childFrame->StyleDisplay();
  298. if (mozilla::StyleDisplay::TableColumnGroup == childDisplay->mDisplay) {
  299. NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(),
  300. "This is not a colgroup");
  301. mColGroups.AppendFrame(nullptr, childFrame);
  302. }
  303. else { // row groups and unknown frames go on the main list for now
  304. mFrames.AppendFrame(nullptr, childFrame);
  305. }
  306. }
  307. // If we have a prev-in-flow, then we're a table that has been split and
  308. // so don't treat this like an append
  309. if (!GetPrevInFlow()) {
  310. // process col groups first so that real cols get constructed before
  311. // anonymous ones due to cells in rows.
  312. InsertColGroups(0, mColGroups);
  313. InsertRowGroups(mFrames);
  314. // calc collapsing borders
  315. if (IsBorderCollapse()) {
  316. SetFullBCDamageArea();
  317. }
  318. }
  319. }
  320. void
  321. nsTableFrame::AttributeChangedFor(nsIFrame* aFrame,
  322. nsIContent* aContent,
  323. nsIAtom* aAttribute)
  324. {
  325. nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
  326. if (cellFrame) {
  327. if ((nsGkAtoms::rowspan == aAttribute) ||
  328. (nsGkAtoms::colspan == aAttribute)) {
  329. nsTableCellMap* cellMap = GetCellMap();
  330. if (cellMap) {
  331. // for now just remove the cell from the map and reinsert it
  332. uint32_t rowIndex = cellFrame->RowIndex();
  333. uint32_t colIndex = cellFrame->ColIndex();
  334. RemoveCell(cellFrame, rowIndex);
  335. AutoTArray<nsTableCellFrame*, 1> cells;
  336. cells.AppendElement(cellFrame);
  337. InsertCells(cells, rowIndex, colIndex - 1);
  338. // XXX Should this use eStyleChange? It currently doesn't need
  339. // to, but it might given more optimization.
  340. PresContext()->PresShell()->
  341. FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
  342. }
  343. }
  344. }
  345. }
  346. /* ****** CellMap methods ******* */
  347. /* return the effective col count */
  348. int32_t
  349. nsTableFrame::GetEffectiveColCount() const
  350. {
  351. int32_t colCount = GetColCount();
  352. if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
  353. nsTableCellMap* cellMap = GetCellMap();
  354. if (!cellMap) {
  355. return 0;
  356. }
  357. // don't count cols at the end that don't have originating cells
  358. for (int32_t colIdx = colCount - 1; colIdx >= 0; colIdx--) {
  359. if (cellMap->GetNumCellsOriginatingInCol(colIdx) > 0) {
  360. break;
  361. }
  362. colCount--;
  363. }
  364. }
  365. return colCount;
  366. }
  367. int32_t
  368. nsTableFrame::GetIndexOfLastRealCol()
  369. {
  370. int32_t numCols = mColFrames.Length();
  371. if (numCols > 0) {
  372. for (int32_t colIdx = numCols - 1; colIdx >= 0; colIdx--) {
  373. nsTableColFrame* colFrame = GetColFrame(colIdx);
  374. if (colFrame) {
  375. if (eColAnonymousCell != colFrame->GetColType()) {
  376. return colIdx;
  377. }
  378. }
  379. }
  380. }
  381. return -1;
  382. }
  383. nsTableColFrame*
  384. nsTableFrame::GetColFrame(int32_t aColIndex) const
  385. {
  386. NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
  387. int32_t numCols = mColFrames.Length();
  388. if ((aColIndex >= 0) && (aColIndex < numCols)) {
  389. return mColFrames.ElementAt(aColIndex);
  390. }
  391. else {
  392. NS_ERROR("invalid col index");
  393. return nullptr;
  394. }
  395. }
  396. int32_t
  397. nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex,
  398. const nsTableCellFrame& aCell) const
  399. {
  400. nsTableCellMap* cellMap = GetCellMap();
  401. NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated.");
  402. return cellMap->GetEffectiveRowSpan(aRowIndex, aCell.ColIndex());
  403. }
  404. int32_t
  405. nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
  406. nsCellMap* aCellMap)
  407. {
  408. nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
  409. uint32_t colIndex = aCell.ColIndex();
  410. uint32_t rowIndex = aCell.RowIndex();
  411. if (aCellMap)
  412. return aCellMap->GetRowSpan(rowIndex, colIndex, true);
  413. else
  414. return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
  415. }
  416. int32_t
  417. nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
  418. nsCellMap* aCellMap) const
  419. {
  420. nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
  421. uint32_t colIndex = aCell.ColIndex();
  422. uint32_t rowIndex = aCell.RowIndex();
  423. if (aCellMap)
  424. return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex);
  425. else
  426. return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
  427. }
  428. bool
  429. nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const
  430. {
  431. nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
  432. return tableCellMap->HasMoreThanOneCell(aRowIndex);
  433. }
  434. void
  435. nsTableFrame::AdjustRowIndices(int32_t aRowIndex,
  436. int32_t aAdjustment)
  437. {
  438. // Iterate over the row groups and adjust the row indices of all rows
  439. // whose index is >= aRowIndex.
  440. RowGroupArray rowGroups;
  441. OrderRowGroups(rowGroups);
  442. for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  443. rowGroups[rgIdx]->AdjustRowIndices(aRowIndex, aAdjustment);
  444. }
  445. }
  446. void
  447. nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
  448. {
  449. // Iterate over the row groups and adjust the row indices of all rows
  450. // omit the rowgroups that will be inserted later
  451. RowGroupArray rowGroups;
  452. OrderRowGroups(rowGroups);
  453. int32_t rowIndex = 0;
  454. nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
  455. nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
  456. while (!excludeRowGroupsEnumerator.AtEnd()) {
  457. excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
  458. excludeRowGroupsEnumerator.Next();
  459. }
  460. for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  461. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  462. if (!excludeRowGroups.GetEntry(rgFrame)) {
  463. const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
  464. for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
  465. if (mozilla::StyleDisplay::TableRow == rows.get()->StyleDisplay()->mDisplay) {
  466. ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex);
  467. rowIndex++;
  468. }
  469. }
  470. }
  471. }
  472. }
  473. void
  474. nsTableFrame::InsertColGroups(int32_t aStartColIndex,
  475. const nsFrameList::Slice& aColGroups)
  476. {
  477. int32_t colIndex = aStartColIndex;
  478. nsFrameList::Enumerator colGroups(aColGroups);
  479. for (; !colGroups.AtEnd(); colGroups.Next()) {
  480. MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame);
  481. nsTableColGroupFrame* cgFrame =
  482. static_cast<nsTableColGroupFrame*>(colGroups.get());
  483. cgFrame->SetStartColumnIndex(colIndex);
  484. // XXXbz this sucks. AddColsToTable will actually remove colgroups from
  485. // the list we're traversing! Need to fix things here. :( I guess this is
  486. // why the old code used pointer-to-last-frame as opposed to
  487. // pointer-to-frame-after-last....
  488. // How about dealing with this by storing a const reference to the
  489. // mNextSibling of the framelist's last frame, instead of storing a pointer
  490. // to the first-after-next frame? Will involve making nsFrameList friend
  491. // of nsIFrame, but it's time for that anyway.
  492. cgFrame->AddColsToTable(colIndex, false,
  493. colGroups.get()->PrincipalChildList());
  494. int32_t numCols = cgFrame->GetColCount();
  495. colIndex += numCols;
  496. }
  497. nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator();
  498. if (!remainingColgroups.AtEnd()) {
  499. nsTableColGroupFrame::ResetColIndices(
  500. static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex);
  501. }
  502. }
  503. void
  504. nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
  505. int32_t aColIndex)
  506. {
  507. mColFrames.InsertElementAt(aColIndex, &aColFrame);
  508. nsTableColType insertedColType = aColFrame.GetColType();
  509. int32_t numCacheCols = mColFrames.Length();
  510. nsTableCellMap* cellMap = GetCellMap();
  511. if (cellMap) {
  512. int32_t numMapCols = cellMap->GetColCount();
  513. if (numCacheCols > numMapCols) {
  514. bool removedFromCache = false;
  515. if (eColAnonymousCell != insertedColType) {
  516. nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
  517. if (lastCol) {
  518. nsTableColType lastColType = lastCol->GetColType();
  519. if (eColAnonymousCell == lastColType) {
  520. // remove the col from the cache
  521. mColFrames.RemoveElementAt(numCacheCols - 1);
  522. // remove the col from the eColGroupAnonymousCell col group
  523. nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
  524. if (lastColGroup) {
  525. lastColGroup->RemoveChild(*lastCol, false);
  526. // remove the col group if it is empty
  527. if (lastColGroup->GetColCount() <= 0) {
  528. mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
  529. }
  530. }
  531. removedFromCache = true;
  532. }
  533. }
  534. }
  535. if (!removedFromCache) {
  536. cellMap->AddColsAtEnd(1);
  537. }
  538. }
  539. }
  540. // for now, just bail and recalc all of the collapsing borders
  541. if (IsBorderCollapse()) {
  542. TableArea damageArea(aColIndex, 0, 1, GetRowCount());
  543. AddBCDamageArea(damageArea);
  544. }
  545. }
  546. void
  547. nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
  548. int32_t aColIndex,
  549. bool aRemoveFromCache,
  550. bool aRemoveFromCellMap)
  551. {
  552. if (aRemoveFromCache) {
  553. mColFrames.RemoveElementAt(aColIndex);
  554. }
  555. if (aRemoveFromCellMap) {
  556. nsTableCellMap* cellMap = GetCellMap();
  557. if (cellMap) {
  558. // If we have some anonymous cols at the end already, we just
  559. // add a new anonymous col.
  560. if (!mColFrames.IsEmpty() &&
  561. mColFrames.LastElement() && // XXXbz is this ever null?
  562. mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
  563. AppendAnonymousColFrames(1);
  564. } else {
  565. // All of our colframes correspond to actual <col> tags. It's possible
  566. // that we still have at least as many <col> tags as we have logical
  567. // columns from cells, but we might have one less. Handle the latter
  568. // case as follows: First ask the cellmap to drop its last col if it
  569. // doesn't have any actual cells in it. Then call
  570. // MatchCellMapToColCache to append an anonymous column if it's needed;
  571. // this needs to be after RemoveColsAtEnd, since it will determine the
  572. // need for a new column frame based on the width of the cell map.
  573. cellMap->RemoveColsAtEnd();
  574. MatchCellMapToColCache(cellMap);
  575. }
  576. }
  577. }
  578. // for now, just bail and recalc all of the collapsing borders
  579. if (IsBorderCollapse()) {
  580. TableArea damageArea(0, 0, GetColCount(), GetRowCount());
  581. AddBCDamageArea(damageArea);
  582. }
  583. }
  584. /** Get the cell map for this table frame. It is not always mCellMap.
  585. * Only the first-in-flow has a legit cell map.
  586. */
  587. nsTableCellMap*
  588. nsTableFrame::GetCellMap() const
  589. {
  590. return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap;
  591. }
  592. // XXX this needs to be moved to nsCSSFrameConstructor
  593. nsTableColGroupFrame*
  594. nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
  595. {
  596. nsIContent* colGroupContent = GetContent();
  597. nsPresContext* presContext = PresContext();
  598. nsIPresShell *shell = presContext->PresShell();
  599. RefPtr<nsStyleContext> colGroupStyle;
  600. colGroupStyle = shell->StyleSet()->
  601. ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext);
  602. // Create a col group frame
  603. nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
  604. ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
  605. newFrame->Init(colGroupContent, this, nullptr);
  606. return (nsTableColGroupFrame *)newFrame;
  607. }
  608. void
  609. nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd)
  610. {
  611. // get the last col group frame
  612. nsTableColGroupFrame* colGroupFrame =
  613. static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
  614. if (!colGroupFrame ||
  615. (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
  616. int32_t colIndex = (colGroupFrame) ?
  617. colGroupFrame->GetStartColumnIndex() +
  618. colGroupFrame->GetColCount() : 0;
  619. colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
  620. if (!colGroupFrame) {
  621. return;
  622. }
  623. // add the new frame to the child list
  624. mColGroups.AppendFrame(this, colGroupFrame);
  625. colGroupFrame->SetStartColumnIndex(colIndex);
  626. }
  627. AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
  628. true);
  629. }
  630. // XXX this needs to be moved to nsCSSFrameConstructor
  631. // Right now it only creates the col frames at the end
  632. void
  633. nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
  634. int32_t aNumColsToAdd,
  635. nsTableColType aColType,
  636. bool aAddToTable)
  637. {
  638. NS_PRECONDITION(aColGroupFrame, "null frame");
  639. NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
  640. nsIPresShell *shell = PresContext()->PresShell();
  641. // Get the last col frame
  642. nsFrameList newColFrames;
  643. int32_t startIndex = mColFrames.Length();
  644. int32_t lastIndex = startIndex + aNumColsToAdd - 1;
  645. for (int32_t childX = startIndex; childX <= lastIndex; childX++) {
  646. nsIContent* iContent;
  647. RefPtr<nsStyleContext> styleContext;
  648. nsStyleContext* parentStyleContext;
  649. // all anonymous cols that we create here use a pseudo style context of the
  650. // col group
  651. iContent = aColGroupFrame->GetContent();
  652. parentStyleContext = aColGroupFrame->StyleContext();
  653. styleContext = shell->StyleSet()->
  654. ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext);
  655. // ASSERTION to check for bug 54454 sneaking back in...
  656. NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
  657. // create the new col frame
  658. nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
  659. ((nsTableColFrame *) colFrame)->SetColType(aColType);
  660. colFrame->Init(iContent, aColGroupFrame, nullptr);
  661. newColFrames.AppendFrame(nullptr, colFrame);
  662. }
  663. nsFrameList& cols = aColGroupFrame->GetWritableChildList();
  664. nsIFrame* oldLastCol = cols.LastChild();
  665. const nsFrameList::Slice& newCols =
  666. cols.InsertFrames(nullptr, oldLastCol, newColFrames);
  667. if (aAddToTable) {
  668. // get the starting col index in the cache
  669. int32_t startColIndex;
  670. if (oldLastCol) {
  671. startColIndex =
  672. static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
  673. } else {
  674. startColIndex = aColGroupFrame->GetStartColumnIndex();
  675. }
  676. aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
  677. }
  678. }
  679. void
  680. nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
  681. {
  682. int32_t numColsInMap = GetColCount();
  683. int32_t numColsInCache = mColFrames.Length();
  684. int32_t numColsToAdd = numColsInMap - numColsInCache;
  685. if (numColsToAdd > 0) {
  686. // this sets the child list, updates the col cache and cell map
  687. AppendAnonymousColFrames(numColsToAdd);
  688. }
  689. if (numColsToAdd < 0) {
  690. int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
  691. // if the cell map has fewer cols than the cache, correct it
  692. if (numColsNotRemoved > 0) {
  693. aCellMap->AddColsAtEnd(numColsNotRemoved);
  694. }
  695. }
  696. }
  697. void
  698. nsTableFrame::DidResizeColumns()
  699. {
  700. NS_PRECONDITION(!GetPrevInFlow(),
  701. "should only be called on first-in-flow");
  702. if (mBits.mResizedColumns)
  703. return; // already marked
  704. for (nsTableFrame *f = this; f;
  705. f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
  706. f->mBits.mResizedColumns = true;
  707. }
  708. void
  709. nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
  710. int32_t aRowIndex)
  711. {
  712. nsTableCellMap* cellMap = GetCellMap();
  713. if (cellMap) {
  714. TableArea damageArea(0, 0, 0, 0);
  715. cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
  716. MatchCellMapToColCache(cellMap);
  717. if (IsBorderCollapse()) {
  718. AddBCDamageArea(damageArea);
  719. }
  720. }
  721. }
  722. void
  723. nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
  724. int32_t aRowIndex,
  725. int32_t aColIndexBefore)
  726. {
  727. nsTableCellMap* cellMap = GetCellMap();
  728. if (cellMap) {
  729. TableArea damageArea(0, 0, 0, 0);
  730. cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
  731. MatchCellMapToColCache(cellMap);
  732. if (IsBorderCollapse()) {
  733. AddBCDamageArea(damageArea);
  734. }
  735. }
  736. }
  737. // this removes the frames from the col group and table, but not the cell map
  738. int32_t
  739. nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames)
  740. {
  741. // only remove cols that are of type eTypeAnonymous cell (they are at the end)
  742. int32_t endIndex = mColFrames.Length() - 1;
  743. int32_t startIndex = (endIndex - aNumFrames) + 1;
  744. int32_t numColsRemoved = 0;
  745. for (int32_t colIdx = endIndex; colIdx >= startIndex; colIdx--) {
  746. nsTableColFrame* colFrame = GetColFrame(colIdx);
  747. if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
  748. nsTableColGroupFrame* cgFrame =
  749. static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
  750. // remove the frame from the colgroup
  751. cgFrame->RemoveChild(*colFrame, false);
  752. // remove the frame from the cache, but not the cell map
  753. RemoveCol(nullptr, colIdx, true, false);
  754. numColsRemoved++;
  755. }
  756. else {
  757. break;
  758. }
  759. }
  760. return (aNumFrames - numColsRemoved);
  761. }
  762. void
  763. nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
  764. int32_t aRowIndex)
  765. {
  766. nsTableCellMap* cellMap = GetCellMap();
  767. if (cellMap) {
  768. TableArea damageArea(0, 0, 0, 0);
  769. cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
  770. MatchCellMapToColCache(cellMap);
  771. if (IsBorderCollapse()) {
  772. AddBCDamageArea(damageArea);
  773. }
  774. }
  775. }
  776. int32_t
  777. nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame)
  778. {
  779. RowGroupArray orderedRowGroups;
  780. OrderRowGroups(orderedRowGroups);
  781. int32_t rowIndex = 0;
  782. for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  783. nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
  784. if (rgFrame == aRowGroupFrame) {
  785. break;
  786. }
  787. int32_t numRows = rgFrame->GetRowCount();
  788. rowIndex += numRows;
  789. }
  790. return rowIndex;
  791. }
  792. // this cannot extend beyond a single row group
  793. void
  794. nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame,
  795. int32_t aRowIndex,
  796. nsTArray<nsTableRowFrame*>& aRowFrames)
  797. {
  798. nsTableCellMap* cellMap = GetCellMap();
  799. if (cellMap) {
  800. int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
  801. InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
  802. }
  803. }
  804. // this cannot extend beyond a single row group
  805. int32_t
  806. nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame,
  807. nsTArray<nsTableRowFrame*>& aRowFrames,
  808. int32_t aRowIndex,
  809. bool aConsiderSpans)
  810. {
  811. #ifdef DEBUG_TABLE_CELLMAP
  812. printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
  813. Dump(true, false, true);
  814. #endif
  815. int32_t numColsToAdd = 0;
  816. nsTableCellMap* cellMap = GetCellMap();
  817. if (cellMap) {
  818. TableArea damageArea(0, 0, 0, 0);
  819. int32_t origNumRows = cellMap->GetRowCount();
  820. int32_t numNewRows = aRowFrames.Length();
  821. cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
  822. MatchCellMapToColCache(cellMap);
  823. if (aRowIndex < origNumRows) {
  824. AdjustRowIndices(aRowIndex, numNewRows);
  825. }
  826. // assign the correct row indices to the new rows. If they were adjusted above
  827. // it may not have been done correctly because each row is constructed with index 0
  828. for (int32_t rowB = 0; rowB < numNewRows; rowB++) {
  829. nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowB);
  830. rowFrame->SetRowIndex(aRowIndex + rowB);
  831. }
  832. if (IsBorderCollapse()) {
  833. AddBCDamageArea(damageArea);
  834. }
  835. }
  836. #ifdef DEBUG_TABLE_CELLMAP
  837. printf("=== insertRowsAfter \n");
  838. Dump(true, false, true);
  839. #endif
  840. return numColsToAdd;
  841. }
  842. // this cannot extend beyond a single row group
  843. void
  844. nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
  845. int32_t aNumRowsToRemove,
  846. bool aConsiderSpans)
  847. {
  848. #ifdef TBD_OPTIMIZATION
  849. // decide if we need to rebalance. we have to do this here because the row group
  850. // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
  851. bool stopTelling = false;
  852. for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
  853. kidFrame = kidFrame->GetNextSibling()) {
  854. nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
  855. if (cellFrame) {
  856. stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
  857. cellFrame->GetMaximumWidth(), true);
  858. }
  859. }
  860. // XXX need to consider what happens if there are cells that have rowspans
  861. // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
  862. #endif
  863. int32_t firstRowIndex = aFirstRowFrame.GetRowIndex();
  864. #ifdef DEBUG_TABLE_CELLMAP
  865. printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
  866. Dump(true, false, true);
  867. #endif
  868. nsTableCellMap* cellMap = GetCellMap();
  869. if (cellMap) {
  870. TableArea damageArea(0, 0, 0, 0);
  871. cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
  872. MatchCellMapToColCache(cellMap);
  873. if (IsBorderCollapse()) {
  874. AddBCDamageArea(damageArea);
  875. }
  876. }
  877. AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
  878. #ifdef DEBUG_TABLE_CELLMAP
  879. printf("=== removeRowsAfter\n");
  880. Dump(true, true, true);
  881. #endif
  882. }
  883. // collect the rows ancestors of aFrame
  884. int32_t
  885. nsTableFrame::CollectRows(nsIFrame* aFrame,
  886. nsTArray<nsTableRowFrame*>& aCollection)
  887. {
  888. NS_PRECONDITION(aFrame, "null frame");
  889. int32_t numRows = 0;
  890. for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
  891. aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
  892. numRows++;
  893. }
  894. return numRows;
  895. }
  896. void
  897. nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups)
  898. {
  899. #ifdef DEBUG_TABLE_CELLMAP
  900. printf("=== insertRowGroupsBefore\n");
  901. Dump(true, false, true);
  902. #endif
  903. nsTableCellMap* cellMap = GetCellMap();
  904. if (cellMap) {
  905. RowGroupArray orderedRowGroups;
  906. OrderRowGroups(orderedRowGroups);
  907. AutoTArray<nsTableRowFrame*, 8> rows;
  908. // Loop over the rowgroups and check if some of them are new, if they are
  909. // insert cellmaps in the order that is predefined by OrderRowGroups,
  910. // XXXbz this code is O(N*M) where N is number of new rowgroups
  911. // and M is number of rowgroups we have!
  912. uint32_t rgIndex;
  913. for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  914. for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
  915. rowgroups.Next()) {
  916. if (orderedRowGroups[rgIndex] == rowgroups.get()) {
  917. nsTableRowGroupFrame* priorRG =
  918. (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
  919. // create and add the cell map for the row group
  920. cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
  921. break;
  922. }
  923. }
  924. }
  925. cellMap->Synchronize(this);
  926. ResetRowIndices(aRowGroups);
  927. //now that the cellmaps are reordered too insert the rows
  928. for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  929. for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
  930. rowgroups.Next()) {
  931. if (orderedRowGroups[rgIndex] == rowgroups.get()) {
  932. nsTableRowGroupFrame* priorRG =
  933. (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
  934. // collect the new row frames in an array and add them to the table
  935. int32_t numRows = CollectRows(rowgroups.get(), rows);
  936. if (numRows > 0) {
  937. int32_t rowIndex = 0;
  938. if (priorRG) {
  939. int32_t priorNumRows = priorRG->GetRowCount();
  940. rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
  941. }
  942. InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
  943. rows.Clear();
  944. }
  945. break;
  946. }
  947. }
  948. }
  949. }
  950. #ifdef DEBUG_TABLE_CELLMAP
  951. printf("=== insertRowGroupsAfter\n");
  952. Dump(true, true, true);
  953. #endif
  954. }
  955. /////////////////////////////////////////////////////////////////////////////
  956. // Child frame enumeration
  957. const nsFrameList&
  958. nsTableFrame::GetChildList(ChildListID aListID) const
  959. {
  960. if (aListID == kColGroupList) {
  961. return mColGroups;
  962. }
  963. return nsContainerFrame::GetChildList(aListID);
  964. }
  965. void
  966. nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const
  967. {
  968. nsContainerFrame::GetChildLists(aLists);
  969. mColGroups.AppendIfNonempty(aLists, kColGroupList);
  970. }
  971. nsRect
  972. nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
  973. *aSnap = false;
  974. return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
  975. }
  976. void
  977. nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
  978. {
  979. nsStyleContext *bgSC;
  980. if (!nsCSSRendering::FindBackground(aFrame, &bgSC))
  981. return;
  982. if (!bgSC->StyleBackground()->HasFixedBackground(aFrame))
  983. return;
  984. mPartHasFixedBackground = true;
  985. }
  986. nsDisplayItemGeometry*
  987. nsDisplayTableItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
  988. {
  989. return new nsDisplayTableItemGeometry(this, aBuilder,
  990. mFrame->GetOffsetTo(mFrame->PresContext()->PresShell()->GetRootFrame()));
  991. }
  992. void
  993. nsDisplayTableItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  994. const nsDisplayItemGeometry* aGeometry,
  995. nsRegion *aInvalidRegion)
  996. {
  997. auto geometry =
  998. static_cast<const nsDisplayTableItemGeometry*>(aGeometry);
  999. bool invalidateForAttachmentFixed = false;
  1000. if (mDrawsBackground && mPartHasFixedBackground) {
  1001. nsPoint frameOffsetToViewport = mFrame->GetOffsetTo(
  1002. mFrame->PresContext()->PresShell()->GetRootFrame());
  1003. invalidateForAttachmentFixed =
  1004. frameOffsetToViewport != geometry->mFrameOffsetToViewport;
  1005. }
  1006. if (invalidateForAttachmentFixed ||
  1007. (aBuilder->ShouldSyncDecodeImages() &&
  1008. geometry->ShouldInvalidateToSyncDecodeImages())) {
  1009. bool snap;
  1010. aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  1011. }
  1012. nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  1013. }
  1014. // A display item that draws all collapsed borders for a table.
  1015. class nsDisplayTableBorderCollapse : public nsDisplayTableItem {
  1016. public:
  1017. nsDisplayTableBorderCollapse(nsDisplayListBuilder* aBuilder,
  1018. nsTableFrame* aFrame)
  1019. : nsDisplayTableItem(aBuilder, aFrame) {
  1020. MOZ_COUNT_CTOR(nsDisplayTableBorderCollapse);
  1021. }
  1022. #ifdef NS_BUILD_REFCNT_LOGGING
  1023. virtual ~nsDisplayTableBorderCollapse() {
  1024. MOZ_COUNT_DTOR(nsDisplayTableBorderCollapse);
  1025. }
  1026. #endif
  1027. virtual void Paint(nsDisplayListBuilder* aBuilder,
  1028. nsRenderingContext* aCtx) override;
  1029. NS_DISPLAY_DECL_NAME("TableBorderCollapse", TYPE_TABLE_BORDER_COLLAPSE)
  1030. };
  1031. void
  1032. nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder,
  1033. nsRenderingContext* aCtx)
  1034. {
  1035. nsPoint pt = ToReferenceFrame();
  1036. DrawTarget* drawTarget = aCtx->GetDrawTarget();
  1037. gfxPoint devPixelOffset =
  1038. nsLayoutUtils::PointToGfxPoint(pt, mFrame->PresContext()->AppUnitsPerDevPixel());
  1039. AutoRestoreTransform autoRestoreTransform(drawTarget);
  1040. drawTarget->SetTransform(
  1041. drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
  1042. static_cast<nsTableFrame*>(mFrame)->PaintBCBorders(*drawTarget, mVisibleRect - pt);
  1043. }
  1044. static inline bool FrameHasBorder(nsIFrame* f) {
  1045. if (!f->StyleVisibility()->IsVisible()) {
  1046. return false;
  1047. }
  1048. if (f->StyleBorder()->HasBorder()) {
  1049. return true;
  1050. }
  1051. return false;
  1052. }
  1053. void nsTableFrame::CalcHasBCBorders() {
  1054. if (!IsBorderCollapse()) {
  1055. SetHasBCBorders(false);
  1056. return;
  1057. }
  1058. if (FrameHasBorder(this)) {
  1059. SetHasBCBorders(true);
  1060. return;
  1061. }
  1062. // Check col and col group has borders.
  1063. for (nsIFrame* f : this->GetChildList(kColGroupList)) {
  1064. if (FrameHasBorder(f)) {
  1065. SetHasBCBorders(true);
  1066. return;
  1067. }
  1068. nsTableColGroupFrame* colGroup = static_cast<nsTableColGroupFrame*>(f);
  1069. for (nsTableColFrame* col = colGroup->GetFirstColumn(); col;
  1070. col = col->GetNextCol()) {
  1071. if (FrameHasBorder(col)) {
  1072. SetHasBCBorders(true);
  1073. return;
  1074. }
  1075. }
  1076. }
  1077. // check row group, row and cell has borders.
  1078. RowGroupArray rowGroups;
  1079. OrderRowGroups(rowGroups);
  1080. for (nsTableRowGroupFrame* rowGroup : rowGroups) {
  1081. if (FrameHasBorder(rowGroup)) {
  1082. SetHasBCBorders(true);
  1083. return;
  1084. }
  1085. for (nsTableRowFrame* row = rowGroup->GetFirstRow(); row;
  1086. row = row->GetNextRow()) {
  1087. if (FrameHasBorder(row)) {
  1088. SetHasBCBorders(true);
  1089. return;
  1090. }
  1091. for (nsTableCellFrame* cell = row->GetFirstCell(); cell;
  1092. cell = cell->GetNextCell()) {
  1093. if (FrameHasBorder(cell)) {
  1094. SetHasBCBorders(true);
  1095. return;
  1096. }
  1097. }
  1098. }
  1099. }
  1100. SetHasBCBorders(false);
  1101. }
  1102. // table paint code is concerned primarily with borders and bg color
  1103. // SEC: TODO: adjust the rect for captions
  1104. void
  1105. nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  1106. const nsDisplayListSet& aLists)
  1107. {
  1108. DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
  1109. DisplayBorderBackgroundOutline(aBuilder, aLists);
  1110. nsDisplayTableBackgroundSet tableBGs(aBuilder, this);
  1111. nsDisplayListCollection lists(aBuilder);
  1112. // This is similar to what
  1113. // nsContainerFrame::BuildDisplayListForNonBlockChildren does, except that we
  1114. // allow the children's background and borders to go in our BorderBackground
  1115. // list. This doesn't really affect background painting --- the children won't
  1116. // actually draw their own backgrounds because the nsTableFrame already drew
  1117. // them, unless a child has its own stacking context, in which case the child
  1118. // won't use its passed-in BorderBackground list anyway. It does affect cell
  1119. // borders though; this lets us get cell borders into the nsTableFrame's
  1120. // BorderBackground list.
  1121. for (nsIFrame* colGroup : FirstContinuation()->GetChildList(kColGroupList)) {
  1122. for (nsIFrame* col : colGroup->PrincipalChildList()) {
  1123. tableBGs.AddColumn((nsTableColFrame*)col);
  1124. }
  1125. }
  1126. for (nsIFrame* kid : PrincipalChildList()) {
  1127. BuildDisplayListForChild(aBuilder, kid, lists);
  1128. }
  1129. tableBGs.MoveTo(aLists);
  1130. lists.MoveTo(aLists);
  1131. if (IsVisibleForPainting(aBuilder)) {
  1132. // In the collapsed border model, overlay all collapsed borders.
  1133. if (IsBorderCollapse()) {
  1134. if (HasBCBorders()) {
  1135. aLists.BorderBackground()->AppendNewToTop(
  1136. new (aBuilder) nsDisplayTableBorderCollapse
  1137. (aBuilder, this));
  1138. }
  1139. } else {
  1140. const nsStyleBorder* borderStyle = StyleBorder();
  1141. if (borderStyle->HasBorder()) {
  1142. aLists.BorderBackground()->AppendNewToTop(
  1143. new (aBuilder) nsDisplayBorder
  1144. (aBuilder, this));
  1145. }
  1146. }
  1147. }
  1148. }
  1149. nsMargin
  1150. nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
  1151. {
  1152. if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
  1153. !IsBorderCollapse())
  1154. return nsMargin(0,0,0,0);
  1155. WritingMode wm = GetWritingMode();
  1156. return GetOuterBCBorder(wm).GetPhysicalMargin(wm);
  1157. }
  1158. nsIFrame::LogicalSides
  1159. nsTableFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
  1160. {
  1161. if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
  1162. StyleBoxDecorationBreak::Clone)) {
  1163. return LogicalSides();
  1164. }
  1165. LogicalSides skip;
  1166. // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
  1167. // account for pagination
  1168. if (nullptr != GetPrevInFlow()) {
  1169. skip |= eLogicalSideBitsBStart;
  1170. }
  1171. if (nullptr != GetNextInFlow()) {
  1172. skip |= eLogicalSideBitsBEnd;
  1173. }
  1174. return skip;
  1175. }
  1176. void
  1177. nsTableFrame::SetColumnDimensions(nscoord aBSize, WritingMode aWM,
  1178. const LogicalMargin& aBorderPadding,
  1179. const nsSize& aContainerSize)
  1180. {
  1181. const nscoord colBSize = aBSize - (aBorderPadding.BStartEnd(aWM) +
  1182. GetRowSpacing(-1) + GetRowSpacing(GetRowCount()));
  1183. int32_t colIdx = 0;
  1184. LogicalPoint colGroupOrigin(aWM,
  1185. aBorderPadding.IStart(aWM) + GetColSpacing(-1),
  1186. aBorderPadding.BStart(aWM) + GetRowSpacing(-1));
  1187. nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
  1188. for (nsIFrame* colGroupFrame : mColGroups) {
  1189. MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame);
  1190. // first we need to figure out the size of the colgroup
  1191. int32_t groupFirstCol = colIdx;
  1192. nscoord colGroupISize = 0;
  1193. nscoord cellSpacingI = 0;
  1194. const nsFrameList& columnList = colGroupFrame->PrincipalChildList();
  1195. for (nsIFrame* colFrame : columnList) {
  1196. if (mozilla::StyleDisplay::TableColumn ==
  1197. colFrame->StyleDisplay()->mDisplay) {
  1198. NS_ASSERTION(colIdx < GetColCount(), "invalid number of columns");
  1199. cellSpacingI = GetColSpacing(colIdx);
  1200. colGroupISize += fif->GetColumnISizeFromFirstInFlow(colIdx) +
  1201. cellSpacingI;
  1202. ++colIdx;
  1203. }
  1204. }
  1205. if (colGroupISize) {
  1206. colGroupISize -= cellSpacingI;
  1207. }
  1208. LogicalRect colGroupRect(aWM, colGroupOrigin.I(aWM), colGroupOrigin.B(aWM),
  1209. colGroupISize, colBSize);
  1210. colGroupFrame->SetRect(aWM, colGroupRect, aContainerSize);
  1211. nsSize colGroupSize = colGroupFrame->GetSize();
  1212. // then we can place the columns correctly within the group
  1213. colIdx = groupFirstCol;
  1214. LogicalPoint colOrigin(aWM);
  1215. for (nsIFrame* colFrame : columnList) {
  1216. if (mozilla::StyleDisplay::TableColumn ==
  1217. colFrame->StyleDisplay()->mDisplay) {
  1218. nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
  1219. LogicalRect colRect(aWM, colOrigin.I(aWM), colOrigin.B(aWM),
  1220. colISize, colBSize);
  1221. colFrame->SetRect(aWM, colRect, colGroupSize);
  1222. cellSpacingI = GetColSpacing(colIdx);
  1223. colOrigin.I(aWM) += colISize + cellSpacingI;
  1224. ++colIdx;
  1225. }
  1226. }
  1227. colGroupOrigin.I(aWM) += colGroupISize + cellSpacingI;
  1228. }
  1229. }
  1230. // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
  1231. // XXX this could be made more general to handle row modifications that change the
  1232. // table bsize, but first we need to scrutinize every Invalidate
  1233. void
  1234. nsTableFrame::ProcessRowInserted(nscoord aNewBSize)
  1235. {
  1236. SetRowInserted(false); // reset the bit that got us here
  1237. nsTableFrame::RowGroupArray rowGroups;
  1238. OrderRowGroups(rowGroups);
  1239. // find the row group containing the inserted row
  1240. for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  1241. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  1242. NS_ASSERTION(rgFrame, "Must have rgFrame here");
  1243. // find the row that was inserted first
  1244. for (nsIFrame* childFrame : rgFrame->PrincipalChildList()) {
  1245. nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
  1246. if (rowFrame) {
  1247. if (rowFrame->IsFirstInserted()) {
  1248. rowFrame->SetFirstInserted(false);
  1249. // damage the table from the 1st row inserted to the end of the table
  1250. nsIFrame::InvalidateFrame();
  1251. // XXXbz didn't we do this up front? Why do we need to do it again?
  1252. SetRowInserted(false);
  1253. return; // found it, so leave
  1254. }
  1255. }
  1256. }
  1257. }
  1258. }
  1259. /* virtual */ void
  1260. nsTableFrame::MarkIntrinsicISizesDirty()
  1261. {
  1262. nsITableLayoutStrategy* tls = LayoutStrategy();
  1263. if (MOZ_UNLIKELY(!tls)) {
  1264. // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
  1265. // walking up the ancestor chain in a table next-in-flow. In this case
  1266. // our original first-in-flow (which owns the TableLayoutStrategy) has
  1267. // already been destroyed and unhooked from the flow chain and thusly
  1268. // LayoutStrategy() returns null. All the frames in the flow will be
  1269. // destroyed so no need to mark anything dirty here. See bug 595758.
  1270. return;
  1271. }
  1272. tls->MarkIntrinsicISizesDirty();
  1273. // XXXldb Call SetBCDamageArea?
  1274. nsContainerFrame::MarkIntrinsicISizesDirty();
  1275. }
  1276. /* virtual */ nscoord
  1277. nsTableFrame::GetMinISize(nsRenderingContext *aRenderingContext)
  1278. {
  1279. if (NeedToCalcBCBorders())
  1280. CalcBCBorders();
  1281. ReflowColGroups(aRenderingContext);
  1282. return LayoutStrategy()->GetMinISize(aRenderingContext);
  1283. }
  1284. /* virtual */ nscoord
  1285. nsTableFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
  1286. {
  1287. if (NeedToCalcBCBorders())
  1288. CalcBCBorders();
  1289. ReflowColGroups(aRenderingContext);
  1290. return LayoutStrategy()->GetPrefISize(aRenderingContext, false);
  1291. }
  1292. /* virtual */ nsIFrame::IntrinsicISizeOffsetData
  1293. nsTableFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis)
  1294. {
  1295. IntrinsicISizeOffsetData result =
  1296. nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
  1297. result.hMargin = 0;
  1298. if (IsBorderCollapse()) {
  1299. result.hPadding = 0;
  1300. WritingMode wm = GetWritingMode();
  1301. LogicalMargin outerBC = GetIncludedOuterBCBorder(wm);
  1302. result.hBorder = outerBC.IStartEnd(wm);
  1303. }
  1304. return result;
  1305. }
  1306. /* virtual */
  1307. LogicalSize
  1308. nsTableFrame::ComputeSize(nsRenderingContext* aRenderingContext,
  1309. WritingMode aWM,
  1310. const LogicalSize& aCBSize,
  1311. nscoord aAvailableISize,
  1312. const LogicalSize& aMargin,
  1313. const LogicalSize& aBorder,
  1314. const LogicalSize& aPadding,
  1315. ComputeSizeFlags aFlags)
  1316. {
  1317. LogicalSize result =
  1318. nsContainerFrame::ComputeSize(aRenderingContext, aWM,
  1319. aCBSize, aAvailableISize,
  1320. aMargin, aBorder, aPadding, aFlags);
  1321. // XXX The code below doesn't make sense if the caller's writing mode
  1322. // is orthogonal to this frame's. Not sure yet what should happen then;
  1323. // for now, just bail out.
  1324. if (aWM.IsVertical() != GetWritingMode().IsVertical()) {
  1325. return result;
  1326. }
  1327. // If we're a container for font size inflation, then shrink
  1328. // wrapping inside of us should not apply font size inflation.
  1329. AutoMaybeDisableFontInflation an(this);
  1330. // Tables never shrink below their min inline-size.
  1331. nscoord minISize = GetMinISize(aRenderingContext);
  1332. if (minISize > result.ISize(aWM)) {
  1333. result.ISize(aWM) = minISize;
  1334. }
  1335. return result;
  1336. }
  1337. nscoord
  1338. nsTableFrame::TableShrinkISizeToFit(nsRenderingContext *aRenderingContext,
  1339. nscoord aISizeInCB)
  1340. {
  1341. // If we're a container for font size inflation, then shrink
  1342. // wrapping inside of us should not apply font size inflation.
  1343. AutoMaybeDisableFontInflation an(this);
  1344. nscoord result;
  1345. nscoord minISize = GetMinISize(aRenderingContext);
  1346. if (minISize > aISizeInCB) {
  1347. result = minISize;
  1348. } else {
  1349. // Tables shrink inline-size to fit with a slightly different algorithm
  1350. // from the one they use for their intrinsic isize (the difference
  1351. // relates to handling of percentage isizes on columns). So this
  1352. // function differs from nsFrame::ShrinkWidthToFit by only the
  1353. // following line.
  1354. // Since we've already called GetMinISize, we don't need to do any
  1355. // of the other stuff GetPrefISize does.
  1356. nscoord prefISize =
  1357. LayoutStrategy()->GetPrefISize(aRenderingContext, true);
  1358. if (prefISize > aISizeInCB) {
  1359. result = aISizeInCB;
  1360. } else {
  1361. result = prefISize;
  1362. }
  1363. }
  1364. return result;
  1365. }
  1366. /* virtual */
  1367. LogicalSize
  1368. nsTableFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
  1369. WritingMode aWM,
  1370. const LogicalSize& aCBSize,
  1371. nscoord aAvailableISize,
  1372. const LogicalSize& aMargin,
  1373. const LogicalSize& aBorder,
  1374. const LogicalSize& aPadding,
  1375. ComputeSizeFlags aFlags)
  1376. {
  1377. // Tables always shrink-wrap.
  1378. nscoord cbBased = aAvailableISize - aMargin.ISize(aWM) - aBorder.ISize(aWM) -
  1379. aPadding.ISize(aWM);
  1380. return LogicalSize(aWM, TableShrinkISizeToFit(aRenderingContext, cbBased),
  1381. NS_UNCONSTRAINEDSIZE);
  1382. }
  1383. // Return true if aParentReflowInput.frame or any of its ancestors within
  1384. // the containing table have non-auto bsize. (e.g. pct or fixed bsize)
  1385. bool
  1386. nsTableFrame::AncestorsHaveStyleBSize(const ReflowInput& aParentReflowInput)
  1387. {
  1388. WritingMode wm = aParentReflowInput.GetWritingMode();
  1389. for (const ReflowInput* rs = &aParentReflowInput;
  1390. rs && rs->mFrame; rs = rs->mParentReflowInput) {
  1391. nsIAtom* frameType = rs->mFrame->GetType();
  1392. if (IS_TABLE_CELL(frameType) ||
  1393. (nsGkAtoms::tableRowFrame == frameType) ||
  1394. (nsGkAtoms::tableRowGroupFrame == frameType)) {
  1395. const nsStyleCoord &bsize = rs->mStylePosition->BSize(wm);
  1396. // calc() with percentages treated like 'auto' on internal table elements
  1397. if (bsize.GetUnit() != eStyleUnit_Auto &&
  1398. (!bsize.IsCalcUnit() || !bsize.HasPercent())) {
  1399. return true;
  1400. }
  1401. }
  1402. else if (nsGkAtoms::tableFrame == frameType) {
  1403. // we reached the containing table, so always return
  1404. return rs->mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto;
  1405. }
  1406. }
  1407. return false;
  1408. }
  1409. // See if a special block-size reflow needs to occur and if so,
  1410. // call RequestSpecialBSizeReflow
  1411. void
  1412. nsTableFrame::CheckRequestSpecialBSizeReflow(const ReflowInput& aReflowInput)
  1413. {
  1414. NS_ASSERTION(IS_TABLE_CELL(aReflowInput.mFrame->GetType()) ||
  1415. aReflowInput.mFrame->GetType() == nsGkAtoms::tableRowFrame ||
  1416. aReflowInput.mFrame->GetType() == nsGkAtoms::tableRowGroupFrame ||
  1417. aReflowInput.mFrame->GetType() == nsGkAtoms::tableFrame,
  1418. "unexpected frame type");
  1419. WritingMode wm = aReflowInput.GetWritingMode();
  1420. if (!aReflowInput.mFrame->GetPrevInFlow() && // 1st in flow
  1421. (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedBSize() || // no computed bsize
  1422. 0 == aReflowInput.ComputedBSize()) &&
  1423. eStyleUnit_Percent == aReflowInput.mStylePosition->BSize(wm).GetUnit() && // pct bsize
  1424. nsTableFrame::AncestorsHaveStyleBSize(*aReflowInput.mParentReflowInput)) {
  1425. nsTableFrame::RequestSpecialBSizeReflow(aReflowInput);
  1426. }
  1427. }
  1428. // Notify the frame and its ancestors (up to the containing table) that a special
  1429. // bsize reflow will occur. During a special bsize reflow, a table, row group,
  1430. // row, or cell returns the last size it was reflowed at. However, the table may
  1431. // change the bsize of row groups, rows, cells in DistributeBSizeToRows after.
  1432. // And the row group can change the bsize of rows, cells in CalculateRowBSizes.
  1433. void
  1434. nsTableFrame::RequestSpecialBSizeReflow(const ReflowInput& aReflowInput)
  1435. {
  1436. // notify the frame and its ancestors of the special reflow, stopping at the containing table
  1437. for (const ReflowInput* rs = &aReflowInput; rs && rs->mFrame; rs = rs->mParentReflowInput) {
  1438. nsIAtom* frameType = rs->mFrame->GetType();
  1439. NS_ASSERTION(IS_TABLE_CELL(frameType) ||
  1440. nsGkAtoms::tableRowFrame == frameType ||
  1441. nsGkAtoms::tableRowGroupFrame == frameType ||
  1442. nsGkAtoms::tableFrame == frameType,
  1443. "unexpected frame type");
  1444. rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
  1445. if (nsGkAtoms::tableFrame == frameType) {
  1446. NS_ASSERTION(rs != &aReflowInput,
  1447. "should not request special bsize reflow for table");
  1448. // always stop when we reach a table
  1449. break;
  1450. }
  1451. }
  1452. }
  1453. /******************************************************************************************
  1454. * Before reflow, intrinsic inline-size calculation is done using GetMinISize
  1455. * and GetPrefISize. This used to be known as pass 1 reflow.
  1456. *
  1457. * After the intrinsic isize calculation, the table determines the
  1458. * column widths using BalanceColumnISizes() and
  1459. * then reflows each child again with a constrained avail isize. This reflow is referred to
  1460. * as the pass 2 reflow.
  1461. *
  1462. * A special bsize reflow (pass 3 reflow) can occur during an initial or resize reflow
  1463. * if (a) a row group, row, cell, or a frame inside a cell has a percent bsize but no computed
  1464. * bsize or (b) in paginated mode, a table has a bsize. (a) supports percent nested tables
  1465. * contained inside cells whose bsizes aren't known until after the pass 2 reflow. (b) is
  1466. * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
  1467. * the special bsize reflow (variety a) are as follows:
  1468. *
  1469. * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
  1470. * to indicate that it should get the reflow. It does this when it has a percent bsize but
  1471. * no computed bsize by calling CheckRequestSpecialBSizeReflow(). This method calls
  1472. * RequestSpecialBSizeReflow() which calls SetNeedSpecialReflow() on its ancestors until
  1473. * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
  1474. * percent bsize frames inside cells, during DidReflow(), the cell's NotifyPercentBSize()
  1475. * is called (the cell is the reflow state's mPercentBSizeObserver in this case).
  1476. * NotifyPercentBSize() calls RequestSpecialBSizeReflow().
  1477. *
  1478. * XXX (jfkthame) This comment appears to be out of date; it refers to methods/flags
  1479. * that are no longer present in the code.
  1480. * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
  1481. * will do the special bsize reflow, setting the reflow state's mFlags.mSpecialBSizeReflow
  1482. * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
  1483. * returns true because in that case another special bsize reflow will be coming along with the
  1484. * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
  1485. * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
  1486. * appropriate bsizes will not be known.
  1487. *
  1488. * 3) Since the bsizes of the table, row groups, rows, and cells was determined during the pass 2
  1489. * reflow, they return their last desired sizes during the special bsize reflow. The reflow only
  1490. * permits percent bsize frames inside the cells to resize based on the cells bsize and that bsize
  1491. * was determined during the pass 2 reflow.
  1492. *
  1493. * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
  1494. * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
  1495. * until the current initiator is its containing table. Since these reflows are only received by
  1496. * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
  1497. *
  1498. * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
  1499. * that all frames will receive the reflow even if they don't really need them.
  1500. *
  1501. * Open issues with the special bsize reflow:
  1502. *
  1503. * 1) At some point there should be 2 kinds of special bsize reflows because (a) and (b) above are
  1504. * really quite different. This would avoid unnecessary reflows during printing.
  1505. * 2) When a cell contains frames whose percent bsizes > 100%, there is data loss (see bug 115245).
  1506. * However, this can also occur if a cell has a fixed bsize and there is no special bsize reflow.
  1507. *
  1508. * XXXldb Special bsize reflow should really be its own method, not
  1509. * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on
  1510. * the contents of the cells to do the necessary block-axis resizing.
  1511. *
  1512. ******************************************************************************************/
  1513. /* Layout the entire inner table. */
  1514. void
  1515. nsTableFrame::Reflow(nsPresContext* aPresContext,
  1516. ReflowOutput& aDesiredSize,
  1517. const ReflowInput& aReflowInput,
  1518. nsReflowStatus& aStatus)
  1519. {
  1520. MarkInReflow();
  1521. DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
  1522. DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  1523. bool isPaginated = aPresContext->IsPaginated();
  1524. WritingMode wm = aReflowInput.GetWritingMode();
  1525. aStatus = NS_FRAME_COMPLETE;
  1526. if (!GetPrevInFlow() && !mTableLayoutStrategy) {
  1527. NS_ERROR("strategy should have been created in Init");
  1528. return;
  1529. }
  1530. // see if collapsing borders need to be calculated
  1531. if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
  1532. CalcBCBorders();
  1533. }
  1534. aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
  1535. // Check for an overflow list, and append any row group frames being pushed
  1536. MoveOverflowToChildList();
  1537. bool haveDesiredBSize = false;
  1538. SetHaveReflowedColGroups(false);
  1539. // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
  1540. // constrained initial reflow and other reflows which require either a strategy init or balance.
  1541. // This isn't done during an unconstrained reflow, because it will occur later when the parent
  1542. // reflows with a constrained isize.
  1543. bool fixupKidPositions = false;
  1544. if (NS_SUBTREE_DIRTY(this) ||
  1545. aReflowInput.ShouldReflowAllKids() ||
  1546. IsGeometryDirty() ||
  1547. aReflowInput.IsBResize()) {
  1548. if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
  1549. // Also check IsBResize(), to handle the first Reflow preceding a
  1550. // special bsize Reflow, when we've already had a special bsize
  1551. // Reflow (where ComputedBSize() would not be
  1552. // NS_UNCONSTRAINEDSIZE, but without a style change in between).
  1553. aReflowInput.IsBResize()) {
  1554. // XXX Eventually, we should modify DistributeBSizeToRows to use
  1555. // nsTableRowFrame::GetInitialBSize instead of nsIFrame::BSize().
  1556. // That way, it will make its calculations based on internal table
  1557. // frame bsizes as they are before they ever had any extra bsize
  1558. // distributed to them. In the meantime, this reflows all the
  1559. // internal table frames, which restores them to their state before
  1560. // DistributeBSizeToRows was called.
  1561. SetGeometryDirty();
  1562. }
  1563. bool needToInitiateSpecialReflow =
  1564. HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
  1565. // see if an extra reflow will be necessary in pagination mode
  1566. // when there is a specified table bsize
  1567. if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize())) {
  1568. nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
  1569. if ((tableSpecifiedBSize > 0) &&
  1570. (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE)) {
  1571. needToInitiateSpecialReflow = true;
  1572. }
  1573. }
  1574. nsIFrame* lastChildReflowed = nullptr;
  1575. NS_ASSERTION(!aReflowInput.mFlags.mSpecialBSizeReflow,
  1576. "Shouldn't be in special bsize reflow here!");
  1577. // do the pass 2 reflow unless this is a special bsize reflow and we will be
  1578. // initiating a special bsize reflow
  1579. // XXXldb I changed this. Should I change it back?
  1580. // if we need to initiate a special bsize reflow, then don't constrain the
  1581. // bsize of the reflow before that
  1582. nscoord availBSize = needToInitiateSpecialReflow
  1583. ? NS_UNCONSTRAINEDSIZE
  1584. : aReflowInput.AvailableBSize();
  1585. ReflowTable(aDesiredSize, aReflowInput, availBSize,
  1586. lastChildReflowed, aStatus);
  1587. // If ComputedWidth is unconstrained, we may need to fix child positions
  1588. // later (in vertical-rl mode) due to use of 0 as a dummy
  1589. // containerSize.width during ReflowChildren.
  1590. fixupKidPositions = wm.IsVerticalRL() &&
  1591. aReflowInput.ComputedWidth() == NS_UNCONSTRAINEDSIZE;
  1592. // reevaluate special bsize reflow conditions
  1593. if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
  1594. needToInitiateSpecialReflow = true;
  1595. }
  1596. // XXXldb Are all these conditions correct?
  1597. if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) {
  1598. // XXXldb Do we need to set the IsBResize flag on any reflow states?
  1599. ReflowInput &mutable_rs =
  1600. const_cast<ReflowInput&>(aReflowInput);
  1601. // distribute extra block-direction space to rows
  1602. CalcDesiredBSize(aReflowInput, aDesiredSize);
  1603. mutable_rs.mFlags.mSpecialBSizeReflow = true;
  1604. ReflowTable(aDesiredSize, aReflowInput, aReflowInput.AvailableBSize(),
  1605. lastChildReflowed, aStatus);
  1606. if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
  1607. // if there is an incomplete child, then set the desired bsize
  1608. // to include it but not the next one
  1609. LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
  1610. aDesiredSize.BSize(wm) =
  1611. borderPadding.BEnd(wm) + GetRowSpacing(GetRowCount()) +
  1612. lastChildReflowed->GetNormalRect().YMost(); // XXX YMost should be B-flavored
  1613. }
  1614. haveDesiredBSize = true;
  1615. mutable_rs.mFlags.mSpecialBSizeReflow = false;
  1616. }
  1617. }
  1618. aDesiredSize.ISize(wm) = aReflowInput.ComputedISize() +
  1619. aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
  1620. if (!haveDesiredBSize) {
  1621. CalcDesiredBSize(aReflowInput, aDesiredSize);
  1622. }
  1623. if (IsRowInserted()) {
  1624. ProcessRowInserted(aDesiredSize.BSize(wm));
  1625. }
  1626. if (fixupKidPositions) {
  1627. // If we didn't already know the containerSize (and so used zero during
  1628. // ReflowChildren), then we need to update the block-position of our kids.
  1629. for (nsIFrame* kid : mFrames) {
  1630. kid->MovePositionBy(nsPoint(aDesiredSize.Width(), 0));
  1631. RePositionViews(kid);
  1632. }
  1633. }
  1634. // Calculate the overflow area contribution from our children. We couldn't
  1635. // do this on the fly during ReflowChildren(), because in vertical-rl mode
  1636. // with unconstrained width, we weren't placing them in their final positions
  1637. // until the fixupKidPositions loop just above.
  1638. for (nsIFrame* kid : mFrames) {
  1639. ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
  1640. }
  1641. LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
  1642. SetColumnDimensions(aDesiredSize.BSize(wm), wm, borderPadding,
  1643. aDesiredSize.PhysicalSize());
  1644. if (NeedToCollapse() &&
  1645. (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableISize())) {
  1646. AdjustForCollapsingRowsCols(aDesiredSize, wm, borderPadding);
  1647. }
  1648. // If there are any relatively-positioned table parts, we need to reflow their
  1649. // absolutely-positioned descendants now that their dimensions are final.
  1650. FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowInput);
  1651. // make sure the table overflow area does include the table rect.
  1652. nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
  1653. if (!ShouldApplyOverflowClipping(this, aReflowInput.mStyleDisplay)) {
  1654. // collapsed border may leak out
  1655. LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
  1656. tableRect.Inflate(bcMargin.GetPhysicalMargin(wm));
  1657. }
  1658. aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
  1659. if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW) ||
  1660. nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
  1661. nsIFrame::InvalidateFrame();
  1662. }
  1663. FinishAndStoreOverflow(&aDesiredSize);
  1664. NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
  1665. }
  1666. void
  1667. nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext,
  1668. ReflowOutput& aDesiredSize,
  1669. const ReflowInput& aReflowInput)
  1670. {
  1671. FrameTArray* positionedParts = GetProperty(PositionedTablePartArray());
  1672. if (!positionedParts) {
  1673. return;
  1674. }
  1675. OverflowChangedTracker overflowTracker;
  1676. overflowTracker.SetSubtreeRoot(this);
  1677. for (size_t i = 0; i < positionedParts->Length(); ++i) {
  1678. nsIFrame* positionedPart = positionedParts->ElementAt(i);
  1679. // As we've already finished reflow, positionedParts's size and overflow
  1680. // areas have already been assigned, so we just pull them back out.
  1681. nsSize size(positionedPart->GetSize());
  1682. ReflowOutput desiredSize(aReflowInput.GetWritingMode());
  1683. desiredSize.Width() = size.width;
  1684. desiredSize.Height() = size.height;
  1685. desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
  1686. // Construct a dummy reflow state and reflow status.
  1687. // XXX(seth): Note that the dummy reflow state doesn't have a correct
  1688. // chain of parent reflow states. It also doesn't necessarily have a
  1689. // correct containing block.
  1690. WritingMode wm = positionedPart->GetWritingMode();
  1691. LogicalSize availSize(wm, size);
  1692. availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
  1693. ReflowInput reflowInput(aPresContext, positionedPart,
  1694. aReflowInput.mRenderingContext, availSize,
  1695. ReflowInput::DUMMY_PARENT_REFLOW_STATE);
  1696. nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
  1697. // Reflow absolutely-positioned descendants of the positioned part.
  1698. // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the bsize and
  1699. // ignoring any change to the reflow status aren't correct. We'll never
  1700. // paginate absolutely positioned frames.
  1701. nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
  1702. positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
  1703. desiredSize,
  1704. reflowInput,
  1705. reflowStatus,
  1706. true);
  1707. // FinishReflowWithAbsoluteFrames has updated overflow on
  1708. // |positionedPart|. We need to make sure that update propagates
  1709. // through the intermediate frames between it and this frame.
  1710. nsIFrame* positionedFrameParent = positionedPart->GetParent();
  1711. if (positionedFrameParent != this) {
  1712. overflowTracker.AddFrame(positionedFrameParent,
  1713. OverflowChangedTracker::CHILDREN_CHANGED);
  1714. }
  1715. }
  1716. // Propagate updated overflow areas up the tree.
  1717. overflowTracker.Flush();
  1718. // Update our own overflow areas. (OverflowChangedTracker doesn't update the
  1719. // subtree root itself.)
  1720. aDesiredSize.SetOverflowAreasToDesiredBounds();
  1721. nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas);
  1722. }
  1723. bool
  1724. nsTableFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
  1725. {
  1726. // As above in Reflow, make sure the table overflow area includes the table
  1727. // rect, and check for collapsed borders leaking out.
  1728. if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
  1729. nsRect bounds(nsPoint(0, 0), GetSize());
  1730. WritingMode wm = GetWritingMode();
  1731. LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
  1732. bounds.Inflate(bcMargin.GetPhysicalMargin(wm));
  1733. aOverflowAreas.UnionAllWith(bounds);
  1734. }
  1735. return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
  1736. }
  1737. void
  1738. nsTableFrame::ReflowTable(ReflowOutput& aDesiredSize,
  1739. const ReflowInput& aReflowInput,
  1740. nscoord aAvailBSize,
  1741. nsIFrame*& aLastChildReflowed,
  1742. nsReflowStatus& aStatus)
  1743. {
  1744. aLastChildReflowed = nullptr;
  1745. if (!GetPrevInFlow()) {
  1746. mTableLayoutStrategy->ComputeColumnISizes(aReflowInput);
  1747. }
  1748. // Constrain our reflow isize to the computed table isize (of the 1st in flow).
  1749. // and our reflow bsize to our avail bsize minus border, padding, cellspacing
  1750. WritingMode wm = aReflowInput.GetWritingMode();
  1751. aDesiredSize.ISize(wm) = aReflowInput.ComputedISize() +
  1752. aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
  1753. TableReflowInput reflowInput(aReflowInput,
  1754. LogicalSize(wm, aDesiredSize.ISize(wm),
  1755. aAvailBSize));
  1756. ReflowChildren(reflowInput, aStatus, aLastChildReflowed,
  1757. aDesiredSize.mOverflowAreas);
  1758. ReflowColGroups(aReflowInput.mRenderingContext);
  1759. }
  1760. nsIFrame*
  1761. nsTableFrame::GetFirstBodyRowGroupFrame()
  1762. {
  1763. nsIFrame* headerFrame = nullptr;
  1764. nsIFrame* footerFrame = nullptr;
  1765. for (nsIFrame* kidFrame : mFrames) {
  1766. const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay();
  1767. // We expect the header and footer row group frames to be first, and we only
  1768. // allow one header and one footer
  1769. if (mozilla::StyleDisplay::TableHeaderGroup == childDisplay->mDisplay) {
  1770. if (headerFrame) {
  1771. // We already have a header frame and so this header frame is treated
  1772. // like an ordinary body row group frame
  1773. return kidFrame;
  1774. }
  1775. headerFrame = kidFrame;
  1776. } else if (mozilla::StyleDisplay::TableFooterGroup == childDisplay->mDisplay) {
  1777. if (footerFrame) {
  1778. // We already have a footer frame and so this footer frame is treated
  1779. // like an ordinary body row group frame
  1780. return kidFrame;
  1781. }
  1782. footerFrame = kidFrame;
  1783. } else if (mozilla::StyleDisplay::TableRowGroup == childDisplay->mDisplay) {
  1784. return kidFrame;
  1785. }
  1786. }
  1787. return nullptr;
  1788. }
  1789. // Table specific version that takes into account repeated header and footer
  1790. // frames when continuing table frames
  1791. void
  1792. nsTableFrame::PushChildren(const RowGroupArray& aRowGroups,
  1793. int32_t aPushFrom)
  1794. {
  1795. NS_PRECONDITION(aPushFrom > 0, "pushing first child");
  1796. // extract the frames from the array into a sibling list
  1797. nsFrameList frames;
  1798. uint32_t childX;
  1799. for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
  1800. nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
  1801. if (!rgFrame->IsRepeatable()) {
  1802. mFrames.RemoveFrame(rgFrame);
  1803. frames.AppendFrame(nullptr, rgFrame);
  1804. }
  1805. }
  1806. if (frames.IsEmpty()) {
  1807. return;
  1808. }
  1809. nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
  1810. if (nextInFlow) {
  1811. // Insert the frames after any repeated header and footer frames.
  1812. nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
  1813. nsIFrame* prevSibling = nullptr;
  1814. if (firstBodyFrame) {
  1815. prevSibling = firstBodyFrame->GetPrevSibling();
  1816. }
  1817. // When pushing and pulling frames we need to check for whether any
  1818. // views need to be reparented.
  1819. ReparentFrameViewList(frames, this, nextInFlow);
  1820. nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling,
  1821. frames);
  1822. }
  1823. else {
  1824. // Add the frames to our overflow list.
  1825. SetOverflowFrames(frames);
  1826. }
  1827. }
  1828. // collapsing row groups, rows, col groups and cols are accounted for after both passes of
  1829. // reflow so that it has no effect on the calculations of reflow.
  1830. void
  1831. nsTableFrame::AdjustForCollapsingRowsCols(ReflowOutput& aDesiredSize,
  1832. const WritingMode aWM,
  1833. const LogicalMargin& aBorderPadding)
  1834. {
  1835. nscoord bTotalOffset = 0; // total offset among all rows in all row groups
  1836. // reset the bit, it will be set again if row/rowgroup or col/colgroup are
  1837. // collapsed
  1838. SetNeedToCollapse(false);
  1839. // collapse the rows and/or row groups as necessary
  1840. // Get the ordered children
  1841. RowGroupArray rowGroups;
  1842. OrderRowGroups(rowGroups);
  1843. nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
  1844. nscoord iSize = firstInFlow->GetCollapsedISize(aWM, aBorderPadding);
  1845. nscoord rgISize = iSize - GetColSpacing(-1) -
  1846. GetColSpacing(GetColCount());
  1847. nsOverflowAreas overflow;
  1848. // Walk the list of children
  1849. for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
  1850. nsTableRowGroupFrame* rgFrame = rowGroups[childX];
  1851. NS_ASSERTION(rgFrame, "Must have row group frame here");
  1852. bTotalOffset += rgFrame->CollapseRowGroupIfNecessary(bTotalOffset, rgISize,
  1853. aWM);
  1854. ConsiderChildOverflow(overflow, rgFrame);
  1855. }
  1856. aDesiredSize.BSize(aWM) -= bTotalOffset;
  1857. aDesiredSize.ISize(aWM) = iSize;
  1858. overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
  1859. FinishAndStoreOverflow(overflow,
  1860. nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
  1861. }
  1862. nscoord
  1863. nsTableFrame::GetCollapsedISize(const WritingMode aWM,
  1864. const LogicalMargin& aBorderPadding)
  1865. {
  1866. NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedISize called on next in flow");
  1867. nscoord iSize = GetColSpacing(GetColCount());
  1868. iSize += aBorderPadding.IStartEnd(aWM);
  1869. nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
  1870. for (nsIFrame* groupFrame : mColGroups) {
  1871. const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
  1872. bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
  1873. nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
  1874. for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
  1875. colFrame = colFrame->GetNextCol()) {
  1876. const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
  1877. nscoord colIdx = colFrame->GetColIndex();
  1878. if (mozilla::StyleDisplay::TableColumn == colDisplay->mDisplay) {
  1879. const nsStyleVisibility* colVis = colFrame->StyleVisibility();
  1880. bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
  1881. nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
  1882. if (!collapseGroup && !collapseCol) {
  1883. iSize += colISize;
  1884. if (ColumnHasCellSpacingBefore(colIdx)) {
  1885. iSize += GetColSpacing(colIdx - 1);
  1886. }
  1887. }
  1888. else {
  1889. SetNeedToCollapse(true);
  1890. }
  1891. }
  1892. }
  1893. }
  1894. return iSize;
  1895. }
  1896. /* virtual */ void
  1897. nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  1898. {
  1899. nsContainerFrame::DidSetStyleContext(aOldStyleContext);
  1900. if (!aOldStyleContext) //avoid this on init
  1901. return;
  1902. if (IsBorderCollapse() &&
  1903. BCRecalcNeeded(aOldStyleContext, StyleContext())) {
  1904. SetFullBCDamageArea();
  1905. }
  1906. //avoid this on init or nextinflow
  1907. if (!mTableLayoutStrategy || GetPrevInFlow())
  1908. return;
  1909. bool isAuto = IsAutoLayout();
  1910. if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
  1911. nsITableLayoutStrategy* temp;
  1912. if (isAuto)
  1913. temp = new BasicTableLayoutStrategy(this);
  1914. else
  1915. temp = new FixedTableLayoutStrategy(this);
  1916. if (temp) {
  1917. delete mTableLayoutStrategy;
  1918. mTableLayoutStrategy = temp;
  1919. }
  1920. }
  1921. }
  1922. void
  1923. nsTableFrame::AppendFrames(ChildListID aListID,
  1924. nsFrameList& aFrameList)
  1925. {
  1926. NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
  1927. "unexpected child list");
  1928. // Because we actually have two child lists, one for col group frames and one
  1929. // for everything else, we need to look at each frame individually
  1930. // XXX The frame construction code should be separating out child frames
  1931. // based on the type, bug 343048.
  1932. while (!aFrameList.IsEmpty()) {
  1933. nsIFrame* f = aFrameList.FirstChild();
  1934. aFrameList.RemoveFrame(f);
  1935. // See what kind of frame we have
  1936. const nsStyleDisplay* display = f->StyleDisplay();
  1937. if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
  1938. if (MOZ_UNLIKELY(GetPrevInFlow())) {
  1939. nsFrameList colgroupFrame(f, f);
  1940. auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
  1941. firstInFlow->AppendFrames(aListID, colgroupFrame);
  1942. continue;
  1943. }
  1944. nsTableColGroupFrame* lastColGroup =
  1945. nsTableColGroupFrame::GetLastRealColGroup(this);
  1946. int32_t startColIndex = (lastColGroup)
  1947. ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
  1948. mColGroups.InsertFrame(this, lastColGroup, f);
  1949. // Insert the colgroup and its cols into the table
  1950. InsertColGroups(startColIndex,
  1951. nsFrameList::Slice(mColGroups, f, f->GetNextSibling()));
  1952. } else if (IsRowGroup(display->mDisplay)) {
  1953. DrainSelfOverflowList(); // ensure the last frame is in mFrames
  1954. // Append the new row group frame to the sibling chain
  1955. mFrames.AppendFrame(nullptr, f);
  1956. // insert the row group and its rows into the table
  1957. InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr));
  1958. } else {
  1959. // Nothing special to do, just add the frame to our child list
  1960. NS_NOTREACHED("How did we get here? Frame construction screwed up");
  1961. mFrames.AppendFrame(nullptr, f);
  1962. }
  1963. }
  1964. #ifdef DEBUG_TABLE_CELLMAP
  1965. printf("=== TableFrame::AppendFrames\n");
  1966. Dump(true, true, true);
  1967. #endif
  1968. PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1969. NS_FRAME_HAS_DIRTY_CHILDREN);
  1970. SetGeometryDirty();
  1971. }
  1972. // Needs to be at file scope or ArrayLength fails to compile.
  1973. struct ChildListInsertions {
  1974. nsIFrame::ChildListID mID;
  1975. nsFrameList mList;
  1976. };
  1977. void
  1978. nsTableFrame::InsertFrames(ChildListID aListID,
  1979. nsIFrame* aPrevFrame,
  1980. nsFrameList& aFrameList)
  1981. {
  1982. // The frames in aFrameList can be a mix of row group frames and col group
  1983. // frames. The problem is that they should go in separate child lists so
  1984. // we need to deal with that here...
  1985. // XXX The frame construction code should be separating out child frames
  1986. // based on the type, bug 343048.
  1987. NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
  1988. "inserting after sibling frame with different parent");
  1989. if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
  1990. (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
  1991. // Treat this like an append; still a workaround for bug 343048.
  1992. AppendFrames(aListID, aFrameList);
  1993. return;
  1994. }
  1995. // Collect ColGroupFrames into a separate list and insert those separately
  1996. // from the other frames (bug 759249).
  1997. ChildListInsertions insertions[2]; // ColGroup, other
  1998. const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
  1999. nsFrameList::FrameLinkEnumerator e(aFrameList);
  2000. for (; !aFrameList.IsEmpty(); e.Next()) {
  2001. nsIFrame* next = e.NextFrame();
  2002. if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) {
  2003. nsFrameList head = aFrameList.ExtractHead(e);
  2004. if (display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) {
  2005. insertions[0].mID = kColGroupList;
  2006. insertions[0].mList.AppendFrames(nullptr, head);
  2007. } else {
  2008. insertions[1].mID = kPrincipalList;
  2009. insertions[1].mList.AppendFrames(nullptr, head);
  2010. }
  2011. if (!next) {
  2012. break;
  2013. }
  2014. display = next->StyleDisplay();
  2015. }
  2016. }
  2017. for (uint32_t i = 0; i < ArrayLength(insertions); ++i) {
  2018. // We pass aPrevFrame for both ColGroup and other frames since
  2019. // HomogenousInsertFrames will only use it if it's a suitable
  2020. // prev-sibling for the frames in the frame list.
  2021. if (!insertions[i].mList.IsEmpty()) {
  2022. HomogenousInsertFrames(insertions[i].mID, aPrevFrame,
  2023. insertions[i].mList);
  2024. }
  2025. }
  2026. }
  2027. void
  2028. nsTableFrame::HomogenousInsertFrames(ChildListID aListID,
  2029. nsIFrame* aPrevFrame,
  2030. nsFrameList& aFrameList)
  2031. {
  2032. // See what kind of frame we have
  2033. const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
  2034. bool isColGroup = mozilla::StyleDisplay::TableColumnGroup == display->mDisplay;
  2035. #ifdef DEBUG
  2036. // Verify that either all siblings have display:table-column-group, or they
  2037. // all have display values different from table-column-group.
  2038. for (nsIFrame* frame : aFrameList) {
  2039. auto nextDisplay = frame->StyleDisplay()->mDisplay;
  2040. MOZ_ASSERT(isColGroup ==
  2041. (nextDisplay == mozilla::StyleDisplay::TableColumnGroup),
  2042. "heterogenous childlist");
  2043. }
  2044. #endif
  2045. if (MOZ_UNLIKELY(isColGroup && GetPrevInFlow())) {
  2046. auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
  2047. firstInFlow->AppendFrames(aListID, aFrameList);
  2048. return;
  2049. }
  2050. if (aPrevFrame) {
  2051. const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay();
  2052. // Make sure they belong on the same frame list
  2053. if ((display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) !=
  2054. (prevDisplay->mDisplay == mozilla::StyleDisplay::TableColumnGroup)) {
  2055. // the previous frame is not valid, see comment at ::AppendFrames
  2056. // XXXbz Using content indices here means XBL will get screwed
  2057. // over... Oh, well.
  2058. nsIFrame* pseudoFrame = aFrameList.FirstChild();
  2059. nsIContent* parentContent = GetContent();
  2060. nsIContent* content = nullptr;
  2061. aPrevFrame = nullptr;
  2062. while (pseudoFrame && (parentContent ==
  2063. (content = pseudoFrame->GetContent()))) {
  2064. pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
  2065. }
  2066. nsCOMPtr<nsIContent> container = content->GetParent();
  2067. if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823.
  2068. int32_t newIndex = container->IndexOf(content);
  2069. nsIFrame* kidFrame;
  2070. nsTableColGroupFrame* lastColGroup = nullptr;
  2071. if (isColGroup) {
  2072. kidFrame = mColGroups.FirstChild();
  2073. lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
  2074. }
  2075. else {
  2076. kidFrame = mFrames.FirstChild();
  2077. }
  2078. // Important: need to start at a value smaller than all valid indices
  2079. int32_t lastIndex = -1;
  2080. while (kidFrame) {
  2081. if (isColGroup) {
  2082. if (kidFrame == lastColGroup) {
  2083. aPrevFrame = kidFrame; // there is no real colgroup after this one
  2084. break;
  2085. }
  2086. }
  2087. pseudoFrame = kidFrame;
  2088. while (pseudoFrame && (parentContent ==
  2089. (content = pseudoFrame->GetContent()))) {
  2090. pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
  2091. }
  2092. int32_t index = container->IndexOf(content);
  2093. if (index > lastIndex && index < newIndex) {
  2094. lastIndex = index;
  2095. aPrevFrame = kidFrame;
  2096. }
  2097. kidFrame = kidFrame->GetNextSibling();
  2098. }
  2099. }
  2100. }
  2101. }
  2102. if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
  2103. NS_ASSERTION(aListID == kColGroupList, "unexpected child list");
  2104. // Insert the column group frames
  2105. const nsFrameList::Slice& newColgroups =
  2106. mColGroups.InsertFrames(this, aPrevFrame, aFrameList);
  2107. // find the starting col index for the first new col group
  2108. int32_t startColIndex = 0;
  2109. if (aPrevFrame) {
  2110. nsTableColGroupFrame* prevColGroup =
  2111. (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
  2112. nsGkAtoms::tableColGroupFrame);
  2113. if (prevColGroup) {
  2114. startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
  2115. }
  2116. }
  2117. InsertColGroups(startColIndex, newColgroups);
  2118. } else if (IsRowGroup(display->mDisplay)) {
  2119. NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  2120. DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
  2121. // Insert the frames in the sibling chain
  2122. const nsFrameList::Slice& newRowGroups =
  2123. mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
  2124. InsertRowGroups(newRowGroups);
  2125. } else {
  2126. NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  2127. NS_NOTREACHED("How did we even get here?");
  2128. // Just insert the frame and don't worry about reflowing it
  2129. mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
  2130. return;
  2131. }
  2132. PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  2133. NS_FRAME_HAS_DIRTY_CHILDREN);
  2134. SetGeometryDirty();
  2135. #ifdef DEBUG_TABLE_CELLMAP
  2136. printf("=== TableFrame::InsertFrames\n");
  2137. Dump(true, true, true);
  2138. #endif
  2139. return;
  2140. }
  2141. void
  2142. nsTableFrame::DoRemoveFrame(ChildListID aListID,
  2143. nsIFrame* aOldFrame)
  2144. {
  2145. if (aListID == kColGroupList) {
  2146. nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
  2147. nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
  2148. int32_t firstColIndex = colGroup->GetStartColumnIndex();
  2149. int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1;
  2150. mColGroups.DestroyFrame(aOldFrame);
  2151. nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
  2152. // remove the cols from the table
  2153. int32_t colIdx;
  2154. for (colIdx = lastColIndex; colIdx >= firstColIndex; colIdx--) {
  2155. nsTableColFrame* colFrame = mColFrames.SafeElementAt(colIdx);
  2156. if (colFrame) {
  2157. RemoveCol(colGroup, colIdx, true, false);
  2158. }
  2159. }
  2160. // If we have some anonymous cols at the end already, we just
  2161. // add more of them.
  2162. if (!mColFrames.IsEmpty() &&
  2163. mColFrames.LastElement() && // XXXbz is this ever null?
  2164. mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
  2165. int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
  2166. if (numAnonymousColsToAdd > 0) {
  2167. // this sets the child list, updates the col cache and cell map
  2168. AppendAnonymousColFrames(numAnonymousColsToAdd);
  2169. }
  2170. } else {
  2171. // All of our colframes correspond to actual <col> tags. It's possible
  2172. // that we still have at least as many <col> tags as we have logical
  2173. // columns from cells, but we might have one less. Handle the latter case
  2174. // as follows: First ask the cellmap to drop its last col if it doesn't
  2175. // have any actual cells in it. Then call MatchCellMapToColCache to
  2176. // append an anonymous column if it's needed; this needs to be after
  2177. // RemoveColsAtEnd, since it will determine the need for a new column
  2178. // frame based on the width of the cell map.
  2179. nsTableCellMap* cellMap = GetCellMap();
  2180. if (cellMap) { // XXXbz is this ever null?
  2181. cellMap->RemoveColsAtEnd();
  2182. MatchCellMapToColCache(cellMap);
  2183. }
  2184. }
  2185. } else {
  2186. NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  2187. nsTableRowGroupFrame* rgFrame =
  2188. static_cast<nsTableRowGroupFrame*>(aOldFrame);
  2189. // remove the row group from the cell map
  2190. nsTableCellMap* cellMap = GetCellMap();
  2191. if (cellMap) {
  2192. cellMap->RemoveGroupCellMap(rgFrame);
  2193. }
  2194. // remove the row group frame from the sibling chain
  2195. mFrames.DestroyFrame(aOldFrame);
  2196. // the removal of a row group changes the cellmap, the columns might change
  2197. if (cellMap) {
  2198. cellMap->Synchronize(this);
  2199. // Create an empty slice
  2200. ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
  2201. TableArea damageArea;
  2202. cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea);
  2203. static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap);
  2204. }
  2205. }
  2206. }
  2207. void
  2208. nsTableFrame::RemoveFrame(ChildListID aListID,
  2209. nsIFrame* aOldFrame)
  2210. {
  2211. NS_ASSERTION(aListID == kColGroupList ||
  2212. mozilla::StyleDisplay::TableColumnGroup !=
  2213. aOldFrame->StyleDisplay()->mDisplay,
  2214. "Wrong list name; use kColGroupList iff colgroup");
  2215. nsIPresShell* shell = PresContext()->PresShell();
  2216. nsTableFrame* lastParent = nullptr;
  2217. while (aOldFrame) {
  2218. nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
  2219. nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent());
  2220. if (parent != lastParent) {
  2221. parent->DrainSelfOverflowList();
  2222. }
  2223. parent->DoRemoveFrame(aListID, aOldFrame);
  2224. aOldFrame = oldFrameNextContinuation;
  2225. if (parent != lastParent) {
  2226. // for now, just bail and recalc all of the collapsing borders
  2227. // as the cellmap changes we need to recalc
  2228. if (parent->IsBorderCollapse()) {
  2229. parent->SetFullBCDamageArea();
  2230. }
  2231. parent->SetGeometryDirty();
  2232. shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
  2233. NS_FRAME_HAS_DIRTY_CHILDREN);
  2234. lastParent = parent;
  2235. }
  2236. }
  2237. #ifdef DEBUG_TABLE_CELLMAP
  2238. printf("=== TableFrame::RemoveFrame\n");
  2239. Dump(true, true, true);
  2240. #endif
  2241. }
  2242. /* virtual */ nsMargin
  2243. nsTableFrame::GetUsedBorder() const
  2244. {
  2245. if (!IsBorderCollapse())
  2246. return nsContainerFrame::GetUsedBorder();
  2247. WritingMode wm = GetWritingMode();
  2248. return GetIncludedOuterBCBorder(wm).GetPhysicalMargin(wm);
  2249. }
  2250. /* virtual */ nsMargin
  2251. nsTableFrame::GetUsedPadding() const
  2252. {
  2253. if (!IsBorderCollapse())
  2254. return nsContainerFrame::GetUsedPadding();
  2255. return nsMargin(0,0,0,0);
  2256. }
  2257. /* virtual */ nsMargin
  2258. nsTableFrame::GetUsedMargin() const
  2259. {
  2260. // The margin is inherited to the table wrapper frame via
  2261. // the ::-moz-table-wrapper rule in ua.css.
  2262. return nsMargin(0, 0, 0, 0);
  2263. }
  2264. NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCProperty, BCPropertyData)
  2265. BCPropertyData*
  2266. nsTableFrame::GetBCProperty() const
  2267. {
  2268. return GetProperty(TableBCProperty());
  2269. }
  2270. BCPropertyData*
  2271. nsTableFrame::GetOrCreateBCProperty()
  2272. {
  2273. BCPropertyData* value = GetProperty(TableBCProperty());
  2274. if (!value) {
  2275. value = new BCPropertyData();
  2276. SetProperty(TableBCProperty(), value);
  2277. }
  2278. return value;
  2279. }
  2280. static void
  2281. DivideBCBorderSize(BCPixelSize aPixelSize,
  2282. BCPixelSize& aSmallHalf,
  2283. BCPixelSize& aLargeHalf)
  2284. {
  2285. aSmallHalf = aPixelSize / 2;
  2286. aLargeHalf = aPixelSize - aSmallHalf;
  2287. }
  2288. LogicalMargin
  2289. nsTableFrame::GetOuterBCBorder(const WritingMode aWM) const
  2290. {
  2291. if (NeedToCalcBCBorders()) {
  2292. const_cast<nsTableFrame*>(this)->CalcBCBorders();
  2293. }
  2294. int32_t d2a = PresContext()->AppUnitsPerDevPixel();
  2295. BCPropertyData* propData = GetBCProperty();
  2296. if (propData) {
  2297. return LogicalMargin(aWM,
  2298. BC_BORDER_START_HALF_COORD(d2a, propData->mBStartBorderWidth),
  2299. BC_BORDER_END_HALF_COORD(d2a, propData->mIEndBorderWidth),
  2300. BC_BORDER_END_HALF_COORD(d2a, propData->mBEndBorderWidth),
  2301. BC_BORDER_START_HALF_COORD(d2a, propData->mIStartBorderWidth));
  2302. }
  2303. return LogicalMargin(aWM);
  2304. }
  2305. LogicalMargin
  2306. nsTableFrame::GetIncludedOuterBCBorder(const WritingMode aWM) const
  2307. {
  2308. if (NeedToCalcBCBorders()) {
  2309. const_cast<nsTableFrame*>(this)->CalcBCBorders();
  2310. }
  2311. int32_t d2a = PresContext()->AppUnitsPerDevPixel();
  2312. BCPropertyData* propData = GetBCProperty();
  2313. if (propData) {
  2314. return LogicalMargin(aWM,
  2315. BC_BORDER_START_HALF_COORD(d2a, propData->mBStartBorderWidth),
  2316. BC_BORDER_END_HALF_COORD(d2a, propData->mIEndCellBorderWidth),
  2317. BC_BORDER_END_HALF_COORD(d2a, propData->mBEndBorderWidth),
  2318. BC_BORDER_START_HALF_COORD(d2a, propData->mIStartCellBorderWidth));
  2319. }
  2320. return LogicalMargin(aWM);
  2321. }
  2322. LogicalMargin
  2323. nsTableFrame::GetExcludedOuterBCBorder(const WritingMode aWM) const
  2324. {
  2325. return GetOuterBCBorder(aWM) - GetIncludedOuterBCBorder(aWM);
  2326. }
  2327. static LogicalMargin
  2328. GetSeparateModelBorderPadding(const WritingMode aWM,
  2329. const ReflowInput* aReflowInput,
  2330. nsStyleContext* aStyleContext)
  2331. {
  2332. // XXXbz Either we _do_ have a reflow state and then we can use its
  2333. // mComputedBorderPadding or we don't and then we get the padding
  2334. // wrong!
  2335. const nsStyleBorder* border = aStyleContext->StyleBorder();
  2336. LogicalMargin borderPadding(aWM, border->GetComputedBorder());
  2337. if (aReflowInput) {
  2338. borderPadding += aReflowInput->ComputedLogicalPadding();
  2339. }
  2340. return borderPadding;
  2341. }
  2342. LogicalMargin
  2343. nsTableFrame::GetChildAreaOffset(const WritingMode aWM,
  2344. const ReflowInput* aReflowInput) const
  2345. {
  2346. return IsBorderCollapse() ? GetIncludedOuterBCBorder(aWM) :
  2347. GetSeparateModelBorderPadding(aWM, aReflowInput, mStyleContext);
  2348. }
  2349. void
  2350. nsTableFrame::InitChildReflowInput(ReflowInput& aReflowInput)
  2351. {
  2352. nsMargin collapseBorder;
  2353. nsMargin padding(0,0,0,0);
  2354. nsMargin* pCollapseBorder = nullptr;
  2355. nsPresContext* presContext = PresContext();
  2356. if (IsBorderCollapse()) {
  2357. nsTableRowGroupFrame* rgFrame =
  2358. static_cast<nsTableRowGroupFrame*>(aReflowInput.mFrame);
  2359. WritingMode wm = GetWritingMode();
  2360. LogicalMargin border = rgFrame->GetBCBorderWidth(wm);
  2361. collapseBorder = border.GetPhysicalMargin(wm);
  2362. pCollapseBorder = &collapseBorder;
  2363. }
  2364. aReflowInput.Init(presContext, nullptr, pCollapseBorder, &padding);
  2365. NS_ASSERTION(!mBits.mResizedColumns ||
  2366. !aReflowInput.mParentReflowInput->mFlags.mSpecialBSizeReflow,
  2367. "should not resize columns on special bsize reflow");
  2368. if (mBits.mResizedColumns) {
  2369. aReflowInput.SetIResize(true);
  2370. }
  2371. }
  2372. // Position and size aKidFrame and update our reflow state. The origin of
  2373. // aKidRect is relative to the upper-left origin of our frame
  2374. void
  2375. nsTableFrame::PlaceChild(TableReflowInput& aReflowInput,
  2376. nsIFrame* aKidFrame,
  2377. nsPoint aKidPosition,
  2378. ReflowOutput& aKidDesiredSize,
  2379. const nsRect& aOriginalKidRect,
  2380. const nsRect& aOriginalKidVisualOverflow)
  2381. {
  2382. WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
  2383. bool isFirstReflow =
  2384. aKidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
  2385. // Place and size the child
  2386. FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
  2387. aKidPosition.x, aKidPosition.y, 0);
  2388. InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
  2389. isFirstReflow);
  2390. // Adjust the running block-offset
  2391. aReflowInput.bCoord += aKidDesiredSize.BSize(wm);
  2392. // If our bsize is constrained, then update the available bsize
  2393. aReflowInput.ReduceAvailableBSizeBy(wm, aKidDesiredSize.BSize(wm));
  2394. }
  2395. void
  2396. nsTableFrame::OrderRowGroups(RowGroupArray& aChildren,
  2397. nsTableRowGroupFrame** aHead,
  2398. nsTableRowGroupFrame** aFoot) const
  2399. {
  2400. aChildren.Clear();
  2401. nsTableRowGroupFrame* head = nullptr;
  2402. nsTableRowGroupFrame* foot = nullptr;
  2403. nsIFrame* kidFrame = mFrames.FirstChild();
  2404. while (kidFrame) {
  2405. const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay();
  2406. nsTableRowGroupFrame* rowGroup =
  2407. static_cast<nsTableRowGroupFrame*>(kidFrame);
  2408. switch (kidDisplay->mDisplay) {
  2409. case mozilla::StyleDisplay::TableHeaderGroup:
  2410. if (head) { // treat additional thead like tbody
  2411. aChildren.AppendElement(rowGroup);
  2412. }
  2413. else {
  2414. head = rowGroup;
  2415. }
  2416. break;
  2417. case mozilla::StyleDisplay::TableFooterGroup:
  2418. if (foot) { // treat additional tfoot like tbody
  2419. aChildren.AppendElement(rowGroup);
  2420. }
  2421. else {
  2422. foot = rowGroup;
  2423. }
  2424. break;
  2425. case mozilla::StyleDisplay::TableRowGroup:
  2426. aChildren.AppendElement(rowGroup);
  2427. break;
  2428. default:
  2429. NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
  2430. // Just ignore it
  2431. break;
  2432. }
  2433. // Get the next sibling but skip it if it's also the next-in-flow, since
  2434. // a next-in-flow will not be part of the current table.
  2435. while (kidFrame) {
  2436. nsIFrame* nif = kidFrame->GetNextInFlow();
  2437. kidFrame = kidFrame->GetNextSibling();
  2438. if (kidFrame != nif)
  2439. break;
  2440. }
  2441. }
  2442. // put the thead first
  2443. if (head) {
  2444. aChildren.InsertElementAt(0, head);
  2445. }
  2446. if (aHead)
  2447. *aHead = head;
  2448. // put the tfoot after the last tbody
  2449. if (foot) {
  2450. aChildren.AppendElement(foot);
  2451. }
  2452. if (aFoot)
  2453. *aFoot = foot;
  2454. }
  2455. nsTableRowGroupFrame*
  2456. nsTableFrame::GetTHead() const
  2457. {
  2458. nsIFrame* kidFrame = mFrames.FirstChild();
  2459. while (kidFrame) {
  2460. if (kidFrame->StyleDisplay()->mDisplay ==
  2461. mozilla::StyleDisplay::TableHeaderGroup) {
  2462. return static_cast<nsTableRowGroupFrame*>(kidFrame);
  2463. }
  2464. // Get the next sibling but skip it if it's also the next-in-flow, since
  2465. // a next-in-flow will not be part of the current table.
  2466. while (kidFrame) {
  2467. nsIFrame* nif = kidFrame->GetNextInFlow();
  2468. kidFrame = kidFrame->GetNextSibling();
  2469. if (kidFrame != nif)
  2470. break;
  2471. }
  2472. }
  2473. return nullptr;
  2474. }
  2475. nsTableRowGroupFrame*
  2476. nsTableFrame::GetTFoot() const
  2477. {
  2478. nsIFrame* kidFrame = mFrames.FirstChild();
  2479. while (kidFrame) {
  2480. if (kidFrame->StyleDisplay()->mDisplay ==
  2481. mozilla::StyleDisplay::TableFooterGroup) {
  2482. return static_cast<nsTableRowGroupFrame*>(kidFrame);
  2483. }
  2484. // Get the next sibling but skip it if it's also the next-in-flow, since
  2485. // a next-in-flow will not be part of the current table.
  2486. while (kidFrame) {
  2487. nsIFrame* nif = kidFrame->GetNextInFlow();
  2488. kidFrame = kidFrame->GetNextSibling();
  2489. if (kidFrame != nif)
  2490. break;
  2491. }
  2492. }
  2493. return nullptr;
  2494. }
  2495. static bool
  2496. IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
  2497. {
  2498. return aFrameHeight < (aPageHeight / 4);
  2499. }
  2500. nsresult
  2501. nsTableFrame::SetupHeaderFooterChild(const TableReflowInput& aReflowInput,
  2502. nsTableRowGroupFrame* aFrame,
  2503. nscoord* aDesiredHeight)
  2504. {
  2505. nsPresContext* presContext = PresContext();
  2506. nscoord pageHeight = presContext->GetPageSize().height;
  2507. // Reflow the child with unconstrained height
  2508. WritingMode wm = aFrame->GetWritingMode();
  2509. LogicalSize availSize = aReflowInput.reflowInput.AvailableSize(wm);
  2510. nsSize containerSize = availSize.GetPhysicalSize(wm);
  2511. // XXX check for containerSize.* == NS_UNCONSTRAINEDSIZE
  2512. availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
  2513. ReflowInput kidReflowInput(presContext, aReflowInput.reflowInput,
  2514. aFrame, availSize, nullptr,
  2515. ReflowInput::CALLER_WILL_INIT);
  2516. InitChildReflowInput(kidReflowInput);
  2517. kidReflowInput.mFlags.mIsTopOfPage = true;
  2518. ReflowOutput desiredSize(aReflowInput.reflowInput);
  2519. desiredSize.ClearSize();
  2520. nsReflowStatus status;
  2521. ReflowChild(aFrame, presContext, desiredSize, kidReflowInput,
  2522. wm, LogicalPoint(wm, aReflowInput.iCoord, aReflowInput.bCoord),
  2523. containerSize, 0, status);
  2524. // The child will be reflowed again "for real" so no need to place it now
  2525. aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight));
  2526. *aDesiredHeight = desiredSize.Height();
  2527. return NS_OK;
  2528. }
  2529. void
  2530. nsTableFrame::PlaceRepeatedFooter(TableReflowInput& aReflowInput,
  2531. nsTableRowGroupFrame *aTfoot,
  2532. nscoord aFooterHeight)
  2533. {
  2534. nsPresContext* presContext = PresContext();
  2535. WritingMode wm = aTfoot->GetWritingMode();
  2536. LogicalSize kidAvailSize = aReflowInput.availSize;
  2537. nsSize containerSize = kidAvailSize.GetPhysicalSize(wm);
  2538. // XXX check for containerSize.* == NS_UNCONSTRAINEDSIZE
  2539. kidAvailSize.BSize(wm) = aFooterHeight;
  2540. ReflowInput footerReflowInput(presContext,
  2541. aReflowInput.reflowInput,
  2542. aTfoot, kidAvailSize,
  2543. nullptr,
  2544. ReflowInput::CALLER_WILL_INIT);
  2545. InitChildReflowInput(footerReflowInput);
  2546. aReflowInput.bCoord += GetRowSpacing(GetRowCount());
  2547. nsRect origTfootRect = aTfoot->GetRect();
  2548. nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
  2549. nsReflowStatus footerStatus;
  2550. ReflowOutput desiredSize(aReflowInput.reflowInput);
  2551. desiredSize.ClearSize();
  2552. LogicalPoint kidPosition(wm, aReflowInput.iCoord, aReflowInput.bCoord);
  2553. ReflowChild(aTfoot, presContext, desiredSize, footerReflowInput,
  2554. wm, kidPosition, containerSize, 0, footerStatus);
  2555. footerReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
  2556. PlaceChild(aReflowInput, aTfoot,
  2557. // We subtract desiredSize.PhysicalSize() from containerSize here
  2558. // to account for the fact that in RTL modes, the origin is
  2559. // on the right-hand side so we're not simply converting a
  2560. // point, we're also swapping the child's origin side.
  2561. kidPosition.GetPhysicalPoint(wm, containerSize -
  2562. desiredSize.PhysicalSize()),
  2563. desiredSize, origTfootRect, origTfootVisualOverflow);
  2564. }
  2565. // Reflow the children based on the avail size and reason in aReflowInput
  2566. void
  2567. nsTableFrame::ReflowChildren(TableReflowInput& aReflowInput,
  2568. nsReflowStatus& aStatus,
  2569. nsIFrame*& aLastChildReflowed,
  2570. nsOverflowAreas& aOverflowAreas)
  2571. {
  2572. aStatus = NS_FRAME_COMPLETE;
  2573. aLastChildReflowed = nullptr;
  2574. nsIFrame* prevKidFrame = nullptr;
  2575. WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
  2576. NS_WARNING_ASSERTION(
  2577. wm.IsVertical() ||
  2578. NS_UNCONSTRAINEDSIZE != aReflowInput.reflowInput.ComputedWidth(),
  2579. "shouldn't have unconstrained width in horizontal mode");
  2580. nsSize containerSize =
  2581. aReflowInput.reflowInput.ComputedSizeAsContainerIfConstrained();
  2582. nsPresContext* presContext = PresContext();
  2583. // XXXldb Should we be checking constrained height instead?
  2584. // tables are not able to pull back children from its next inflow, so even
  2585. // under paginated contexts tables are should not paginate if they are inside
  2586. // column set
  2587. bool isPaginated = presContext->IsPaginated() &&
  2588. NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm) &&
  2589. aReflowInput.reflowInput.mFlags.mTableIsSplittable;
  2590. aOverflowAreas.Clear();
  2591. bool reflowAllKids = aReflowInput.reflowInput.ShouldReflowAllKids() ||
  2592. mBits.mResizedColumns ||
  2593. IsGeometryDirty();
  2594. RowGroupArray rowGroups;
  2595. nsTableRowGroupFrame *thead, *tfoot;
  2596. OrderRowGroups(rowGroups, &thead, &tfoot);
  2597. bool pageBreak = false;
  2598. nscoord footerHeight = 0;
  2599. // Determine the repeatablility of headers and footers, and also the desired
  2600. // height of any repeatable footer.
  2601. // The repeatability of headers on continued tables is handled
  2602. // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
  2603. // We handle the repeatability of footers again here because we need to
  2604. // determine the footer's height anyway. We could perhaps optimize by
  2605. // using the footer's prev-in-flow's height instead of reflowing it again,
  2606. // but there's no real need.
  2607. if (isPaginated) {
  2608. bool reorder = false;
  2609. if (thead && !GetPrevInFlow()) {
  2610. if (thead->GetNextInFlow()) {
  2611. reorder = true;
  2612. }
  2613. nscoord desiredHeight;
  2614. nsresult rv = SetupHeaderFooterChild(aReflowInput, thead, &desiredHeight);
  2615. if (NS_FAILED(rv))
  2616. return;
  2617. }
  2618. if (tfoot) {
  2619. if (tfoot->GetNextInFlow()) {
  2620. reorder = true;
  2621. }
  2622. nsresult rv = SetupHeaderFooterChild(aReflowInput, tfoot, &footerHeight);
  2623. if (NS_FAILED(rv))
  2624. return;
  2625. }
  2626. if (reorder) {
  2627. // Reorder row groups, because the reflow may have changed what's next-in-flow.
  2628. OrderRowGroups(rowGroups, &thead, &tfoot);
  2629. }
  2630. }
  2631. // if the child is a tbody in paginated mode reduce the height by a repeated footer
  2632. bool allowRepeatedFooter = false;
  2633. for (size_t childX = 0; childX < rowGroups.Length(); childX++) {
  2634. nsIFrame* kidFrame = rowGroups[childX];
  2635. nsTableRowGroupFrame* rowGroupFrame = rowGroups[childX];
  2636. nscoord cellSpacingB = GetRowSpacing(rowGroupFrame->GetStartRowIndex()+
  2637. rowGroupFrame->GetRowCount());
  2638. // Get the frame state bits
  2639. // See if we should only reflow the dirty child frames
  2640. if (reflowAllKids ||
  2641. NS_SUBTREE_DIRTY(kidFrame) ||
  2642. (aReflowInput.reflowInput.mFlags.mSpecialBSizeReflow &&
  2643. (isPaginated || kidFrame->HasAnyStateBits(
  2644. NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) {
  2645. if (pageBreak) {
  2646. if (allowRepeatedFooter) {
  2647. PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
  2648. }
  2649. else if (tfoot && tfoot->IsRepeatable()) {
  2650. tfoot->SetRepeatable(false);
  2651. }
  2652. PushChildren(rowGroups, childX);
  2653. aStatus = NS_FRAME_NOT_COMPLETE;
  2654. break;
  2655. }
  2656. LogicalSize kidAvailSize(aReflowInput.availSize);
  2657. allowRepeatedFooter = false;
  2658. if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.BSize(wm))) {
  2659. nsTableRowGroupFrame* kidRG =
  2660. static_cast<nsTableRowGroupFrame*>(kidFrame);
  2661. if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
  2662. // the child is a tbody and there is a repeatable footer
  2663. NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
  2664. if (footerHeight + cellSpacingB < kidAvailSize.BSize(wm)) {
  2665. allowRepeatedFooter = true;
  2666. kidAvailSize.BSize(wm) -= footerHeight + cellSpacingB;
  2667. }
  2668. }
  2669. }
  2670. nsRect oldKidRect = kidFrame->GetRect();
  2671. nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
  2672. ReflowOutput desiredSize(aReflowInput.reflowInput);
  2673. desiredSize.ClearSize();
  2674. // Reflow the child into the available space
  2675. ReflowInput kidReflowInput(presContext, aReflowInput.reflowInput,
  2676. kidFrame,
  2677. kidAvailSize,
  2678. nullptr,
  2679. ReflowInput::CALLER_WILL_INIT);
  2680. InitChildReflowInput(kidReflowInput);
  2681. // If this isn't the first row group, and the previous row group has a
  2682. // nonzero YMost, then we can't be at the top of the page.
  2683. // We ignore a repeated head row group in this check to avoid causing
  2684. // infinite loops in some circumstances - see bug 344883.
  2685. if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
  2686. (rowGroups[childX - 1]->GetNormalRect().YMost() > 0)) {
  2687. kidReflowInput.mFlags.mIsTopOfPage = false;
  2688. }
  2689. aReflowInput.bCoord += cellSpacingB;
  2690. aReflowInput.ReduceAvailableBSizeBy(wm, cellSpacingB);
  2691. // record the presence of a next in flow, it might get destroyed so we
  2692. // need to reorder the row group array
  2693. bool reorder = false;
  2694. if (kidFrame->GetNextInFlow()) {
  2695. reorder = true;
  2696. }
  2697. LogicalPoint kidPosition(wm, aReflowInput.iCoord, aReflowInput.bCoord);
  2698. ReflowChild(kidFrame, presContext, desiredSize, kidReflowInput,
  2699. wm, kidPosition, containerSize, 0, aStatus);
  2700. kidReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
  2701. if (reorder) {
  2702. // Reorder row groups, because the reflow may have changed what's next-in-flow.
  2703. OrderRowGroups(rowGroups, &thead, &tfoot);
  2704. childX = rowGroups.IndexOf(kidFrame);
  2705. if (childX == RowGroupArray::NoIndex) {
  2706. // XXXbz can this happen?
  2707. childX = rowGroups.Length();
  2708. }
  2709. }
  2710. if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
  2711. ShouldAvoidBreakInside(aReflowInput.reflowInput)) {
  2712. aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  2713. break;
  2714. }
  2715. // see if the rowgroup did not fit on this page might be pushed on
  2716. // the next page
  2717. if (isPaginated &&
  2718. (NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
  2719. (NS_FRAME_IS_COMPLETE(aStatus) &&
  2720. (NS_UNCONSTRAINEDSIZE != kidReflowInput.AvailableHeight()) &&
  2721. kidReflowInput.AvailableHeight() < desiredSize.Height()))) {
  2722. if (ShouldAvoidBreakInside(aReflowInput.reflowInput)) {
  2723. aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  2724. break;
  2725. }
  2726. // if we are on top of the page place with dataloss
  2727. if (kidReflowInput.mFlags.mIsTopOfPage) {
  2728. if (childX+1 < rowGroups.Length()) {
  2729. nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
  2730. if (nextRowGroupFrame) {
  2731. PlaceChild(aReflowInput, kidFrame,
  2732. kidPosition.GetPhysicalPoint(wm,
  2733. containerSize - desiredSize.PhysicalSize()),
  2734. desiredSize, oldKidRect, oldKidVisualOverflow);
  2735. if (allowRepeatedFooter) {
  2736. PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
  2737. }
  2738. else if (tfoot && tfoot->IsRepeatable()) {
  2739. tfoot->SetRepeatable(false);
  2740. }
  2741. aStatus = NS_FRAME_NOT_COMPLETE;
  2742. PushChildren(rowGroups, childX + 1);
  2743. aLastChildReflowed = kidFrame;
  2744. break;
  2745. }
  2746. }
  2747. }
  2748. else { // we are not on top, push this rowgroup onto the next page
  2749. if (prevKidFrame) { // we had a rowgroup before so push this
  2750. if (allowRepeatedFooter) {
  2751. PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
  2752. }
  2753. else if (tfoot && tfoot->IsRepeatable()) {
  2754. tfoot->SetRepeatable(false);
  2755. }
  2756. aStatus = NS_FRAME_NOT_COMPLETE;
  2757. PushChildren(rowGroups, childX);
  2758. aLastChildReflowed = prevKidFrame;
  2759. break;
  2760. }
  2761. else { // we can't push so lets make clear how much space we need
  2762. PlaceChild(aReflowInput, kidFrame,
  2763. kidPosition.GetPhysicalPoint(wm,
  2764. containerSize - desiredSize.PhysicalSize()),
  2765. desiredSize, oldKidRect, oldKidVisualOverflow);
  2766. aLastChildReflowed = kidFrame;
  2767. if (allowRepeatedFooter) {
  2768. PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
  2769. aLastChildReflowed = tfoot;
  2770. }
  2771. break;
  2772. }
  2773. }
  2774. }
  2775. aLastChildReflowed = kidFrame;
  2776. pageBreak = false;
  2777. // see if there is a page break after this row group or before the next one
  2778. if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
  2779. (NS_UNCONSTRAINEDSIZE != kidReflowInput.AvailableHeight())) {
  2780. nsIFrame* nextKid =
  2781. (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
  2782. pageBreak = PageBreakAfter(kidFrame, nextKid);
  2783. }
  2784. // Place the child
  2785. PlaceChild(aReflowInput, kidFrame,
  2786. kidPosition.GetPhysicalPoint(wm, containerSize -
  2787. desiredSize.PhysicalSize()),
  2788. desiredSize, oldKidRect, oldKidVisualOverflow);
  2789. // Remember where we just were in case we end up pushing children
  2790. prevKidFrame = kidFrame;
  2791. MOZ_ASSERT(!NS_FRAME_IS_NOT_COMPLETE(aStatus) || isPaginated,
  2792. "Table contents should only fragment in paginated contexts");
  2793. // Special handling for incomplete children
  2794. if (isPaginated && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
  2795. nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
  2796. if (!kidNextInFlow) {
  2797. // The child doesn't have a next-in-flow so create a continuing
  2798. // frame. This hooks the child into the flow
  2799. kidNextInFlow = presContext->PresShell()->FrameConstructor()->
  2800. CreateContinuingFrame(presContext, kidFrame, this);
  2801. // Insert the kid's new next-in-flow into our sibling list...
  2802. mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow);
  2803. // and in rowGroups after childX so that it will get pushed below.
  2804. rowGroups.InsertElementAt(childX + 1,
  2805. static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
  2806. } else if (kidNextInFlow == kidFrame->GetNextSibling()) {
  2807. // OrderRowGroups excludes NIFs in the child list from 'rowGroups'
  2808. // so we deal with that here to make sure they get pushed.
  2809. MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow),
  2810. "OrderRowGroups must not put our NIF in 'rowGroups'");
  2811. rowGroups.InsertElementAt(childX + 1,
  2812. static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
  2813. }
  2814. // We've used up all of our available space so push the remaining
  2815. // children.
  2816. if (allowRepeatedFooter) {
  2817. PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
  2818. }
  2819. else if (tfoot && tfoot->IsRepeatable()) {
  2820. tfoot->SetRepeatable(false);
  2821. }
  2822. nsIFrame* nextSibling = kidFrame->GetNextSibling();
  2823. if (nextSibling) {
  2824. PushChildren(rowGroups, childX + 1);
  2825. }
  2826. break;
  2827. }
  2828. }
  2829. else { // it isn't being reflowed
  2830. aReflowInput.bCoord += cellSpacingB;
  2831. LogicalRect kidRect(wm, kidFrame->GetNormalRect(), containerSize);
  2832. if (kidRect.BStart(wm) != aReflowInput.bCoord) {
  2833. // invalidate the old position
  2834. kidFrame->InvalidateFrameSubtree();
  2835. // move to the new position
  2836. kidFrame->MovePositionBy(wm, LogicalPoint(wm, 0, aReflowInput.bCoord -
  2837. kidRect.BStart(wm)));
  2838. RePositionViews(kidFrame);
  2839. // invalidate the new position
  2840. kidFrame->InvalidateFrameSubtree();
  2841. }
  2842. aReflowInput.bCoord += kidRect.BSize(wm);
  2843. // If our bsize is constrained then update the available bsize.
  2844. aReflowInput.ReduceAvailableBSizeBy(wm, cellSpacingB + kidRect.BSize(wm));
  2845. }
  2846. }
  2847. // We've now propagated the column resizes and geometry changes to all
  2848. // the children.
  2849. mBits.mResizedColumns = false;
  2850. ClearGeometryDirty();
  2851. }
  2852. void
  2853. nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext)
  2854. {
  2855. if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
  2856. ReflowOutput kidMet(GetWritingMode());
  2857. nsPresContext *presContext = PresContext();
  2858. for (nsIFrame* kidFrame : mColGroups) {
  2859. if (NS_SUBTREE_DIRTY(kidFrame)) {
  2860. // The column groups don't care about dimensions or reflow states.
  2861. ReflowInput
  2862. kidReflowInput(presContext, kidFrame, aRenderingContext,
  2863. LogicalSize(kidFrame->GetWritingMode()));
  2864. nsReflowStatus cgStatus;
  2865. ReflowChild(kidFrame, presContext, kidMet, kidReflowInput, 0, 0, 0,
  2866. cgStatus);
  2867. FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0);
  2868. }
  2869. }
  2870. SetHaveReflowedColGroups(true);
  2871. }
  2872. }
  2873. void
  2874. nsTableFrame::CalcDesiredBSize(const ReflowInput& aReflowInput,
  2875. ReflowOutput& aDesiredSize)
  2876. {
  2877. WritingMode wm = aReflowInput.GetWritingMode();
  2878. nsTableCellMap* cellMap = GetCellMap();
  2879. if (!cellMap) {
  2880. NS_ERROR("never ever call me until the cell map is built!");
  2881. aDesiredSize.BSize(wm) = 0;
  2882. return;
  2883. }
  2884. LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
  2885. // get the natural bsize based on the last child's (row group) rect
  2886. RowGroupArray rowGroups;
  2887. OrderRowGroups(rowGroups);
  2888. if (rowGroups.IsEmpty()) {
  2889. // tables can be used as rectangular items without content
  2890. nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
  2891. if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedBSize) &&
  2892. (tableSpecifiedBSize > 0) &&
  2893. eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
  2894. // empty tables should not have a size in quirks mode
  2895. aDesiredSize.BSize(wm) = tableSpecifiedBSize;
  2896. } else {
  2897. aDesiredSize.BSize(wm) = 0;
  2898. }
  2899. return;
  2900. }
  2901. int32_t rowCount = cellMap->GetRowCount();
  2902. int32_t colCount = cellMap->GetColCount();
  2903. nscoord desiredBSize = borderPadding.BStartEnd(wm);
  2904. if (rowCount > 0 && colCount > 0) {
  2905. desiredBSize += GetRowSpacing(-1);
  2906. for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  2907. desiredBSize += rowGroups[rgIdx]->BSize(wm) +
  2908. GetRowSpacing(rowGroups[rgIdx]->GetRowCount() +
  2909. rowGroups[rgIdx]->GetStartRowIndex());
  2910. }
  2911. }
  2912. // see if a specified table bsize requires dividing additional space to rows
  2913. if (!GetPrevInFlow()) {
  2914. nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
  2915. if ((tableSpecifiedBSize > 0) &&
  2916. (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE) &&
  2917. (tableSpecifiedBSize > desiredBSize)) {
  2918. // proportionately distribute the excess bsize to unconstrained rows in each
  2919. // unconstrained row group.
  2920. DistributeBSizeToRows(aReflowInput, tableSpecifiedBSize - desiredBSize);
  2921. // this might have changed the overflow area incorporate the childframe overflow area.
  2922. for (nsIFrame* kidFrame : mFrames) {
  2923. ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
  2924. }
  2925. desiredBSize = tableSpecifiedBSize;
  2926. }
  2927. }
  2928. aDesiredSize.BSize(wm) = desiredBSize;
  2929. }
  2930. static
  2931. void ResizeCells(nsTableFrame& aTableFrame)
  2932. {
  2933. nsTableFrame::RowGroupArray rowGroups;
  2934. aTableFrame.OrderRowGroups(rowGroups);
  2935. WritingMode wm = aTableFrame.GetWritingMode();
  2936. ReflowOutput tableDesiredSize(wm);
  2937. tableDesiredSize.SetSize(wm, aTableFrame.GetLogicalSize(wm));
  2938. tableDesiredSize.SetOverflowAreasToDesiredBounds();
  2939. for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  2940. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  2941. ReflowOutput groupDesiredSize(wm);
  2942. groupDesiredSize.SetSize(wm, rgFrame->GetLogicalSize(wm));
  2943. groupDesiredSize.SetOverflowAreasToDesiredBounds();
  2944. nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  2945. while (rowFrame) {
  2946. rowFrame->DidResize();
  2947. rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
  2948. rowFrame = rowFrame->GetNextRow();
  2949. }
  2950. rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
  2951. tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
  2952. rgFrame->GetPosition());
  2953. }
  2954. aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
  2955. }
  2956. void
  2957. nsTableFrame::DistributeBSizeToRows(const ReflowInput& aReflowInput,
  2958. nscoord aAmount)
  2959. {
  2960. WritingMode wm = aReflowInput.GetWritingMode();
  2961. LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
  2962. nsSize containerSize =
  2963. aReflowInput.ComputedSizeAsContainerIfConstrained();
  2964. RowGroupArray rowGroups;
  2965. OrderRowGroups(rowGroups);
  2966. nscoord amountUsed = 0;
  2967. // distribute space to each pct bsize row whose row group doesn't have a computed
  2968. // bsize, and base the pct on the table bsize. If the row group had a computed
  2969. // bsize, then this was already done in nsTableRowGroupFrame::CalculateRowBSizes
  2970. nscoord pctBasis = aReflowInput.ComputedBSize() - GetRowSpacing(-1, GetRowCount());
  2971. nscoord bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(0);
  2972. nscoord bEndRG = bOriginRG;
  2973. uint32_t rgIdx;
  2974. for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  2975. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  2976. nscoord amountUsedByRG = 0;
  2977. nscoord bOriginRow = 0;
  2978. LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), containerSize);
  2979. if (!rgFrame->HasStyleBSize()) {
  2980. nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  2981. while (rowFrame) {
  2982. // We don't know the final width of the rowGroupFrame yet, so use 0,0
  2983. // as a dummy containerSize here; we'll adjust the row positions at
  2984. // the end, after the rowGroup size is finalized.
  2985. const nsSize dummyContainerSize;
  2986. LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(),
  2987. dummyContainerSize);
  2988. nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex());
  2989. if ((amountUsed < aAmount) && rowFrame->HasPctBSize()) {
  2990. nscoord pctBSize = rowFrame->GetInitialBSize(pctBasis);
  2991. nscoord amountForRow = std::min(aAmount - amountUsed,
  2992. pctBSize - rowNormalRect.BSize(wm));
  2993. if (amountForRow > 0) {
  2994. // XXXbz we don't need to move the row's b-position to bOriginRow?
  2995. nsRect origRowRect = rowFrame->GetRect();
  2996. nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
  2997. rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm),
  2998. newRowBSize));
  2999. bOriginRow += newRowBSize + cellSpacingB;
  3000. bEndRG += newRowBSize + cellSpacingB;
  3001. amountUsed += amountForRow;
  3002. amountUsedByRG += amountForRow;
  3003. //rowFrame->DidResize();
  3004. nsTableFrame::RePositionViews(rowFrame);
  3005. rgFrame->InvalidateFrameWithRect(origRowRect);
  3006. rgFrame->InvalidateFrame();
  3007. }
  3008. }
  3009. else {
  3010. if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm) &&
  3011. !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
  3012. rowFrame->InvalidateFrameSubtree();
  3013. rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
  3014. rowNormalRect.BStart(wm)));
  3015. nsTableFrame::RePositionViews(rowFrame);
  3016. rowFrame->InvalidateFrameSubtree();
  3017. }
  3018. bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB;
  3019. bEndRG += rowNormalRect.BSize(wm) + cellSpacingB;
  3020. }
  3021. rowFrame = rowFrame->GetNextRow();
  3022. }
  3023. if (amountUsed > 0) {
  3024. if (rgNormalRect.BStart(wm) != bOriginRG) {
  3025. rgFrame->InvalidateFrameSubtree();
  3026. }
  3027. nsRect origRgNormalRect = rgFrame->GetRect();
  3028. nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
  3029. rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
  3030. rgNormalRect.BStart(wm)));
  3031. rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm),
  3032. rgNormalRect.BSize(wm) + amountUsedByRG));
  3033. nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
  3034. origRgVisualOverflow, false);
  3035. }
  3036. }
  3037. else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
  3038. rgFrame->InvalidateFrameSubtree();
  3039. rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
  3040. rgNormalRect.BStart(wm)));
  3041. // Make sure child views are properly positioned
  3042. nsTableFrame::RePositionViews(rgFrame);
  3043. rgFrame->InvalidateFrameSubtree();
  3044. }
  3045. bOriginRG = bEndRG;
  3046. }
  3047. if (amountUsed >= aAmount) {
  3048. ResizeCells(*this);
  3049. return;
  3050. }
  3051. // get the first row without a style bsize where its row group has an
  3052. // unconstrained bsize
  3053. nsTableRowGroupFrame* firstUnStyledRG = nullptr;
  3054. nsTableRowFrame* firstUnStyledRow = nullptr;
  3055. for (rgIdx = 0; rgIdx < rowGroups.Length() && !firstUnStyledRG; rgIdx++) {
  3056. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  3057. if (!rgFrame->HasStyleBSize()) {
  3058. nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3059. while (rowFrame) {
  3060. if (!rowFrame->HasStyleBSize()) {
  3061. firstUnStyledRG = rgFrame;
  3062. firstUnStyledRow = rowFrame;
  3063. break;
  3064. }
  3065. rowFrame = rowFrame->GetNextRow();
  3066. }
  3067. }
  3068. }
  3069. nsTableRowFrame* lastEligibleRow = nullptr;
  3070. // Accumulate the correct divisor. This will be the total bsize of all
  3071. // unstyled rows inside unstyled row groups, unless there are none, in which
  3072. // case, it will be number of all rows. If the unstyled rows don't have a
  3073. // bsize, divide the space equally among them.
  3074. nscoord divisor = 0;
  3075. int32_t eligibleRows = 0;
  3076. bool expandEmptyRows = false;
  3077. if (!firstUnStyledRow) {
  3078. // there is no unstyled row
  3079. divisor = GetRowCount();
  3080. }
  3081. else {
  3082. for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  3083. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  3084. if (!firstUnStyledRG || !rgFrame->HasStyleBSize()) {
  3085. nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3086. while (rowFrame) {
  3087. if (!firstUnStyledRG || !rowFrame->HasStyleBSize()) {
  3088. NS_ASSERTION(rowFrame->BSize(wm) >= 0,
  3089. "negative row frame block-size");
  3090. divisor += rowFrame->BSize(wm);
  3091. eligibleRows++;
  3092. lastEligibleRow = rowFrame;
  3093. }
  3094. rowFrame = rowFrame->GetNextRow();
  3095. }
  3096. }
  3097. }
  3098. if (divisor <= 0) {
  3099. if (eligibleRows > 0) {
  3100. expandEmptyRows = true;
  3101. }
  3102. else {
  3103. NS_ERROR("invalid divisor");
  3104. return;
  3105. }
  3106. }
  3107. }
  3108. // allocate the extra bsize to the unstyled row groups and rows
  3109. nscoord bSizeToDistribute = aAmount - amountUsed;
  3110. bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(-1);
  3111. bEndRG = bOriginRG;
  3112. for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  3113. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  3114. nscoord amountUsedByRG = 0;
  3115. nscoord bOriginRow = 0;
  3116. LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), containerSize);
  3117. nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
  3118. // see if there is an eligible row group or we distribute to all rows
  3119. if (!firstUnStyledRG || !rgFrame->HasStyleBSize() || !eligibleRows) {
  3120. for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3121. rowFrame; rowFrame = rowFrame->GetNextRow()) {
  3122. nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex());
  3123. // We don't know the final width of the rowGroupFrame yet, so use 0,0
  3124. // as a dummy containerSize here; we'll adjust the row positions at
  3125. // the end, after the rowGroup size is finalized.
  3126. const nsSize dummyContainerSize;
  3127. LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(),
  3128. dummyContainerSize);
  3129. nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
  3130. // see if there is an eligible row or we distribute to all rows
  3131. if (!firstUnStyledRow || !rowFrame->HasStyleBSize() || !eligibleRows) {
  3132. float ratio;
  3133. if (eligibleRows) {
  3134. if (!expandEmptyRows) {
  3135. // The amount of additional space each row gets is proportional
  3136. // to its bsize
  3137. ratio = float(rowNormalRect.BSize(wm)) / float(divisor);
  3138. } else {
  3139. // empty rows get all the same additional space
  3140. ratio = 1.0f / float(eligibleRows);
  3141. }
  3142. }
  3143. else {
  3144. // all rows get the same additional space
  3145. ratio = 1.0f / float(divisor);
  3146. }
  3147. // give rows their additional space, except for the last row which
  3148. // gets the remainder
  3149. nscoord amountForRow =
  3150. (rowFrame == lastEligibleRow)
  3151. ? aAmount - amountUsed
  3152. : NSToCoordRound(((float)(bSizeToDistribute)) * ratio);
  3153. amountForRow = std::min(amountForRow, aAmount - amountUsed);
  3154. if (bOriginRow != rowNormalRect.BStart(wm)) {
  3155. rowFrame->InvalidateFrameSubtree();
  3156. }
  3157. // update the row bsize
  3158. nsRect origRowRect = rowFrame->GetRect();
  3159. nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
  3160. rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
  3161. rowNormalRect.BStart(wm)));
  3162. rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm),
  3163. newRowBSize));
  3164. bOriginRow += newRowBSize + cellSpacingB;
  3165. bEndRG += newRowBSize + cellSpacingB;
  3166. amountUsed += amountForRow;
  3167. amountUsedByRG += amountForRow;
  3168. NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
  3169. //rowFrame->DidResize();
  3170. nsTableFrame::RePositionViews(rowFrame);
  3171. nsTableFrame::InvalidateTableFrame(rowFrame, origRowRect,
  3172. rowVisualOverflow, false);
  3173. }
  3174. else {
  3175. if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm)) {
  3176. rowFrame->InvalidateFrameSubtree();
  3177. rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
  3178. rowNormalRect.BStart(wm)));
  3179. nsTableFrame::RePositionViews(rowFrame);
  3180. rowFrame->InvalidateFrameSubtree();
  3181. }
  3182. bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB;
  3183. bEndRG += rowNormalRect.BSize(wm) + cellSpacingB;
  3184. }
  3185. }
  3186. if (amountUsed > 0) {
  3187. if (rgNormalRect.BStart(wm) != bOriginRG) {
  3188. rgFrame->InvalidateFrameSubtree();
  3189. }
  3190. nsRect origRgNormalRect = rgFrame->GetRect();
  3191. rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
  3192. rgNormalRect.BStart(wm)));
  3193. rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm),
  3194. rgNormalRect.BSize(wm) + amountUsedByRG));
  3195. nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
  3196. rgVisualOverflow, false);
  3197. }
  3198. // For vertical-rl mode, we needed to position the rows relative to the
  3199. // right-hand (block-start) side of the group; but we couldn't do that
  3200. // above, as we didn't know the rowGroupFrame's final block size yet.
  3201. // So we used a dummyContainerSize of 0,0 earlier, placing the rows to
  3202. // the left of the rowGroupFrame's (physical) origin. Now we move them
  3203. // all rightwards by its final width.
  3204. if (wm.IsVerticalRL()) {
  3205. nscoord rgWidth = rgFrame->GetSize().width;
  3206. for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3207. rowFrame; rowFrame = rowFrame->GetNextRow()) {
  3208. rowFrame->InvalidateFrameSubtree();
  3209. rowFrame->MovePositionBy(nsPoint(rgWidth, 0));
  3210. nsTableFrame::RePositionViews(rowFrame);
  3211. rowFrame->InvalidateFrameSubtree();
  3212. }
  3213. }
  3214. }
  3215. else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
  3216. rgFrame->InvalidateFrameSubtree();
  3217. rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
  3218. rgNormalRect.BStart(wm)));
  3219. // Make sure child views are properly positioned
  3220. nsTableFrame::RePositionViews(rgFrame);
  3221. rgFrame->InvalidateFrameSubtree();
  3222. }
  3223. bOriginRG = bEndRG;
  3224. }
  3225. ResizeCells(*this);
  3226. }
  3227. nscoord
  3228. nsTableFrame::GetColumnISizeFromFirstInFlow(int32_t aColIndex)
  3229. {
  3230. MOZ_ASSERT(this == FirstInFlow());
  3231. nsTableColFrame* colFrame = GetColFrame(aColIndex);
  3232. return colFrame ? colFrame->GetFinalISize() : 0;
  3233. }
  3234. nscoord
  3235. nsTableFrame::GetColSpacing()
  3236. {
  3237. if (IsBorderCollapse())
  3238. return 0;
  3239. return StyleTableBorder()->mBorderSpacingCol;
  3240. }
  3241. // XXX: could cache this. But be sure to check style changes if you do!
  3242. nscoord
  3243. nsTableFrame::GetColSpacing(int32_t aColIndex)
  3244. {
  3245. NS_ASSERTION(aColIndex >= -1 && aColIndex <= GetColCount(),
  3246. "Column index exceeds the bounds of the table");
  3247. // Index is irrelevant for ordinary tables. We check that it falls within
  3248. // appropriate bounds to increase confidence of correctness in situations
  3249. // where it does matter.
  3250. return GetColSpacing();
  3251. }
  3252. nscoord
  3253. nsTableFrame::GetColSpacing(int32_t aStartColIndex,
  3254. int32_t aEndColIndex)
  3255. {
  3256. NS_ASSERTION(aStartColIndex >= -1 && aStartColIndex <= GetColCount(),
  3257. "Start column index exceeds the bounds of the table");
  3258. NS_ASSERTION(aEndColIndex >= -1 && aEndColIndex <= GetColCount(),
  3259. "End column index exceeds the bounds of the table");
  3260. NS_ASSERTION(aStartColIndex <= aEndColIndex,
  3261. "End index must not be less than start index");
  3262. // Only one possible value so just multiply it out. Tables where index
  3263. // matters will override this function
  3264. return GetColSpacing() * (aEndColIndex - aStartColIndex);
  3265. }
  3266. nscoord
  3267. nsTableFrame::GetRowSpacing()
  3268. {
  3269. if (IsBorderCollapse())
  3270. return 0;
  3271. return StyleTableBorder()->mBorderSpacingRow;
  3272. }
  3273. // XXX: could cache this. But be sure to check style changes if you do!
  3274. nscoord
  3275. nsTableFrame::GetRowSpacing(int32_t aRowIndex)
  3276. {
  3277. NS_ASSERTION(aRowIndex >= -1 && aRowIndex <= GetRowCount(),
  3278. "Row index exceeds the bounds of the table");
  3279. // Index is irrelevant for ordinary tables. We check that it falls within
  3280. // appropriate bounds to increase confidence of correctness in situations
  3281. // where it does matter.
  3282. return GetRowSpacing();
  3283. }
  3284. nscoord
  3285. nsTableFrame::GetRowSpacing(int32_t aStartRowIndex,
  3286. int32_t aEndRowIndex)
  3287. {
  3288. NS_ASSERTION(aStartRowIndex >= -1 && aStartRowIndex <= GetRowCount(),
  3289. "Start row index exceeds the bounds of the table");
  3290. NS_ASSERTION(aEndRowIndex >= -1 && aEndRowIndex <= GetRowCount(),
  3291. "End row index exceeds the bounds of the table");
  3292. NS_ASSERTION(aStartRowIndex <= aEndRowIndex,
  3293. "End index must not be less than start index");
  3294. // Only one possible value so just multiply it out. Tables where index
  3295. // matters will override this function
  3296. return GetRowSpacing() * (aEndRowIndex - aStartRowIndex);
  3297. }
  3298. /* virtual */ nscoord
  3299. nsTableFrame::GetLogicalBaseline(WritingMode aWM) const
  3300. {
  3301. nscoord baseline;
  3302. if (!GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, &baseline)) {
  3303. baseline = BSize(aWM);
  3304. }
  3305. return baseline;
  3306. }
  3307. /* virtual */ bool
  3308. nsTableFrame::GetNaturalBaselineBOffset(WritingMode aWM,
  3309. BaselineSharingGroup aBaselineGroup,
  3310. nscoord* aBaseline) const
  3311. {
  3312. RowGroupArray orderedRowGroups;
  3313. OrderRowGroups(orderedRowGroups);
  3314. // XXX not sure if this should be the size of the containing block instead.
  3315. nsSize containerSize = mRect.Size();
  3316. auto TableBaseline = [aWM, containerSize] (nsTableRowGroupFrame* aRowGroup,
  3317. nsTableRowFrame* aRow) {
  3318. nscoord rgBStart = LogicalRect(aWM, aRowGroup->GetNormalRect(),
  3319. containerSize).BStart(aWM);
  3320. nscoord rowBStart = LogicalRect(aWM, aRow->GetNormalRect(),
  3321. containerSize).BStart(aWM);
  3322. return rgBStart + rowBStart + aRow->GetRowBaseline(aWM);
  3323. };
  3324. if (aBaselineGroup == BaselineSharingGroup::eFirst) {
  3325. for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  3326. nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
  3327. nsTableRowFrame* row = rgFrame->GetFirstRow();
  3328. if (row) {
  3329. *aBaseline = TableBaseline(rgFrame, row);
  3330. return true;
  3331. }
  3332. }
  3333. } else {
  3334. for (uint32_t rgIndex = orderedRowGroups.Length(); rgIndex-- > 0;) {
  3335. nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
  3336. nsTableRowFrame* row = rgFrame->GetLastRow();
  3337. if (row) {
  3338. *aBaseline = BSize(aWM) - TableBaseline(rgFrame, row);
  3339. return true;
  3340. }
  3341. }
  3342. }
  3343. return false;
  3344. }
  3345. /* ----- global methods ----- */
  3346. nsTableFrame*
  3347. NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  3348. {
  3349. return new (aPresShell) nsTableFrame(aContext);
  3350. }
  3351. NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)
  3352. nsTableFrame*
  3353. nsTableFrame::GetTableFrame(nsIFrame* aFrame)
  3354. {
  3355. for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
  3356. ancestor = ancestor->GetParent()) {
  3357. if (nsGkAtoms::tableFrame == ancestor->GetType()) {
  3358. return static_cast<nsTableFrame*>(ancestor);
  3359. }
  3360. }
  3361. NS_RUNTIMEABORT("unable to find table parent");
  3362. return nullptr;
  3363. }
  3364. nsTableFrame*
  3365. nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
  3366. nsIFrame* aFrame,
  3367. bool* aDidPassThrough)
  3368. {
  3369. MOZ_ASSERT(aMustPassThrough == aFrame ||
  3370. nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
  3371. "aMustPassThrough should be an ancestor");
  3372. // Retrieve the table frame, and check if we hit aMustPassThrough on the
  3373. // way.
  3374. *aDidPassThrough = false;
  3375. nsTableFrame* tableFrame = nullptr;
  3376. for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
  3377. if (ancestor == aMustPassThrough) {
  3378. *aDidPassThrough = true;
  3379. }
  3380. if (nsGkAtoms::tableFrame == ancestor->GetType()) {
  3381. tableFrame = static_cast<nsTableFrame*>(ancestor);
  3382. break;
  3383. }
  3384. }
  3385. MOZ_ASSERT(tableFrame, "Should have a table frame here");
  3386. return tableFrame;
  3387. }
  3388. bool
  3389. nsTableFrame::IsAutoBSize(WritingMode aWM)
  3390. {
  3391. const nsStyleCoord &bsize = StylePosition()->BSize(aWM);
  3392. // Don't consider calc() here like this quirk for percent.
  3393. return bsize.GetUnit() == eStyleUnit_Auto ||
  3394. (bsize.GetUnit() == eStyleUnit_Percent &&
  3395. bsize.GetPercentValue() <= 0.0f);
  3396. }
  3397. nscoord
  3398. nsTableFrame::CalcBorderBoxBSize(const ReflowInput& aState)
  3399. {
  3400. nscoord bSize = aState.ComputedBSize();
  3401. if (NS_AUTOHEIGHT != bSize) {
  3402. WritingMode wm = aState.GetWritingMode();
  3403. LogicalMargin borderPadding = GetChildAreaOffset(wm, &aState);
  3404. bSize += borderPadding.BStartEnd(wm);
  3405. }
  3406. bSize = std::max(0, bSize);
  3407. return bSize;
  3408. }
  3409. bool
  3410. nsTableFrame::IsAutoLayout()
  3411. {
  3412. if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
  3413. return true;
  3414. // a fixed-layout inline-table must have a inline size
  3415. // and tables with inline size set to '-moz-max-content' must be
  3416. // auto-layout (at least as long as
  3417. // FixedTableLayoutStrategy::GetPrefISize returns nscoord_MAX)
  3418. const nsStyleCoord &iSize = StylePosition()->ISize(GetWritingMode());
  3419. return (iSize.GetUnit() == eStyleUnit_Auto) ||
  3420. (iSize.GetUnit() == eStyleUnit_Enumerated &&
  3421. iSize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
  3422. }
  3423. #ifdef DEBUG_FRAME_DUMP
  3424. nsresult
  3425. nsTableFrame::GetFrameName(nsAString& aResult) const
  3426. {
  3427. return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
  3428. }
  3429. #endif
  3430. // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
  3431. // is of type aChildType
  3432. nsIFrame*
  3433. nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
  3434. nsIFrame* aPriorChildFrame,
  3435. nsIAtom* aChildType)
  3436. {
  3437. nsIFrame* result = nullptr;
  3438. if (!aPriorChildFrame) {
  3439. return result;
  3440. }
  3441. if (aChildType == aPriorChildFrame->GetType()) {
  3442. return aPriorChildFrame;
  3443. }
  3444. // aPriorChildFrame is not of type aChildType, so we need start from
  3445. // the beginnng and find the closest one
  3446. nsIFrame* lastMatchingFrame = nullptr;
  3447. nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild();
  3448. while (childFrame && (childFrame != aPriorChildFrame)) {
  3449. if (aChildType == childFrame->GetType()) {
  3450. lastMatchingFrame = childFrame;
  3451. }
  3452. childFrame = childFrame->GetNextSibling();
  3453. }
  3454. return lastMatchingFrame;
  3455. }
  3456. #ifdef DEBUG
  3457. void
  3458. nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
  3459. {
  3460. if (!aKidFrame)
  3461. return;
  3462. for (nsIFrame* cFrame : aKidFrame->PrincipalChildList()) {
  3463. nsTableRowFrame *rowFrame = do_QueryFrame(cFrame);
  3464. if (rowFrame) {
  3465. printf("row(%d)=%p ", rowFrame->GetRowIndex(),
  3466. static_cast<void*>(rowFrame));
  3467. for (nsIFrame* childFrame : cFrame->PrincipalChildList()) {
  3468. nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
  3469. if (cellFrame) {
  3470. uint32_t colIndex = cellFrame->ColIndex();
  3471. printf("cell(%u)=%p ", colIndex, static_cast<void*>(childFrame));
  3472. }
  3473. }
  3474. printf("\n");
  3475. }
  3476. else {
  3477. DumpRowGroup(rowFrame);
  3478. }
  3479. }
  3480. }
  3481. void
  3482. nsTableFrame::Dump(bool aDumpRows,
  3483. bool aDumpCols,
  3484. bool aDumpCellMap)
  3485. {
  3486. printf("***START TABLE DUMP*** \n");
  3487. // dump the columns widths array
  3488. printf("mColWidths=");
  3489. int32_t numCols = GetColCount();
  3490. int32_t colIdx;
  3491. nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
  3492. for (colIdx = 0; colIdx < numCols; colIdx++) {
  3493. printf("%d ", fif->GetColumnISizeFromFirstInFlow(colIdx));
  3494. }
  3495. printf("\n");
  3496. if (aDumpRows) {
  3497. nsIFrame* kidFrame = mFrames.FirstChild();
  3498. while (kidFrame) {
  3499. DumpRowGroup(kidFrame);
  3500. kidFrame = kidFrame->GetNextSibling();
  3501. }
  3502. }
  3503. if (aDumpCols) {
  3504. // output col frame cache
  3505. printf("\n col frame cache ->");
  3506. for (colIdx = 0; colIdx < numCols; colIdx++) {
  3507. nsTableColFrame* colFrame = mColFrames.ElementAt(colIdx);
  3508. if (0 == (colIdx % 8)) {
  3509. printf("\n");
  3510. }
  3511. printf ("%d=%p ", colIdx, static_cast<void*>(colFrame));
  3512. nsTableColType colType = colFrame->GetColType();
  3513. switch (colType) {
  3514. case eColContent:
  3515. printf(" content ");
  3516. break;
  3517. case eColAnonymousCol:
  3518. printf(" anonymous-column ");
  3519. break;
  3520. case eColAnonymousColGroup:
  3521. printf(" anonymous-colgroup ");
  3522. break;
  3523. case eColAnonymousCell:
  3524. printf(" anonymous-cell ");
  3525. break;
  3526. }
  3527. }
  3528. printf("\n colgroups->");
  3529. for (nsIFrame* childFrame : mColGroups) {
  3530. if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
  3531. nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
  3532. colGroupFrame->Dump(1);
  3533. }
  3534. }
  3535. for (colIdx = 0; colIdx < numCols; colIdx++) {
  3536. printf("\n");
  3537. nsTableColFrame* colFrame = GetColFrame(colIdx);
  3538. colFrame->Dump(1);
  3539. }
  3540. }
  3541. if (aDumpCellMap) {
  3542. nsTableCellMap* cellMap = GetCellMap();
  3543. cellMap->Dump();
  3544. }
  3545. printf(" ***END TABLE DUMP*** \n");
  3546. }
  3547. #endif
  3548. bool
  3549. nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const
  3550. {
  3551. // Since fixed-layout tables should not have their column sizes change
  3552. // as they load, we assume that all columns are significant.
  3553. if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
  3554. return true;
  3555. // the first column is always significant
  3556. if (aColIndex == 0)
  3557. return true;
  3558. nsTableCellMap* cellMap = GetCellMap();
  3559. if (!cellMap)
  3560. return false;
  3561. return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
  3562. }
  3563. /********************************************************************************
  3564. * Collapsing Borders
  3565. *
  3566. * The CSS spec says to resolve border conflicts in this order:
  3567. * 1) any border with the style HIDDEN wins
  3568. * 2) the widest border with a style that is not NONE wins
  3569. * 3) the border styles are ranked in this order, highest to lowest precedence:
  3570. * double, solid, dashed, dotted, ridge, outset, groove, inset
  3571. * 4) borders that are of equal width and style (differ only in color) have this precedence:
  3572. * cell, row, rowgroup, col, colgroup, table
  3573. * 5) if all border styles are NONE, then that's the computed border style.
  3574. *******************************************************************************/
  3575. #ifdef DEBUG
  3576. #define VerifyNonNegativeDamageRect(r) \
  3577. NS_ASSERTION((r).StartCol() >= 0, "negative col index"); \
  3578. NS_ASSERTION((r).StartRow() >= 0, "negative row index"); \
  3579. NS_ASSERTION((r).ColCount() >= 0, "negative cols damage"); \
  3580. NS_ASSERTION((r).RowCount() >= 0, "negative rows damage");
  3581. #define VerifyDamageRect(r) \
  3582. VerifyNonNegativeDamageRect(r); \
  3583. NS_ASSERTION((r).EndCol() <= GetColCount(), \
  3584. "cols damage extends outside table"); \
  3585. NS_ASSERTION((r).EndRow() <= GetRowCount(), \
  3586. "rows damage extends outside table");
  3587. #endif
  3588. void
  3589. nsTableFrame::AddBCDamageArea(const TableArea& aValue)
  3590. {
  3591. NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
  3592. #ifdef DEBUG
  3593. VerifyDamageRect(aValue);
  3594. #endif
  3595. SetNeedToCalcBCBorders(true);
  3596. SetNeedToCalcHasBCBorders(true);
  3597. // Get the property
  3598. BCPropertyData* value = GetOrCreateBCProperty();
  3599. if (value) {
  3600. #ifdef DEBUG
  3601. VerifyNonNegativeDamageRect(value->mDamageArea);
  3602. #endif
  3603. // Clamp the old damage area to the current table area in case it shrunk.
  3604. int32_t cols = GetColCount();
  3605. if (value->mDamageArea.EndCol() > cols) {
  3606. if (value->mDamageArea.StartCol() > cols) {
  3607. value->mDamageArea.StartCol() = cols;
  3608. value->mDamageArea.ColCount() = 0;
  3609. }
  3610. else {
  3611. value->mDamageArea.ColCount() = cols - value->mDamageArea.StartCol();
  3612. }
  3613. }
  3614. int32_t rows = GetRowCount();
  3615. if (value->mDamageArea.EndRow() > rows) {
  3616. if (value->mDamageArea.StartRow() > rows) {
  3617. value->mDamageArea.StartRow() = rows;
  3618. value->mDamageArea.RowCount() = 0;
  3619. }
  3620. else {
  3621. value->mDamageArea.RowCount() = rows - value->mDamageArea.StartRow();
  3622. }
  3623. }
  3624. // Construct a union of the new and old damage areas.
  3625. value->mDamageArea.UnionArea(value->mDamageArea, aValue);
  3626. }
  3627. }
  3628. void
  3629. nsTableFrame::SetFullBCDamageArea()
  3630. {
  3631. NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
  3632. SetNeedToCalcBCBorders(true);
  3633. SetNeedToCalcHasBCBorders(true);
  3634. BCPropertyData* value = GetOrCreateBCProperty();
  3635. if (value) {
  3636. value->mDamageArea = TableArea(0, 0, GetColCount(), GetRowCount());
  3637. }
  3638. }
  3639. /* BCCellBorder represents a border segment which can be either an inline-dir
  3640. * or a block-dir segment. For each segment we need to know the color, width,
  3641. * style, who owns it and how long it is in cellmap coordinates.
  3642. * Ownership of these segments is important to calculate which corners should
  3643. * be bevelled. This structure has dual use, its used first to compute the
  3644. * dominant border for inline-dir and block-dir segments and to store the
  3645. * preliminary computed border results in the BCCellBorders structure.
  3646. * This temporary storage is not symmetric with respect to inline-dir and
  3647. * block-dir border segments, its always column oriented. For each column in
  3648. * the cellmap there is a temporary stored block-dir and inline-dir segment.
  3649. * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
  3650. */
  3651. struct BCCellBorder
  3652. {
  3653. BCCellBorder() { Reset(0, 1); }
  3654. void Reset(uint32_t aRowIndex, uint32_t aRowSpan);
  3655. nscolor color; // border segment color
  3656. BCPixelSize width; // border segment width in pixel coordinates !!
  3657. uint8_t style; // border segment style, possible values are defined
  3658. // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
  3659. BCBorderOwner owner; // border segment owner, possible values are defined
  3660. // in celldata.h. In the cellmap for each border
  3661. // segment we store the owner and later when
  3662. // painting we know the owner and can retrieve the
  3663. // style info from the corresponding frame
  3664. int32_t rowIndex; // rowIndex of temporary stored inline-dir border
  3665. // segments relative to the table
  3666. int32_t rowSpan; // row span of temporary stored inline-dir border
  3667. // segments
  3668. };
  3669. void
  3670. BCCellBorder::Reset(uint32_t aRowIndex,
  3671. uint32_t aRowSpan)
  3672. {
  3673. style = NS_STYLE_BORDER_STYLE_NONE;
  3674. color = 0;
  3675. width = 0;
  3676. owner = eTableOwner;
  3677. rowIndex = aRowIndex;
  3678. rowSpan = aRowSpan;
  3679. }
  3680. class BCMapCellIterator;
  3681. /*****************************************************************
  3682. * BCMapCellInfo
  3683. * This structure stores information about the cellmap and all involved
  3684. * table related frames that are used during the computation of winning borders
  3685. * in CalcBCBorders so that they do need to be looked up again and again when
  3686. * iterating over the cells.
  3687. ****************************************************************/
  3688. struct BCMapCellInfo
  3689. {
  3690. explicit BCMapCellInfo(nsTableFrame* aTableFrame);
  3691. void ResetCellInfo();
  3692. void SetInfo(nsTableRowFrame* aNewRow,
  3693. int32_t aColIndex,
  3694. BCCellData* aCellData,
  3695. BCMapCellIterator* aIter,
  3696. nsCellMap* aCellMap = nullptr);
  3697. // The BCMapCellInfo has functions to set the continous
  3698. // border widths (see nsTablePainter.cpp for a description of the continous
  3699. // borders concept). The widths are computed inside these functions based on
  3700. // the current position inside the table and the cached frames that correspond
  3701. // to this position. The widths are stored in member variables of the internal
  3702. // table frames.
  3703. void SetTableBStartIStartContBCBorder();
  3704. void SetRowGroupIStartContBCBorder();
  3705. void SetRowGroupIEndContBCBorder();
  3706. void SetRowGroupBEndContBCBorder();
  3707. void SetRowIStartContBCBorder();
  3708. void SetRowIEndContBCBorder();
  3709. void SetColumnBStartIEndContBCBorder();
  3710. void SetColumnBEndContBCBorder();
  3711. void SetColGroupBEndContBCBorder();
  3712. void SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup,
  3713. nsTableRowFrame* aNextRow);
  3714. // functions to set the border widths on the table related frames, where the
  3715. // knowledge about the current position in the table is used.
  3716. void SetTableBStartBorderWidth(BCPixelSize aWidth);
  3717. void SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth);
  3718. void SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth);
  3719. void SetTableBEndBorderWidth(BCPixelSize aWidth);
  3720. void SetIStartBorderWidths(BCPixelSize aWidth);
  3721. void SetIEndBorderWidths(BCPixelSize aWidth);
  3722. void SetBStartBorderWidths(BCPixelSize aWidth);
  3723. void SetBEndBorderWidths(BCPixelSize aWidth);
  3724. // functions to compute the borders; they depend on the
  3725. // knowledge about the current position in the table. The edge functions
  3726. // should be called if a table edge is involved, otherwise the internal
  3727. // functions should be called.
  3728. BCCellBorder GetBStartEdgeBorder();
  3729. BCCellBorder GetBEndEdgeBorder();
  3730. BCCellBorder GetIStartEdgeBorder();
  3731. BCCellBorder GetIEndEdgeBorder();
  3732. BCCellBorder GetIEndInternalBorder();
  3733. BCCellBorder GetIStartInternalBorder();
  3734. BCCellBorder GetBStartInternalBorder();
  3735. BCCellBorder GetBEndInternalBorder();
  3736. // functions to set the internal position information
  3737. void SetColumn(int32_t aColX);
  3738. // Increment the row as we loop over the rows of a rowspan
  3739. void IncrementRow(bool aResetToBStartRowOfCell = false);
  3740. // Helper functions to get extent of the cell
  3741. int32_t GetCellEndRowIndex() const;
  3742. int32_t GetCellEndColIndex() const;
  3743. // storage of table information
  3744. nsTableFrame* mTableFrame;
  3745. int32_t mNumTableRows;
  3746. int32_t mNumTableCols;
  3747. BCPropertyData* mTableBCData;
  3748. WritingMode mTableWM;
  3749. // a cell can only belong to one rowgroup
  3750. nsTableRowGroupFrame* mRowGroup;
  3751. // a cell with a rowspan has a bstart and a bend row, and rows in between
  3752. nsTableRowFrame* mStartRow;
  3753. nsTableRowFrame* mEndRow;
  3754. nsTableRowFrame* mCurrentRowFrame;
  3755. // a cell with a colspan has an istart and iend column and columns in between
  3756. // they can belong to different colgroups
  3757. nsTableColGroupFrame* mColGroup;
  3758. nsTableColGroupFrame* mCurrentColGroupFrame;
  3759. nsTableColFrame* mStartCol;
  3760. nsTableColFrame* mEndCol;
  3761. nsTableColFrame* mCurrentColFrame;
  3762. // cell information
  3763. BCCellData* mCellData;
  3764. nsBCTableCellFrame* mCell;
  3765. int32_t mRowIndex;
  3766. int32_t mRowSpan;
  3767. int32_t mColIndex;
  3768. int32_t mColSpan;
  3769. // flags to describe the position of the cell with respect to the row- and
  3770. // colgroups, for instance mRgAtStart documents that the bStart cell border hits
  3771. // a rowgroup border
  3772. bool mRgAtStart;
  3773. bool mRgAtEnd;
  3774. bool mCgAtStart;
  3775. bool mCgAtEnd;
  3776. };
  3777. BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
  3778. : mTableFrame(aTableFrame)
  3779. , mNumTableRows(aTableFrame->GetRowCount())
  3780. , mNumTableCols(aTableFrame->GetColCount())
  3781. , mTableBCData(mTableFrame->GetProperty(TableBCProperty()))
  3782. , mTableWM(aTableFrame->StyleContext())
  3783. {
  3784. ResetCellInfo();
  3785. }
  3786. void
  3787. BCMapCellInfo::ResetCellInfo()
  3788. {
  3789. mCellData = nullptr;
  3790. mRowGroup = nullptr;
  3791. mStartRow = nullptr;
  3792. mEndRow = nullptr;
  3793. mColGroup = nullptr;
  3794. mStartCol = nullptr;
  3795. mEndCol = nullptr;
  3796. mCell = nullptr;
  3797. mRowIndex = mRowSpan = mColIndex = mColSpan = 0;
  3798. mRgAtStart = mRgAtEnd = mCgAtStart = mCgAtEnd = false;
  3799. }
  3800. inline int32_t
  3801. BCMapCellInfo::GetCellEndRowIndex() const
  3802. {
  3803. return mRowIndex + mRowSpan - 1;
  3804. }
  3805. inline int32_t
  3806. BCMapCellInfo::GetCellEndColIndex() const
  3807. {
  3808. return mColIndex + mColSpan - 1;
  3809. }
  3810. class BCMapCellIterator
  3811. {
  3812. public:
  3813. BCMapCellIterator(nsTableFrame* aTableFrame,
  3814. const TableArea& aDamageArea);
  3815. void First(BCMapCellInfo& aMapCellInfo);
  3816. void Next(BCMapCellInfo& aMapCellInfo);
  3817. void PeekIEnd(BCMapCellInfo& aRefInfo,
  3818. uint32_t aRowIndex,
  3819. BCMapCellInfo& aAjaInfo);
  3820. void PeekBEnd(BCMapCellInfo& aRefInfo,
  3821. uint32_t aColIndex,
  3822. BCMapCellInfo& aAjaInfo);
  3823. bool IsNewRow() { return mIsNewRow; }
  3824. nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
  3825. nsTableRowFrame* GetCurrentRow() const { return mRow; }
  3826. nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup; }
  3827. int32_t mRowGroupStart;
  3828. int32_t mRowGroupEnd;
  3829. bool mAtEnd;
  3830. nsCellMap* mCellMap;
  3831. private:
  3832. bool SetNewRow(nsTableRowFrame* row = nullptr);
  3833. bool SetNewRowGroup(bool aFindFirstDamagedRow);
  3834. nsTableFrame* mTableFrame;
  3835. nsTableCellMap* mTableCellMap;
  3836. nsTableFrame::RowGroupArray mRowGroups;
  3837. nsTableRowGroupFrame* mRowGroup;
  3838. int32_t mRowGroupIndex;
  3839. uint32_t mNumTableRows;
  3840. nsTableRowFrame* mRow;
  3841. nsTableRowFrame* mPrevRow;
  3842. bool mIsNewRow;
  3843. int32_t mRowIndex;
  3844. uint32_t mNumTableCols;
  3845. int32_t mColIndex;
  3846. nsPoint mAreaStart; // These are not really points in the usual
  3847. nsPoint mAreaEnd; // sense; they're column/row coordinates
  3848. // in the cell map.
  3849. };
  3850. BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
  3851. const TableArea& aDamageArea)
  3852. : mTableFrame(aTableFrame)
  3853. {
  3854. mTableCellMap = aTableFrame->GetCellMap();
  3855. mAreaStart.x = aDamageArea.StartCol();
  3856. mAreaStart.y = aDamageArea.StartRow();
  3857. mAreaEnd.x = aDamageArea.EndCol() - 1;
  3858. mAreaEnd.y = aDamageArea.EndRow() - 1;
  3859. mNumTableRows = mTableFrame->GetRowCount();
  3860. mRow = nullptr;
  3861. mRowIndex = 0;
  3862. mNumTableCols = mTableFrame->GetColCount();
  3863. mColIndex = 0;
  3864. mRowGroupIndex = -1;
  3865. // Get the ordered row groups
  3866. aTableFrame->OrderRowGroups(mRowGroups);
  3867. mAtEnd = true; // gets reset when First() is called
  3868. }
  3869. // fill fields that we need for border collapse computation on a given cell
  3870. void
  3871. BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow,
  3872. int32_t aColIndex,
  3873. BCCellData* aCellData,
  3874. BCMapCellIterator* aIter,
  3875. nsCellMap* aCellMap)
  3876. {
  3877. // fill the cell information
  3878. mCellData = aCellData;
  3879. mColIndex = aColIndex;
  3880. // initialize the row information if it was not previously set for cells in
  3881. // this row
  3882. mRowIndex = 0;
  3883. if (aNewRow) {
  3884. mStartRow = aNewRow;
  3885. mRowIndex = aNewRow->GetRowIndex();
  3886. }
  3887. // fill cell frame info and row information
  3888. mCell = nullptr;
  3889. mRowSpan = 1;
  3890. mColSpan = 1;
  3891. if (aCellData) {
  3892. mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
  3893. if (mCell) {
  3894. if (!mStartRow) {
  3895. mStartRow = mCell->GetTableRowFrame();
  3896. if (!mStartRow) ABORT0();
  3897. mRowIndex = mStartRow->GetRowIndex();
  3898. }
  3899. mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
  3900. mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
  3901. }
  3902. }
  3903. if (!mStartRow) {
  3904. mStartRow = aIter->GetCurrentRow();
  3905. }
  3906. if (1 == mRowSpan) {
  3907. mEndRow = mStartRow;
  3908. }
  3909. else {
  3910. mEndRow = mStartRow->GetNextRow();
  3911. if (mEndRow) {
  3912. for (int32_t span = 2; mEndRow && span < mRowSpan; span++) {
  3913. mEndRow = mEndRow->GetNextRow();
  3914. }
  3915. NS_ASSERTION(mEndRow, "spanned row not found");
  3916. }
  3917. else {
  3918. NS_ERROR("error in cell map");
  3919. mRowSpan = 1;
  3920. mEndRow = mStartRow;
  3921. }
  3922. }
  3923. // row group frame info
  3924. // try to reuse the rgStart and rgEnd from the iterator as calls to
  3925. // GetRowCount() are computationally expensive and should be avoided if
  3926. // possible
  3927. uint32_t rgStart = aIter->mRowGroupStart;
  3928. uint32_t rgEnd = aIter->mRowGroupEnd;
  3929. mRowGroup = mStartRow->GetTableRowGroupFrame();
  3930. if (mRowGroup != aIter->GetCurrentRowGroup()) {
  3931. rgStart = mRowGroup->GetStartRowIndex();
  3932. rgEnd = rgStart + mRowGroup->GetRowCount() - 1;
  3933. }
  3934. uint32_t rowIndex = mStartRow->GetRowIndex();
  3935. mRgAtStart = rgStart == rowIndex;
  3936. mRgAtEnd = rgEnd == rowIndex + mRowSpan - 1;
  3937. // col frame info
  3938. mStartCol = mTableFrame->GetColFrame(aColIndex);
  3939. if (!mStartCol) ABORT0();
  3940. mEndCol = mStartCol;
  3941. if (mColSpan > 1) {
  3942. nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex +
  3943. mColSpan -1);
  3944. if (!colFrame) ABORT0();
  3945. mEndCol = colFrame;
  3946. }
  3947. // col group frame info
  3948. mColGroup = mStartCol->GetTableColGroupFrame();
  3949. int32_t cgStart = mColGroup->GetStartColumnIndex();
  3950. int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1);
  3951. mCgAtStart = cgStart == aColIndex;
  3952. mCgAtEnd = cgEnd == aColIndex + mColSpan - 1;
  3953. }
  3954. bool
  3955. BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
  3956. {
  3957. mAtEnd = true;
  3958. mPrevRow = mRow;
  3959. if (aRow) {
  3960. mRow = aRow;
  3961. }
  3962. else if (mRow) {
  3963. mRow = mRow->GetNextRow();
  3964. }
  3965. if (mRow) {
  3966. mRowIndex = mRow->GetRowIndex();
  3967. // get to the first entry with an originating cell
  3968. int32_t rgRowIndex = mRowIndex - mRowGroupStart;
  3969. if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length())
  3970. ABORT1(false);
  3971. const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
  3972. for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
  3973. CellData* cellData = row.SafeElementAt(mColIndex);
  3974. if (!cellData) { // add a dead cell data
  3975. TableArea damageArea;
  3976. cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex,
  3977. false, 0, damageArea);
  3978. if (!cellData) ABORT1(false);
  3979. }
  3980. if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
  3981. break;
  3982. }
  3983. }
  3984. mIsNewRow = true;
  3985. mAtEnd = false;
  3986. }
  3987. else ABORT1(false);
  3988. return !mAtEnd;
  3989. }
  3990. bool
  3991. BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow)
  3992. {
  3993. mAtEnd = true;
  3994. int32_t numRowGroups = mRowGroups.Length();
  3995. mCellMap = nullptr;
  3996. for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
  3997. mRowGroup = mRowGroups[mRowGroupIndex];
  3998. int32_t rowCount = mRowGroup->GetRowCount();
  3999. mRowGroupStart = mRowGroup->GetStartRowIndex();
  4000. mRowGroupEnd = mRowGroupStart + rowCount - 1;
  4001. if (rowCount > 0) {
  4002. mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
  4003. if (!mCellMap) ABORT1(false);
  4004. nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
  4005. if (aFindFirstDamagedRow) {
  4006. if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
  4007. // the damage area starts in the row group
  4008. if (aFindFirstDamagedRow) {
  4009. // find the correct first damaged row
  4010. int32_t numRows = mAreaStart.y - mRowGroupStart;
  4011. for (int32_t i = 0; i < numRows; i++) {
  4012. firstRow = firstRow->GetNextRow();
  4013. if (!firstRow) ABORT1(false);
  4014. }
  4015. }
  4016. }
  4017. else {
  4018. continue;
  4019. }
  4020. }
  4021. if (SetNewRow(firstRow)) { // sets mAtEnd
  4022. break;
  4023. }
  4024. }
  4025. }
  4026. return !mAtEnd;
  4027. }
  4028. void
  4029. BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
  4030. {
  4031. aMapInfo.ResetCellInfo();
  4032. SetNewRowGroup(true); // sets mAtEnd
  4033. while (!mAtEnd) {
  4034. if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
  4035. BCCellData* cellData =
  4036. static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y -
  4037. mRowGroupStart,
  4038. mAreaStart.x));
  4039. if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
  4040. aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
  4041. return;
  4042. }
  4043. else {
  4044. NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) ,
  4045. "damage area expanded incorrectly");
  4046. }
  4047. }
  4048. SetNewRowGroup(true); // sets mAtEnd
  4049. }
  4050. }
  4051. void
  4052. BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
  4053. {
  4054. if (mAtEnd) ABORT0();
  4055. aMapInfo.ResetCellInfo();
  4056. mIsNewRow = false;
  4057. mColIndex++;
  4058. while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
  4059. for (; mColIndex <= mAreaEnd.x; mColIndex++) {
  4060. int32_t rgRowIndex = mRowIndex - mRowGroupStart;
  4061. BCCellData* cellData =
  4062. static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
  4063. if (!cellData) { // add a dead cell data
  4064. TableArea damageArea;
  4065. cellData =
  4066. static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
  4067. rgRowIndex, false, 0,
  4068. damageArea));
  4069. if (!cellData) ABORT0();
  4070. }
  4071. if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
  4072. aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
  4073. return;
  4074. }
  4075. }
  4076. if (mRowIndex >= mRowGroupEnd) {
  4077. SetNewRowGroup(false); // could set mAtEnd
  4078. }
  4079. else {
  4080. SetNewRow(); // could set mAtEnd
  4081. }
  4082. }
  4083. mAtEnd = true;
  4084. }
  4085. void
  4086. BCMapCellIterator::PeekIEnd(BCMapCellInfo& aRefInfo,
  4087. uint32_t aRowIndex,
  4088. BCMapCellInfo& aAjaInfo)
  4089. {
  4090. aAjaInfo.ResetCellInfo();
  4091. int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan;
  4092. uint32_t rgRowIndex = aRowIndex - mRowGroupStart;
  4093. BCCellData* cellData =
  4094. static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
  4095. if (!cellData) { // add a dead cell data
  4096. NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
  4097. TableArea damageArea;
  4098. cellData =
  4099. static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
  4100. rgRowIndex, false, 0,
  4101. damageArea));
  4102. if (!cellData) ABORT0();
  4103. }
  4104. nsTableRowFrame* row = nullptr;
  4105. if (cellData->IsRowSpan()) {
  4106. rgRowIndex -= cellData->GetRowSpanOffset();
  4107. cellData =
  4108. static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
  4109. if (!cellData)
  4110. ABORT0();
  4111. }
  4112. else {
  4113. row = mRow;
  4114. }
  4115. aAjaInfo.SetInfo(row, colIndex, cellData, this);
  4116. }
  4117. void
  4118. BCMapCellIterator::PeekBEnd(BCMapCellInfo& aRefInfo,
  4119. uint32_t aColIndex,
  4120. BCMapCellInfo& aAjaInfo)
  4121. {
  4122. aAjaInfo.ResetCellInfo();
  4123. int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
  4124. int32_t rgRowIndex = rowIndex - mRowGroupStart;
  4125. nsTableRowGroupFrame* rg = mRowGroup;
  4126. nsCellMap* cellMap = mCellMap;
  4127. nsTableRowFrame* nextRow = nullptr;
  4128. if (rowIndex > mRowGroupEnd) {
  4129. int32_t nextRgIndex = mRowGroupIndex;
  4130. do {
  4131. nextRgIndex++;
  4132. rg = mRowGroups.SafeElementAt(nextRgIndex);
  4133. if (rg) {
  4134. cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
  4135. rgRowIndex = 0;
  4136. nextRow = rg->GetFirstRow();
  4137. }
  4138. }
  4139. while (rg && !nextRow);
  4140. if(!rg) return;
  4141. }
  4142. else {
  4143. // get the row within the same row group
  4144. nextRow = mRow;
  4145. for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) {
  4146. nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
  4147. }
  4148. }
  4149. BCCellData* cellData =
  4150. static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
  4151. if (!cellData) { // add a dead cell data
  4152. NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
  4153. TableArea damageArea;
  4154. cellData =
  4155. static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr,
  4156. rgRowIndex, false, 0,
  4157. damageArea));
  4158. if (!cellData) ABORT0();
  4159. }
  4160. if (cellData->IsColSpan()) {
  4161. aColIndex -= cellData->GetColSpanOffset();
  4162. cellData =
  4163. static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
  4164. }
  4165. aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
  4166. }
  4167. // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
  4168. // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
  4169. static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE
  4170. 2, // NS_STYLE_BORDER_STYLE_GROOVE
  4171. 4, // NS_STYLE_BORDER_STYLE_RIDGE
  4172. 5, // NS_STYLE_BORDER_STYLE_DOTTED
  4173. 6, // NS_STYLE_BORDER_STYLE_DASHED
  4174. 7, // NS_STYLE_BORDER_STYLE_SOLID
  4175. 8, // NS_STYLE_BORDER_STYLE_DOUBLE
  4176. 1, // NS_STYLE_BORDER_STYLE_INSET
  4177. 3, // NS_STYLE_BORDER_STYLE_OUTSET
  4178. 9 };// NS_STYLE_BORDER_STYLE_HIDDEN
  4179. // priority rules follow CSS 2.1 spec
  4180. // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
  4181. // and the lowest: 'inset'. none is even weaker
  4182. #define CELL_CORNER true
  4183. /** return the border style, border color and optionally the width in
  4184. * pixel for a given frame and side
  4185. * @param aFrame - query the info for this frame
  4186. * @param aTableWM - the writing-mode of the frame
  4187. * @param aSide - the side of the frame
  4188. * @param aStyle - the border style
  4189. * @param aColor - the border color
  4190. * @param aWidth - the border width in px
  4191. */
  4192. static void
  4193. GetColorAndStyle(const nsIFrame* aFrame,
  4194. WritingMode aTableWM,
  4195. LogicalSide aSide,
  4196. uint8_t* aStyle,
  4197. nscolor* aColor,
  4198. BCPixelSize* aWidth = nullptr)
  4199. {
  4200. NS_PRECONDITION(aFrame, "null frame");
  4201. NS_PRECONDITION(aStyle && aColor, "null argument");
  4202. // initialize out arg
  4203. *aColor = 0;
  4204. if (aWidth) {
  4205. *aWidth = 0;
  4206. }
  4207. const nsStyleBorder* styleData = aFrame->StyleBorder();
  4208. mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide);
  4209. *aStyle = styleData->GetBorderStyle(physicalSide);
  4210. if ((NS_STYLE_BORDER_STYLE_NONE == *aStyle) ||
  4211. (NS_STYLE_BORDER_STYLE_HIDDEN == *aStyle)) {
  4212. return;
  4213. }
  4214. *aColor = aFrame->StyleContext()->GetVisitedDependentColor(
  4215. nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[physicalSide]);
  4216. if (aWidth) {
  4217. nscoord width = styleData->GetComputedBorderWidth(physicalSide);
  4218. *aWidth = aFrame->PresContext()->AppUnitsToDevPixels(width);
  4219. }
  4220. }
  4221. /** coerce the paint style as required by CSS2.1
  4222. * @param aFrame - query the info for this frame
  4223. * @param aTableWM - the writing mode of the frame
  4224. * @param aSide - the side of the frame
  4225. * @param aStyle - the border style
  4226. * @param aColor - the border color
  4227. */
  4228. static void
  4229. GetPaintStyleInfo(const nsIFrame* aFrame,
  4230. WritingMode aTableWM,
  4231. LogicalSide aSide,
  4232. uint8_t* aStyle,
  4233. nscolor* aColor)
  4234. {
  4235. GetColorAndStyle(aFrame, aTableWM, aSide, aStyle, aColor);
  4236. if (NS_STYLE_BORDER_STYLE_INSET == *aStyle) {
  4237. *aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
  4238. } else if (NS_STYLE_BORDER_STYLE_OUTSET == *aStyle) {
  4239. *aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
  4240. }
  4241. }
  4242. class nsDelayedCalcBCBorders : public Runnable {
  4243. public:
  4244. explicit nsDelayedCalcBCBorders(nsIFrame* aFrame) :
  4245. mFrame(aFrame) {}
  4246. NS_IMETHOD Run() override {
  4247. if (mFrame) {
  4248. nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
  4249. if (tableFrame->NeedToCalcBCBorders()) {
  4250. tableFrame->CalcBCBorders();
  4251. }
  4252. }
  4253. return NS_OK;
  4254. }
  4255. private:
  4256. nsWeakFrame mFrame;
  4257. };
  4258. bool
  4259. nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
  4260. nsStyleContext* aNewStyleContext)
  4261. {
  4262. // Attention: the old style context is the one we're forgetting,
  4263. // and hence possibly completely bogus for GetStyle* purposes.
  4264. // We use PeekStyleData instead.
  4265. const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder();
  4266. if (!oldStyleData)
  4267. return false;
  4268. const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder();
  4269. nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
  4270. if (!change)
  4271. return false;
  4272. if (change & nsChangeHint_NeedReflow)
  4273. return true; // the caller only needs to mark the bc damage area
  4274. if (change & nsChangeHint_RepaintFrame) {
  4275. // we need to recompute the borders and the caller needs to mark
  4276. // the bc damage area
  4277. // XXX In principle this should only be necessary for border style changes
  4278. // However the bc painting code tries to maximize the drawn border segments
  4279. // so it stores in the cellmap where a new border segment starts and this
  4280. // introduces a unwanted cellmap data dependence on color
  4281. nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
  4282. NS_DispatchToCurrentThread(evt);
  4283. return true;
  4284. }
  4285. return false;
  4286. }
  4287. // Compare two border segments, this comparison depends whether the two
  4288. // segments meet at a corner and whether the second segment is inline-dir.
  4289. // The return value is whichever of aBorder1 or aBorder2 dominates.
  4290. static const BCCellBorder&
  4291. CompareBorders(bool aIsCorner, // Pass true for corner calculations
  4292. const BCCellBorder& aBorder1,
  4293. const BCCellBorder& aBorder2,
  4294. bool aSecondIsInlineDir,
  4295. bool* aFirstDominates = nullptr)
  4296. {
  4297. bool firstDominates = true;
  4298. if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
  4299. firstDominates = (aIsCorner) ? false : true;
  4300. }
  4301. else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
  4302. firstDominates = (aIsCorner) ? true : false;
  4303. }
  4304. else if (aBorder1.width < aBorder2.width) {
  4305. firstDominates = false;
  4306. }
  4307. else if (aBorder1.width == aBorder2.width) {
  4308. if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
  4309. firstDominates = false;
  4310. }
  4311. else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
  4312. if (aBorder1.owner == aBorder2.owner) {
  4313. firstDominates = !aSecondIsInlineDir;
  4314. }
  4315. else if (aBorder1.owner < aBorder2.owner) {
  4316. firstDominates = false;
  4317. }
  4318. }
  4319. }
  4320. if (aFirstDominates)
  4321. *aFirstDominates = firstDominates;
  4322. if (firstDominates)
  4323. return aBorder1;
  4324. return aBorder2;
  4325. }
  4326. /** calc the dominant border by considering the table, row/col group, row/col,
  4327. * cell.
  4328. * Depending on whether the side is block-dir or inline-dir and whether
  4329. * adjacent frames are taken into account the ownership of a single border
  4330. * segment is defined. The return value is the dominating border
  4331. * The cellmap stores only bstart and istart borders for each cellmap position.
  4332. * If the cell border is owned by the cell that is istart-wards of the border
  4333. * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
  4334. * scenarios with a adjacent owner.
  4335. * @param xxxFrame - the frame for style information, might be zero if
  4336. * it should not be considered
  4337. * @param aTableWM - the writing mode of the frame
  4338. * @param aSide - side of the frames that should be considered
  4339. * @param aAja - the border comparison takes place from the point of
  4340. * a frame that is adjacent to the cellmap entry, for
  4341. * when a cell owns its lower border it will be the
  4342. * adjacent owner as in the cellmap only bstart and
  4343. * istart borders are stored.
  4344. */
  4345. static BCCellBorder
  4346. CompareBorders(const nsIFrame* aTableFrame,
  4347. const nsIFrame* aColGroupFrame,
  4348. const nsIFrame* aColFrame,
  4349. const nsIFrame* aRowGroupFrame,
  4350. const nsIFrame* aRowFrame,
  4351. const nsIFrame* aCellFrame,
  4352. WritingMode aTableWM,
  4353. LogicalSide aSide,
  4354. bool aAja)
  4355. {
  4356. BCCellBorder border, tempBorder;
  4357. bool inlineAxis = IsBlock(aSide);
  4358. // start with the table as dominant if present
  4359. if (aTableFrame) {
  4360. GetColorAndStyle(aTableFrame, aTableWM, aSide,
  4361. &border.style, &border.color, &border.width);
  4362. border.owner = eTableOwner;
  4363. if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4364. return border;
  4365. }
  4366. }
  4367. // see if the colgroup is dominant
  4368. if (aColGroupFrame) {
  4369. GetColorAndStyle(aColGroupFrame, aTableWM, aSide,
  4370. &tempBorder.style, &tempBorder.color, &tempBorder.width);
  4371. tempBorder.owner = aAja && !inlineAxis ? eAjaColGroupOwner : eColGroupOwner;
  4372. // pass here and below false for aSecondIsInlineDir as it is only used for corner calculations.
  4373. border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4374. if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4375. return border;
  4376. }
  4377. }
  4378. // see if the col is dominant
  4379. if (aColFrame) {
  4380. GetColorAndStyle(aColFrame, aTableWM, aSide,
  4381. &tempBorder.style, &tempBorder.color, &tempBorder.width);
  4382. tempBorder.owner = aAja && !inlineAxis ? eAjaColOwner : eColOwner;
  4383. border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4384. if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4385. return border;
  4386. }
  4387. }
  4388. // see if the rowgroup is dominant
  4389. if (aRowGroupFrame) {
  4390. GetColorAndStyle(aRowGroupFrame, aTableWM, aSide,
  4391. &tempBorder.style, &tempBorder.color, &tempBorder.width);
  4392. tempBorder.owner = aAja && inlineAxis ? eAjaRowGroupOwner : eRowGroupOwner;
  4393. border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4394. if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4395. return border;
  4396. }
  4397. }
  4398. // see if the row is dominant
  4399. if (aRowFrame) {
  4400. GetColorAndStyle(aRowFrame, aTableWM, aSide,
  4401. &tempBorder.style, &tempBorder.color, &tempBorder.width);
  4402. tempBorder.owner = aAja && inlineAxis ? eAjaRowOwner : eRowOwner;
  4403. border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4404. if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4405. return border;
  4406. }
  4407. }
  4408. // see if the cell is dominant
  4409. if (aCellFrame) {
  4410. GetColorAndStyle(aCellFrame, aTableWM, aSide,
  4411. &tempBorder.style, &tempBorder.color, &tempBorder.width);
  4412. tempBorder.owner = aAja ? eAjaCellOwner : eCellOwner;
  4413. border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4414. }
  4415. return border;
  4416. }
  4417. static bool
  4418. Perpendicular(mozilla::LogicalSide aSide1,
  4419. mozilla::LogicalSide aSide2)
  4420. {
  4421. return IsInline(aSide1) != IsInline(aSide2);
  4422. }
  4423. // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
  4424. struct BCCornerInfo
  4425. {
  4426. BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide =
  4427. subElem = hasDashDot = numSegs = bevel = 0; ownerSide = eLogicalSideBStart;
  4428. ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; }
  4429. void Set(mozilla::LogicalSide aSide,
  4430. BCCellBorder border);
  4431. void Update(mozilla::LogicalSide aSide,
  4432. BCCellBorder border);
  4433. nscolor ownerColor; // color of borderOwner
  4434. uint16_t ownerWidth; // pixel width of borderOwner
  4435. uint16_t subWidth; // pixel width of the largest border intersecting the border perpendicular
  4436. // to ownerSide
  4437. uint32_t ownerSide:2; // LogicalSide (e.g eLogicalSideBStart, etc) of the border
  4438. // owning the corner relative to the corner
  4439. uint32_t ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner
  4440. uint32_t ownerStyle:8; // border style of ownerElem
  4441. uint32_t subSide:2; // side of border with subWidth relative to the corner
  4442. uint32_t subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner
  4443. uint32_t subStyle:8; // border style of subElem
  4444. uint32_t hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled
  4445. uint32_t numSegs:3; // number of segments entering corner
  4446. uint32_t bevel:1; // is the corner beveled (uses the above two fields together with subWidth)
  4447. uint32_t unused:1;
  4448. };
  4449. void
  4450. BCCornerInfo::Set(mozilla::LogicalSide aSide,
  4451. BCCellBorder aBorder)
  4452. {
  4453. ownerElem = aBorder.owner;
  4454. ownerStyle = aBorder.style;
  4455. ownerWidth = aBorder.width;
  4456. ownerColor = aBorder.color;
  4457. ownerSide = aSide;
  4458. hasDashDot = 0;
  4459. numSegs = 0;
  4460. if (aBorder.width > 0) {
  4461. numSegs++;
  4462. hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
  4463. (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
  4464. }
  4465. bevel = 0;
  4466. subWidth = 0;
  4467. // the following will get set later
  4468. subSide = IsInline(aSide) ? eLogicalSideBStart : eLogicalSideIStart;
  4469. subElem = eTableOwner;
  4470. subStyle = NS_STYLE_BORDER_STYLE_SOLID;
  4471. }
  4472. void
  4473. BCCornerInfo::Update(mozilla::LogicalSide aSide,
  4474. BCCellBorder aBorder)
  4475. {
  4476. bool existingWins = false;
  4477. if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
  4478. Set(aSide, aBorder);
  4479. }
  4480. else {
  4481. bool isInline = IsInline(aSide); // relative to the corner
  4482. BCCellBorder oldBorder, tempBorder;
  4483. oldBorder.owner = (BCBorderOwner) ownerElem;
  4484. oldBorder.style = ownerStyle;
  4485. oldBorder.width = ownerWidth;
  4486. oldBorder.color = ownerColor;
  4487. LogicalSide oldSide = LogicalSide(ownerSide);
  4488. tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, isInline, &existingWins);
  4489. ownerElem = tempBorder.owner;
  4490. ownerStyle = tempBorder.style;
  4491. ownerWidth = tempBorder.width;
  4492. ownerColor = tempBorder.color;
  4493. if (existingWins) { // existing corner is dominant
  4494. if (::Perpendicular(LogicalSide(ownerSide), aSide)) {
  4495. // see if the new sub info replaces the old
  4496. BCCellBorder subBorder;
  4497. subBorder.owner = (BCBorderOwner) subElem;
  4498. subBorder.style = subStyle;
  4499. subBorder.width = subWidth;
  4500. subBorder.color = 0; // we are not interested in subBorder color
  4501. bool firstWins;
  4502. tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, isInline, &firstWins);
  4503. subElem = tempBorder.owner;
  4504. subStyle = tempBorder.style;
  4505. subWidth = tempBorder.width;
  4506. if (!firstWins) {
  4507. subSide = aSide;
  4508. }
  4509. }
  4510. }
  4511. else { // input args are dominant
  4512. ownerSide = aSide;
  4513. if (::Perpendicular(oldSide, LogicalSide(ownerSide))) {
  4514. subElem = oldBorder.owner;
  4515. subStyle = oldBorder.style;
  4516. subWidth = oldBorder.width;
  4517. subSide = oldSide;
  4518. }
  4519. }
  4520. if (aBorder.width > 0) {
  4521. numSegs++;
  4522. if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
  4523. (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
  4524. hasDashDot = 1;
  4525. }
  4526. }
  4527. // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
  4528. bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
  4529. }
  4530. }
  4531. struct BCCorners
  4532. {
  4533. BCCorners(int32_t aNumCorners,
  4534. int32_t aStartIndex);
  4535. ~BCCorners() { delete [] corners; }
  4536. BCCornerInfo& operator [](int32_t i) const
  4537. { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
  4538. return corners[clamped(i, startIndex, endIndex) - startIndex]; }
  4539. int32_t startIndex;
  4540. int32_t endIndex;
  4541. BCCornerInfo* corners;
  4542. };
  4543. BCCorners::BCCorners(int32_t aNumCorners,
  4544. int32_t aStartIndex)
  4545. {
  4546. NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
  4547. startIndex = aStartIndex;
  4548. endIndex = aStartIndex + aNumCorners - 1;
  4549. corners = new BCCornerInfo[aNumCorners];
  4550. }
  4551. struct BCCellBorders
  4552. {
  4553. BCCellBorders(int32_t aNumBorders,
  4554. int32_t aStartIndex);
  4555. ~BCCellBorders() { delete [] borders; }
  4556. BCCellBorder& operator [](int32_t i) const
  4557. { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
  4558. return borders[clamped(i, startIndex, endIndex) - startIndex]; }
  4559. int32_t startIndex;
  4560. int32_t endIndex;
  4561. BCCellBorder* borders;
  4562. };
  4563. BCCellBorders::BCCellBorders(int32_t aNumBorders,
  4564. int32_t aStartIndex)
  4565. {
  4566. NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
  4567. startIndex = aStartIndex;
  4568. endIndex = aStartIndex + aNumBorders - 1;
  4569. borders = new BCCellBorder[aNumBorders];
  4570. }
  4571. // this function sets the new border properties and returns true if the border
  4572. // segment will start a new segment and not be accumulated into the previous
  4573. // segment.
  4574. static bool
  4575. SetBorder(const BCCellBorder& aNewBorder,
  4576. BCCellBorder& aBorder)
  4577. {
  4578. bool changed = (aNewBorder.style != aBorder.style) ||
  4579. (aNewBorder.width != aBorder.width) ||
  4580. (aNewBorder.color != aBorder.color);
  4581. aBorder.color = aNewBorder.color;
  4582. aBorder.width = aNewBorder.width;
  4583. aBorder.style = aNewBorder.style;
  4584. aBorder.owner = aNewBorder.owner;
  4585. return changed;
  4586. }
  4587. // this function will set the inline-dir border. It will return true if the
  4588. // existing segment will not be continued. Having a block-dir owner of a corner
  4589. // should also start a new segment.
  4590. static bool
  4591. SetInlineDirBorder(const BCCellBorder& aNewBorder,
  4592. const BCCornerInfo& aCorner,
  4593. BCCellBorder& aBorder)
  4594. {
  4595. bool startSeg = ::SetBorder(aNewBorder, aBorder);
  4596. if (!startSeg) {
  4597. startSeg = !IsInline(LogicalSide(aCorner.ownerSide));
  4598. }
  4599. return startSeg;
  4600. }
  4601. // Make the damage area larger on the top and bottom by at least one row and on the left and right
  4602. // at least one column. This is done so that adjacent elements are part of the border calculations.
  4603. // The extra segments and borders outside the actual damage area will not be updated in the cell map,
  4604. // because they in turn would need info from adjacent segments outside the damage area to be accurate.
  4605. void
  4606. nsTableFrame::ExpandBCDamageArea(TableArea& aArea) const
  4607. {
  4608. int32_t numRows = GetRowCount();
  4609. int32_t numCols = GetColCount();
  4610. int32_t dStartX = aArea.StartCol();
  4611. int32_t dEndX = aArea.EndCol() - 1;
  4612. int32_t dStartY = aArea.StartRow();
  4613. int32_t dEndY = aArea.EndRow() - 1;
  4614. // expand the damage area in each direction
  4615. if (dStartX > 0) {
  4616. dStartX--;
  4617. }
  4618. if (dEndX < (numCols - 1)) {
  4619. dEndX++;
  4620. }
  4621. if (dStartY > 0) {
  4622. dStartY--;
  4623. }
  4624. if (dEndY < (numRows - 1)) {
  4625. dEndY++;
  4626. }
  4627. // Check the damage area so that there are no cells spanning in or out. If there are any then
  4628. // make the damage area as big as the table, similarly to the way the cell map decides whether
  4629. // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
  4630. // no spanners, but it may not be worth the effort in general, and it would need to be done in the
  4631. // cell map as well.
  4632. bool haveSpanner = false;
  4633. if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
  4634. nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
  4635. // Get the ordered row groups
  4636. RowGroupArray rowGroups;
  4637. OrderRowGroups(rowGroups);
  4638. // Scope outside loop to be used as hint.
  4639. nsCellMap* cellMap = nullptr;
  4640. for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
  4641. nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
  4642. int32_t rgStartY = rgFrame->GetStartRowIndex();
  4643. int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1;
  4644. if (dEndY < rgStartY)
  4645. break;
  4646. cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
  4647. if (!cellMap) ABORT0();
  4648. // check for spanners from above and below
  4649. if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
  4650. if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length())
  4651. ABORT0();
  4652. const nsCellMap::CellDataArray& row =
  4653. cellMap->mRows[dStartY - rgStartY];
  4654. for (int32_t x = dStartX; x <= dEndX; x++) {
  4655. CellData* cellData = row.SafeElementAt(x);
  4656. if (cellData && (cellData->IsRowSpan())) {
  4657. haveSpanner = true;
  4658. break;
  4659. }
  4660. }
  4661. if (dEndY < rgEndY) {
  4662. if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
  4663. ABORT0();
  4664. const nsCellMap::CellDataArray& row2 =
  4665. cellMap->mRows[dEndY + 1 - rgStartY];
  4666. for (int32_t x = dStartX; x <= dEndX; x++) {
  4667. CellData* cellData = row2.SafeElementAt(x);
  4668. if (cellData && (cellData->IsRowSpan())) {
  4669. haveSpanner = true;
  4670. break;
  4671. }
  4672. }
  4673. }
  4674. }
  4675. // check for spanners on the left and right
  4676. int32_t iterStartY = -1;
  4677. int32_t iterEndY = -1;
  4678. if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
  4679. // the damage area starts in the row group
  4680. iterStartY = dStartY;
  4681. iterEndY = std::min(dEndY, rgEndY);
  4682. }
  4683. else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
  4684. // the damage area ends in the row group
  4685. iterStartY = rgStartY;
  4686. iterEndY = dEndY;
  4687. }
  4688. else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
  4689. // the damage area contains the row group
  4690. iterStartY = rgStartY;
  4691. iterEndY = rgEndY;
  4692. }
  4693. if ((iterStartY >= 0) && (iterEndY >= 0)) {
  4694. for (int32_t y = iterStartY; y <= iterEndY; y++) {
  4695. if (uint32_t(y - rgStartY) >= cellMap->mRows.Length())
  4696. ABORT0();
  4697. const nsCellMap::CellDataArray& row =
  4698. cellMap->mRows[y - rgStartY];
  4699. CellData* cellData = row.SafeElementAt(dStartX);
  4700. if (cellData && (cellData->IsColSpan())) {
  4701. haveSpanner = true;
  4702. break;
  4703. }
  4704. if (dEndX < (numCols - 1)) {
  4705. cellData = row.SafeElementAt(dEndX + 1);
  4706. if (cellData && (cellData->IsColSpan())) {
  4707. haveSpanner = true;
  4708. break;
  4709. }
  4710. }
  4711. }
  4712. }
  4713. }
  4714. }
  4715. if (haveSpanner) {
  4716. // make the damage area the whole table
  4717. aArea.StartCol() = 0;
  4718. aArea.StartRow() = 0;
  4719. aArea.ColCount() = numCols;
  4720. aArea.RowCount() = numRows;
  4721. }
  4722. else {
  4723. aArea.StartCol() = dStartX;
  4724. aArea.StartRow() = dStartY;
  4725. aArea.ColCount() = 1 + dEndX - dStartX;
  4726. aArea.RowCount() = 1 + dEndY - dStartY;
  4727. }
  4728. }
  4729. #define ADJACENT true
  4730. #define INLINE_DIR true
  4731. void
  4732. BCMapCellInfo::SetTableBStartIStartContBCBorder()
  4733. {
  4734. BCCellBorder currentBorder;
  4735. //calculate continuous top first row & rowgroup border: special case
  4736. //because it must include the table in the collapse
  4737. if (mStartRow) {
  4738. currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
  4739. mStartRow, nullptr, mTableWM,
  4740. eLogicalSideBStart, !ADJACENT);
  4741. mStartRow->SetContinuousBCBorderWidth(eLogicalSideBStart,
  4742. currentBorder.width);
  4743. }
  4744. if (mCgAtEnd && mColGroup) {
  4745. //calculate continuous top colgroup border once per colgroup
  4746. currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
  4747. mStartRow, nullptr, mTableWM,
  4748. eLogicalSideBStart, !ADJACENT);
  4749. mColGroup->SetContinuousBCBorderWidth(eLogicalSideBStart,
  4750. currentBorder.width);
  4751. }
  4752. if (0 == mColIndex) {
  4753. currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol, nullptr,
  4754. nullptr, nullptr, mTableWM,
  4755. eLogicalSideIStart, !ADJACENT);
  4756. mTableFrame->SetContinuousIStartBCBorderWidth(currentBorder.width);
  4757. }
  4758. }
  4759. void
  4760. BCMapCellInfo::SetRowGroupIStartContBCBorder()
  4761. {
  4762. BCCellBorder currentBorder;
  4763. //get row group continuous borders
  4764. if (mRgAtEnd && mRowGroup) { //once per row group, so check for bottom
  4765. currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol,
  4766. mRowGroup, nullptr, nullptr, mTableWM,
  4767. eLogicalSideIStart, !ADJACENT);
  4768. mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIStart,
  4769. currentBorder.width);
  4770. }
  4771. }
  4772. void
  4773. BCMapCellInfo::SetRowGroupIEndContBCBorder()
  4774. {
  4775. BCCellBorder currentBorder;
  4776. //get row group continuous borders
  4777. if (mRgAtEnd && mRowGroup) { //once per mRowGroup, so check for bottom
  4778. currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
  4779. nullptr, nullptr, mTableWM, eLogicalSideIEnd,
  4780. ADJACENT);
  4781. mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIEnd,
  4782. currentBorder.width);
  4783. }
  4784. }
  4785. void
  4786. BCMapCellInfo::SetColumnBStartIEndContBCBorder()
  4787. {
  4788. BCCellBorder currentBorder;
  4789. //calculate column continuous borders
  4790. //we only need to do this once, so we'll do it only on the first row
  4791. currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
  4792. mCurrentColFrame, mRowGroup, mStartRow,
  4793. nullptr, mTableWM, eLogicalSideBStart,
  4794. !ADJACENT);
  4795. mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBStart,
  4796. currentBorder.width);
  4797. if (mNumTableCols == GetCellEndColIndex() + 1) {
  4798. currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
  4799. mCurrentColFrame, nullptr, nullptr, nullptr,
  4800. mTableWM, eLogicalSideIEnd, !ADJACENT);
  4801. }
  4802. else {
  4803. currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame,
  4804. mCurrentColFrame, nullptr,nullptr, nullptr,
  4805. mTableWM, eLogicalSideIEnd, !ADJACENT);
  4806. }
  4807. mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd,
  4808. currentBorder.width);
  4809. }
  4810. void
  4811. BCMapCellInfo::SetColumnBEndContBCBorder()
  4812. {
  4813. BCCellBorder currentBorder;
  4814. //get col continuous border
  4815. currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
  4816. mCurrentColFrame, mRowGroup, mEndRow,
  4817. nullptr, mTableWM, eLogicalSideBEnd, ADJACENT);
  4818. mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBEnd,
  4819. currentBorder.width);
  4820. }
  4821. void
  4822. BCMapCellInfo::SetColGroupBEndContBCBorder()
  4823. {
  4824. BCCellBorder currentBorder;
  4825. if (mColGroup) {
  4826. currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
  4827. mEndRow, nullptr, mTableWM,
  4828. eLogicalSideBEnd, ADJACENT);
  4829. mColGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
  4830. }
  4831. }
  4832. void
  4833. BCMapCellInfo::SetRowGroupBEndContBCBorder()
  4834. {
  4835. BCCellBorder currentBorder;
  4836. if (mRowGroup) {
  4837. currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
  4838. mEndRow, nullptr, mTableWM,
  4839. eLogicalSideBEnd, ADJACENT);
  4840. mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
  4841. }
  4842. }
  4843. void
  4844. BCMapCellInfo::SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup,
  4845. nsTableRowFrame* aNextRow)
  4846. {
  4847. BCCellBorder currentBorder, adjacentBorder;
  4848. const nsIFrame* rowgroup = mRgAtEnd ? mRowGroup : nullptr;
  4849. currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mEndRow,
  4850. nullptr, mTableWM, eLogicalSideBEnd, ADJACENT);
  4851. adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup,
  4852. aNextRow, nullptr, mTableWM, eLogicalSideBStart,
  4853. !ADJACENT);
  4854. currentBorder = CompareBorders(false, currentBorder, adjacentBorder,
  4855. INLINE_DIR);
  4856. if (aNextRow) {
  4857. aNextRow->SetContinuousBCBorderWidth(eLogicalSideBStart,
  4858. currentBorder.width);
  4859. }
  4860. if (mRgAtEnd && mRowGroup) {
  4861. mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
  4862. }
  4863. }
  4864. void
  4865. BCMapCellInfo::SetRowIStartContBCBorder()
  4866. {
  4867. //get row continuous borders
  4868. if (mCurrentRowFrame) {
  4869. BCCellBorder currentBorder;
  4870. currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol,
  4871. mRowGroup, mCurrentRowFrame, nullptr,
  4872. mTableWM, eLogicalSideIStart, !ADJACENT);
  4873. mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIStart,
  4874. currentBorder.width);
  4875. }
  4876. }
  4877. void
  4878. BCMapCellInfo::SetRowIEndContBCBorder()
  4879. {
  4880. if (mCurrentRowFrame) {
  4881. BCCellBorder currentBorder;
  4882. currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
  4883. mCurrentRowFrame, nullptr, mTableWM,
  4884. eLogicalSideIEnd, ADJACENT);
  4885. mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd,
  4886. currentBorder.width);
  4887. }
  4888. }
  4889. void
  4890. BCMapCellInfo::SetTableBStartBorderWidth(BCPixelSize aWidth)
  4891. {
  4892. mTableBCData->mBStartBorderWidth = std::max(mTableBCData->mBStartBorderWidth,
  4893. aWidth);
  4894. }
  4895. void
  4896. BCMapCellInfo::SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth)
  4897. {
  4898. // update the iStart first cell border
  4899. if (aRowB == 0) {
  4900. mTableBCData->mIStartCellBorderWidth = aWidth;
  4901. }
  4902. mTableBCData->mIStartBorderWidth = std::max(mTableBCData->mIStartBorderWidth,
  4903. aWidth);
  4904. }
  4905. void
  4906. BCMapCellInfo::SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth)
  4907. {
  4908. // update the iEnd first cell border
  4909. if (aRowB == 0) {
  4910. mTableBCData->mIEndCellBorderWidth = aWidth;
  4911. }
  4912. mTableBCData->mIEndBorderWidth = std::max(mTableBCData->mIEndBorderWidth,
  4913. aWidth);
  4914. }
  4915. void
  4916. BCMapCellInfo::SetIEndBorderWidths(BCPixelSize aWidth)
  4917. {
  4918. // update the borders of the cells and cols affected
  4919. if (mCell) {
  4920. mCell->SetBorderWidth(eLogicalSideIEnd, std::max(aWidth,
  4921. mCell->GetBorderWidth(eLogicalSideIEnd)));
  4922. }
  4923. if (mEndCol) {
  4924. BCPixelSize half = BC_BORDER_START_HALF(aWidth);
  4925. mEndCol->SetIEndBorderWidth(
  4926. std::max(nscoord(half), mEndCol->GetIEndBorderWidth()));
  4927. }
  4928. }
  4929. void
  4930. BCMapCellInfo::SetBEndBorderWidths(BCPixelSize aWidth)
  4931. {
  4932. // update the borders of the affected cells and rows
  4933. if (mCell) {
  4934. mCell->SetBorderWidth(eLogicalSideBEnd, std::max(aWidth,
  4935. mCell->GetBorderWidth(eLogicalSideBEnd)));
  4936. }
  4937. if (mEndRow) {
  4938. BCPixelSize half = BC_BORDER_START_HALF(aWidth);
  4939. mEndRow->SetBEndBCBorderWidth(
  4940. std::max(nscoord(half), mEndRow->GetBEndBCBorderWidth()));
  4941. }
  4942. }
  4943. void
  4944. BCMapCellInfo::SetBStartBorderWidths(BCPixelSize aWidth)
  4945. {
  4946. if (mCell) {
  4947. mCell->SetBorderWidth(eLogicalSideBStart, std::max(aWidth,
  4948. mCell->GetBorderWidth(eLogicalSideBStart)));
  4949. }
  4950. if (mStartRow) {
  4951. BCPixelSize half = BC_BORDER_END_HALF(aWidth);
  4952. mStartRow->SetBStartBCBorderWidth(
  4953. std::max(nscoord(half), mStartRow->GetBStartBCBorderWidth()));
  4954. }
  4955. }
  4956. void
  4957. BCMapCellInfo::SetIStartBorderWidths(BCPixelSize aWidth)
  4958. {
  4959. if (mCell) {
  4960. mCell->SetBorderWidth(eLogicalSideIStart, std::max(aWidth,
  4961. mCell->GetBorderWidth(eLogicalSideIStart)));
  4962. }
  4963. if (mStartCol) {
  4964. BCPixelSize half = BC_BORDER_END_HALF(aWidth);
  4965. mStartCol->SetIStartBorderWidth(
  4966. std::max(nscoord(half), mStartCol->GetIStartBorderWidth()));
  4967. }
  4968. }
  4969. void
  4970. BCMapCellInfo::SetTableBEndBorderWidth(BCPixelSize aWidth)
  4971. {
  4972. mTableBCData->mBEndBorderWidth = std::max(mTableBCData->mBEndBorderWidth,
  4973. aWidth);
  4974. }
  4975. void
  4976. BCMapCellInfo::SetColumn(int32_t aColX)
  4977. {
  4978. mCurrentColFrame = mTableFrame->GetColFrame(aColX);
  4979. if (!mCurrentColFrame) {
  4980. NS_ERROR("null mCurrentColFrame");
  4981. }
  4982. mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*>
  4983. (mCurrentColFrame->GetParent());
  4984. if (!mCurrentColGroupFrame) {
  4985. NS_ERROR("null mCurrentColGroupFrame");
  4986. }
  4987. }
  4988. void
  4989. BCMapCellInfo::IncrementRow(bool aResetToBStartRowOfCell)
  4990. {
  4991. mCurrentRowFrame =
  4992. aResetToBStartRowOfCell ? mStartRow : mCurrentRowFrame->GetNextRow();
  4993. }
  4994. BCCellBorder
  4995. BCMapCellInfo::GetBStartEdgeBorder()
  4996. {
  4997. return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
  4998. mRowGroup, mStartRow, mCell, mTableWM,
  4999. eLogicalSideBStart, !ADJACENT);
  5000. }
  5001. BCCellBorder
  5002. BCMapCellInfo::GetBEndEdgeBorder()
  5003. {
  5004. return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
  5005. mRowGroup, mEndRow, mCell, mTableWM,
  5006. eLogicalSideBEnd, ADJACENT);
  5007. }
  5008. BCCellBorder
  5009. BCMapCellInfo::GetIStartEdgeBorder()
  5010. {
  5011. return CompareBorders(mTableFrame, mColGroup, mStartCol, mRowGroup,
  5012. mCurrentRowFrame, mCell, mTableWM, eLogicalSideIStart,
  5013. !ADJACENT);
  5014. }
  5015. BCCellBorder
  5016. BCMapCellInfo::GetIEndEdgeBorder()
  5017. {
  5018. return CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
  5019. mCurrentRowFrame, mCell, mTableWM, eLogicalSideIEnd,
  5020. ADJACENT);
  5021. }
  5022. BCCellBorder
  5023. BCMapCellInfo::GetIEndInternalBorder()
  5024. {
  5025. const nsIFrame* cg = mCgAtEnd ? mColGroup : nullptr;
  5026. return CompareBorders(nullptr, cg, mEndCol, nullptr, nullptr, mCell,
  5027. mTableWM, eLogicalSideIEnd, ADJACENT);
  5028. }
  5029. BCCellBorder
  5030. BCMapCellInfo::GetIStartInternalBorder()
  5031. {
  5032. const nsIFrame* cg = mCgAtStart ? mColGroup : nullptr;
  5033. return CompareBorders(nullptr, cg, mStartCol, nullptr, nullptr, mCell,
  5034. mTableWM, eLogicalSideIStart, !ADJACENT);
  5035. }
  5036. BCCellBorder
  5037. BCMapCellInfo::GetBEndInternalBorder()
  5038. {
  5039. const nsIFrame* rg = mRgAtEnd ? mRowGroup : nullptr;
  5040. return CompareBorders(nullptr, nullptr, nullptr, rg, mEndRow, mCell,
  5041. mTableWM, eLogicalSideBEnd, ADJACENT);
  5042. }
  5043. BCCellBorder
  5044. BCMapCellInfo::GetBStartInternalBorder()
  5045. {
  5046. const nsIFrame* rg = mRgAtStart ? mRowGroup : nullptr;
  5047. return CompareBorders(nullptr, nullptr, nullptr, rg, mStartRow, mCell,
  5048. mTableWM, eLogicalSideBStart, !ADJACENT);
  5049. }
  5050. /* XXX This comment is still written in physical (horizontal-tb) terms.
  5051. Here is the order for storing border edges in the cell map as a cell is processed. There are
  5052. n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
  5053. 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
  5054. a bottom edge from a cell above will take care of it.
  5055. 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
  5056. a right edge from a cell to the left will take care of it.
  5057. 3) Store the right edge (or edges if a row span)
  5058. 4) Store the bottom edge (or edges if a col span)
  5059. Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
  5060. calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
  5061. would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
  5062. borders already have about an extra 8 byte per cell map entry overhead (this could be
  5063. reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
  5064. rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
  5065. left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
  5066. edges per cell and n=rowspan left and right border edges per cell.
  5067. 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
  5068. Never store the top-right corner, since it will get stored as a right-top corner.
  5069. 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
  5070. since it will get stored as a bottom-left corner.
  5071. 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
  5072. the top edge of the table. Never store the right-bottom corner since it will get stored as a
  5073. bottom-right corner.
  5074. 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
  5075. otherwise, since it will get stored as either a right-top corner by a cell below or
  5076. a bottom-left corner from a cell to the right.
  5077. 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
  5078. the top side of a colspan in its interior. Never store the corner otherwise, since it will
  5079. get stored as a right-top corner by a cell from below.
  5080. XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
  5081. In order to draw borders in rtl conditions somehow correct, the existing structure which relies
  5082. heavily on the assumption that the next cell sibling will be on the right side, has been modified.
  5083. We flip the border during painting and during style lookup. Look for tableIsLTR for places where
  5084. the flipping is done.
  5085. */
  5086. // Calc the dominant border at every cell edge and corner within the current damage area
  5087. void
  5088. nsTableFrame::CalcBCBorders()
  5089. {
  5090. NS_ASSERTION(IsBorderCollapse(),
  5091. "calling CalcBCBorders on separated-border table");
  5092. nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
  5093. int32_t numRows = GetRowCount();
  5094. int32_t numCols = GetColCount();
  5095. if (!numRows || !numCols)
  5096. return; // nothing to do
  5097. // Get the property holding the table damage area and border widths
  5098. BCPropertyData* propData = GetBCProperty();
  5099. if (!propData) ABORT0();
  5100. // calculate an expanded damage area
  5101. TableArea damageArea(propData->mDamageArea);
  5102. ExpandBCDamageArea(damageArea);
  5103. // segments that are on the table border edges need
  5104. // to be initialized only once
  5105. bool tableBorderReset[4];
  5106. for (uint32_t sideX = 0; sideX < ArrayLength(tableBorderReset); sideX++) {
  5107. tableBorderReset[sideX] = false;
  5108. }
  5109. // block-dir borders indexed in inline-direction (cols)
  5110. BCCellBorders lastBlockDirBorders(damageArea.ColCount() + 1,
  5111. damageArea.StartCol());
  5112. if (!lastBlockDirBorders.borders) ABORT0();
  5113. BCCellBorder lastBStartBorder, lastBEndBorder;
  5114. // inline-dir borders indexed in inline-direction (cols)
  5115. BCCellBorders lastBEndBorders(damageArea.ColCount() + 1,
  5116. damageArea.StartCol());
  5117. if (!lastBEndBorders.borders) ABORT0();
  5118. bool startSeg;
  5119. bool gotRowBorder = false;
  5120. BCMapCellInfo info(this), ajaInfo(this);
  5121. BCCellBorder currentBorder, adjacentBorder;
  5122. BCCorners bStartCorners(damageArea.ColCount() + 1, damageArea.StartCol());
  5123. if (!bStartCorners.corners) ABORT0();
  5124. BCCorners bEndCorners(damageArea.ColCount() + 1, damageArea.StartCol());
  5125. if (!bEndCorners.corners) ABORT0();
  5126. BCMapCellIterator iter(this, damageArea);
  5127. for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
  5128. // see if lastBStartBorder, lastBEndBorder need to be reset
  5129. if (iter.IsNewRow()) {
  5130. gotRowBorder = false;
  5131. lastBStartBorder.Reset(info.mRowIndex, info.mRowSpan);
  5132. lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
  5133. }
  5134. else if (info.mColIndex > damageArea.StartCol()) {
  5135. lastBEndBorder = lastBEndBorders[info.mColIndex - 1];
  5136. if (info.mRowIndex >
  5137. (lastBEndBorder.rowIndex - lastBEndBorder.rowSpan)) {
  5138. // the bStart border's iStart edge butts against the middle of a rowspan
  5139. lastBStartBorder.Reset(info.mRowIndex, info.mRowSpan);
  5140. }
  5141. if (lastBEndBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
  5142. // the bEnd border's iStart edge butts against the middle of a rowspan
  5143. lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
  5144. }
  5145. }
  5146. // find the dominant border considering the cell's bStart border and the table,
  5147. // row group, row if the border is at the bStart of the table, otherwise it was
  5148. // processed in a previous row
  5149. if (0 == info.mRowIndex) {
  5150. if (!tableBorderReset[eLogicalSideBStart]) {
  5151. propData->mBStartBorderWidth = 0;
  5152. tableBorderReset[eLogicalSideBStart] = true;
  5153. }
  5154. for (int32_t colIdx = info.mColIndex;
  5155. colIdx <= info.GetCellEndColIndex(); colIdx++) {
  5156. info.SetColumn(colIdx);
  5157. currentBorder = info.GetBStartEdgeBorder();
  5158. // update/store the bStart-iStart & bStart-iEnd corners of the seg
  5159. BCCornerInfo& tlCorner = bStartCorners[colIdx]; // bStart-iStart
  5160. if (0 == colIdx) {
  5161. // we are on the iEnd side of the corner
  5162. tlCorner.Set(eLogicalSideIEnd, currentBorder);
  5163. }
  5164. else {
  5165. tlCorner.Update(eLogicalSideIEnd, currentBorder);
  5166. tableCellMap->SetBCBorderCorner(eBStartIStart, *iter.mCellMap, 0, 0, colIdx,
  5167. LogicalSide(tlCorner.ownerSide),
  5168. tlCorner.subWidth,
  5169. tlCorner.bevel);
  5170. }
  5171. bStartCorners[colIdx + 1].Set(eLogicalSideIStart, currentBorder); // bStart-iEnd
  5172. // update lastBStartBorder and see if a new segment starts
  5173. startSeg = SetInlineDirBorder(currentBorder, tlCorner, lastBStartBorder);
  5174. // store the border segment in the cell map
  5175. tableCellMap->SetBCBorderEdge(eLogicalSideBStart, *iter.mCellMap, 0, 0, colIdx,
  5176. 1, currentBorder.owner,
  5177. currentBorder.width, startSeg);
  5178. info.SetTableBStartBorderWidth(currentBorder.width);
  5179. info.SetBStartBorderWidths(currentBorder.width);
  5180. info.SetColumnBStartIEndContBCBorder();
  5181. }
  5182. info.SetTableBStartIStartContBCBorder();
  5183. }
  5184. else {
  5185. // see if the bStart border needs to be the start of a segment due to a
  5186. // block-dir border owning the corner
  5187. if (info.mColIndex > 0) {
  5188. BCData& data = info.mCellData->mData;
  5189. if (!data.IsBStartStart()) {
  5190. LogicalSide cornerSide;
  5191. bool bevel;
  5192. data.GetCorner(cornerSide, bevel);
  5193. if (IsBlock(cornerSide)) {
  5194. data.SetBStartStart(true);
  5195. }
  5196. }
  5197. }
  5198. }
  5199. // find the dominant border considering the cell's iStart border and the
  5200. // table, col group, col if the border is at the iStart of the table,
  5201. // otherwise it was processed in a previous col
  5202. if (0 == info.mColIndex) {
  5203. if (!tableBorderReset[eLogicalSideIStart]) {
  5204. propData->mIStartBorderWidth = 0;
  5205. tableBorderReset[eLogicalSideIStart] = true;
  5206. }
  5207. info.mCurrentRowFrame = nullptr;
  5208. for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
  5209. rowB++) {
  5210. info.IncrementRow(rowB == info.mRowIndex);
  5211. currentBorder = info.GetIStartEdgeBorder();
  5212. BCCornerInfo& tlCorner = (0 == rowB) ? bStartCorners[0] : bEndCorners[0];
  5213. tlCorner.Update(eLogicalSideBEnd, currentBorder);
  5214. tableCellMap->SetBCBorderCorner(eBStartIStart, *iter.mCellMap,
  5215. iter.mRowGroupStart, rowB, 0,
  5216. LogicalSide(tlCorner.ownerSide),
  5217. tlCorner.subWidth,
  5218. tlCorner.bevel);
  5219. bEndCorners[0].Set(eLogicalSideBStart, currentBorder); // bEnd-iStart
  5220. // update lastBlockDirBorders and see if a new segment starts
  5221. startSeg = SetBorder(currentBorder, lastBlockDirBorders[0]);
  5222. // store the border segment in the cell map
  5223. tableCellMap->SetBCBorderEdge(eLogicalSideIStart, *iter.mCellMap,
  5224. iter.mRowGroupStart, rowB, info.mColIndex,
  5225. 1, currentBorder.owner,
  5226. currentBorder.width, startSeg);
  5227. info.SetTableIStartBorderWidth(rowB , currentBorder.width);
  5228. info.SetIStartBorderWidths(currentBorder.width);
  5229. info.SetRowIStartContBCBorder();
  5230. }
  5231. info.SetRowGroupIStartContBCBorder();
  5232. }
  5233. // find the dominant border considering the cell's iEnd border, adjacent
  5234. // cells and the table, row group, row
  5235. if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
  5236. // touches iEnd edge of table
  5237. if (!tableBorderReset[eLogicalSideIEnd]) {
  5238. propData->mIEndBorderWidth = 0;
  5239. tableBorderReset[eLogicalSideIEnd] = true;
  5240. }
  5241. info.mCurrentRowFrame = nullptr;
  5242. for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
  5243. rowB++) {
  5244. info.IncrementRow(rowB == info.mRowIndex);
  5245. currentBorder = info.GetIEndEdgeBorder();
  5246. // update/store the bStart-iEnd & bEnd-iEnd corners
  5247. BCCornerInfo& trCorner = (0 == rowB) ?
  5248. bStartCorners[info.GetCellEndColIndex() + 1] :
  5249. bEndCorners[info.GetCellEndColIndex() + 1];
  5250. trCorner.Update(eLogicalSideBEnd, currentBorder); // bStart-iEnd
  5251. tableCellMap->SetBCBorderCorner(eBStartIEnd, *iter.mCellMap,
  5252. iter.mRowGroupStart, rowB,
  5253. info.GetCellEndColIndex(),
  5254. LogicalSide(trCorner.ownerSide),
  5255. trCorner.subWidth,
  5256. trCorner.bevel);
  5257. BCCornerInfo& brCorner = bEndCorners[info.GetCellEndColIndex() + 1];
  5258. brCorner.Set(eLogicalSideBStart, currentBorder); // bEnd-iEnd
  5259. tableCellMap->SetBCBorderCorner(eBEndIEnd, *iter.mCellMap,
  5260. iter.mRowGroupStart, rowB,
  5261. info.GetCellEndColIndex(),
  5262. LogicalSide(brCorner.ownerSide),
  5263. brCorner.subWidth,
  5264. brCorner.bevel);
  5265. // update lastBlockDirBorders and see if a new segment starts
  5266. startSeg = SetBorder(currentBorder,
  5267. lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
  5268. // store the border segment in the cell map and update cellBorders
  5269. tableCellMap->SetBCBorderEdge(eLogicalSideIEnd, *iter.mCellMap,
  5270. iter.mRowGroupStart, rowB,
  5271. info.GetCellEndColIndex(), 1,
  5272. currentBorder.owner, currentBorder.width,
  5273. startSeg);
  5274. info.SetTableIEndBorderWidth(rowB, currentBorder.width);
  5275. info.SetIEndBorderWidths(currentBorder.width);
  5276. info.SetRowIEndContBCBorder();
  5277. }
  5278. info.SetRowGroupIEndContBCBorder();
  5279. }
  5280. else {
  5281. int32_t segLength = 0;
  5282. BCMapCellInfo priorAjaInfo(this);
  5283. for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
  5284. rowB += segLength) {
  5285. iter.PeekIEnd(info, rowB, ajaInfo);
  5286. currentBorder = info.GetIEndInternalBorder();
  5287. adjacentBorder = ajaInfo.GetIStartInternalBorder();
  5288. currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
  5289. adjacentBorder, !INLINE_DIR);
  5290. segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowB);
  5291. segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowB);
  5292. // update lastBlockDirBorders and see if a new segment starts
  5293. startSeg = SetBorder(currentBorder,
  5294. lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
  5295. // store the border segment in the cell map and update cellBorders
  5296. if (info.GetCellEndColIndex() < damageArea.EndCol() &&
  5297. rowB >= damageArea.StartRow() && rowB < damageArea.EndRow()) {
  5298. tableCellMap->SetBCBorderEdge(eLogicalSideIEnd, *iter.mCellMap,
  5299. iter.mRowGroupStart, rowB,
  5300. info.GetCellEndColIndex(), segLength,
  5301. currentBorder.owner,
  5302. currentBorder.width, startSeg);
  5303. info.SetIEndBorderWidths(currentBorder.width);
  5304. ajaInfo.SetIStartBorderWidths(currentBorder.width);
  5305. }
  5306. // update the bStart-iEnd corner
  5307. bool hitsSpanOnIEnd = (rowB > ajaInfo.mRowIndex) &&
  5308. (rowB < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
  5309. BCCornerInfo* trCorner = ((0 == rowB) || hitsSpanOnIEnd) ?
  5310. &bStartCorners[info.GetCellEndColIndex() + 1] :
  5311. &bEndCorners[info.GetCellEndColIndex() + 1];
  5312. trCorner->Update(eLogicalSideBEnd, currentBorder);
  5313. // if this is not the first time through,
  5314. // consider the segment to the iEnd side
  5315. if (rowB != info.mRowIndex) {
  5316. currentBorder = priorAjaInfo.GetBEndInternalBorder();
  5317. adjacentBorder = ajaInfo.GetBStartInternalBorder();
  5318. currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
  5319. adjacentBorder, INLINE_DIR);
  5320. trCorner->Update(eLogicalSideIEnd, currentBorder);
  5321. }
  5322. // store the bStart-iEnd corner in the cell map
  5323. if (info.GetCellEndColIndex() < damageArea.EndCol() &&
  5324. rowB >= damageArea.StartRow()) {
  5325. if (0 != rowB) {
  5326. tableCellMap->SetBCBorderCorner(eBStartIEnd, *iter.mCellMap,
  5327. iter.mRowGroupStart, rowB,
  5328. info.GetCellEndColIndex(),
  5329. LogicalSide(trCorner->ownerSide),
  5330. trCorner->subWidth,
  5331. trCorner->bevel);
  5332. }
  5333. // store any corners this cell spans together with the aja cell
  5334. for (int32_t rX = rowB + 1; rX < rowB + segLength; rX++) {
  5335. tableCellMap->SetBCBorderCorner(eBEndIEnd, *iter.mCellMap,
  5336. iter.mRowGroupStart, rX,
  5337. info.GetCellEndColIndex(),
  5338. LogicalSide(trCorner->ownerSide),
  5339. trCorner->subWidth, false);
  5340. }
  5341. }
  5342. // update bEnd-iEnd corner, bStartCorners, bEndCorners
  5343. hitsSpanOnIEnd = (rowB + segLength <
  5344. ajaInfo.mRowIndex + ajaInfo.mRowSpan);
  5345. BCCornerInfo& brCorner = (hitsSpanOnIEnd) ?
  5346. bStartCorners[info.GetCellEndColIndex() + 1] :
  5347. bEndCorners[info.GetCellEndColIndex() + 1];
  5348. brCorner.Set(eLogicalSideBStart, currentBorder);
  5349. priorAjaInfo = ajaInfo;
  5350. }
  5351. }
  5352. for (int32_t colIdx = info.mColIndex + 1;
  5353. colIdx <= info.GetCellEndColIndex(); colIdx++) {
  5354. lastBlockDirBorders[colIdx].Reset(0,1);
  5355. }
  5356. // find the dominant border considering the cell's bEnd border, adjacent
  5357. // cells and the table, row group, row
  5358. if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
  5359. // touches bEnd edge of table
  5360. if (!tableBorderReset[eLogicalSideBEnd]) {
  5361. propData->mBEndBorderWidth = 0;
  5362. tableBorderReset[eLogicalSideBEnd] = true;
  5363. }
  5364. for (int32_t colIdx = info.mColIndex;
  5365. colIdx <= info.GetCellEndColIndex(); colIdx++) {
  5366. info.SetColumn(colIdx);
  5367. currentBorder = info.GetBEndEdgeBorder();
  5368. // update/store the bEnd-iStart & bEnd-IEnd corners
  5369. BCCornerInfo& blCorner = bEndCorners[colIdx]; // bEnd-iStart
  5370. blCorner.Update(eLogicalSideIEnd, currentBorder);
  5371. tableCellMap->SetBCBorderCorner(eBEndIStart, *iter.mCellMap,
  5372. iter.mRowGroupStart,
  5373. info.GetCellEndRowIndex(),
  5374. colIdx,
  5375. LogicalSide(blCorner.ownerSide),
  5376. blCorner.subWidth, blCorner.bevel);
  5377. BCCornerInfo& brCorner = bEndCorners[colIdx + 1]; // bEnd-iEnd
  5378. brCorner.Update(eLogicalSideIStart, currentBorder);
  5379. if (info.mNumTableCols == colIdx + 1) { // bEnd-IEnd corner of the table
  5380. tableCellMap->SetBCBorderCorner(eBEndIEnd, *iter.mCellMap,
  5381. iter.mRowGroupStart,
  5382. info.GetCellEndRowIndex(), colIdx,
  5383. LogicalSide(brCorner.ownerSide),
  5384. brCorner.subWidth,
  5385. brCorner.bevel, true);
  5386. }
  5387. // update lastBEndBorder and see if a new segment starts
  5388. startSeg = SetInlineDirBorder(currentBorder, blCorner, lastBEndBorder);
  5389. if (!startSeg) {
  5390. // make sure that we did not compare apples to oranges i.e. the
  5391. // current border should be a continuation of the lastBEndBorder,
  5392. // as it is a bEnd border
  5393. // add 1 to the info.GetCellEndRowIndex()
  5394. startSeg = (lastBEndBorder.rowIndex !=
  5395. (info.GetCellEndRowIndex() + 1));
  5396. }
  5397. // store the border segment in the cell map and update cellBorders
  5398. tableCellMap->SetBCBorderEdge(eLogicalSideBEnd, *iter.mCellMap,
  5399. iter.mRowGroupStart,
  5400. info.GetCellEndRowIndex(),
  5401. colIdx, 1, currentBorder.owner,
  5402. currentBorder.width, startSeg);
  5403. // update lastBEndBorders
  5404. lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
  5405. lastBEndBorder.rowSpan = info.mRowSpan;
  5406. lastBEndBorders[colIdx] = lastBEndBorder;
  5407. info.SetBEndBorderWidths(currentBorder.width);
  5408. info.SetTableBEndBorderWidth(currentBorder.width);
  5409. info.SetColumnBEndContBCBorder();
  5410. }
  5411. info.SetRowGroupBEndContBCBorder();
  5412. info.SetColGroupBEndContBCBorder();
  5413. }
  5414. else {
  5415. int32_t segLength = 0;
  5416. for (int32_t colIdx = info.mColIndex;
  5417. colIdx <= info.GetCellEndColIndex(); colIdx += segLength) {
  5418. iter.PeekBEnd(info, colIdx, ajaInfo);
  5419. currentBorder = info.GetBEndInternalBorder();
  5420. adjacentBorder = ajaInfo.GetBStartInternalBorder();
  5421. currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
  5422. adjacentBorder, INLINE_DIR);
  5423. segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colIdx);
  5424. segLength = std::min(segLength, info.mColIndex + info.mColSpan - colIdx);
  5425. // update, store the bEnd-iStart corner
  5426. BCCornerInfo& blCorner = bEndCorners[colIdx]; // bEnd-iStart
  5427. bool hitsSpanBelow = (colIdx > ajaInfo.mColIndex) &&
  5428. (colIdx < ajaInfo.mColIndex + ajaInfo.mColSpan);
  5429. bool update = true;
  5430. if (colIdx == info.mColIndex && colIdx > damageArea.StartCol()) {
  5431. int32_t prevRowIndex = lastBEndBorders[colIdx - 1].rowIndex;
  5432. if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
  5433. // hits a rowspan on the iEnd side
  5434. update = false;
  5435. // the corner was taken care of during the cell on the iStart side
  5436. }
  5437. else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
  5438. // spans below the cell to the iStart side
  5439. bStartCorners[colIdx] = blCorner;
  5440. blCorner.Set(eLogicalSideIEnd, currentBorder);
  5441. update = false;
  5442. }
  5443. }
  5444. if (update) {
  5445. blCorner.Update(eLogicalSideIEnd, currentBorder);
  5446. }
  5447. if (info.GetCellEndRowIndex() < damageArea.EndRow() &&
  5448. colIdx >= damageArea.StartCol()) {
  5449. if (hitsSpanBelow) {
  5450. tableCellMap->SetBCBorderCorner(eBEndIStart, *iter.mCellMap,
  5451. iter.mRowGroupStart,
  5452. info.GetCellEndRowIndex(), colIdx,
  5453. LogicalSide(blCorner.ownerSide),
  5454. blCorner.subWidth, blCorner.bevel);
  5455. }
  5456. // store any corners this cell spans together with the aja cell
  5457. for (int32_t c = colIdx + 1; c < colIdx + segLength; c++) {
  5458. BCCornerInfo& corner = bEndCorners[c];
  5459. corner.Set(eLogicalSideIEnd, currentBorder);
  5460. tableCellMap->SetBCBorderCorner(eBEndIStart, *iter.mCellMap,
  5461. iter.mRowGroupStart,
  5462. info.GetCellEndRowIndex(), c,
  5463. LogicalSide(corner.ownerSide),
  5464. corner.subWidth,
  5465. false);
  5466. }
  5467. }
  5468. // update lastBEndBorders and see if a new segment starts
  5469. startSeg = SetInlineDirBorder(currentBorder, blCorner, lastBEndBorder);
  5470. if (!startSeg) {
  5471. // make sure that we did not compare apples to oranges i.e. the
  5472. // current border should be a continuation of the lastBEndBorder,
  5473. // as it is a bEnd border
  5474. // add 1 to the info.GetCellEndRowIndex()
  5475. startSeg = (lastBEndBorder.rowIndex !=
  5476. info.GetCellEndRowIndex() + 1);
  5477. }
  5478. lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
  5479. lastBEndBorder.rowSpan = info.mRowSpan;
  5480. for (int32_t c = colIdx; c < colIdx + segLength; c++) {
  5481. lastBEndBorders[c] = lastBEndBorder;
  5482. }
  5483. // store the border segment the cell map and update cellBorders
  5484. if (info.GetCellEndRowIndex() < damageArea.EndRow() &&
  5485. colIdx >= damageArea.StartCol() && colIdx < damageArea.EndCol()) {
  5486. tableCellMap->SetBCBorderEdge(eLogicalSideBEnd, *iter.mCellMap,
  5487. iter.mRowGroupStart,
  5488. info.GetCellEndRowIndex(),
  5489. colIdx, segLength, currentBorder.owner,
  5490. currentBorder.width, startSeg);
  5491. info.SetBEndBorderWidths(currentBorder.width);
  5492. ajaInfo.SetBStartBorderWidths(currentBorder.width);
  5493. }
  5494. // update bEnd-iEnd corner
  5495. BCCornerInfo& brCorner = bEndCorners[colIdx + segLength];
  5496. brCorner.Update(eLogicalSideIStart, currentBorder);
  5497. }
  5498. if (!gotRowBorder && 1 == info.mRowSpan &&
  5499. (ajaInfo.mStartRow || info.mRgAtEnd)) {
  5500. //get continuous row/row group border
  5501. //we need to check the row group's bEnd border if this is
  5502. //the last row in the row group, but only a cell with rowspan=1
  5503. //will know whether *this* row is at the bEnd
  5504. const nsIFrame* nextRowGroup =
  5505. ajaInfo.mRgAtStart ? ajaInfo.mRowGroup : nullptr;
  5506. info.SetInnerRowGroupBEndContBCBorder(nextRowGroup, ajaInfo.mStartRow);
  5507. gotRowBorder = true;
  5508. }
  5509. }
  5510. // see if the cell to the iEnd side had a rowspan and its bEnd-iStart border
  5511. // needs be joined with this one's bEnd
  5512. // if there is a cell to the iEnd and the cell to iEnd side was a rowspan
  5513. if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) &&
  5514. (lastBEndBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) {
  5515. BCCornerInfo& corner = bEndCorners[info.GetCellEndColIndex() + 1];
  5516. if (!IsBlock(LogicalSide(corner.ownerSide))) {
  5517. // not a block-dir owner
  5518. BCCellBorder& thisBorder = lastBEndBorder;
  5519. BCCellBorder& nextBorder = lastBEndBorders[info.mColIndex + 1];
  5520. if ((thisBorder.color == nextBorder.color) &&
  5521. (thisBorder.width == nextBorder.width) &&
  5522. (thisBorder.style == nextBorder.style)) {
  5523. // set the flag on the next border indicating it is not the start of a
  5524. // new segment
  5525. if (iter.mCellMap) {
  5526. tableCellMap->ResetBStartStart(eLogicalSideBEnd, *iter.mCellMap,
  5527. info.GetCellEndRowIndex(),
  5528. info.GetCellEndColIndex() + 1);
  5529. }
  5530. }
  5531. }
  5532. }
  5533. } // for (iter.First(info); info.mCell; iter.Next(info)) {
  5534. // reset the bc flag and damage area
  5535. SetNeedToCalcBCBorders(false);
  5536. propData->mDamageArea = TableArea(0, 0, 0, 0);
  5537. #ifdef DEBUG_TABLE_CELLMAP
  5538. mCellMap->Dump();
  5539. #endif
  5540. }
  5541. class BCPaintBorderIterator;
  5542. struct BCBlockDirSeg
  5543. {
  5544. BCBlockDirSeg();
  5545. void Start(BCPaintBorderIterator& aIter,
  5546. BCBorderOwner aBorderOwner,
  5547. BCPixelSize aBlockSegISize,
  5548. BCPixelSize aInlineSegBSize);
  5549. void Initialize(BCPaintBorderIterator& aIter);
  5550. void GetBEndCorner(BCPaintBorderIterator& aIter,
  5551. BCPixelSize aInlineSegBSize);
  5552. void Paint(BCPaintBorderIterator& aIter,
  5553. DrawTarget& aDrawTarget,
  5554. BCPixelSize aInlineSegBSize);
  5555. void AdvanceOffsetB();
  5556. void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
  5557. union {
  5558. nsTableColFrame* mCol;
  5559. int32_t mColWidth;
  5560. };
  5561. nscoord mOffsetI; // i-offset with respect to the table edge
  5562. nscoord mOffsetB; // b-offset with respect to the table edge
  5563. nscoord mLength; // block-dir length including corners
  5564. BCPixelSize mWidth; // thickness in pixels
  5565. nsTableCellFrame* mAjaCell; // previous sibling to the first cell
  5566. // where the segment starts, it can be
  5567. // the owner of a segment
  5568. nsTableCellFrame* mFirstCell; // cell at the start of the segment
  5569. nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment
  5570. nsTableRowFrame* mFirstRow; // row at the start of the segment
  5571. nsTableCellFrame* mLastCell; // cell at the current end of the
  5572. // segment
  5573. uint8_t mOwner; // owner of the border, defines the
  5574. // style
  5575. LogicalSide mBStartBevelSide; // direction to bevel at the bStart
  5576. nscoord mBStartBevelOffset; // how much to bevel at the bStart
  5577. BCPixelSize mBEndInlineSegBSize; // bSize of the crossing
  5578. // inline-dir border
  5579. nscoord mBEndOffset; // how much longer is the segment due
  5580. // to the inline-dir border, by this
  5581. // amount the next segment needs to be
  5582. // shifted.
  5583. bool mIsBEndBevel; // should we bevel at the bEnd
  5584. };
  5585. struct BCInlineDirSeg
  5586. {
  5587. BCInlineDirSeg();
  5588. void Start(BCPaintBorderIterator& aIter,
  5589. BCBorderOwner aBorderOwner,
  5590. BCPixelSize aBEndBlockSegISize,
  5591. BCPixelSize aInlineSegBSize);
  5592. void GetIEndCorner(BCPaintBorderIterator& aIter,
  5593. BCPixelSize aIStartSegISize);
  5594. void AdvanceOffsetI();
  5595. void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
  5596. void Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget);
  5597. nscoord mOffsetI; // i-offset with respect to the table edge
  5598. nscoord mOffsetB; // b-offset with respect to the table edge
  5599. nscoord mLength; // inline-dir length including corners
  5600. BCPixelSize mWidth; // border thickness in pixels
  5601. nscoord mIStartBevelOffset; // how much to bevel at the iStart
  5602. LogicalSide mIStartBevelSide; // direction to bevel at the iStart
  5603. bool mIsIEndBevel; // should we bevel at the iEnd end
  5604. nscoord mIEndBevelOffset; // how much to bevel at the iEnd
  5605. LogicalSide mIEndBevelSide; // direction to bevel at the iEnd
  5606. nscoord mEndOffset; // how much longer is the segment due
  5607. // to the block-dir border, by this
  5608. // amount the next segment needs to be
  5609. // shifted.
  5610. uint8_t mOwner; // owner of the border, defines the
  5611. // style
  5612. nsTableCellFrame* mFirstCell; // cell at the start of the segment
  5613. nsTableCellFrame* mAjaCell; // neighboring cell to the first cell
  5614. // where the segment starts, it can be
  5615. // the owner of a segment
  5616. };
  5617. // Iterates over borders (iStart border, corner, bStart border) in the cell map within a damage area
  5618. // from iStart to iEnd, bStart to bEnd. All members are in terms of the 1st in flow frames, except
  5619. // where suffixed by InFlow.
  5620. class BCPaintBorderIterator
  5621. {
  5622. public:
  5623. explicit BCPaintBorderIterator(nsTableFrame* aTable);
  5624. ~BCPaintBorderIterator() { if (mBlockDirInfo) {
  5625. delete [] mBlockDirInfo;
  5626. }}
  5627. void Reset();
  5628. /**
  5629. * Determine the damage area in terms of rows and columns and finalize
  5630. * mInitialOffsetI and mInitialOffsetB.
  5631. * @param aDirtyRect - dirty rect in table coordinates
  5632. * @return - true if we need to paint something given dirty rect
  5633. */
  5634. bool SetDamageArea(const nsRect& aDamageRect);
  5635. void First();
  5636. void Next();
  5637. void AccumulateOrPaintInlineDirSegment(DrawTarget& aDrawTarget);
  5638. void AccumulateOrPaintBlockDirSegment(DrawTarget& aDrawTarget);
  5639. void ResetVerInfo();
  5640. void StoreColumnWidth(int32_t aIndex);
  5641. bool BlockDirSegmentOwnsCorner();
  5642. nsTableFrame* mTable;
  5643. nsTableFrame* mTableFirstInFlow;
  5644. nsTableCellMap* mTableCellMap;
  5645. nsCellMap* mCellMap;
  5646. WritingMode mTableWM;
  5647. const nsStyleBackground* mTableBgColor;
  5648. nsTableFrame::RowGroupArray mRowGroups;
  5649. nsTableRowGroupFrame* mPrevRg;
  5650. nsTableRowGroupFrame* mRg;
  5651. bool mIsRepeatedHeader;
  5652. bool mIsRepeatedFooter;
  5653. nsTableRowGroupFrame* mStartRg; // first row group in the damagearea
  5654. int32_t mRgIndex; // current row group index in the
  5655. // mRowgroups array
  5656. int32_t mFifRgFirstRowIndex; // start row index of the first in
  5657. // flow of the row group
  5658. int32_t mRgFirstRowIndex; // row index of the first row in the
  5659. // row group
  5660. int32_t mRgLastRowIndex; // row index of the last row in the row
  5661. // group
  5662. int32_t mNumTableRows; // number of rows in the table and all
  5663. // continuations
  5664. int32_t mNumTableCols; // number of columns in the table
  5665. int32_t mColIndex; // with respect to the table
  5666. int32_t mRowIndex; // with respect to the table
  5667. int32_t mRepeatedHeaderRowIndex; // row index in a repeated
  5668. //header, it's equivalent to
  5669. // mRowIndex when we're in a repeated
  5670. // header, and set to the last row
  5671. // index of a repeated header when
  5672. // we're not
  5673. bool mIsNewRow;
  5674. bool mAtEnd; // the iterator cycled over all
  5675. // borders
  5676. nsTableRowFrame* mPrevRow;
  5677. nsTableRowFrame* mRow;
  5678. nsTableRowFrame* mStartRow; //first row in a inside the damagearea
  5679. // cell properties
  5680. nsTableCellFrame* mPrevCell;
  5681. nsTableCellFrame* mCell;
  5682. BCCellData* mPrevCellData;
  5683. BCCellData* mCellData;
  5684. BCData* mBCData;
  5685. bool IsTableBStartMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();}
  5686. bool IsTableIEndMost() {return (mColIndex >= mNumTableCols);}
  5687. bool IsTableBEndMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();}
  5688. bool IsTableIStartMost() {return (mColIndex == 0);}
  5689. bool IsDamageAreaBStartMost() const
  5690. { return mRowIndex == mDamageArea.StartRow(); }
  5691. bool IsDamageAreaIEndMost() const
  5692. { return mColIndex >= mDamageArea.EndCol(); }
  5693. bool IsDamageAreaBEndMost() const
  5694. { return mRowIndex >= mDamageArea.EndRow(); }
  5695. bool IsDamageAreaIStartMost() const
  5696. { return mColIndex == mDamageArea.StartCol(); }
  5697. int32_t GetRelativeColIndex() const
  5698. { return mColIndex - mDamageArea.StartCol(); }
  5699. TableArea mDamageArea; // damageArea in cellmap coordinates
  5700. bool IsAfterRepeatedHeader()
  5701. { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1)); }
  5702. bool StartRepeatedFooter() const
  5703. {
  5704. return mIsRepeatedFooter && mRowIndex == mRgFirstRowIndex &&
  5705. mRowIndex != mDamageArea.StartRow();
  5706. }
  5707. nscoord mInitialOffsetI; // offsetI of the first border with
  5708. // respect to the table
  5709. nscoord mInitialOffsetB; // offsetB of the first border with
  5710. // respect to the table
  5711. nscoord mNextOffsetB; // offsetB of the next segment
  5712. BCBlockDirSeg* mBlockDirInfo; // this array is used differently when
  5713. // inline-dir and block-dir borders are drawn
  5714. // When inline-dir border are drawn we cache
  5715. // the column widths and the width of the
  5716. // block-dir borders that arrive from bStart
  5717. // When we draw block-dir borders we store
  5718. // lengths and width for block-dir borders
  5719. // before they are drawn while we move over
  5720. // the columns in the damage area
  5721. // It has one more elements than columns are
  5722. // in the table.
  5723. BCInlineDirSeg mInlineSeg; // the inline-dir segment while we
  5724. // move over the colums
  5725. BCPixelSize mPrevInlineSegBSize; // the bSize of the previous
  5726. // inline-dir border
  5727. private:
  5728. bool SetNewRow(nsTableRowFrame* aRow = nullptr);
  5729. bool SetNewRowGroup();
  5730. void SetNewData(int32_t aRowIndex, int32_t aColIndex);
  5731. };
  5732. BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
  5733. : mTable(aTable)
  5734. , mTableFirstInFlow(static_cast<nsTableFrame*>(aTable->FirstInFlow()))
  5735. , mTableCellMap(aTable->GetCellMap())
  5736. , mTableWM(aTable->StyleContext())
  5737. {
  5738. mBlockDirInfo = nullptr;
  5739. LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
  5740. // y position of first row in damage area
  5741. mInitialOffsetB =
  5742. mTable->GetPrevInFlow() ? 0 : childAreaOffset.BStart(mTableWM);
  5743. mNumTableRows = mTable->GetRowCount();
  5744. mNumTableCols = mTable->GetColCount();
  5745. // Get the ordered row groups
  5746. mTable->OrderRowGroups(mRowGroups);
  5747. // initialize to a non existing index
  5748. mRepeatedHeaderRowIndex = -99;
  5749. nsIFrame* bgFrame =
  5750. nsCSSRendering::FindNonTransparentBackgroundFrame(aTable);
  5751. mTableBgColor = bgFrame->StyleBackground();
  5752. }
  5753. bool
  5754. BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect)
  5755. {
  5756. nsSize containerSize = mTable->GetSize();
  5757. LogicalRect dirtyRect(mTableWM, aDirtyRect, containerSize);
  5758. uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex;
  5759. startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
  5760. bool done = false;
  5761. bool haveIntersect = false;
  5762. // find startRowIndex, endRowIndex
  5763. nscoord rowB = mInitialOffsetB;
  5764. for (uint32_t rgIdx = 0; rgIdx < mRowGroups.Length() && !done; rgIdx++) {
  5765. nsTableRowGroupFrame* rgFrame = mRowGroups[rgIdx];
  5766. for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
  5767. rowFrame = rowFrame->GetNextRow()) {
  5768. // get the row rect relative to the table rather than the row group
  5769. nscoord rowBSize = rowFrame->BSize(mTableWM);
  5770. if (haveIntersect) {
  5771. // conservatively estimate the half border widths outside the row
  5772. nscoord borderHalf = mTable->GetPrevInFlow() ? 0 :
  5773. mTable->PresContext()->DevPixelsToAppUnits(rowFrame->GetBStartBCBorderWidth() + 1);
  5774. if (dirtyRect.BEnd(mTableWM) >= rowB - borderHalf) {
  5775. nsTableRowFrame* fifRow =
  5776. static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
  5777. endRowIndex = fifRow->GetRowIndex();
  5778. }
  5779. else done = true;
  5780. }
  5781. else {
  5782. // conservatively estimate the half border widths outside the row
  5783. nscoord borderHalf = mTable->GetNextInFlow() ? 0 :
  5784. mTable->PresContext()->DevPixelsToAppUnits(rowFrame->GetBEndBCBorderWidth() + 1);
  5785. if (rowB + rowBSize + borderHalf >= dirtyRect.BStart(mTableWM)) {
  5786. mStartRg = rgFrame;
  5787. mStartRow = rowFrame;
  5788. nsTableRowFrame* fifRow =
  5789. static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
  5790. startRowIndex = endRowIndex = fifRow->GetRowIndex();
  5791. haveIntersect = true;
  5792. }
  5793. else {
  5794. mInitialOffsetB += rowBSize;
  5795. }
  5796. }
  5797. rowB += rowBSize;
  5798. }
  5799. }
  5800. mNextOffsetB = mInitialOffsetB;
  5801. // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
  5802. // XXX but I don't understand it, so not changing it for now
  5803. // table wrapper borders overflow the table, so the table might be
  5804. // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
  5805. // on the table
  5806. if (!haveIntersect)
  5807. return false;
  5808. // find startColIndex, endColIndex, startColX
  5809. haveIntersect = false;
  5810. if (0 == mNumTableCols)
  5811. return false;
  5812. LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
  5813. // inline position of first col in damage area
  5814. mInitialOffsetI = childAreaOffset.IStart(mTableWM);
  5815. nscoord x = 0;
  5816. int32_t colIdx;
  5817. for (colIdx = 0; colIdx != mNumTableCols; colIdx++) {
  5818. nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colIdx);
  5819. if (!colFrame) ABORT1(false);
  5820. // get the col rect relative to the table rather than the col group
  5821. nscoord colISize = colFrame->ISize(mTableWM);
  5822. if (haveIntersect) {
  5823. // conservatively estimate the iStart half border width outside the col
  5824. nscoord iStartBorderHalf =
  5825. mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetIStartBorderWidth() + 1);
  5826. if (dirtyRect.IEnd(mTableWM) >= x - iStartBorderHalf) {
  5827. endColIndex = colIdx;
  5828. }
  5829. else break;
  5830. }
  5831. else {
  5832. // conservatively estimate the iEnd half border width outside the col
  5833. nscoord iEndBorderHalf =
  5834. mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetIEndBorderWidth() + 1);
  5835. if (x + colISize + iEndBorderHalf >= dirtyRect.IStart(mTableWM)) {
  5836. startColIndex = endColIndex = colIdx;
  5837. haveIntersect = true;
  5838. }
  5839. else {
  5840. mInitialOffsetI += colISize;
  5841. }
  5842. }
  5843. x += colISize;
  5844. }
  5845. if (!haveIntersect)
  5846. return false;
  5847. mDamageArea = TableArea(startColIndex, startRowIndex,
  5848. 1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex),
  5849. 1 + endRowIndex - startRowIndex);
  5850. Reset();
  5851. mBlockDirInfo = new BCBlockDirSeg[mDamageArea.ColCount() + 1];
  5852. if (!mBlockDirInfo)
  5853. return false;
  5854. return true;
  5855. }
  5856. void
  5857. BCPaintBorderIterator::Reset()
  5858. {
  5859. mAtEnd = true; // gets reset when First() is called
  5860. mRg = mStartRg;
  5861. mPrevRow = nullptr;
  5862. mRow = mStartRow;
  5863. mRowIndex = 0;
  5864. mColIndex = 0;
  5865. mRgIndex = -1;
  5866. mPrevCell = nullptr;
  5867. mCell = nullptr;
  5868. mPrevCellData = nullptr;
  5869. mCellData = nullptr;
  5870. mBCData = nullptr;
  5871. ResetVerInfo();
  5872. }
  5873. /**
  5874. * Set the iterator data to a new cellmap coordinate
  5875. * @param aRowIndex - the row index
  5876. * @param aColIndex - the col index
  5877. */
  5878. void
  5879. BCPaintBorderIterator::SetNewData(int32_t aY,
  5880. int32_t aX)
  5881. {
  5882. if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0();
  5883. mColIndex = aX;
  5884. mRowIndex = aY;
  5885. mPrevCellData = mCellData;
  5886. if (IsTableIEndMost() && IsTableBEndMost()) {
  5887. mCell = nullptr;
  5888. mBCData = &mTableCellMap->mBCInfo->mBEndIEndCorner;
  5889. }
  5890. else if (IsTableIEndMost()) {
  5891. mCellData = nullptr;
  5892. mBCData = &mTableCellMap->mBCInfo->mIEndBorders.ElementAt(aY);
  5893. }
  5894. else if (IsTableBEndMost()) {
  5895. mCellData = nullptr;
  5896. mBCData = &mTableCellMap->mBCInfo->mBEndBorders.ElementAt(aX);
  5897. }
  5898. else {
  5899. if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) {
  5900. mBCData = nullptr;
  5901. mCellData =
  5902. (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex);
  5903. if (mCellData) {
  5904. mBCData = &mCellData->mData;
  5905. if (!mCellData->IsOrig()) {
  5906. if (mCellData->IsRowSpan()) {
  5907. aY -= mCellData->GetRowSpanOffset();
  5908. }
  5909. if (mCellData->IsColSpan()) {
  5910. aX -= mCellData->GetColSpanOffset();
  5911. }
  5912. if ((aX >= 0) && (aY >= 0)) {
  5913. mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX];
  5914. }
  5915. }
  5916. if (mCellData->IsOrig()) {
  5917. mPrevCell = mCell;
  5918. mCell = mCellData->GetCellFrame();
  5919. }
  5920. }
  5921. }
  5922. }
  5923. }
  5924. /**
  5925. * Set the iterator to a new row
  5926. * @param aRow - the new row frame, if null the iterator will advance to the
  5927. * next row
  5928. */
  5929. bool
  5930. BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow)
  5931. {
  5932. mPrevRow = mRow;
  5933. mRow = (aRow) ? aRow : mRow->GetNextRow();
  5934. if (mRow) {
  5935. mIsNewRow = true;
  5936. mRowIndex = mRow->GetRowIndex();
  5937. mColIndex = mDamageArea.StartCol();
  5938. mPrevInlineSegBSize = 0;
  5939. if (mIsRepeatedHeader) {
  5940. mRepeatedHeaderRowIndex = mRowIndex;
  5941. }
  5942. }
  5943. else {
  5944. mAtEnd = true;
  5945. }
  5946. return !mAtEnd;
  5947. }
  5948. /**
  5949. * Advance the iterator to the next row group
  5950. */
  5951. bool
  5952. BCPaintBorderIterator::SetNewRowGroup()
  5953. {
  5954. mRgIndex++;
  5955. mIsRepeatedHeader = false;
  5956. mIsRepeatedFooter = false;
  5957. NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds");
  5958. if (uint32_t(mRgIndex) < mRowGroups.Length()) {
  5959. mPrevRg = mRg;
  5960. mRg = mRowGroups[mRgIndex];
  5961. nsTableRowGroupFrame* fifRg =
  5962. static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow());
  5963. mFifRgFirstRowIndex = fifRg->GetStartRowIndex();
  5964. mRgFirstRowIndex = mRg->GetStartRowIndex();
  5965. mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1;
  5966. if (SetNewRow(mRg->GetFirstRow())) {
  5967. mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr);
  5968. if (!mCellMap) ABORT1(false);
  5969. }
  5970. if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) {
  5971. // if mRowGroup doesn't have a prev in flow, then it may be a repeated
  5972. // header or footer
  5973. const nsStyleDisplay* display = mRg->StyleDisplay();
  5974. if (mRowIndex == mDamageArea.StartRow()) {
  5975. mIsRepeatedHeader = (mozilla::StyleDisplay::TableHeaderGroup == display->mDisplay);
  5976. } else {
  5977. mIsRepeatedFooter = (mozilla::StyleDisplay::TableFooterGroup == display->mDisplay);
  5978. }
  5979. }
  5980. }
  5981. else {
  5982. mAtEnd = true;
  5983. }
  5984. return !mAtEnd;
  5985. }
  5986. /**
  5987. * Move the iterator to the first position in the damageArea
  5988. */
  5989. void
  5990. BCPaintBorderIterator::First()
  5991. {
  5992. if (!mTable || mDamageArea.StartCol() >= mNumTableCols ||
  5993. mDamageArea.StartRow() >= mNumTableRows) ABORT0();
  5994. mAtEnd = false;
  5995. uint32_t numRowGroups = mRowGroups.Length();
  5996. for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) {
  5997. nsTableRowGroupFrame* rowG = mRowGroups[rgY];
  5998. int32_t start = rowG->GetStartRowIndex();
  5999. int32_t end = start + rowG->GetRowCount() - 1;
  6000. if (mDamageArea.StartRow() >= start && mDamageArea.StartRow() <= end) {
  6001. mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex
  6002. if (SetNewRowGroup()) {
  6003. while (mRowIndex < mDamageArea.StartRow() && !mAtEnd) {
  6004. SetNewRow();
  6005. }
  6006. if (!mAtEnd) {
  6007. SetNewData(mDamageArea.StartRow(), mDamageArea.StartCol());
  6008. }
  6009. }
  6010. return;
  6011. }
  6012. }
  6013. mAtEnd = true;
  6014. }
  6015. /**
  6016. * Advance the iterator to the next position
  6017. */
  6018. void
  6019. BCPaintBorderIterator::Next()
  6020. {
  6021. if (mAtEnd) ABORT0();
  6022. mIsNewRow = false;
  6023. mColIndex++;
  6024. if (mColIndex > mDamageArea.EndCol()) {
  6025. mRowIndex++;
  6026. if (mRowIndex == mDamageArea.EndRow()) {
  6027. mColIndex = mDamageArea.StartCol();
  6028. }
  6029. else if (mRowIndex < mDamageArea.EndRow()) {
  6030. if (mRowIndex <= mRgLastRowIndex) {
  6031. SetNewRow();
  6032. }
  6033. else {
  6034. SetNewRowGroup();
  6035. }
  6036. }
  6037. else {
  6038. mAtEnd = true;
  6039. }
  6040. }
  6041. if (!mAtEnd) {
  6042. SetNewData(mRowIndex, mColIndex);
  6043. }
  6044. }
  6045. // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine
  6046. // them
  6047. // XXX Update terminology from physical to logical
  6048. /** Compute the vertical offset of a vertical border segment
  6049. * @param aCornerOwnerSide - which side owns the corner
  6050. * @param aCornerSubWidth - how wide is the nonwinning side of the corner
  6051. * @param aHorWidth - how wide is the horizontal edge of the corner
  6052. * @param aIsStartOfSeg - does this corner start a new segment
  6053. * @param aIsBevel - is this corner beveled
  6054. * @return - offset in twips
  6055. */
  6056. static nscoord
  6057. CalcVerCornerOffset(LogicalSide aCornerOwnerSide,
  6058. BCPixelSize aCornerSubWidth,
  6059. BCPixelSize aHorWidth,
  6060. bool aIsStartOfSeg,
  6061. bool aIsBevel,
  6062. nsPresContext* aPresContext)
  6063. {
  6064. nscoord offset = 0;
  6065. // XXX These should be replaced with appropriate side-specific macros (which?)
  6066. BCPixelSize smallHalf, largeHalf;
  6067. if (IsBlock(aCornerOwnerSide)) {
  6068. DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
  6069. if (aIsBevel) {
  6070. offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6071. }
  6072. else {
  6073. offset = (eLogicalSideBStart == aCornerOwnerSide) ? smallHalf : -largeHalf;
  6074. }
  6075. }
  6076. else {
  6077. DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
  6078. if (aIsBevel) {
  6079. offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6080. }
  6081. else {
  6082. offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
  6083. }
  6084. }
  6085. return aPresContext->DevPixelsToAppUnits(offset);
  6086. }
  6087. /** Compute the horizontal offset of a horizontal border segment
  6088. * @param aCornerOwnerSide - which side owns the corner
  6089. * @param aCornerSubWidth - how wide is the nonwinning side of the corner
  6090. * @param aVerWidth - how wide is the vertical edge of the corner
  6091. * @param aIsStartOfSeg - does this corner start a new segment
  6092. * @param aIsBevel - is this corner beveled
  6093. * @return - offset in twips
  6094. */
  6095. static nscoord
  6096. CalcHorCornerOffset(LogicalSide aCornerOwnerSide,
  6097. BCPixelSize aCornerSubWidth,
  6098. BCPixelSize aVerWidth,
  6099. bool aIsStartOfSeg,
  6100. bool aIsBevel,
  6101. nsPresContext* aPresContext)
  6102. {
  6103. nscoord offset = 0;
  6104. // XXX These should be replaced with appropriate side-specific macros (which?)
  6105. BCPixelSize smallHalf, largeHalf;
  6106. if (IsInline(aCornerOwnerSide)) {
  6107. DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
  6108. if (aIsBevel) {
  6109. offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6110. }
  6111. else {
  6112. offset = (eLogicalSideIStart == aCornerOwnerSide) ? smallHalf : -largeHalf;
  6113. }
  6114. }
  6115. else {
  6116. DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
  6117. if (aIsBevel) {
  6118. offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6119. }
  6120. else {
  6121. offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
  6122. }
  6123. }
  6124. return aPresContext->DevPixelsToAppUnits(offset);
  6125. }
  6126. BCBlockDirSeg::BCBlockDirSeg()
  6127. {
  6128. mCol = nullptr;
  6129. mFirstCell = mLastCell = mAjaCell = nullptr;
  6130. mOffsetI = mOffsetB = mLength = mWidth = mBStartBevelOffset = 0;
  6131. mBStartBevelSide = eLogicalSideBStart;
  6132. mOwner = eCellOwner;
  6133. }
  6134. /**
  6135. * Start a new block-direction segment
  6136. * @param aIter - iterator containing the structural information
  6137. * @param aBorderOwner - determines the border style
  6138. * @param aBlockSegISize - the width of segment in pixel
  6139. * @param aInlineSegBSize - the width of the inline-dir segment joining the corner
  6140. * at the start
  6141. */
  6142. void
  6143. BCBlockDirSeg::Start(BCPaintBorderIterator& aIter,
  6144. BCBorderOwner aBorderOwner,
  6145. BCPixelSize aBlockSegISize,
  6146. BCPixelSize aInlineSegBSize)
  6147. {
  6148. LogicalSide ownerSide = eLogicalSideBStart;
  6149. bool bevel = false;
  6150. nscoord cornerSubWidth = (aIter.mBCData) ?
  6151. aIter.mBCData->GetCorner(ownerSide, bevel) : 0;
  6152. bool bStartBevel = (aBlockSegISize > 0) ? bevel : false;
  6153. BCPixelSize maxInlineSegBSize = std::max(aIter.mPrevInlineSegBSize, aInlineSegBSize);
  6154. nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
  6155. maxInlineSegBSize, true,
  6156. bStartBevel, aIter.mTable->PresContext());
  6157. mBStartBevelOffset = bStartBevel ?
  6158. aIter.mTable->PresContext()->DevPixelsToAppUnits(maxInlineSegBSize): 0;
  6159. // XXX this assumes that only corners where 2 segments join can be beveled
  6160. mBStartBevelSide = (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart;
  6161. mOffsetB += offset;
  6162. mLength = -offset;
  6163. mWidth = aBlockSegISize;
  6164. mOwner = aBorderOwner;
  6165. mFirstCell = aIter.mCell;
  6166. mFirstRowGroup = aIter.mRg;
  6167. mFirstRow = aIter.mRow;
  6168. if (aIter.GetRelativeColIndex() > 0) {
  6169. mAjaCell = aIter.mBlockDirInfo[aIter.GetRelativeColIndex() - 1].mLastCell;
  6170. }
  6171. }
  6172. /**
  6173. * Initialize the block-dir segments with information that will persist for any
  6174. * block-dir segment in this column
  6175. * @param aIter - iterator containing the structural information
  6176. */
  6177. void
  6178. BCBlockDirSeg::Initialize(BCPaintBorderIterator& aIter)
  6179. {
  6180. int32_t relColIndex = aIter.GetRelativeColIndex();
  6181. mCol = aIter.IsTableIEndMost() ? aIter.mBlockDirInfo[relColIndex - 1].mCol :
  6182. aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex);
  6183. if (!mCol) ABORT0();
  6184. if (0 == relColIndex) {
  6185. mOffsetI = aIter.mInitialOffsetI;
  6186. }
  6187. // set mOffsetI for the next column
  6188. if (!aIter.IsDamageAreaIEndMost()) {
  6189. aIter.mBlockDirInfo[relColIndex + 1].mOffsetI =
  6190. mOffsetI + mCol->ISize(aIter.mTableWM);
  6191. }
  6192. mOffsetB = aIter.mInitialOffsetB;
  6193. mLastCell = aIter.mCell;
  6194. }
  6195. /**
  6196. * Compute the offsets for the bEnd corner of a block-dir segment
  6197. * @param aIter - iterator containing the structural information
  6198. * @param aInlineSegBSize - the width of the inline-dir segment joining the corner
  6199. * at the start
  6200. */
  6201. void
  6202. BCBlockDirSeg::GetBEndCorner(BCPaintBorderIterator& aIter,
  6203. BCPixelSize aInlineSegBSize)
  6204. {
  6205. LogicalSide ownerSide = eLogicalSideBStart;
  6206. nscoord cornerSubWidth = 0;
  6207. bool bevel = false;
  6208. if (aIter.mBCData) {
  6209. cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
  6210. }
  6211. mIsBEndBevel = (mWidth > 0) ? bevel : false;
  6212. mBEndInlineSegBSize = std::max(aIter.mPrevInlineSegBSize, aInlineSegBSize);
  6213. mBEndOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
  6214. mBEndInlineSegBSize, false,
  6215. mIsBEndBevel, aIter.mTable->PresContext());
  6216. mLength += mBEndOffset;
  6217. }
  6218. /**
  6219. * Paint the block-dir segment
  6220. * @param aIter - iterator containing the structural information
  6221. * @param aDrawTarget - the draw target
  6222. * @param aInlineSegBSize - the width of the inline-dir segment joining the
  6223. * corner at the start
  6224. */
  6225. void
  6226. BCBlockDirSeg::Paint(BCPaintBorderIterator& aIter,
  6227. DrawTarget& aDrawTarget,
  6228. BCPixelSize aInlineSegBSize)
  6229. {
  6230. // get the border style, color and paint the segment
  6231. LogicalSide side =
  6232. aIter.IsDamageAreaIEndMost() ? eLogicalSideIEnd : eLogicalSideIStart;
  6233. int32_t relColIndex = aIter.GetRelativeColIndex();
  6234. nsTableColFrame* col = mCol; if (!col) ABORT0();
  6235. nsTableCellFrame* cell = mFirstCell; // ???
  6236. nsIFrame* owner = nullptr;
  6237. uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
  6238. nscolor color = 0xFFFFFFFF;
  6239. // All the tables frames have the same presContext, so we just use any one
  6240. // that exists here:
  6241. int32_t appUnitsPerDevPixel = col->PresContext()->AppUnitsPerDevPixel();
  6242. switch (mOwner) {
  6243. case eTableOwner:
  6244. owner = aIter.mTable;
  6245. break;
  6246. case eAjaColGroupOwner:
  6247. side = eLogicalSideIEnd;
  6248. if (!aIter.IsTableIEndMost() && (relColIndex > 0)) {
  6249. col = aIter.mBlockDirInfo[relColIndex - 1].mCol;
  6250. }
  6251. MOZ_FALLTHROUGH;
  6252. case eColGroupOwner:
  6253. if (col) {
  6254. owner = col->GetParent();
  6255. }
  6256. break;
  6257. case eAjaColOwner:
  6258. side = eLogicalSideIEnd;
  6259. if (!aIter.IsTableIEndMost() && (relColIndex > 0)) {
  6260. col = aIter.mBlockDirInfo[relColIndex - 1].mCol;
  6261. }
  6262. MOZ_FALLTHROUGH;
  6263. case eColOwner:
  6264. owner = col;
  6265. break;
  6266. case eAjaRowGroupOwner:
  6267. NS_ERROR("a neighboring rowgroup can never own a vertical border");
  6268. MOZ_FALLTHROUGH;
  6269. case eRowGroupOwner:
  6270. NS_ASSERTION(aIter.IsTableIStartMost() || aIter.IsTableIEndMost(),
  6271. "row group can own border only at table edge");
  6272. owner = mFirstRowGroup;
  6273. break;
  6274. case eAjaRowOwner:
  6275. NS_ERROR("program error");
  6276. MOZ_FALLTHROUGH;
  6277. case eRowOwner:
  6278. NS_ASSERTION(aIter.IsTableIStartMost() || aIter.IsTableIEndMost(),
  6279. "row can own border only at table edge");
  6280. owner = mFirstRow;
  6281. break;
  6282. case eAjaCellOwner:
  6283. side = eLogicalSideIEnd;
  6284. cell = mAjaCell;
  6285. MOZ_FALLTHROUGH;
  6286. case eCellOwner:
  6287. owner = cell;
  6288. break;
  6289. }
  6290. if (owner) {
  6291. ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &style, &color);
  6292. }
  6293. BCPixelSize smallHalf, largeHalf;
  6294. DivideBCBorderSize(mWidth, smallHalf, largeHalf);
  6295. LogicalRect segRect(aIter.mTableWM,
  6296. mOffsetI - aIter.mTable->PresContext()->DevPixelsToAppUnits(largeHalf),
  6297. mOffsetB,
  6298. aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth), mLength);
  6299. nscoord bEndBevelOffset = (mIsBEndBevel) ?
  6300. aIter.mTable->PresContext()->DevPixelsToAppUnits(mBEndInlineSegBSize) : 0;
  6301. LogicalSide bEndBevelSide =
  6302. (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart;
  6303. // Convert logical to physical sides/coordinates for DrawTableBorderSegment.
  6304. nsRect physicalRect = segRect.GetPhysicalRect(aIter.mTableWM,
  6305. aIter.mTable->GetSize());
  6306. // XXX For reversed vertical writing-modes (with direction:rtl), we need to
  6307. // invert physicalRect's y-position here, with respect to the table.
  6308. // However, it's not worth fixing the border positions here until the
  6309. // ordering of the table columns themselves is also fixed (bug 1180528).
  6310. uint8_t startBevelSide = aIter.mTableWM.PhysicalSide(mBStartBevelSide);
  6311. uint8_t endBevelSide = aIter.mTableWM.PhysicalSide(bEndBevelSide);
  6312. nscoord startBevelOffset = mBStartBevelOffset;
  6313. nscoord endBevelOffset = bEndBevelOffset;
  6314. // In vertical-rl mode, the 'start' and 'end' of the block-dir (horizontal)
  6315. // border segment need to be swapped because DrawTableBorderSegment will
  6316. // apply the 'start' bevel at the left edge, and 'end' at the right.
  6317. // (Note: In this case, startBevelSide/endBevelSide will usually both be
  6318. // "top" or "bottom". DrawTableBorderSegment works purely with physical
  6319. // coordinates, so it expects startBevelOffset to be the indentation-from-
  6320. // the-left for the "start" (left) end of the border-segment, and
  6321. // endBevelOffset is the indentation-from-the-right for the "end" (right)
  6322. // end of the border-segment. We've got them reversed, since our block dir
  6323. // is RTL, so we have to swap them here.)
  6324. if (aIter.mTableWM.IsVerticalRL()) {
  6325. Swap(startBevelSide, endBevelSide);
  6326. Swap(startBevelOffset, endBevelOffset);
  6327. }
  6328. nsCSSRendering::DrawTableBorderSegment(aDrawTarget, style, color,
  6329. aIter.mTableBgColor, physicalRect,
  6330. appUnitsPerDevPixel,
  6331. aIter.mTable->PresContext()->AppUnitsPerDevPixel(),
  6332. startBevelSide, startBevelOffset,
  6333. endBevelSide, endBevelOffset);
  6334. }
  6335. /**
  6336. * Advance the start point of a segment
  6337. */
  6338. void
  6339. BCBlockDirSeg::AdvanceOffsetB()
  6340. {
  6341. mOffsetB += mLength - mBEndOffset;
  6342. }
  6343. /**
  6344. * Accumulate the current segment
  6345. */
  6346. void
  6347. BCBlockDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
  6348. {
  6349. mLastCell = aIter.mCell;
  6350. mLength += aIter.mRow->BSize(aIter.mTableWM);
  6351. }
  6352. BCInlineDirSeg::BCInlineDirSeg()
  6353. {
  6354. mOffsetI = mOffsetB = mLength = mWidth = mIStartBevelOffset = 0;
  6355. mIStartBevelSide = eLogicalSideBStart;
  6356. mFirstCell = mAjaCell = nullptr;
  6357. }
  6358. /** Initialize an inline-dir border segment for painting
  6359. * @param aIter - iterator storing the current and adjacent frames
  6360. * @param aBorderOwner - which frame owns the border
  6361. * @param aBEndBlockSegISize - block-dir segment width coming from up
  6362. * @param aInlineSegBSize - the thickness of the segment
  6363. + */
  6364. void
  6365. BCInlineDirSeg::Start(BCPaintBorderIterator& aIter,
  6366. BCBorderOwner aBorderOwner,
  6367. BCPixelSize aBEndBlockSegISize,
  6368. BCPixelSize aInlineSegBSize)
  6369. {
  6370. LogicalSide cornerOwnerSide = eLogicalSideBStart;
  6371. bool bevel = false;
  6372. mOwner = aBorderOwner;
  6373. nscoord cornerSubWidth = (aIter.mBCData) ?
  6374. aIter.mBCData->GetCorner(cornerOwnerSide,
  6375. bevel) : 0;
  6376. bool iStartBevel = (aInlineSegBSize > 0) ? bevel : false;
  6377. int32_t relColIndex = aIter.GetRelativeColIndex();
  6378. nscoord maxBlockSegISize = std::max(aIter.mBlockDirInfo[relColIndex].mWidth,
  6379. aBEndBlockSegISize);
  6380. nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth,
  6381. maxBlockSegISize, true, iStartBevel,
  6382. aIter.mTable->PresContext());
  6383. mIStartBevelOffset = (iStartBevel && (aInlineSegBSize > 0)) ? maxBlockSegISize : 0;
  6384. // XXX this assumes that only corners where 2 segments join can be beveled
  6385. mIStartBevelSide = (aBEndBlockSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart;
  6386. mOffsetI += offset;
  6387. mLength = -offset;
  6388. mWidth = aInlineSegBSize;
  6389. mFirstCell = aIter.mCell;
  6390. mAjaCell = (aIter.IsDamageAreaBStartMost()) ? nullptr :
  6391. aIter.mBlockDirInfo[relColIndex].mLastCell;
  6392. }
  6393. /**
  6394. * Compute the offsets for the iEnd corner of an inline-dir segment
  6395. * @param aIter - iterator containing the structural information
  6396. * @param aIStartSegISize - the iSize of the block-dir segment joining the corner
  6397. * at the start
  6398. */
  6399. void
  6400. BCInlineDirSeg::GetIEndCorner(BCPaintBorderIterator& aIter,
  6401. BCPixelSize aIStartSegISize)
  6402. {
  6403. LogicalSide ownerSide = eLogicalSideBStart;
  6404. nscoord cornerSubWidth = 0;
  6405. bool bevel = false;
  6406. if (aIter.mBCData) {
  6407. cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
  6408. }
  6409. mIsIEndBevel = (mWidth > 0) ? bevel : 0;
  6410. int32_t relColIndex = aIter.GetRelativeColIndex();
  6411. nscoord verWidth = std::max(aIter.mBlockDirInfo[relColIndex].mWidth,
  6412. aIStartSegISize);
  6413. mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth,
  6414. false, mIsIEndBevel, aIter.mTable->PresContext());
  6415. mLength += mEndOffset;
  6416. mIEndBevelOffset = (mIsIEndBevel) ?
  6417. aIter.mTable->PresContext()->DevPixelsToAppUnits(verWidth) : 0;
  6418. mIEndBevelSide = (aIStartSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart;
  6419. }
  6420. /**
  6421. * Paint the inline-dir segment
  6422. * @param aIter - iterator containing the structural information
  6423. * @param aDrawTarget - the draw target
  6424. */
  6425. void
  6426. BCInlineDirSeg::Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget)
  6427. {
  6428. // get the border style, color and paint the segment
  6429. LogicalSide side =
  6430. aIter.IsDamageAreaBEndMost() ? eLogicalSideBEnd : eLogicalSideBStart;
  6431. nsIFrame* rg = aIter.mRg; if (!rg) ABORT0();
  6432. nsIFrame* row = aIter.mRow; if (!row) ABORT0();
  6433. nsIFrame* cell = mFirstCell;
  6434. nsIFrame* col;
  6435. nsIFrame* owner = nullptr;
  6436. // All the tables frames have the same presContext, so we just use any one
  6437. // that exists here:
  6438. int32_t appUnitsPerDevPixel = row->PresContext()->AppUnitsPerDevPixel();
  6439. uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
  6440. nscolor color = 0xFFFFFFFF;
  6441. switch (mOwner) {
  6442. case eTableOwner:
  6443. owner = aIter.mTable;
  6444. break;
  6445. case eAjaColGroupOwner:
  6446. NS_ERROR("neighboring colgroups can never own an inline-dir border");
  6447. MOZ_FALLTHROUGH;
  6448. case eColGroupOwner:
  6449. NS_ASSERTION(aIter.IsTableBStartMost() || aIter.IsTableBEndMost(),
  6450. "col group can own border only at the table edge");
  6451. col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
  6452. if (!col) ABORT0();
  6453. owner = col->GetParent();
  6454. break;
  6455. case eAjaColOwner:
  6456. NS_ERROR("neighboring column can never own an inline-dir border");
  6457. MOZ_FALLTHROUGH;
  6458. case eColOwner:
  6459. NS_ASSERTION(aIter.IsTableBStartMost() || aIter.IsTableBEndMost(),
  6460. "col can own border only at the table edge");
  6461. owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
  6462. break;
  6463. case eAjaRowGroupOwner:
  6464. side = eLogicalSideBEnd;
  6465. rg = (aIter.IsTableBEndMost()) ? aIter.mRg : aIter.mPrevRg;
  6466. MOZ_FALLTHROUGH;
  6467. case eRowGroupOwner:
  6468. owner = rg;
  6469. break;
  6470. case eAjaRowOwner:
  6471. side = eLogicalSideBEnd;
  6472. row = (aIter.IsTableBEndMost()) ? aIter.mRow : aIter.mPrevRow;
  6473. MOZ_FALLTHROUGH;
  6474. case eRowOwner:
  6475. owner = row;
  6476. break;
  6477. case eAjaCellOwner:
  6478. side = eLogicalSideBEnd;
  6479. // if this is null due to the damage area origin-y > 0, then the border
  6480. // won't show up anyway
  6481. cell = mAjaCell;
  6482. MOZ_FALLTHROUGH;
  6483. case eCellOwner:
  6484. owner = cell;
  6485. break;
  6486. }
  6487. if (owner) {
  6488. ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &style, &color);
  6489. }
  6490. BCPixelSize smallHalf, largeHalf;
  6491. DivideBCBorderSize(mWidth, smallHalf, largeHalf);
  6492. LogicalRect segRect(aIter.mTableWM, mOffsetI,
  6493. mOffsetB - aIter.mTable->PresContext()->DevPixelsToAppUnits(largeHalf),
  6494. mLength,
  6495. aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth));
  6496. // Convert logical to physical sides/coordinates for DrawTableBorderSegment.
  6497. nsRect physicalRect = segRect.GetPhysicalRect(aIter.mTableWM,
  6498. aIter.mTable->GetSize());
  6499. uint8_t startBevelSide = aIter.mTableWM.PhysicalSide(mIStartBevelSide);
  6500. uint8_t endBevelSide = aIter.mTableWM.PhysicalSide(mIEndBevelSide);
  6501. nscoord startBevelOffset =
  6502. aIter.mTable->PresContext()->DevPixelsToAppUnits(mIStartBevelOffset);
  6503. nscoord endBevelOffset = mIEndBevelOffset;
  6504. // With inline-RTL directionality, the 'start' and 'end' of the inline-dir
  6505. // border segment need to be swapped because DrawTableBorderSegment will
  6506. // apply the 'start' bevel physically at the left or top edge, and 'end' at
  6507. // the right or bottom.
  6508. // (Note: startBevelSide/endBevelSide will be "top" or "bottom" in horizontal
  6509. // writing mode, or "left" or "right" in vertical mode.
  6510. // DrawTableBorderSegment works purely with physical coordinates, so it
  6511. // expects startBevelOffset to be the indentation-from-the-left or top end
  6512. // of the border-segment, and endBevelOffset is the indentation-from-the-
  6513. // right or bottom end. If the writing mode is inline-RTL, our "start" and
  6514. // "end" will be reversed from this physical-coord view, so we have to swap
  6515. // them here.
  6516. if (!aIter.mTableWM.IsBidiLTR()) {
  6517. Swap(startBevelSide, endBevelSide);
  6518. Swap(startBevelOffset, endBevelOffset);
  6519. }
  6520. nsCSSRendering::DrawTableBorderSegment(aDrawTarget, style, color,
  6521. aIter.mTableBgColor, physicalRect,
  6522. appUnitsPerDevPixel,
  6523. aIter.mTable->PresContext()->AppUnitsPerDevPixel(),
  6524. startBevelSide, startBevelOffset,
  6525. endBevelSide, endBevelOffset);
  6526. }
  6527. /**
  6528. * Advance the start point of a segment
  6529. */
  6530. void
  6531. BCInlineDirSeg::AdvanceOffsetI()
  6532. {
  6533. mOffsetI += (mLength - mEndOffset);
  6534. }
  6535. /**
  6536. * Accumulate the current segment
  6537. */
  6538. void
  6539. BCInlineDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
  6540. {
  6541. mLength += aIter.mBlockDirInfo[aIter.GetRelativeColIndex()].mColWidth;
  6542. }
  6543. /**
  6544. * store the column width information while painting inline-dir segment
  6545. */
  6546. void
  6547. BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex)
  6548. {
  6549. if (IsTableIEndMost()) {
  6550. mBlockDirInfo[aIndex].mColWidth = mBlockDirInfo[aIndex - 1].mColWidth;
  6551. }
  6552. else {
  6553. nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex);
  6554. if (!col) ABORT0();
  6555. mBlockDirInfo[aIndex].mColWidth = col->ISize(mTableWM);
  6556. }
  6557. }
  6558. /**
  6559. * Determine if a block-dir segment owns the corner
  6560. */
  6561. bool
  6562. BCPaintBorderIterator::BlockDirSegmentOwnsCorner()
  6563. {
  6564. LogicalSide cornerOwnerSide = eLogicalSideBStart;
  6565. bool bevel = false;
  6566. if (mBCData) {
  6567. mBCData->GetCorner(cornerOwnerSide, bevel);
  6568. }
  6569. // unitialized ownerside, bevel
  6570. return (eLogicalSideBStart == cornerOwnerSide) ||
  6571. (eLogicalSideBEnd == cornerOwnerSide);
  6572. }
  6573. /**
  6574. * Paint if necessary an inline-dir segment, otherwise accumulate it
  6575. * @param aDrawTarget - the draw target
  6576. */
  6577. void
  6578. BCPaintBorderIterator::AccumulateOrPaintInlineDirSegment(DrawTarget& aDrawTarget)
  6579. {
  6580. int32_t relColIndex = GetRelativeColIndex();
  6581. // store the current col width if it hasn't been already
  6582. if (mBlockDirInfo[relColIndex].mColWidth < 0) {
  6583. StoreColumnWidth(relColIndex);
  6584. }
  6585. BCBorderOwner borderOwner = eCellOwner;
  6586. BCBorderOwner ignoreBorderOwner;
  6587. bool isSegStart = true;
  6588. bool ignoreSegStart;
  6589. nscoord iStartSegISize =
  6590. mBCData ? mBCData->GetIStartEdge(ignoreBorderOwner, ignoreSegStart) : 0;
  6591. nscoord bStartSegBSize =
  6592. mBCData ? mBCData->GetBStartEdge(borderOwner, isSegStart) : 0;
  6593. if (mIsNewRow || (IsDamageAreaIStartMost() && IsDamageAreaBEndMost())) {
  6594. // reset for every new row and on the bottom of the last row
  6595. mInlineSeg.mOffsetB = mNextOffsetB;
  6596. mNextOffsetB = mNextOffsetB + mRow->BSize(mTableWM);
  6597. mInlineSeg.mOffsetI = mInitialOffsetI;
  6598. mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
  6599. }
  6600. if (!IsDamageAreaIStartMost() && (isSegStart || IsDamageAreaIEndMost() ||
  6601. BlockDirSegmentOwnsCorner())) {
  6602. // paint the previous seg or the current one if IsDamageAreaIEndMost()
  6603. if (mInlineSeg.mLength > 0) {
  6604. mInlineSeg.GetIEndCorner(*this, iStartSegISize);
  6605. if (mInlineSeg.mWidth > 0) {
  6606. mInlineSeg.Paint(*this, aDrawTarget);
  6607. }
  6608. mInlineSeg.AdvanceOffsetI();
  6609. }
  6610. mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
  6611. }
  6612. mInlineSeg.IncludeCurrentBorder(*this);
  6613. mBlockDirInfo[relColIndex].mWidth = iStartSegISize;
  6614. mBlockDirInfo[relColIndex].mLastCell = mCell;
  6615. }
  6616. /**
  6617. * Paint if necessary a block-dir segment, otherwise accumulate it
  6618. * @param aDrawTarget - the draw target
  6619. */
  6620. void
  6621. BCPaintBorderIterator::AccumulateOrPaintBlockDirSegment(DrawTarget& aDrawTarget)
  6622. {
  6623. BCBorderOwner borderOwner = eCellOwner;
  6624. BCBorderOwner ignoreBorderOwner;
  6625. bool isSegStart = true;
  6626. bool ignoreSegStart;
  6627. nscoord blockSegISize =
  6628. mBCData ? mBCData->GetIStartEdge(borderOwner, isSegStart) : 0;
  6629. nscoord inlineSegBSize =
  6630. mBCData ? mBCData->GetBStartEdge(ignoreBorderOwner, ignoreSegStart) : 0;
  6631. int32_t relColIndex = GetRelativeColIndex();
  6632. BCBlockDirSeg& blockDirSeg = mBlockDirInfo[relColIndex];
  6633. if (!blockDirSeg.mCol) { // on the first damaged row and the first segment in the
  6634. // col
  6635. blockDirSeg.Initialize(*this);
  6636. blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
  6637. }
  6638. if (!IsDamageAreaBStartMost() && (isSegStart || IsDamageAreaBEndMost() ||
  6639. IsAfterRepeatedHeader() ||
  6640. StartRepeatedFooter())) {
  6641. // paint the previous seg or the current one if IsDamageAreaBEndMost()
  6642. if (blockDirSeg.mLength > 0) {
  6643. blockDirSeg.GetBEndCorner(*this, inlineSegBSize);
  6644. if (blockDirSeg.mWidth > 0) {
  6645. blockDirSeg.Paint(*this, aDrawTarget, inlineSegBSize);
  6646. }
  6647. blockDirSeg.AdvanceOffsetB();
  6648. }
  6649. blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
  6650. }
  6651. blockDirSeg.IncludeCurrentBorder(*this);
  6652. mPrevInlineSegBSize = inlineSegBSize;
  6653. }
  6654. /**
  6655. * Reset the block-dir information cache
  6656. */
  6657. void
  6658. BCPaintBorderIterator::ResetVerInfo()
  6659. {
  6660. if (mBlockDirInfo) {
  6661. memset(mBlockDirInfo, 0, mDamageArea.ColCount() * sizeof(BCBlockDirSeg));
  6662. // XXX reinitialize properly
  6663. for (auto xIndex : MakeRange(mDamageArea.ColCount())) {
  6664. mBlockDirInfo[xIndex].mColWidth = -1;
  6665. }
  6666. }
  6667. }
  6668. /**
  6669. * Method to paint BCBorders, this does not use currently display lists although
  6670. * it will do this in future
  6671. * @param aDrawTarget - the rendering context
  6672. * @param aDirtyRect - inside this rectangle the BC Borders will redrawn
  6673. */
  6674. void
  6675. nsTableFrame::PaintBCBorders(DrawTarget& aDrawTarget, const nsRect& aDirtyRect)
  6676. {
  6677. // We first transfer the aDirtyRect into cellmap coordinates to compute which
  6678. // cell borders need to be painted
  6679. BCPaintBorderIterator iter(this);
  6680. if (!iter.SetDamageArea(aDirtyRect))
  6681. return;
  6682. // XXX comment still has physical terminology
  6683. // First, paint all of the vertical borders from top to bottom and left to
  6684. // right as they become complete. They are painted first, since they are less
  6685. // efficient to paint than horizontal segments. They were stored with as few
  6686. // segments as possible (since horizontal borders are painted last and
  6687. // possibly over them). For every cell in a row that fails in the damage are
  6688. // we look up if the current border would start a new segment, if so we paint
  6689. // the previously stored vertical segment and start a new segment. After
  6690. // this we the now active segment with the current border. These
  6691. // segments are stored in mBlockDirInfo to be used on the next row
  6692. for (iter.First(); !iter.mAtEnd; iter.Next()) {
  6693. iter.AccumulateOrPaintBlockDirSegment(aDrawTarget);
  6694. }
  6695. // Next, paint all of the inline-dir border segments from bStart to bEnd reuse
  6696. // the mBlockDirInfo array to keep track of col widths and block-dir segments for
  6697. // corner calculations
  6698. iter.Reset();
  6699. for (iter.First(); !iter.mAtEnd; iter.Next()) {
  6700. iter.AccumulateOrPaintInlineDirSegment(aDrawTarget);
  6701. }
  6702. }
  6703. bool
  6704. nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols)
  6705. {
  6706. bool result = false;
  6707. nsTableCellMap* cellMap = GetCellMap();
  6708. NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
  6709. if (cellMap) {
  6710. result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
  6711. }
  6712. return result;
  6713. }
  6714. bool
  6715. nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols)
  6716. {
  6717. bool result = false;
  6718. nsTableCellMap* cellMap = GetCellMap();
  6719. NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
  6720. if (cellMap) {
  6721. result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
  6722. }
  6723. return result;
  6724. }
  6725. /* static */
  6726. void
  6727. nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame,
  6728. const nsRect& aOrigRect,
  6729. const nsRect& aOrigVisualOverflow,
  6730. bool aIsFirstReflow)
  6731. {
  6732. nsIFrame* parent = aFrame->GetParent();
  6733. NS_ASSERTION(parent, "What happened here?");
  6734. if (parent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
  6735. // Don't bother; we'll invalidate the parent's overflow rect when
  6736. // we finish reflowing it.
  6737. return;
  6738. }
  6739. // The part that looks at both the rect and the overflow rect is a
  6740. // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent
  6741. // description of its hackishness.
  6742. //
  6743. // This doesn't really make sense now that we have DLBI.
  6744. // This code can probably be simplified a fair bit.
  6745. nsRect visualOverflow = aFrame->GetVisualOverflowRect();
  6746. if (aIsFirstReflow ||
  6747. aOrigRect.TopLeft() != aFrame->GetPosition() ||
  6748. aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) {
  6749. // Invalidate the old and new overflow rects. Note that if the
  6750. // frame moved, we can't just use aOrigVisualOverflow, since it's in
  6751. // coordinates relative to the old position. So invalidate via
  6752. // aFrame's parent, and reposition that overflow rect to the right
  6753. // place.
  6754. // XXXbz this doesn't handle outlines, does it?
  6755. aFrame->InvalidateFrame();
  6756. parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft());
  6757. } else if (aOrigRect.Size() != aFrame->GetSize() ||
  6758. aOrigVisualOverflow.Size() != visualOverflow.Size()){
  6759. aFrame->InvalidateFrameWithRect(aOrigVisualOverflow);
  6760. aFrame->InvalidateFrame();
  6761. parent->InvalidateFrameWithRect(aOrigRect);
  6762. parent->InvalidateFrame();
  6763. }
  6764. }