summaryrefslogtreecommitdiffstats
path: root/layout/generic
diff options
context:
space:
mode:
Diffstat (limited to 'layout/generic')
-rw-r--r--layout/generic/AsyncScrollBase.cpp133
-rw-r--r--layout/generic/AsyncScrollBase.h105
-rw-r--r--layout/generic/BlockReflowInput.cpp1140
-rw-r--r--layout/generic/BlockReflowInput.h396
-rw-r--r--layout/generic/CSSAlignUtils.cpp159
-rw-r--r--layout/generic/CSSAlignUtils.h61
-rw-r--r--layout/generic/DetailsFrame.cpp131
-rw-r--r--layout/generic/DetailsFrame.h62
-rw-r--r--layout/generic/FrameChildList.cpp59
-rw-r--r--layout/generic/FrameChildList.h126
-rw-r--r--layout/generic/JustificationUtils.h144
-rw-r--r--layout/generic/MathMLTextRunFactory.cpp801
-rw-r--r--layout/generic/MathMLTextRunFactory.h42
-rw-r--r--layout/generic/ReflowInput.cpp3071
-rw-r--r--layout/generic/ReflowInput.h1017
-rw-r--r--layout/generic/ReflowOutput.cpp67
-rw-r--r--layout/generic/ReflowOutput.h348
-rw-r--r--layout/generic/RubyUtils.cpp203
-rw-r--r--layout/generic/RubyUtils.h234
-rw-r--r--layout/generic/ScrollSnap.cpp311
-rw-r--r--layout/generic/ScrollSnap.h41
-rw-r--r--layout/generic/ScrollVelocityQueue.cpp97
-rw-r--r--layout/generic/ScrollVelocityQueue.h92
-rw-r--r--layout/generic/ScrollbarActivity.cpp469
-rw-r--r--layout/generic/ScrollbarActivity.h160
-rw-r--r--layout/generic/Selection.h420
-rw-r--r--layout/generic/SelectionChangeListener.h57
-rw-r--r--layout/generic/StickyScrollContainer.cpp391
-rw-r--r--layout/generic/StickyScrollContainer.h110
-rw-r--r--layout/generic/TextOverflow.cpp837
-rw-r--r--layout/generic/TextOverflow.h259
-rw-r--r--layout/generic/Visibility.h46
-rw-r--r--layout/generic/WritingModes.h2075
-rw-r--r--layout/generic/broken-image.pngbin0 -> 253 bytes
-rw-r--r--layout/generic/crashtests/1001233.html18
-rw-r--r--layout/generic/crashtests/1001258-1.html26
-rw-r--r--layout/generic/crashtests/1003441.xul37
-rw-r--r--layout/generic/crashtests/1015562.html20
-rw-r--r--layout/generic/crashtests/1015563-1.html4
-rw-r--r--layout/generic/crashtests/1015563-2.html7
-rw-r--r--layout/generic/crashtests/1015844.html25
-rw-r--r--layout/generic/crashtests/1032450.html12
-rw-r--r--layout/generic/crashtests/1032613-1.svg10
-rw-r--r--layout/generic/crashtests/1032613-2.html17
-rw-r--r--layout/generic/crashtests/1037903.html6
-rw-r--r--layout/generic/crashtests/1039454-1.html12
-rw-r--r--layout/generic/crashtests/1042489.html6
-rw-r--r--layout/generic/crashtests/1054010-1.html97
-rw-r--r--layout/generic/crashtests/1058954-1.html13
-rw-r--r--layout/generic/crashtests/1134531.html4
-rw-r--r--layout/generic/crashtests/1134667.html2
-rw-r--r--layout/generic/crashtests/1137723-1.html29
-rw-r--r--layout/generic/crashtests/1137723-2.html29
-rw-r--r--layout/generic/crashtests/1140268-1.html18
-rw-r--r--layout/generic/crashtests/1145768.html21
-rw-r--r--layout/generic/crashtests/1146103.html6
-rw-r--r--layout/generic/crashtests/1146107.html6
-rw-r--r--layout/generic/crashtests/1146114.html6
-rw-r--r--layout/generic/crashtests/1153695.html25
-rw-r--r--layout/generic/crashtests/1156222.html6
-rw-r--r--layout/generic/crashtests/1156257.html19
-rw-r--r--layout/generic/crashtests/1157011.html4
-rw-r--r--layout/generic/crashtests/1169420-1.html8
-rw-r--r--layout/generic/crashtests/1169420-2.html8
-rw-r--r--layout/generic/crashtests/1183431.html6
-rw-r--r--layout/generic/crashtests/1221112-1.html32
-rw-r--r--layout/generic/crashtests/1221112-2.html27
-rw-r--r--layout/generic/crashtests/1221874-1.html16
-rw-r--r--layout/generic/crashtests/1222783.xhtml19
-rw-r--r--layout/generic/crashtests/1223568-1.html2
-rw-r--r--layout/generic/crashtests/1223568-2.html6
-rw-r--r--layout/generic/crashtests/1224230-1.html22
-rw-r--r--layout/generic/crashtests/1225005.html4
-rw-r--r--layout/generic/crashtests/1225118.html4
-rw-r--r--layout/generic/crashtests/1225376.html10
-rw-r--r--layout/generic/crashtests/1225592.html13
-rw-r--r--layout/generic/crashtests/1229437-1.html8
-rw-r--r--layout/generic/crashtests/1229437-2.html5
-rw-r--r--layout/generic/crashtests/1233191.html9
-rw-r--r--layout/generic/crashtests/1272983-1.html15
-rw-r--r--layout/generic/crashtests/1272983-2.html15
-rw-r--r--layout/generic/crashtests/1275059.html3
-rw-r--r--layout/generic/crashtests/1278007.html26
-rw-r--r--layout/generic/crashtests/1278461-1.html23
-rw-r--r--layout/generic/crashtests/1278461-2.html22
-rw-r--r--layout/generic/crashtests/1279814.html35
-rw-r--r--layout/generic/crashtests/1297427-non-equal-centers.html14
-rw-r--r--layout/generic/crashtests/1304441.html9
-rw-r--r--layout/generic/crashtests/1316649.html54
-rw-r--r--layout/generic/crashtests/225868-1-inner.html14
-rw-r--r--layout/generic/crashtests/225868-1.html7
-rw-r--r--layout/generic/crashtests/255468.xhtml24
-rw-r--r--layout/generic/crashtests/255982-1.html13
-rw-r--r--layout/generic/crashtests/255982-2.html10
-rw-r--r--layout/generic/crashtests/255982-3.html10
-rw-r--r--layout/generic/crashtests/255982-4.html13
-rw-r--r--layout/generic/crashtests/25888-1.html6
-rw-r--r--layout/generic/crashtests/25888-2.html8
-rw-r--r--layout/generic/crashtests/264937-1.html18
-rw-r--r--layout/generic/crashtests/265867-1.html11
-rw-r--r--layout/generic/crashtests/265867-2.html3
-rw-r--r--layout/generic/crashtests/286491.html26
-rw-r--r--layout/generic/crashtests/289864-1.html5
-rw-r--r--layout/generic/crashtests/289864-1.jpgbin0 -> 42186 bytes
-rw-r--r--layout/generic/crashtests/295292-1.html13
-rw-r--r--layout/generic/crashtests/295292-2.html23
-rw-r--r--layout/generic/crashtests/302260-1.html21
-rw-r--r--layout/generic/crashtests/307979-1.html27
-rw-r--r--layout/generic/crashtests/309322-1.html56
-rw-r--r--layout/generic/crashtests/309322-2.html56
-rw-r--r--layout/generic/crashtests/309322-3.html48
-rw-r--r--layout/generic/crashtests/309322-4.html48
-rw-r--r--layout/generic/crashtests/310556-1.xhtml21
-rw-r--r--layout/generic/crashtests/321224.xul6
-rw-r--r--layout/generic/crashtests/322780-1.xul6
-rw-r--r--layout/generic/crashtests/323381-1.html1
-rw-r--r--layout/generic/crashtests/323381-2.html1
-rw-r--r--layout/generic/crashtests/323386-1.html1
-rw-r--r--layout/generic/crashtests/323389-1.html7
-rw-r--r--layout/generic/crashtests/323389-2.html8
-rw-r--r--layout/generic/crashtests/323493-1.html16
-rw-r--r--layout/generic/crashtests/323495-1.html14
-rw-r--r--layout/generic/crashtests/324318-1.html29
-rw-r--r--layout/generic/crashtests/328946-1.html1
-rw-r--r--layout/generic/crashtests/331284-1.xhtml13
-rw-r--r--layout/generic/crashtests/331292.html258
-rw-r--r--layout/generic/crashtests/334105-1.xhtml35
-rw-r--r--layout/generic/crashtests/334107-1.xhtml9
-rw-r--r--layout/generic/crashtests/334147-1.xhtml16
-rw-r--r--layout/generic/crashtests/334148-1.xhtml14
-rw-r--r--layout/generic/crashtests/334602-1.html12
-rw-r--r--layout/generic/crashtests/337412-1.html29
-rw-r--r--layout/generic/crashtests/337883-1.html20
-rw-r--r--layout/generic/crashtests/337883-2.html21
-rw-r--r--layout/generic/crashtests/339769-1.html22
-rw-r--r--layout/generic/crashtests/342322-1.html28
-rw-r--r--layout/generic/crashtests/343206-1.xhtml21
-rw-r--r--layout/generic/crashtests/344557-1.html32
-rw-r--r--layout/generic/crashtests/345139-1.xhtml53
-rw-r--r--layout/generic/crashtests/345617-1.html8
-rw-r--r--layout/generic/crashtests/348510-1.html7
-rw-r--r--layout/generic/crashtests/348510-2.html7
-rw-r--r--layout/generic/crashtests/348887-1-inner.html21
-rw-r--r--layout/generic/crashtests/348887-1.html9
-rw-r--r--layout/generic/crashtests/348991-1.xhtml8
-rw-r--r--layout/generic/crashtests/350370.html42
-rw-r--r--layout/generic/crashtests/354458-1.html10
-rw-r--r--layout/generic/crashtests/354458-2.html26
-rw-r--r--layout/generic/crashtests/355426-1.html27
-rw-r--r--layout/generic/crashtests/359371-1.html66
-rw-r--r--layout/generic/crashtests/359371-2.html64
-rw-r--r--layout/generic/crashtests/360599.html25
-rw-r--r--layout/generic/crashtests/361109.html9
-rw-r--r--layout/generic/crashtests/363448.html23
-rw-r--r--layout/generic/crashtests/363722-1.html9
-rw-r--r--layout/generic/crashtests/363722-2.html10
-rw-r--r--layout/generic/crashtests/363848-1.xhtml10
-rw-r--r--layout/generic/crashtests/364220.html17
-rw-r--r--layout/generic/crashtests/364407-1.html44
-rw-r--r--layout/generic/crashtests/364686-1.xhtml12
-rw-r--r--layout/generic/crashtests/366021-1.xhtml24
-rw-r--r--layout/generic/crashtests/366667-1.html6
-rw-r--r--layout/generic/crashtests/366952-1.html17
-rw-r--r--layout/generic/crashtests/367246-1.html9
-rw-r--r--layout/generic/crashtests/367360.html30
-rw-r--r--layout/generic/crashtests/368330-1.html15
-rw-r--r--layout/generic/crashtests/368461-1.xhtml11
-rw-r--r--layout/generic/crashtests/368568.html14
-rw-r--r--layout/generic/crashtests/368752.html20
-rw-r--r--layout/generic/crashtests/368860-1.html12
-rw-r--r--layout/generic/crashtests/368863-1.html5
-rw-r--r--layout/generic/crashtests/369038-1.xhtml29
-rw-r--r--layout/generic/crashtests/369150-1.html22
-rw-r--r--layout/generic/crashtests/369150-2.html22
-rw-r--r--layout/generic/crashtests/369227-1.xhtml19
-rw-r--r--layout/generic/crashtests/369542-1.html7
-rw-r--r--layout/generic/crashtests/369542-2.html15
-rw-r--r--layout/generic/crashtests/369547-1.html16
-rw-r--r--layout/generic/crashtests/370174-1.html566
-rw-r--r--layout/generic/crashtests/370174-2.html13
-rw-r--r--layout/generic/crashtests/370174-3.html26
-rw-r--r--layout/generic/crashtests/370174-4.html24
-rw-r--r--layout/generic/crashtests/370699-1.html14
-rw-r--r--layout/generic/crashtests/370794-1.html12
-rw-r--r--layout/generic/crashtests/370866-1.xhtml14
-rw-r--r--layout/generic/crashtests/370884-1.xhtml14
-rw-r--r--layout/generic/crashtests/371348-1.xhtml41
-rw-r--r--layout/generic/crashtests/371561-1.html8
-rw-r--r--layout/generic/crashtests/371566-1.xhtml13
-rw-r--r--layout/generic/crashtests/372376-1.xhtml39
-rw-r--r--layout/generic/crashtests/373859-1.html16
-rw-r--r--layout/generic/crashtests/373868-1.xhtml19
-rw-r--r--layout/generic/crashtests/374090.html11
-rw-r--r--layout/generic/crashtests/374420.xhtml34
-rw-r--r--layout/generic/crashtests/375462-1.html781
-rw-r--r--layout/generic/crashtests/375831.html11
-rw-r--r--layout/generic/crashtests/376419.html28
-rw-r--r--layout/generic/crashtests/377522.html18
-rw-r--r--layout/generic/crashtests/37757-1.html1
-rw-r--r--layout/generic/crashtests/379217-1.xhtml7
-rw-r--r--layout/generic/crashtests/379217-2.xhtml10
-rw-r--r--layout/generic/crashtests/379917-1.xhtml35
-rw-r--r--layout/generic/crashtests/380012-1.html42
-rw-r--r--layout/generic/crashtests/381152-1.html11
-rw-r--r--layout/generic/crashtests/381786-1.html17
-rw-r--r--layout/generic/crashtests/382129-1.xhtml7
-rw-r--r--layout/generic/crashtests/382131-1.html25
-rw-r--r--layout/generic/crashtests/382199-1.html8
-rw-r--r--layout/generic/crashtests/382208-1.xhtml7
-rw-r--r--layout/generic/crashtests/382262-1.html10
-rw-r--r--layout/generic/crashtests/382396-1.xhtml7
-rw-r--r--layout/generic/crashtests/382745-1-binding.xml3
-rw-r--r--layout/generic/crashtests/382745-1.xhtml10
-rw-r--r--layout/generic/crashtests/383089-1.html86
-rw-r--r--layout/generic/crashtests/385265-1.xhtml13
-rw-r--r--layout/generic/crashtests/385295-1.xhtml5
-rw-r--r--layout/generic/crashtests/385344-1.html12
-rw-r--r--layout/generic/crashtests/385344-2.html10
-rw-r--r--layout/generic/crashtests/385414-1.html5
-rw-r--r--layout/generic/crashtests/385414-2.html5
-rw-r--r--layout/generic/crashtests/385426-1.html5
-rw-r--r--layout/generic/crashtests/385526.html116
-rw-r--r--layout/generic/crashtests/385681.html34
-rw-r--r--layout/generic/crashtests/385885-1.xul19
-rw-r--r--layout/generic/crashtests/386799-1.html7
-rw-r--r--layout/generic/crashtests/386807-1.html19
-rw-r--r--layout/generic/crashtests/386812-1.html23
-rw-r--r--layout/generic/crashtests/386827-1.html16
-rw-r--r--layout/generic/crashtests/387058-1.html16
-rw-r--r--layout/generic/crashtests/387058-2.html17
-rw-r--r--layout/generic/crashtests/387088-1.html5
-rw-r--r--layout/generic/crashtests/387209-1.html6
-rw-r--r--layout/generic/crashtests/387213-1.html10
-rw-r--r--layout/generic/crashtests/387215-1.xhtml15
-rw-r--r--layout/generic/crashtests/387219-1.xhtml8
-rw-r--r--layout/generic/crashtests/387233-1.html21
-rw-r--r--layout/generic/crashtests/387233-2.html18
-rw-r--r--layout/generic/crashtests/387282-1.html7
-rw-r--r--layout/generic/crashtests/388175-1.html24
-rw-r--r--layout/generic/crashtests/388367-1.html7
-rw-r--r--layout/generic/crashtests/388709-1.html15
-rw-r--r--layout/generic/crashtests/389635-1.html14
-rw-r--r--layout/generic/crashtests/390050-1.html48
-rw-r--r--layout/generic/crashtests/390050-2.html22
-rw-r--r--layout/generic/crashtests/390050-3.html4
-rw-r--r--layout/generic/crashtests/390762-1.html30
-rw-r--r--layout/generic/crashtests/391053-1.xhtml16
-rw-r--r--layout/generic/crashtests/391894-1.html17
-rw-r--r--layout/generic/crashtests/392698-1.html16
-rw-r--r--layout/generic/crashtests/393758-1.xhtml10
-rw-r--r--layout/generic/crashtests/393906-1.html12
-rw-r--r--layout/generic/crashtests/393923-1.html15
-rw-r--r--layout/generic/crashtests/393956-1.html25
-rw-r--r--layout/generic/crashtests/393956-2.html26
-rw-r--r--layout/generic/crashtests/393956-3.html11
-rw-r--r--layout/generic/crashtests/393956-4.html11
-rw-r--r--layout/generic/crashtests/394237-1.html38
-rw-r--r--layout/generic/crashtests/394818-1.html13
-rw-r--r--layout/generic/crashtests/394818-2.html16
-rw-r--r--layout/generic/crashtests/394820-1.html19
-rw-r--r--layout/generic/crashtests/395316-1.html13
-rw-r--r--layout/generic/crashtests/395450-1.xhtml28
-rw-r--r--layout/generic/crashtests/397007-1.html37
-rw-r--r--layout/generic/crashtests/397187-1.html32
-rw-r--r--layout/generic/crashtests/397844-1.xhtml55
-rw-r--r--layout/generic/crashtests/397844-2.xhtml55
-rw-r--r--layout/generic/crashtests/397852-1.xhtml7
-rw-r--r--layout/generic/crashtests/398181-1.html10
-rw-r--r--layout/generic/crashtests/398181-2.html11
-rw-r--r--layout/generic/crashtests/398322-1.html17
-rw-r--r--layout/generic/crashtests/398322-2.html12
-rw-r--r--layout/generic/crashtests/398332-1.html19
-rw-r--r--layout/generic/crashtests/398332-2.html27
-rw-r--r--layout/generic/crashtests/398332-3.html4
-rw-r--r--layout/generic/crashtests/399407-1.xhtml25
-rw-r--r--layout/generic/crashtests/399412-1.html32
-rw-r--r--layout/generic/crashtests/399843-1.html64
-rw-r--r--layout/generic/crashtests/400078-1.html20
-rw-r--r--layout/generic/crashtests/400190.html63
-rw-r--r--layout/generic/crashtests/400223-1.html24
-rw-r--r--layout/generic/crashtests/400232-1.html11
-rw-r--r--layout/generic/crashtests/400244-1.html31
-rw-r--r--layout/generic/crashtests/400768-1.xhtml9
-rw-r--r--layout/generic/crashtests/400768-2.xhtml7
-rw-r--r--layout/generic/crashtests/401042-1.xhtml17
-rw-r--r--layout/generic/crashtests/402380-1.html13
-rw-r--r--layout/generic/crashtests/402380-2.html18
-rw-r--r--layout/generic/crashtests/402872-1.html3
-rw-r--r--layout/generic/crashtests/402872-2.html2
-rw-r--r--layout/generic/crashtests/403004.html3
-rw-r--r--layout/generic/crashtests/403143-1.html19
-rw-r--r--layout/generic/crashtests/403576-1.html5
-rw-r--r--layout/generic/crashtests/404140-1.html7
-rw-r--r--layout/generic/crashtests/404146-1.html28
-rw-r--r--layout/generic/crashtests/404204-1.html7
-rw-r--r--layout/generic/crashtests/404215-1.html29
-rw-r--r--layout/generic/crashtests/404215-2.html37
-rw-r--r--layout/generic/crashtests/404215-3.html32
-rw-r--r--layout/generic/crashtests/404219-1.html30
-rw-r--r--layout/generic/crashtests/404219-2.html31
-rw-r--r--layout/generic/crashtests/406137.html16
-rw-r--r--layout/generic/crashtests/406380.html12
-rw-r--r--layout/generic/crashtests/406902-1.html47
-rw-r--r--layout/generic/crashtests/407009-1.xhtml7
-rw-r--r--layout/generic/crashtests/408304-1.xhtml5
-rw-r--r--layout/generic/crashtests/408602-1.html12
-rw-r--r--layout/generic/crashtests/408737-1.html14
-rw-r--r--layout/generic/crashtests/408737-2.html14
-rw-r--r--layout/generic/crashtests/408749-1.xhtml1
-rw-r--r--layout/generic/crashtests/408883-1.html39
-rw-r--r--layout/generic/crashtests/410198.html8
-rw-r--r--layout/generic/crashtests/410228-1.html7
-rw-r--r--layout/generic/crashtests/410232-1.html14
-rw-r--r--layout/generic/crashtests/410595-1.html7
-rw-r--r--layout/generic/crashtests/411213-1.html9
-rw-r--r--layout/generic/crashtests/411213-2.xml8
-rw-r--r--layout/generic/crashtests/411835.html19
-rw-r--r--layout/generic/crashtests/411851-1.html8
-rw-r--r--layout/generic/crashtests/412014-1.html17
-rw-r--r--layout/generic/crashtests/412201-1.xhtml1
-rw-r--r--layout/generic/crashtests/412543-1.html17
-rw-r--r--layout/generic/crashtests/413048-1.html9
-rw-r--r--layout/generic/crashtests/413079-1.xhtml10
-rw-r--r--layout/generic/crashtests/413079-2.xhtml12
-rw-r--r--layout/generic/crashtests/413079-3.xhtml12
-rw-r--r--layout/generic/crashtests/413085-1.html23
-rw-r--r--layout/generic/crashtests/413085-2.html14
-rw-r--r--layout/generic/crashtests/413582-1.xhtml9
-rw-r--r--layout/generic/crashtests/413582-2.html9
-rw-r--r--layout/generic/crashtests/413712-1.xhtml18
-rw-r--r--layout/generic/crashtests/414061-1.html12
-rw-r--r--layout/generic/crashtests/414180-1.xul7
-rw-r--r--layout/generic/crashtests/414719-1.html25
-rw-r--r--layout/generic/crashtests/415685-1.html14
-rw-r--r--layout/generic/crashtests/416165.html23
-rw-r--r--layout/generic/crashtests/416264-1.html8
-rw-r--r--layout/generic/crashtests/416476-1.html2
-rw-r--r--layout/generic/crashtests/417109-1.xhtml28
-rw-r--r--layout/generic/crashtests/417848-1.xhtml6
-rw-r--r--layout/generic/crashtests/417902-1.html23
-rw-r--r--layout/generic/crashtests/417902-2.html28
-rw-r--r--layout/generic/crashtests/418532-1.html9
-rw-r--r--layout/generic/crashtests/418932-1.html2
-rw-r--r--layout/generic/crashtests/419352.html3
-rw-r--r--layout/generic/crashtests/420000-1.html10
-rw-r--r--layout/generic/crashtests/420718.html1
-rw-r--r--layout/generic/crashtests/420785-1.xhtml26
-rw-r--r--layout/generic/crashtests/421404-1.html20
-rw-r--r--layout/generic/crashtests/421671.html202
-rw-r--r--layout/generic/crashtests/422283-1.html10
-rw-r--r--layout/generic/crashtests/422301-1.html24
-rw-r--r--layout/generic/crashtests/423055-1.html10
-rw-r--r--layout/generic/crashtests/423098.html22
-rw-r--r--layout/generic/crashtests/423264-1.html19
-rw-r--r--layout/generic/crashtests/424629.html21
-rw-r--r--layout/generic/crashtests/425253-1.html5
-rw-r--r--layout/generic/crashtests/426040-1.html28
-rw-r--r--layout/generic/crashtests/426272-1.html18
-rw-r--r--layout/generic/crashtests/428263-1.html18
-rw-r--r--layout/generic/crashtests/429458.xhtml27
-rw-r--r--layout/generic/crashtests/429960-1.html17
-rw-r--r--layout/generic/crashtests/429960-2.html18
-rw-r--r--layout/generic/crashtests/429969-1.html24
-rw-r--r--layout/generic/crashtests/429981-1.html27
-rw-r--r--layout/generic/crashtests/430332-1.html17
-rw-r--r--layout/generic/crashtests/430344-1.html5
-rw-r--r--layout/generic/crashtests/430352-1.html5
-rw-r--r--layout/generic/crashtests/430744-1.html10
-rw-r--r--layout/generic/crashtests/430991.html24
-rw-r--r--layout/generic/crashtests/431260-1.html34
-rw-r--r--layout/generic/crashtests/431260-2.html26
-rw-r--r--layout/generic/crashtests/435529.html20
-rw-r--r--layout/generic/crashtests/436194-1.html18
-rw-r--r--layout/generic/crashtests/436602-1.html8
-rw-r--r--layout/generic/crashtests/436822-1.html22
-rw-r--r--layout/generic/crashtests/436823.html10
-rw-r--r--layout/generic/crashtests/436969-1.html10
-rw-r--r--layout/generic/crashtests/437156-1.html10
-rw-r--r--layout/generic/crashtests/437565-1.xhtml7
-rw-r--r--layout/generic/crashtests/437565-2.xhtml24
-rw-r--r--layout/generic/crashtests/437565-3.xhtml23
-rw-r--r--layout/generic/crashtests/438259-1.html13
-rw-r--r--layout/generic/crashtests/438266-1.html33
-rw-r--r--layout/generic/crashtests/438509-1.html80
-rw-r--r--layout/generic/crashtests/442860-1.xul1
-rw-r--r--layout/generic/crashtests/443528-1.html19
-rw-r--r--layout/generic/crashtests/444230-1.html1
-rw-r--r--layout/generic/crashtests/444484-1.html27
-rw-r--r--layout/generic/crashtests/444726-1.xhtml10
-rw-r--r--layout/generic/crashtests/444861-1.html18
-rw-r--r--layout/generic/crashtests/445288.html15
-rw-r--r--layout/generic/crashtests/448903-1.html5
-rw-r--r--layout/generic/crashtests/448996-1.html26
-rw-r--r--layout/generic/crashtests/451315-1.html5
-rw-r--r--layout/generic/crashtests/451317-1.html24
-rw-r--r--layout/generic/crashtests/451334-1.html10
-rw-r--r--layout/generic/crashtests/452157-1.html8
-rw-r--r--layout/generic/crashtests/452157-2.html39
-rw-r--r--layout/generic/crashtests/452157-3.html39
-rw-r--r--layout/generic/crashtests/453762-1.html4
-rw-r--r--layout/generic/crashtests/455171-1.html5
-rw-r--r--layout/generic/crashtests/455171-2.html7
-rw-r--r--layout/generic/crashtests/455171-3.html2
-rw-r--r--layout/generic/crashtests/455643-1.xhtml19
-rw-r--r--layout/generic/crashtests/457375.html5
-rw-r--r--layout/generic/crashtests/457380-1.html26
-rw-r--r--layout/generic/crashtests/459968.html33
-rw-r--r--layout/generic/crashtests/460910-1.xml14
-rw-r--r--layout/generic/crashtests/461294-1.html1
-rw-r--r--layout/generic/crashtests/462968.xhtml5
-rw-r--r--layout/generic/crashtests/463350-1.html17
-rw-r--r--layout/generic/crashtests/463350-2.html17
-rw-r--r--layout/generic/crashtests/463350-3.html15
-rw-r--r--layout/generic/crashtests/463741-1.html20
-rw-r--r--layout/generic/crashtests/463785.xhtml40
-rw-r--r--layout/generic/crashtests/465651-1.html45
-rw-r--r--layout/generic/crashtests/467137-1.html24
-rw-r--r--layout/generic/crashtests/467213-1.html9
-rw-r--r--layout/generic/crashtests/467487-1.html11
-rw-r--r--layout/generic/crashtests/467493-1.html7
-rw-r--r--layout/generic/crashtests/467493-2.html25
-rw-r--r--layout/generic/crashtests/467875-1.xhtml10
-rw-r--r--layout/generic/crashtests/467914-1.html3
-rw-r--r--layout/generic/crashtests/468207-1.html5
-rw-r--r--layout/generic/crashtests/468771-1.xhtml27
-rw-r--r--layout/generic/crashtests/468771-2.xhtml22
-rw-r--r--layout/generic/crashtests/469859-1.xhtml32
-rw-r--r--layout/generic/crashtests/472587-1.xhtml28
-rw-r--r--layout/generic/crashtests/472617-1.xhtml4
-rw-r--r--layout/generic/crashtests/472774-1.html25
-rw-r--r--layout/generic/crashtests/472776-1.html20
-rw-r--r--layout/generic/crashtests/472950-1.html21
-rw-r--r--layout/generic/crashtests/472957.xhtml14
-rw-r--r--layout/generic/crashtests/473278-1.xhtml1
-rw-r--r--layout/generic/crashtests/473894-1.html6
-rw-r--r--layout/generic/crashtests/476241-1.html2
-rw-r--r--layout/generic/crashtests/477731-1.html6
-rw-r--r--layout/generic/crashtests/477928.html18
-rw-r--r--layout/generic/crashtests/478131-1.html7
-rw-r--r--layout/generic/crashtests/478170-1.html17
-rw-r--r--layout/generic/crashtests/478185-1.html61
-rw-r--r--layout/generic/crashtests/479938-1.html23
-rw-r--r--layout/generic/crashtests/480345-1.html5
-rw-r--r--layout/generic/crashtests/481921-iframe.html12
-rw-r--r--layout/generic/crashtests/481921.html20
-rw-r--r--layout/generic/crashtests/481921.oggbin0 -> 42852 bytes
-rw-r--r--layout/generic/crashtests/489462-1.html21
-rw-r--r--layout/generic/crashtests/489477.html21
-rw-r--r--layout/generic/crashtests/489480-1.xhtml1
-rw-r--r--layout/generic/crashtests/493111-1.html22
-rw-r--r--layout/generic/crashtests/493118-1.html6
-rw-r--r--layout/generic/crashtests/493649.html5
-rw-r--r--layout/generic/crashtests/494283-1.xhtml4
-rw-r--r--layout/generic/crashtests/494283-2.html6
-rw-r--r--layout/generic/crashtests/494300-1.xul49
-rw-r--r--layout/generic/crashtests/494332-1.html7
-rw-r--r--layout/generic/crashtests/495875-1.html7
-rw-r--r--layout/generic/crashtests/495875-2.html7
-rw-r--r--layout/generic/crashtests/496742.html11
-rw-r--r--layout/generic/crashtests/499138-iframe.html17
-rw-r--r--layout/generic/crashtests/499138.html18
-rw-r--r--layout/generic/crashtests/499857-1.html33
-rw-r--r--layout/generic/crashtests/499862-1.html9
-rw-r--r--layout/generic/crashtests/499885-1.xhtml6
-rw-r--r--layout/generic/crashtests/501535-1.html6
-rw-r--r--layout/generic/crashtests/503961-1.xhtml25
-rw-r--r--layout/generic/crashtests/503961-2.html32
-rw-r--r--layout/generic/crashtests/505912-1.html6
-rw-r--r--layout/generic/crashtests/508154-1.xhtml1
-rw-r--r--layout/generic/crashtests/508168-1.html6
-rw-r--r--layout/generic/crashtests/508816-1.xul9
-rw-r--r--layout/generic/crashtests/508908-1.html24
-rw-r--r--layout/generic/crashtests/509749-1.html5
-rw-r--r--layout/generic/crashtests/511482.html42
-rw-r--r--layout/generic/crashtests/512724-1.html1
-rw-r--r--layout/generic/crashtests/512725-1.html6
-rw-r--r--layout/generic/crashtests/512749-1.html1
-rw-r--r--layout/generic/crashtests/513110-1.html23
-rw-r--r--layout/generic/crashtests/513110-2.xhtml5
-rw-r--r--layout/generic/crashtests/513394-1.html16
-rw-r--r--layout/generic/crashtests/514098-1.xhtml16
-rw-r--r--layout/generic/crashtests/514800-1.html4
-rw-r--r--layout/generic/crashtests/515811-1.html5
-rw-r--r--layout/generic/crashtests/517968.html6
-rw-r--r--layout/generic/crashtests/519031.xhtml6
-rw-r--r--layout/generic/crashtests/520340.html2
-rw-r--r--layout/generic/crashtests/522170-1.html1
-rw-r--r--layout/generic/crashtests/526217.html16
-rw-r--r--layout/generic/crashtests/533379-1.html16
-rw-r--r--layout/generic/crashtests/533379-2.html16
-rw-r--r--layout/generic/crashtests/534082-1.html7
-rw-r--r--layout/generic/crashtests/534366-1.html38
-rw-r--r--layout/generic/crashtests/534366-2.html42
-rw-r--r--layout/generic/crashtests/536692-1.xhtml5
-rw-r--r--layout/generic/crashtests/537645.xhtml11
-rw-r--r--layout/generic/crashtests/541277-1.html5
-rw-r--r--layout/generic/crashtests/541277-2.html5
-rw-r--r--layout/generic/crashtests/541714-1.html3
-rw-r--r--layout/generic/crashtests/541714-2.html3
-rw-r--r--layout/generic/crashtests/542136-1.html23
-rw-r--r--layout/generic/crashtests/545571-1.html8
-rw-r--r--layout/generic/crashtests/547338.xul27
-rw-r--r--layout/generic/crashtests/547843-1.xhtml1
-rw-r--r--layout/generic/crashtests/551635-1.html16
-rw-r--r--layout/generic/crashtests/553504-1.xhtml4
-rw-r--r--layout/generic/crashtests/564368-1.xhtml27
-rw-r--r--layout/generic/crashtests/564968.xhtml30
-rw-r--r--layout/generic/crashtests/569193-1.html6
-rw-r--r--layout/generic/crashtests/570160.html53
-rw-r--r--layout/generic/crashtests/570289-1.html1
-rw-r--r--layout/generic/crashtests/571618-1.svg1
-rw-r--r--layout/generic/crashtests/571975-1.html5
-rw-r--r--layout/generic/crashtests/571995.xhtml8
-rw-r--r--layout/generic/crashtests/574958.xhtml16
-rw-r--r--layout/generic/crashtests/578977.html11
-rw-r--r--layout/generic/crashtests/578977.xul10
-rw-r--r--layout/generic/crashtests/580504-1.xhtml22
-rw-r--r--layout/generic/crashtests/585598-1.xhtml7
-rw-r--r--layout/generic/crashtests/586806-1.html27
-rw-r--r--layout/generic/crashtests/586806-2.html1
-rw-r--r--layout/generic/crashtests/586806-3.html9
-rw-r--r--layout/generic/crashtests/586973-1.html9
-rw-r--r--layout/generic/crashtests/589002-1.html4
-rw-r--r--layout/generic/crashtests/590404.html1
-rw-r--r--layout/generic/crashtests/591141.html7
-rw-r--r--layout/generic/crashtests/592118.html4
-rw-r--r--layout/generic/crashtests/594808-1.html7
-rw-r--r--layout/generic/crashtests/595435-1.xhtml8
-rw-r--r--layout/generic/crashtests/595740-1.html8
-rw-r--r--layout/generic/crashtests/597240-1.xhtml20
-rw-r--r--layout/generic/crashtests/600100.xhtml1
-rw-r--r--layout/generic/crashtests/603490-1.html16
-rw-r--r--layout/generic/crashtests/603510-1.html23
-rw-r--r--layout/generic/crashtests/604314-1.html16
-rw-r--r--layout/generic/crashtests/604843.html28
-rw-r--r--layout/generic/crashtests/605340.html12
-rw-r--r--layout/generic/crashtests/606642.xhtml16
-rw-r--r--layout/generic/crashtests/613455-1.svg12
-rw-r--r--layout/generic/crashtests/613629-1.xhtml14
-rw-r--r--layout/generic/crashtests/616052-1.html4
-rw-r--r--layout/generic/crashtests/619021.html5
-rw-r--r--layout/generic/crashtests/621424-1.html1
-rw-r--r--layout/generic/crashtests/621841-1.html20
-rw-r--r--layout/generic/crashtests/622596.html6
-rw-r--r--layout/generic/crashtests/641724.html315
-rw-r--r--layout/generic/crashtests/645072-1.html16
-rw-r--r--layout/generic/crashtests/645072-2.html17
-rw-r--r--layout/generic/crashtests/646561-1.html2
-rw-r--r--layout/generic/crashtests/646983-1.html6
-rw-r--r--layout/generic/crashtests/647332-1.html2
-rw-r--r--layout/generic/crashtests/650499-1.html15
-rw-r--r--layout/generic/crashtests/654002-1.html24
-rw-r--r--layout/generic/crashtests/654002-2.html26
-rw-r--r--layout/generic/crashtests/655462-1.html10
-rw-r--r--layout/generic/crashtests/656130-1.html17
-rw-r--r--layout/generic/crashtests/656130-2.html24
-rw-r--r--layout/generic/crashtests/660416.html17
-rw-r--r--layout/generic/crashtests/665853.html29
-rw-r--r--layout/generic/crashtests/667025.html22
-rw-r--r--layout/generic/crashtests/673770.html20
-rw-r--r--layout/generic/crashtests/679933-1.html13
-rw-r--r--layout/generic/crashtests/681489-1.html1
-rw-r--r--layout/generic/crashtests/682649-1.html18
-rw-r--r--layout/generic/crashtests/683702-1.xhtml24
-rw-r--r--layout/generic/crashtests/683712.html9
-rw-r--r--layout/generic/crashtests/688996-1.html18
-rw-r--r--layout/generic/crashtests/688996-2.html15
-rw-r--r--layout/generic/crashtests/691210.html5
-rw-r--r--layout/generic/crashtests/700031.xhtml9
-rw-r--r--layout/generic/crashtests/718516.html70
-rw-r--r--layout/generic/crashtests/723108.html10
-rw-r--r--layout/generic/crashtests/724235.html28
-rw-r--r--layout/generic/crashtests/724978.xhtml219
-rw-r--r--layout/generic/crashtests/730559.html1
-rw-r--r--layout/generic/crashtests/734777.html2
-rw-r--r--layout/generic/crashtests/737313-1.html5
-rw-r--r--layout/generic/crashtests/737313-2.html5
-rw-r--r--layout/generic/crashtests/737313-3.html5
-rw-r--r--layout/generic/crashtests/740199-1.xhtml1
-rw-r--r--layout/generic/crashtests/747688.html6
-rw-r--r--layout/generic/crashtests/750066-iframe.html32
-rw-r--r--layout/generic/crashtests/750066.html34
-rw-r--r--layout/generic/crashtests/757413-2.html12
-rw-r--r--layout/generic/crashtests/757413.xhtml34
-rw-r--r--layout/generic/crashtests/762764-1.html18
-rw-r--r--layout/generic/crashtests/762902.html12
-rw-r--r--layout/generic/crashtests/765409.html25
-rw-r--r--layout/generic/crashtests/765621.html21
-rw-r--r--layout/generic/crashtests/767765.html32
-rw-r--r--layout/generic/crashtests/769120.html11
-rw-r--r--layout/generic/crashtests/769303-1.html33
-rw-r--r--layout/generic/crashtests/769303-2.html19
-rw-r--r--layout/generic/crashtests/777838.html28
-rw-r--r--layout/generic/crashtests/783228.html40
-rw-r--r--layout/generic/crashtests/784600.html17
-rw-r--r--layout/generic/crashtests/785555.html12
-rw-r--r--layout/generic/crashtests/786740-1.html31
-rw-r--r--layout/generic/crashtests/790260-1.html12
-rw-r--r--layout/generic/crashtests/791601.xhtml4
-rw-r--r--layout/generic/crashtests/794693.html9
-rw-r--r--layout/generic/crashtests/798020-1.html4
-rw-r--r--layout/generic/crashtests/798235-1.html8
-rw-r--r--layout/generic/crashtests/799207-1.html6
-rw-r--r--layout/generic/crashtests/799207-2.html6
-rw-r--r--layout/generic/crashtests/801268-1.html6
-rw-r--r--layout/generic/crashtests/804089-1.xhtml15
-rw-r--r--layout/generic/crashtests/807565-1.html2
-rw-r--r--layout/generic/crashtests/807565-2.html8
-rw-r--r--layout/generic/crashtests/810303.html15
-rw-r--r--layout/generic/crashtests/810726-2.html57
-rw-r--r--layout/generic/crashtests/810726.html8
-rw-r--r--layout/generic/crashtests/812822-1.html8
-rw-r--r--layout/generic/crashtests/812879-1.html6
-rw-r--r--layout/generic/crashtests/812879-2.html35
-rw-r--r--layout/generic/crashtests/812893.html15
-rw-r--r--layout/generic/crashtests/814995.html20
-rw-r--r--layout/generic/crashtests/822910.xhtml34
-rw-r--r--layout/generic/crashtests/824297-1.html12
-rw-r--r--layout/generic/crashtests/825810-1.html11
-rw-r--r--layout/generic/crashtests/825810-2.html11
-rw-r--r--layout/generic/crashtests/826483-1.html16
-rw-r--r--layout/generic/crashtests/826532-1.html15
-rw-r--r--layout/generic/crashtests/827076.html2
-rw-r--r--layout/generic/crashtests/827168-1.html12
-rw-r--r--layout/generic/crashtests/836895.html13
-rw-r--r--layout/generic/crashtests/837007.xhtml9
-rw-r--r--layout/generic/crashtests/840787.html18
-rw-r--r--layout/generic/crashtests/840818.html8
-rw-r--r--layout/generic/crashtests/842132-1.html27
-rw-r--r--layout/generic/crashtests/842166.html22
-rw-r--r--layout/generic/crashtests/844529-1.html4
-rw-r--r--layout/generic/crashtests/847130.xhtml15
-rw-r--r--layout/generic/crashtests/847208.html16
-rw-r--r--layout/generic/crashtests/847209.html16
-rw-r--r--layout/generic/crashtests/847211-1.html19
-rw-r--r--layout/generic/crashtests/849603.html47
-rw-r--r--layout/generic/crashtests/850931.html32
-rw-r--r--layout/generic/crashtests/851396-1.html9
-rw-r--r--layout/generic/crashtests/854263-1.html27
-rw-r--r--layout/generic/crashtests/862185.html5
-rw-r--r--layout/generic/crashtests/862947-1.html16
-rw-r--r--layout/generic/crashtests/863935.html25
-rw-r--r--layout/generic/crashtests/866547-1.html14
-rw-r--r--layout/generic/crashtests/868906.html54
-rw-r--r--layout/generic/crashtests/876074-1.html20
-rw-r--r--layout/generic/crashtests/876155.html15
-rw-r--r--layout/generic/crashtests/885009-1.html7
-rw-r--r--layout/generic/crashtests/893496-1.html12
-rw-r--r--layout/generic/crashtests/893523.html7
-rw-r--r--layout/generic/crashtests/898871-iframe.xhtml7
-rw-r--r--layout/generic/crashtests/898871.html44
-rw-r--r--layout/generic/crashtests/898871.jpgbin0 -> 15000 bytes
-rw-r--r--layout/generic/crashtests/914501.html17
-rw-r--r--layout/generic/crashtests/914891.html9
-rw-r--r--layout/generic/crashtests/915475.xhtml5
-rw-r--r--layout/generic/crashtests/927558.html24
-rw-r--r--layout/generic/crashtests/943509-1.html9
-rw-r--r--layout/generic/crashtests/944909-1.html9
-rw-r--r--layout/generic/crashtests/946167-1.html20
-rw-r--r--layout/generic/crashtests/947158-iframe.html777
-rw-r--r--layout/generic/crashtests/947158.html32
-rw-r--r--layout/generic/crashtests/949932.html13
-rw-r--r--layout/generic/crashtests/961859.html18
-rw-r--r--layout/generic/crashtests/964078.html4
-rw-r--r--layout/generic/crashtests/970710.html40
-rw-r--r--layout/generic/crashtests/973701-1.xhtml5
-rw-r--r--layout/generic/crashtests/973701-2.xhtml6
-rw-r--r--layout/generic/crashtests/986899.html12
-rw-r--r--layout/generic/crashtests/crashtests.list644
-rw-r--r--layout/generic/crashtests/details-containing-only-text.html9
-rw-r--r--layout/generic/crashtests/details-display-none-summary-1.html11
-rw-r--r--layout/generic/crashtests/details-display-none-summary-2.html12
-rw-r--r--layout/generic/crashtests/details-display-none-summary-3.html13
-rw-r--r--layout/generic/crashtests/details-open-overflow-auto.html39
-rw-r--r--layout/generic/crashtests/details-open-overflow-hidden.html39
-rw-r--r--layout/generic/crashtests/details-three-columns.html30
-rw-r--r--layout/generic/crashtests/first-letter-638937-1.html45
-rw-r--r--layout/generic/crashtests/first-letter-638937-2.html11
-rw-r--r--layout/generic/crashtests/flex-nested-abspos-1.html7
-rw-r--r--layout/generic/crashtests/font-inflation-762332.html2
-rw-r--r--layout/generic/crashtests/image.jpgbin0 -> 2646 bytes
-rw-r--r--layout/generic/crashtests/large-border-radius-dashed.html1
-rw-r--r--layout/generic/crashtests/large-border-radius-dashed2.html1
-rw-r--r--layout/generic/crashtests/large-border-radius-dotted.html1
-rw-r--r--layout/generic/crashtests/large-border-radius-dotted2.html1
-rw-r--r--layout/generic/crashtests/outline-on-frameset.xhtml1
-rw-r--r--layout/generic/crashtests/simple_blank.swfbin0 -> 37 bytes
-rw-r--r--layout/generic/crashtests/solidblue.pngbin0 -> 135 bytes
-rw-r--r--layout/generic/crashtests/summary-position-out-of-flow.html30
-rw-r--r--layout/generic/crashtests/text-overflow-bug666751-1.html12
-rw-r--r--layout/generic/crashtests/text-overflow-bug666751-2.html12
-rw-r--r--layout/generic/crashtests/text-overflow-bug670564.xhtml3
-rw-r--r--layout/generic/crashtests/text-overflow-bug671796.xhtml5
-rw-r--r--layout/generic/crashtests/text-overflow-bug713610.html6
-rw-r--r--layout/generic/crashtests/text-overflow-form-elements.html144
-rw-r--r--layout/generic/crashtests/text-overflow-iframe.html115
-rw-r--r--layout/generic/folder.pngbin0 -> 619 bytes
-rw-r--r--layout/generic/frame-graph.py41
-rw-r--r--layout/generic/jar.mn7
-rw-r--r--layout/generic/loading-image.pngbin0 -> 268 bytes
-rw-r--r--layout/generic/moz.build227
-rw-r--r--layout/generic/nsAbsoluteContainingBlock.cpp763
-rw-r--r--layout/generic/nsAbsoluteContainingBlock.h174
-rw-r--r--layout/generic/nsAtomicContainerFrame.h47
-rw-r--r--layout/generic/nsAutoCopyListener.h52
-rw-r--r--layout/generic/nsBRFrame.cpp282
-rw-r--r--layout/generic/nsBackdropFrame.cpp96
-rw-r--r--layout/generic/nsBackdropFrame.h47
-rw-r--r--layout/generic/nsBlockDebugFlags.h21
-rw-r--r--layout/generic/nsBlockFrame.cpp7611
-rw-r--r--layout/generic/nsBlockFrame.h1038
-rw-r--r--layout/generic/nsBlockReflowContext.cpp464
-rw-r--r--layout/generic/nsBlockReflowContext.h98
-rw-r--r--layout/generic/nsBulletFrame.cpp1090
-rw-r--r--layout/generic/nsBulletFrame.h150
-rw-r--r--layout/generic/nsCanvasFrame.cpp744
-rw-r--r--layout/generic/nsCanvasFrame.h241
-rw-r--r--layout/generic/nsColumnSetFrame.cpp1169
-rw-r--r--layout/generic/nsColumnSetFrame.h229
-rw-r--r--layout/generic/nsContainerFrame.cpp2332
-rw-r--r--layout/generic/nsContainerFrame.h923
-rw-r--r--layout/generic/nsDirection.h19
-rw-r--r--layout/generic/nsFirstLetterFrame.cpp415
-rw-r--r--layout/generic/nsFirstLetterFrame.h95
-rw-r--r--layout/generic/nsFlexContainerFrame.cpp4804
-rw-r--r--layout/generic/nsFlexContainerFrame.h327
-rw-r--r--layout/generic/nsFloatManager.cpp603
-rw-r--r--layout/generic/nsFloatManager.h400
-rw-r--r--layout/generic/nsFontInflationData.cpp379
-rw-r--r--layout/generic/nsFontInflationData.h75
-rw-r--r--layout/generic/nsFrame.cpp11380
-rw-r--r--layout/generic/nsFrame.h920
-rw-r--r--layout/generic/nsFrameIdList.h180
-rw-r--r--layout/generic/nsFrameList.cpp549
-rw-r--r--layout/generic/nsFrameList.h579
-rw-r--r--layout/generic/nsFrameSelection.h768
-rw-r--r--layout/generic/nsFrameSetFrame.cpp1648
-rw-r--r--layout/generic/nsFrameSetFrame.h218
-rw-r--r--layout/generic/nsFrameState.cpp78
-rw-r--r--layout/generic/nsFrameState.h79
-rw-r--r--layout/generic/nsFrameStateBits.h675
-rw-r--r--layout/generic/nsFrameUtil.cpp670
-rw-r--r--layout/generic/nsGfxScrollFrame.cpp6162
-rw-r--r--layout/generic/nsGfxScrollFrame.h1498
-rw-r--r--layout/generic/nsGridContainerFrame.cpp7106
-rw-r--r--layout/generic/nsGridContainerFrame.h458
-rw-r--r--layout/generic/nsHTMLCanvasFrame.cpp437
-rw-r--r--layout/generic/nsHTMLCanvasFrame.h110
-rw-r--r--layout/generic/nsHTMLParts.h211
-rw-r--r--layout/generic/nsIAnonymousContentCreator.h84
-rw-r--r--layout/generic/nsIFrame.h4021
-rw-r--r--layout/generic/nsIFrameInlines.h163
-rw-r--r--layout/generic/nsIFrameUtil.h45
-rw-r--r--layout/generic/nsILineIterator.h124
-rw-r--r--layout/generic/nsIObjectFrame.h39
-rw-r--r--layout/generic/nsIPageSequenceFrame.h62
-rw-r--r--layout/generic/nsIScrollPositionListener.h23
-rw-r--r--layout/generic/nsIScrollableFrame.h480
-rw-r--r--layout/generic/nsIStatefulFrame.h39
-rw-r--r--layout/generic/nsImageFrame.cpp2443
-rw-r--r--layout/generic/nsImageFrame.h469
-rw-r--r--layout/generic/nsImageMap.cpp1020
-rw-r--r--layout/generic/nsImageMap.h97
-rw-r--r--layout/generic/nsInlineFrame.cpp1201
-rw-r--r--layout/generic/nsInlineFrame.h230
-rw-r--r--layout/generic/nsIntervalSet.cpp88
-rw-r--r--layout/generic/nsIntervalSet.h89
-rw-r--r--layout/generic/nsLeafFrame.cpp115
-rw-r--r--layout/generic/nsLeafFrame.h106
-rw-r--r--layout/generic/nsLineBox.cpp989
-rw-r--r--layout/generic/nsLineBox.h1782
-rw-r--r--layout/generic/nsLineLayout.cpp3400
-rw-r--r--layout/generic/nsLineLayout.h741
-rw-r--r--layout/generic/nsPageContentFrame.cpp124
-rw-r--r--layout/generic/nsPageContentFrame.h58
-rw-r--r--layout/generic/nsPageFrame.cpp747
-rw-r--r--layout/generic/nsPageFrame.h136
-rw-r--r--layout/generic/nsPlaceholderFrame.cpp278
-rw-r--r--layout/generic/nsPlaceholderFrame.h179
-rw-r--r--layout/generic/nsPluginFrame.cpp1887
-rw-r--r--layout/generic/nsPluginFrame.h381
-rw-r--r--layout/generic/nsQueryFrame.h102
-rw-r--r--layout/generic/nsRubyBaseContainerFrame.cpp836
-rw-r--r--layout/generic/nsRubyBaseContainerFrame.h95
-rw-r--r--layout/generic/nsRubyBaseFrame.cpp54
-rw-r--r--layout/generic/nsRubyBaseFrame.h42
-rw-r--r--layout/generic/nsRubyContentFrame.cpp41
-rw-r--r--layout/generic/nsRubyContentFrame.h34
-rw-r--r--layout/generic/nsRubyFrame.cpp403
-rw-r--r--layout/generic/nsRubyFrame.h68
-rw-r--r--layout/generic/nsRubyTextContainerFrame.cpp181
-rw-r--r--layout/generic/nsRubyTextContainerFrame.h74
-rw-r--r--layout/generic/nsRubyTextFrame.cpp97
-rw-r--r--layout/generic/nsRubyTextFrame.h57
-rw-r--r--layout/generic/nsSelection.cpp6837
-rw-r--r--layout/generic/nsSimplePageSequenceFrame.cpp884
-rw-r--r--layout/generic/nsSimplePageSequenceFrame.h173
-rw-r--r--layout/generic/nsSplittableFrame.cpp309
-rw-r--r--layout/generic/nsSplittableFrame.h120
-rw-r--r--layout/generic/nsSubDocumentFrame.cpp1263
-rw-r--r--layout/generic/nsSubDocumentFrame.h168
-rw-r--r--layout/generic/nsTextFrame.cpp10051
-rw-r--r--layout/generic/nsTextFrame.h840
-rw-r--r--layout/generic/nsTextFrameUtils.cpp304
-rw-r--r--layout/generic/nsTextFrameUtils.h178
-rw-r--r--layout/generic/nsTextRunTransformations.cpp692
-rw-r--r--layout/generic/nsTextRunTransformations.h222
-rw-r--r--layout/generic/nsVideoFrame.cpp695
-rw-r--r--layout/generic/nsVideoFrame.h146
-rw-r--r--layout/generic/nsViewportFrame.cpp416
-rw-r--r--layout/generic/nsViewportFrame.h108
-rw-r--r--layout/generic/test/bug1174521.html14
-rw-r--r--layout/generic/test/bug344830_testembed.svg8
-rw-r--r--layout/generic/test/bug421839-2-page.html55
-rw-r--r--layout/generic/test/bug633762_iframe.html8
-rw-r--r--layout/generic/test/chrome.ini17
-rw-r--r--layout/generic/test/file_BrokenImageReference.pngbin0 -> 253 bytes
-rw-r--r--layout/generic/test/file_Dolske.pngbin0 -> 5976 bytes
-rw-r--r--layout/generic/test/file_IconTestServer.sjs94
-rw-r--r--layout/generic/test/file_LoadingImageReference.pngbin0 -> 268 bytes
-rw-r--r--layout/generic/test/file_SlowImage.sjs45
-rw-r--r--layout/generic/test/file_bug1307853.html23
-rw-r--r--layout/generic/test/file_bug448987.html50
-rw-r--r--layout/generic/test/file_bug448987_notref.html20
-rw-r--r--layout/generic/test/file_bug448987_ref.html50
-rw-r--r--layout/generic/test/file_bug449653_1.html18
-rw-r--r--layout/generic/test/file_bug449653_1_ref.html13
-rw-r--r--layout/generic/test/file_bug514732_1.html9
-rw-r--r--layout/generic/test/file_bug514732_helper.html300
-rw-r--r--layout/generic/test/file_bug514732_window.xul81
-rw-r--r--layout/generic/test/file_bug579767_1.html10
-rw-r--r--layout/generic/test/file_bug579767_2.html10
-rw-r--r--layout/generic/test/file_scroll_position_restore.html111186
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-1.svg13
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-2.svg13
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-3.svg17
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-ref.svg5
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-1.svg13
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-2.svg13
-rw-r--r--layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-ref.svg5
-rw-r--r--layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg5
-rw-r--r--layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg^headers^4
-rw-r--r--layout/generic/test/file_taintedfilters_red-flood-for-feImage.svg5
-rw-r--r--layout/generic/test/frame_selection_underline-ref.xhtml49
-rw-r--r--layout/generic/test/frame_selection_underline.css51
-rw-r--r--layout/generic/test/frame_selection_underline.xhtml51
-rw-r--r--layout/generic/test/mochitest.ini140
-rw-r--r--layout/generic/test/page_scroll_with_fixed_pos_window.html117
-rw-r--r--layout/generic/test/plugin_clipping_helper.xhtml59
-rw-r--r--layout/generic/test/plugin_clipping_helper2.xhtml102
-rw-r--r--layout/generic/test/plugin_clipping_helper_table.xhtml44
-rw-r--r--layout/generic/test/plugin_clipping_helper_transformed.xhtml42
-rw-r--r--layout/generic/test/plugin_clipping_lib.js163
-rw-r--r--layout/generic/test/plugin_focus_helper.html134
-rw-r--r--layout/generic/test/selection_expanding_xbl.xml11
-rw-r--r--layout/generic/test/test_backspace_delete.xul325
-rw-r--r--layout/generic/test/test_bug1062406.html39
-rw-r--r--layout/generic/test/test_bug1174521.html39
-rw-r--r--layout/generic/test/test_bug1198135.html89
-rw-r--r--layout/generic/test/test_bug1307853.html28
-rw-r--r--layout/generic/test/test_bug240933.html69
-rw-r--r--layout/generic/test/test_bug263683.html98
-rw-r--r--layout/generic/test/test_bug288789.html117
-rw-r--r--layout/generic/test/test_bug290397.html40
-rw-r--r--layout/generic/test/test_bug323656.html51
-rw-r--r--layout/generic/test/test_bug344830.html41
-rw-r--r--layout/generic/test/test_bug348681.html492
-rw-r--r--layout/generic/test/test_bug382429.html36
-rw-r--r--layout/generic/test/test_bug384527.html34
-rw-r--r--layout/generic/test/test_bug385751.html32
-rw-r--r--layout/generic/test/test_bug389630.html32
-rw-r--r--layout/generic/test/test_bug391747.html48
-rw-r--r--layout/generic/test/test_bug392746.html71
-rw-r--r--layout/generic/test/test_bug392923.html41
-rw-r--r--layout/generic/test/test_bug394173.html33
-rw-r--r--layout/generic/test/test_bug394239.html46
-rw-r--r--layout/generic/test/test_bug402380.html36
-rw-r--r--layout/generic/test/test_bug404872.html38
-rw-r--r--layout/generic/test/test_bug405178.html51
-rw-r--r--layout/generic/test/test_bug416168.html44
-rw-r--r--layout/generic/test/test_bug421436.html31
-rw-r--r--layout/generic/test/test_bug421839-1.html80
-rw-r--r--layout/generic/test/test_bug421839-2.html35
-rw-r--r--layout/generic/test/test_bug424627.html41
-rw-r--r--layout/generic/test/test_bug438840.html52
-rw-r--r--layout/generic/test/test_bug448860.html64
-rw-r--r--layout/generic/test/test_bug448987.html72
-rw-r--r--layout/generic/test/test_bug449653.html44
-rw-r--r--layout/generic/test/test_bug460532.html58
-rw-r--r--layout/generic/test/test_bug468167.html55
-rw-r--r--layout/generic/test/test_bug469613.xul85
-rw-r--r--layout/generic/test/test_bug469774.xul72
-rw-r--r--layout/generic/test/test_bug470212.html53
-rw-r--r--layout/generic/test/test_bug488417.html59
-rw-r--r--layout/generic/test/test_bug496275.html289
-rw-r--r--layout/generic/test/test_bug503813.html43
-rw-r--r--layout/generic/test/test_bug507902.html382
-rw-r--r--layout/generic/test/test_bug508115.xul66
-rw-r--r--layout/generic/test/test_bug514732-2.xul40
-rw-r--r--layout/generic/test/test_bug514732.html27
-rw-r--r--layout/generic/test/test_bug522632.html32
-rw-r--r--layout/generic/test/test_bug524925.html35
-rw-r--r--layout/generic/test/test_bug527306.html49
-rw-r--r--layout/generic/test/test_bug579767.html78
-rw-r--r--layout/generic/test/test_bug589621.html37
-rw-r--r--layout/generic/test/test_bug589623.html36
-rw-r--r--layout/generic/test/test_bug597333.html38
-rw-r--r--layout/generic/test/test_bug632379.xul217
-rw-r--r--layout/generic/test/test_bug633762.html57
-rw-r--r--layout/generic/test/test_bug666225.html42
-rw-r--r--layout/generic/test/test_bug719503.html20
-rw-r--r--layout/generic/test/test_bug719515.html22
-rw-r--r--layout/generic/test/test_bug719518.html26
-rw-r--r--layout/generic/test/test_bug719523.html20
-rw-r--r--layout/generic/test/test_bug735641.html46
-rw-r--r--layout/generic/test/test_bug748961.html45
-rw-r--r--layout/generic/test/test_bug756984.html143
-rw-r--r--layout/generic/test/test_bug784410.html69
-rw-r--r--layout/generic/test/test_bug785324.html44
-rw-r--r--layout/generic/test/test_bug791616.html65
-rw-r--r--layout/generic/test/test_bug831780.html32
-rw-r--r--layout/generic/test/test_bug841361.html56
-rw-r--r--layout/generic/test/test_bug904810.html112
-rw-r--r--layout/generic/test/test_bug938772.html23
-rw-r--r--layout/generic/test/test_bug970363.html51
-rw-r--r--layout/generic/test/test_contained_plugin_transplant.html40
-rw-r--r--layout/generic/test/test_image_selection.html93
-rw-r--r--layout/generic/test/test_image_selection_2.html42
-rw-r--r--layout/generic/test/test_intrinsic_size_on_loading.html53
-rw-r--r--layout/generic/test/test_invalidate_during_plugin_paint.html58
-rw-r--r--layout/generic/test/test_movement_by_characters.html97
-rw-r--r--layout/generic/test/test_movement_by_words.html510
-rw-r--r--layout/generic/test/test_overflow_event.html50
-rw-r--r--layout/generic/test/test_page_scroll_with_fixed_pos.html17
-rw-r--r--layout/generic/test/test_plugin_clipping.xhtml19
-rw-r--r--layout/generic/test/test_plugin_clipping2.xhtml21
-rw-r--r--layout/generic/test/test_plugin_clipping_table.xhtml17
-rw-r--r--layout/generic/test/test_plugin_clipping_transformed.xhtml28
-rw-r--r--layout/generic/test/test_plugin_focus.html28
-rw-r--r--layout/generic/test/test_plugin_mouse_coords.html81
-rw-r--r--layout/generic/test/test_plugin_position.xhtml82
-rw-r--r--layout/generic/test/test_scroll_animation_restore.html128
-rw-r--r--layout/generic/test/test_scroll_behavior.html309
-rw-r--r--layout/generic/test/test_scroll_position_iframe.html37
-rw-r--r--layout/generic/test/test_scroll_position_restore.html41
-rw-r--r--layout/generic/test/test_selection_expanding.html409
-rw-r--r--layout/generic/test/test_selection_preventDefault.html175
-rw-r--r--layout/generic/test/test_selection_splitText-normalize.html173
-rw-r--r--layout/generic/test/test_selection_touchevents.html57
-rw-r--r--layout/generic/test/test_selection_underline.html350
-rw-r--r--layout/generic/test/test_taintedfilters.html96
951 files changed, 255315 insertions, 0 deletions
diff --git a/layout/generic/AsyncScrollBase.cpp b/layout/generic/AsyncScrollBase.cpp
new file mode 100644
index 000000000..e85444f0f
--- /dev/null
+++ b/layout/generic/AsyncScrollBase.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AsyncScrollBase.h"
+#include "gfxPrefs.h"
+
+using namespace mozilla;
+
+AsyncScrollBase::AsyncScrollBase(nsPoint aStartPos)
+ : mIsFirstIteration(true)
+ , mStartPos(aStartPos)
+{
+}
+
+void
+AsyncScrollBase::Update(TimeStamp aTime,
+ nsPoint aDestination,
+ const nsSize& aCurrentVelocity)
+{
+ TimeDuration duration = ComputeDuration(aTime);
+ nsSize currentVelocity = aCurrentVelocity;
+
+ if (!mIsFirstIteration) {
+ // If an additional event has not changed the destination, then do not let
+ // another minimum duration reset slow things down. If it would then
+ // instead continue with the existing timing function.
+ if (aDestination == mDestination &&
+ aTime + duration > mStartTime + mDuration)
+ {
+ return;
+ }
+
+ currentVelocity = VelocityAt(aTime);
+ mStartPos = PositionAt(aTime);
+ }
+
+ mStartTime = aTime;
+ mDuration = duration;
+ mDestination = aDestination;
+ InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
+ aDestination.x);
+ InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
+ aDestination.y);
+ mIsFirstIteration = false;
+}
+
+TimeDuration
+AsyncScrollBase::ComputeDuration(TimeStamp aTime)
+{
+ // Average last 3 delta durations (rounding errors up to 2ms are negligible for us)
+ int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
+ mPrevEventTime[2] = mPrevEventTime[1];
+ mPrevEventTime[1] = mPrevEventTime[0];
+ mPrevEventTime[0] = aTime;
+
+ // Modulate duration according to events rate (quicker events -> shorter durations).
+ // The desired effect is to use longer duration when scrolling slowly, such that
+ // it's easier to follow, but reduce the duration to make it feel more snappy when
+ // scrolling quickly. To reduce fluctuations of the duration, we average event
+ // intervals using the recent 4 timestamps (now + three prev -> 3 intervals).
+ int32_t durationMS = clamped<int32_t>(eventsDeltaMs * mIntervalRatio, mOriginMinMS, mOriginMaxMS);
+
+ return TimeDuration::FromMilliseconds(durationMS);
+}
+
+void
+AsyncScrollBase::InitializeHistory(TimeStamp aTime)
+{
+ // Starting a new scroll (i.e. not when extending an existing scroll animation),
+ // create imaginary prev timestamps with maximum relevant intervals between them.
+
+ // Longest relevant interval (which results in maximum duration)
+ TimeDuration maxDelta = TimeDuration::FromMilliseconds(mOriginMaxMS / mIntervalRatio);
+ mPrevEventTime[0] = aTime - maxDelta;
+ mPrevEventTime[1] = mPrevEventTime[0] - maxDelta;
+ mPrevEventTime[2] = mPrevEventTime[1] - maxDelta;
+}
+
+void
+AsyncScrollBase::InitTimingFunction(nsSMILKeySpline& aTimingFunction,
+ nscoord aCurrentPos,
+ nscoord aCurrentVelocity,
+ nscoord aDestination)
+{
+ if (aDestination == aCurrentPos || gfxPrefs::SmoothScrollCurrentVelocityWeighting() == 0) {
+ aTimingFunction.Init(0, 0, 1 - gfxPrefs::SmoothScrollStopDecelerationWeighting(), 1);
+ return;
+ }
+
+ const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
+ double slope = aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
+ double normalization = sqrt(1.0 + slope * slope);
+ double dt = 1.0 / normalization * gfxPrefs::SmoothScrollCurrentVelocityWeighting();
+ double dxy = slope / normalization * gfxPrefs::SmoothScrollCurrentVelocityWeighting();
+ aTimingFunction.Init(dt, dxy, 1 - gfxPrefs::SmoothScrollStopDecelerationWeighting(), 1);
+}
+
+nsPoint
+AsyncScrollBase::PositionAt(TimeStamp aTime) const
+{
+ double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
+ double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
+ return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x + progressX * mDestination.x),
+ NSToCoordRound((1 - progressY) * mStartPos.y + progressY * mDestination.y));
+}
+
+nsSize
+AsyncScrollBase::VelocityAt(TimeStamp aTime) const
+{
+ double timeProgress = ProgressAt(aTime);
+ return nsSize(VelocityComponent(timeProgress, mTimingFunctionX,
+ mStartPos.x, mDestination.x),
+ VelocityComponent(timeProgress, mTimingFunctionY,
+ mStartPos.y, mDestination.y));
+}
+
+nscoord
+AsyncScrollBase::VelocityComponent(double aTimeProgress,
+ const nsSMILKeySpline& aTimingFunction,
+ nscoord aStart,
+ nscoord aDestination) const
+{
+ double dt, dxy;
+ aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
+ if (dt == 0)
+ return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
+
+ const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
+ double slope = dxy / dt;
+ return NSToCoordRound(slope * (aDestination - aStart) / (mDuration / oneSecond));
+}
diff --git a/layout/generic/AsyncScrollBase.h b/layout/generic/AsyncScrollBase.h
new file mode 100644
index 000000000..45e9d3530
--- /dev/null
+++ b/layout/generic/AsyncScrollBase.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layout_AsyncScrollBase_h_
+#define mozilla_layout_AsyncScrollBase_h_
+
+#include "mozilla/TimeStamp.h"
+#include "nsPoint.h"
+#include "nsSMILKeySpline.h"
+
+namespace mozilla {
+
+// This is the base class for driving scroll wheel animation on both the
+// compositor and main thread.
+class AsyncScrollBase
+{
+public:
+ typedef mozilla::TimeStamp TimeStamp;
+ typedef mozilla::TimeDuration TimeDuration;
+
+ explicit AsyncScrollBase(nsPoint aStartPos);
+
+ void Update(TimeStamp aTime,
+ nsPoint aDestination,
+ const nsSize& aCurrentVelocity);
+
+ // Get the velocity at a point in time in nscoords/sec.
+ nsSize VelocityAt(TimeStamp aTime) const;
+
+ // Returns the expected scroll position at a given point in time, in app
+ // units, relative to the scroll frame.
+ nsPoint PositionAt(TimeStamp aTime) const;
+
+ bool IsFinished(TimeStamp aTime) {
+ return aTime > mStartTime + mDuration;
+ }
+
+protected:
+ double ProgressAt(TimeStamp aTime) const {
+ return clamped((aTime - mStartTime) / mDuration, 0.0, 1.0);
+ }
+
+ nscoord VelocityComponent(double aTimeProgress,
+ const nsSMILKeySpline& aTimingFunction,
+ nscoord aStart, nscoord aDestination) const;
+
+ // Calculate duration, possibly dynamically according to events rate and
+ // event origin. (also maintain previous timestamps - which are only used
+ // here).
+ TimeDuration ComputeDuration(TimeStamp aTime);
+
+ // Initialize event history.
+ void InitializeHistory(TimeStamp aTime);
+
+ // Initializes the timing function in such a way that the current velocity is
+ // preserved.
+ void InitTimingFunction(nsSMILKeySpline& aTimingFunction,
+ nscoord aCurrentPos, nscoord aCurrentVelocity,
+ nscoord aDestination);
+
+ // mPrevEventTime holds previous 3 timestamps for intervals averaging (to
+ // reduce duration fluctuations). When AsyncScroll is constructed and no
+ // previous timestamps are available (indicated with mIsFirstIteration),
+ // initialize mPrevEventTime using imaginary previous timestamps with maximum
+ // relevant intervals between them.
+ TimeStamp mPrevEventTime[3];
+ bool mIsFirstIteration;
+
+ TimeStamp mStartTime;
+
+ // Cached Preferences value.
+ //
+ // These values are minimum and maximum animation duration per event origin,
+ // and a global ratio which defines how longer is the animation's duration
+ // compared to the average recent events intervals (such that for a relatively
+ // consistent events rate, the next event arrives before current animation ends)
+ int32_t mOriginMinMS;
+ int32_t mOriginMaxMS;
+ double mIntervalRatio;
+
+ nsPoint mStartPos;
+ TimeDuration mDuration;
+ nsPoint mDestination;
+ nsSMILKeySpline mTimingFunctionX;
+ nsSMILKeySpline mTimingFunctionY;
+};
+
+// Helper for accelerated wheel deltas. This can be called from the main thread
+// or the APZ Controller thread.
+static inline double
+ComputeAcceleratedWheelDelta(double aDelta, int32_t aCounter, int32_t aFactor)
+{
+ if (!aDelta) {
+ return aDelta;
+ }
+ return (aDelta * aCounter * double(aFactor) / 10);
+}
+
+static const uint32_t kScrollSeriesTimeoutMs = 80; // in milliseconds
+
+} // namespace mozilla
+
+#endif // mozilla_layout_AsyncScrollBase_h_
diff --git a/layout/generic/BlockReflowInput.cpp b/layout/generic/BlockReflowInput.cpp
new file mode 100644
index 000000000..10084fd8b
--- /dev/null
+++ b/layout/generic/BlockReflowInput.cpp
@@ -0,0 +1,1140 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* state used in reflow of block frames */
+
+#include "BlockReflowInput.h"
+
+#include "LayoutLogging.h"
+#include "nsBlockFrame.h"
+#include "nsLineLayout.h"
+#include "nsPresContext.h"
+#include "nsIFrameInlines.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Preferences.h"
+#include <algorithm>
+
+#ifdef DEBUG
+#include "nsBlockDebugFlags.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+static bool sFloatFragmentsInsideColumnEnabled;
+static bool sFloatFragmentsInsideColumnPrefCached;
+
+BlockReflowInput::BlockReflowInput(const ReflowInput& aReflowInput,
+ nsPresContext* aPresContext,
+ nsBlockFrame* aFrame,
+ bool aBStartMarginRoot,
+ bool aBEndMarginRoot,
+ bool aBlockNeedsFloatManager,
+ nscoord aConsumedBSize)
+ : mBlock(aFrame),
+ mPresContext(aPresContext),
+ mReflowInput(aReflowInput),
+ mContentArea(aReflowInput.GetWritingMode()),
+ mPushedFloats(nullptr),
+ mOverflowTracker(nullptr),
+ mBorderPadding(mReflowInput.ComputedLogicalBorderPadding()),
+ mPrevBEndMargin(),
+ mLineNumber(0),
+ mFloatBreakType(StyleClear::None),
+ mConsumedBSize(aConsumedBSize)
+{
+ if (!sFloatFragmentsInsideColumnPrefCached) {
+ sFloatFragmentsInsideColumnPrefCached = true;
+ Preferences::AddBoolVarCache(&sFloatFragmentsInsideColumnEnabled,
+ "layout.float-fragments-inside-column.enabled");
+ }
+ mFlags.mFloatFragmentsInsideColumnEnabled = sFloatFragmentsInsideColumnEnabled;
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ mFlags.mIsFirstInflow = !aFrame->GetPrevInFlow();
+ mFlags.mIsOverflowContainer = IS_TRUE_OVERFLOW_CONTAINER(aFrame);
+
+ nsIFrame::LogicalSides logicalSkipSides =
+ aFrame->GetLogicalSkipSides(&aReflowInput);
+ mBorderPadding.ApplySkipSides(logicalSkipSides);
+
+ // Note that mContainerSize is the physical size, needed to
+ // convert logical block-coordinates in vertical-rl writing mode
+ // (measured from a RHS origin) to physical coordinates within the
+ // containing block.
+ // If aReflowInput doesn't have a constrained ComputedWidth(), we set
+ // mContainerSize.width to zero, which means lines will be positioned
+ // (physically) incorrectly; we will fix them up at the end of
+ // nsBlockFrame::Reflow, after we know the total block-size of the
+ // frame.
+ mContainerSize.width = aReflowInput.ComputedWidth();
+ if (mContainerSize.width == NS_UNCONSTRAINEDSIZE) {
+ mContainerSize.width = 0;
+ }
+
+ mContainerSize.width += mBorderPadding.LeftRight(wm);
+
+ // For now at least, we don't do that fix-up for mContainerHeight.
+ // It's only used in nsBidiUtils::ReorderFrames for vertical rtl
+ // writing modes, which aren't fully supported for the time being.
+ mContainerSize.height = aReflowInput.ComputedHeight() +
+ mBorderPadding.TopBottom(wm);
+
+ if ((aBStartMarginRoot && !logicalSkipSides.BStart()) ||
+ 0 != mBorderPadding.BStart(wm)) {
+ mFlags.mIsBStartMarginRoot = true;
+ mFlags.mShouldApplyBStartMargin = true;
+ }
+ if ((aBEndMarginRoot && !logicalSkipSides.BEnd()) ||
+ 0 != mBorderPadding.BEnd(wm)) {
+ mFlags.mIsBEndMarginRoot = true;
+ }
+ if (aBlockNeedsFloatManager) {
+ mFlags.mBlockNeedsFloatManager = true;
+ }
+
+ mFloatManager = aReflowInput.mFloatManager;
+
+ NS_ASSERTION(mFloatManager,
+ "FloatManager should be set in BlockReflowInput" );
+ if (mFloatManager) {
+ // Save the coordinate system origin for later.
+ mFloatManager->GetTranslation(mFloatManagerI, mFloatManagerB);
+ mFloatManager->PushState(&mFloatManagerStateBefore); // never popped
+ }
+
+ mReflowStatus = NS_FRAME_COMPLETE;
+
+ mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow());
+
+ LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedISize(),
+ "have unconstrained width; this should only result "
+ "from very large sizes, not attempts at intrinsic "
+ "width calculation");
+ mContentArea.ISize(wm) = aReflowInput.ComputedISize();
+
+ // Compute content area height. Unlike the width, if we have a
+ // specified style height we ignore it since extra content is
+ // managed by the "overflow" property. When we don't have a
+ // specified style height then we may end up limiting our height if
+ // the availableHeight is constrained (this situation occurs when we
+ // are paginated).
+ if (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize()) {
+ // We are in a paginated situation. The bottom edge is just inside
+ // the bottom border and padding. The content area height doesn't
+ // include either border or padding edge.
+ mBEndEdge = aReflowInput.AvailableBSize() - mBorderPadding.BEnd(wm);
+ mContentArea.BSize(wm) = std::max(0, mBEndEdge - mBorderPadding.BStart(wm));
+ }
+ else {
+ // When we are not in a paginated situation then we always use
+ // a constrained height.
+ mFlags.mHasUnconstrainedBSize = true;
+ mContentArea.BSize(wm) = mBEndEdge = NS_UNCONSTRAINEDSIZE;
+ }
+ mContentArea.IStart(wm) = mBorderPadding.IStart(wm);
+ mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm);
+
+ mPrevChild = nullptr;
+ mCurrentLine = aFrame->LinesEnd();
+
+ mMinLineHeight = aReflowInput.CalcLineHeight();
+}
+
+nscoord
+BlockReflowInput::GetConsumedBSize()
+{
+ if (mConsumedBSize == NS_INTRINSICSIZE) {
+ mConsumedBSize = mBlock->GetConsumedBSize();
+ }
+
+ return mConsumedBSize;
+}
+
+void
+BlockReflowInput::ComputeReplacedBlockOffsetsForFloats(
+ nsIFrame* aFrame,
+ const LogicalRect& aFloatAvailableSpace,
+ nscoord& aIStartResult,
+ nscoord& aIEndResult) const
+{
+ WritingMode wm = mReflowInput.GetWritingMode();
+ // The frame is clueless about the float manager and therefore we
+ // only give it free space. An example is a table frame - the
+ // tables do not flow around floats.
+ // However, we can let its margins intersect floats.
+ NS_ASSERTION(aFloatAvailableSpace.IStart(wm) >= mContentArea.IStart(wm),
+ "bad avail space rect inline-coord");
+ NS_ASSERTION(aFloatAvailableSpace.ISize(wm) == 0 ||
+ aFloatAvailableSpace.IEnd(wm) <= mContentArea.IEnd(wm),
+ "bad avail space rect inline-size");
+
+ nscoord iStartOffset, iEndOffset;
+ if (aFloatAvailableSpace.ISize(wm) == mContentArea.ISize(wm)) {
+ // We don't need to compute margins when there are no floats around.
+ iStartOffset = 0;
+ iEndOffset = 0;
+ } else {
+ LogicalMargin frameMargin(wm);
+ SizeComputationInput os(aFrame, mReflowInput.mRenderingContext,
+ wm, mContentArea.ISize(wm));
+ frameMargin =
+ os.ComputedLogicalMargin().ConvertTo(wm, aFrame->GetWritingMode());
+
+ nscoord iStartFloatIOffset =
+ aFloatAvailableSpace.IStart(wm) - mContentArea.IStart(wm);
+ iStartOffset = std::max(iStartFloatIOffset, frameMargin.IStart(wm)) -
+ frameMargin.IStart(wm);
+ iStartOffset = std::max(iStartOffset, 0); // in case of negative margin
+ nscoord iEndFloatIOffset =
+ mContentArea.IEnd(wm) - aFloatAvailableSpace.IEnd(wm);
+ iEndOffset = std::max(iEndFloatIOffset, frameMargin.IEnd(wm)) -
+ frameMargin.IEnd(wm);
+ iEndOffset = std::max(iEndOffset, 0); // in case of negative margin
+ }
+ aIStartResult = iStartOffset;
+ aIEndResult = iEndOffset;
+}
+
+static nscoord
+GetBEndMarginClone(nsIFrame* aFrame,
+ nsRenderingContext* aRenderingContext,
+ const LogicalRect& aContentArea,
+ WritingMode aWritingMode)
+{
+ if (aFrame->StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone) {
+ SizeComputationInput os(aFrame, aRenderingContext, aWritingMode,
+ aContentArea.ISize(aWritingMode));
+ return os.ComputedLogicalMargin().
+ ConvertTo(aWritingMode,
+ aFrame->GetWritingMode()).BEnd(aWritingMode);
+ }
+ return 0;
+}
+
+// Compute the amount of available space for reflowing a block frame
+// at the current Y coordinate. This method assumes that
+// GetAvailableSpace has already been called.
+void
+BlockReflowInput::ComputeBlockAvailSpace(nsIFrame* aFrame,
+ const nsFlowAreaRect& aFloatAvailableSpace,
+ bool aBlockAvoidsFloats,
+ LogicalRect& aResult)
+{
+#ifdef REALLY_NOISY_REFLOW
+ printf("CBAS frame=%p has floats %d\n",
+ aFrame, aFloatAvailableSpace.mHasFloats);
+#endif
+ WritingMode wm = mReflowInput.GetWritingMode();
+ aResult.BStart(wm) = mBCoord;
+ aResult.BSize(wm) = mFlags.mHasUnconstrainedBSize
+ ? NS_UNCONSTRAINEDSIZE
+ : mReflowInput.AvailableBSize() - mBCoord
+ - GetBEndMarginClone(aFrame, mReflowInput.mRenderingContext, mContentArea, wm);
+ // mBCoord might be greater than mBEndEdge if the block's top margin pushes
+ // it off the page/column. Negative available height can confuse other code
+ // and is nonsense in principle.
+
+ // XXX Do we really want this condition to be this restrictive (i.e.,
+ // more restrictive than it used to be)? The |else| here is allowed
+ // by the CSS spec, but only out of desperation given implementations,
+ // and the behavior it leads to is quite undesirable (it can cause
+ // things to become extremely narrow when they'd fit quite well a
+ // little bit lower). Should the else be a quirk or something that
+ // applies to a specific set of frame classes and no new ones?
+ // If we did that, then for those frames where the condition below is
+ // true but nsBlockFrame::BlockCanIntersectFloats is false,
+ // nsBlockFrame::ISizeToClearPastFloats would need to use the
+ // shrink-wrap formula, max(MIN_ISIZE, min(avail width, PREF_ISIZE))
+ // rather than just using MIN_ISIZE.
+ NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) ==
+ !aBlockAvoidsFloats,
+ "unexpected replaced width");
+ if (!aBlockAvoidsFloats) {
+ if (aFloatAvailableSpace.mHasFloats) {
+ // Use the float-edge property to determine how the child block
+ // will interact with the float.
+ const nsStyleBorder* borderStyle = aFrame->StyleBorder();
+ switch (borderStyle->mFloatEdge) {
+ default:
+ case StyleFloatEdge::ContentBox: // content and only content does runaround of floats
+ // The child block will flow around the float. Therefore
+ // give it all of the available space.
+ aResult.IStart(wm) = mContentArea.IStart(wm);
+ aResult.ISize(wm) = mContentArea.ISize(wm);
+ break;
+ case StyleFloatEdge::MarginBox:
+ {
+ // The child block's margins should be placed adjacent to,
+ // but not overlap the float.
+ aResult.IStart(wm) = aFloatAvailableSpace.mRect.IStart(wm);
+ aResult.ISize(wm) = aFloatAvailableSpace.mRect.ISize(wm);
+ }
+ break;
+ }
+ }
+ else {
+ // Since there are no floats present the float-edge property
+ // doesn't matter therefore give the block element all of the
+ // available space since it will flow around the float itself.
+ aResult.IStart(wm) = mContentArea.IStart(wm);
+ aResult.ISize(wm) = mContentArea.ISize(wm);
+ }
+ }
+ else {
+ nscoord iStartOffset, iEndOffset;
+ ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace.mRect,
+ iStartOffset, iEndOffset);
+ aResult.IStart(wm) = mContentArea.IStart(wm) + iStartOffset;
+ aResult.ISize(wm) = mContentArea.ISize(wm) - iStartOffset - iEndOffset;
+ }
+
+#ifdef REALLY_NOISY_REFLOW
+ printf(" CBAS: result %d %d %d %d\n", aResult.IStart(wm), aResult.BStart(wm),
+ aResult.ISize(wm), aResult.BSize(wm));
+#endif
+}
+
+bool
+BlockReflowInput::ReplacedBlockFitsInAvailSpace(nsIFrame* aReplacedBlock,
+ const nsFlowAreaRect& aFloatAvailableSpace) const
+{
+ if (!aFloatAvailableSpace.mHasFloats) {
+ // If there aren't any floats here, then we always fit.
+ // We check this before calling ISizeToClearPastFloats, which is
+ // somewhat expensive.
+ return true;
+ }
+ WritingMode wm = mReflowInput.GetWritingMode();
+ nsBlockFrame::ReplacedElementISizeToClear replacedISize =
+ nsBlockFrame::ISizeToClearPastFloats(*this, aFloatAvailableSpace.mRect,
+ aReplacedBlock);
+ // The inline-start side of the replaced element should be offset by
+ // the larger of the float intrusion or the replaced element's own
+ // start margin. The inline-end side is similar, except for Web
+ // compatibility we ignore the margin.
+ return std::max(aFloatAvailableSpace.mRect.IStart(wm) -
+ mContentArea.IStart(wm),
+ replacedISize.marginIStart) +
+ replacedISize.borderBoxISize +
+ (mContentArea.IEnd(wm) -
+ aFloatAvailableSpace.mRect.IEnd(wm)) <=
+ mContentArea.ISize(wm);
+}
+
+nsFlowAreaRect
+BlockReflowInput::GetFloatAvailableSpaceWithState(
+ nscoord aBCoord,
+ nsFloatManager::SavedState *aState) const
+{
+ WritingMode wm = mReflowInput.GetWritingMode();
+#ifdef DEBUG
+ // Verify that the caller setup the coordinate system properly
+ nscoord wI, wB;
+ mFloatManager->GetTranslation(wI, wB);
+
+ NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
+ "bad coord system");
+#endif
+
+ nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX)
+ ? nscoord_MAX : std::max(mContentArea.BEnd(wm) - aBCoord, 0);
+ nsFlowAreaRect result =
+ mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::BAND_FROM_POINT,
+ blockSize, mContentArea, aState,
+ ContainerSize());
+ // Keep the inline size >= 0 for compatibility with nsSpaceManager.
+ if (result.mRect.ISize(wm) < 0) {
+ result.mRect.ISize(wm) = 0;
+ }
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("%s: band=%d,%d,%d,%d hasfloats=%d\n", __func__,
+ result.mRect.IStart(wm), result.mRect.BStart(wm),
+ result.mRect.ISize(wm), result.mRect.BSize(wm), result.mHasFloats);
+ }
+#endif
+ return result;
+}
+
+nsFlowAreaRect
+BlockReflowInput::GetFloatAvailableSpaceForBSize(
+ nscoord aBCoord, nscoord aBSize,
+ nsFloatManager::SavedState *aState) const
+{
+ WritingMode wm = mReflowInput.GetWritingMode();
+#ifdef DEBUG
+ // Verify that the caller setup the coordinate system properly
+ nscoord wI, wB;
+ mFloatManager->GetTranslation(wI, wB);
+
+ NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
+ "bad coord system");
+#endif
+ nsFlowAreaRect result =
+ mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::WIDTH_WITHIN_HEIGHT,
+ aBSize, mContentArea, aState, ContainerSize());
+ // Keep the width >= 0 for compatibility with nsSpaceManager.
+ if (result.mRect.ISize(wm) < 0) {
+ result.mRect.ISize(wm) = 0;
+ }
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("%s: space=%d,%d,%d,%d hasfloats=%d\n", __func__,
+ result.mRect.IStart(wm), result.mRect.BStart(wm),
+ result.mRect.ISize(wm), result.mRect.BSize(wm), result.mHasFloats);
+ }
+#endif
+ return result;
+}
+
+/*
+ * Reconstruct the vertical margin before the line |aLine| in order to
+ * do an incremental reflow that begins with |aLine| without reflowing
+ * the line before it. |aLine| may point to the fencepost at the end of
+ * the line list, and it is used this way since we (for now, anyway)
+ * always need to recover margins at the end of a block.
+ *
+ * The reconstruction involves walking backward through the line list to
+ * find any collapsed margins preceding the line that would have been in
+ * the reflow state's |mPrevBEndMargin| when we reflowed that line in
+ * a full reflow (under the rule in CSS2 that all adjacent vertical
+ * margins of blocks collapse).
+ */
+void
+BlockReflowInput::ReconstructMarginBefore(nsLineList::iterator aLine)
+{
+ mPrevBEndMargin.Zero();
+ nsBlockFrame *block = mBlock;
+
+ nsLineList::iterator firstLine = block->LinesBegin();
+ for (;;) {
+ --aLine;
+ if (aLine->IsBlock()) {
+ mPrevBEndMargin = aLine->GetCarriedOutBEndMargin();
+ break;
+ }
+ if (!aLine->IsEmpty()) {
+ break;
+ }
+ if (aLine == firstLine) {
+ // If the top margin was carried out (and thus already applied),
+ // set it to zero. Either way, we're done.
+ if (!mFlags.mIsBStartMarginRoot) {
+ mPrevBEndMargin.Zero();
+ }
+ break;
+ }
+ }
+}
+
+void
+BlockReflowInput::SetupPushedFloatList()
+{
+ MOZ_ASSERT(!mFlags.mIsFloatListInBlockPropertyTable == !mPushedFloats,
+ "flag mismatch");
+ if (!mFlags.mIsFloatListInBlockPropertyTable) {
+ // If we're being re-Reflow'd without our next-in-flow having been
+ // reflowed, some pushed floats from our previous reflow might
+ // still be on our pushed floats list. However, that's
+ // actually fine, since they'll all end up being stolen and
+ // reordered into the correct order again.
+ // (nsBlockFrame::ReflowDirtyLines ensures that any lines with
+ // pushed floats are reflowed.)
+ mPushedFloats = mBlock->EnsurePushedFloats();
+ mFlags.mIsFloatListInBlockPropertyTable = true;
+ }
+}
+
+void
+BlockReflowInput::AppendPushedFloatChain(nsIFrame* aFloatCont)
+{
+ SetupPushedFloatList();
+ while (true) {
+ aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_FLOAT);
+ mPushedFloats->AppendFrame(mBlock, aFloatCont);
+ aFloatCont = aFloatCont->GetNextInFlow();
+ if (!aFloatCont || aFloatCont->GetParent() != mBlock) {
+ break;
+ }
+ DebugOnly<nsresult> rv = mBlock->StealFrame(aFloatCont);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame should succeed");
+ }
+}
+
+/**
+ * Restore information about floats into the float manager for an
+ * incremental reflow, and simultaneously push the floats by
+ * |aDeltaBCoord|, which is the amount |aLine| was pushed relative to its
+ * parent. The recovery of state is one of the things that makes
+ * incremental reflow O(N^2) and this state should really be kept
+ * around, attached to the frame tree.
+ */
+void
+BlockReflowInput::RecoverFloats(nsLineList::iterator aLine,
+ nscoord aDeltaBCoord)
+{
+ WritingMode wm = mReflowInput.GetWritingMode();
+ if (aLine->HasFloats()) {
+ // Place the floats into the space-manager again. Also slide
+ // them, just like the regular frames on the line.
+ nsFloatCache* fc = aLine->GetFirstFloat();
+ while (fc) {
+ nsIFrame* floatFrame = fc->mFloat;
+ if (aDeltaBCoord != 0) {
+ floatFrame->MovePositionBy(nsPoint(0, aDeltaBCoord));
+ nsContainerFrame::PositionFrameView(floatFrame);
+ nsContainerFrame::PositionChildViews(floatFrame);
+ }
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
+ nscoord tI, tB;
+ mFloatManager->GetTranslation(tI, tB);
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("RecoverFloats: tIB=%d,%d (%d,%d) ",
+ tI, tB, mFloatManagerI, mFloatManagerB);
+ nsFrame::ListTag(stdout, floatFrame);
+ LogicalRect region = nsFloatManager::GetRegionFor(wm, floatFrame,
+ ContainerSize());
+ printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n",
+ aDeltaBCoord, region.IStart(wm), region.BStart(wm),
+ region.ISize(wm), region.BSize(wm));
+ }
+#endif
+ mFloatManager->AddFloat(floatFrame,
+ nsFloatManager::GetRegionFor(wm, floatFrame,
+ ContainerSize()),
+ wm, ContainerSize());
+ fc = fc->Next();
+ }
+ } else if (aLine->IsBlock()) {
+ nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager, wm,
+ ContainerSize());
+ }
+}
+
+/**
+ * Everything done in this function is done O(N) times for each pass of
+ * reflow so it is O(N*M) where M is the number of incremental reflow
+ * passes. That's bad. Don't do stuff here.
+ *
+ * When this function is called, |aLine| has just been slid by |aDeltaBCoord|
+ * and the purpose of RecoverStateFrom is to ensure that the
+ * BlockReflowInput is in the same state that it would have been in
+ * had the line just been reflowed.
+ *
+ * Most of the state recovery that we have to do involves floats.
+ */
+void
+BlockReflowInput::RecoverStateFrom(nsLineList::iterator aLine,
+ nscoord aDeltaBCoord)
+{
+ // Make the line being recovered the current line
+ mCurrentLine = aLine;
+
+ // Place floats for this line into the float manager
+ if (aLine->HasFloats() || aLine->IsBlock()) {
+ RecoverFloats(aLine, aDeltaBCoord);
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
+ mFloatManager->List(stdout);
+ }
+#endif
+ }
+}
+
+// This is called by the line layout's AddFloat method when a
+// place-holder frame is reflowed in a line. If the float is a
+// left-most child (it's x coordinate is at the line's left margin)
+// then the float is place immediately, otherwise the float
+// placement is deferred until the line has been reflowed.
+
+// XXXldb This behavior doesn't quite fit with CSS1 and CSS2 --
+// technically we're supposed let the current line flow around the
+// float as well unless it won't fit next to what we already have.
+// But nobody else implements it that way...
+bool
+BlockReflowInput::AddFloat(nsLineLayout* aLineLayout,
+ nsIFrame* aFloat,
+ nscoord aAvailableISize)
+{
+ NS_PRECONDITION(aLineLayout, "must have line layout");
+ NS_PRECONDITION(mBlock->LinesEnd() != mCurrentLine, "null ptr");
+ NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "aFloat must be an out-of-flow frame");
+
+ MOZ_ASSERT(aFloat->GetParent(), "float must have parent");
+ MOZ_ASSERT(aFloat->GetParent()->IsFrameOfType(nsIFrame::eBlockFrame),
+ "float's parent must be block");
+ MOZ_ASSERT(aFloat->GetParent() == mBlock ||
+ (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
+ "float should be in this block unless it was marked as "
+ "pushed float");
+ if (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
+ // If, in a previous reflow, the float was pushed entirely to
+ // another column/page, we need to steal it back. (We might just
+ // push it again, though.) Likewise, if that previous reflow
+ // reflowed this block but not its next continuation, we might need
+ // to steal it from our own float-continuations list.
+ //
+ // For more about pushed floats, see the comment above
+ // nsBlockFrame::DrainPushedFloats.
+ nsBlockFrame *floatParent =
+ static_cast<nsBlockFrame*>(aFloat->GetParent());
+ floatParent->StealFrame(aFloat);
+
+ aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
+
+ // Appending is fine, since if a float was pushed to the next
+ // page/column, all later floats were also pushed.
+ mBlock->mFloats.AppendFrame(mBlock, aFloat);
+ }
+
+ // Because we are in the middle of reflowing a placeholder frame
+ // within a line (and possibly nested in an inline frame or two
+ // that's a child of our block) we need to restore the space
+ // manager's translation to the space that the block resides in
+ // before placing the float.
+ nscoord oI, oB;
+ mFloatManager->GetTranslation(oI, oB);
+ nscoord dI = oI - mFloatManagerI;
+ nscoord dB = oB - mFloatManagerB;
+ mFloatManager->Translate(-dI, -dB);
+
+ bool placed;
+
+ // Now place the float immediately if possible. Otherwise stash it
+ // away in mBelowCurrentLineFloats and place it later.
+ // If one or more floats has already been pushed to the next line,
+ // don't let this one go on the current line, since that would violate
+ // float ordering.
+ LogicalRect floatAvailableSpace = GetFloatAvailableSpace().mRect;
+ if (mBelowCurrentLineFloats.IsEmpty() &&
+ (aLineLayout->LineIsEmpty() ||
+ mBlock->ComputeFloatISize(*this, floatAvailableSpace, aFloat)
+ <= aAvailableISize)) {
+ // And then place it
+ placed = FlowAndPlaceFloat(aFloat);
+ if (placed) {
+ // Pass on updated available space to the current inline reflow engine
+ WritingMode wm = mReflowInput.GetWritingMode();
+ // If we have mLineBSize, we are reflowing the line again due to
+ // LineReflowStatus::RedoMoreFloats. We should use mLineBSize to query the
+ // correct available space.
+ nsFlowAreaRect floatAvailSpace =
+ mLineBSize.isNothing()
+ ? GetFloatAvailableSpace(mBCoord)
+ : GetFloatAvailableSpaceForBSize(mBCoord, mLineBSize.value(), nullptr);
+ LogicalRect availSpace(wm, floatAvailSpace.mRect.IStart(wm), mBCoord,
+ floatAvailSpace.mRect.ISize(wm),
+ floatAvailSpace.mRect.BSize(wm));
+ aLineLayout->UpdateBand(wm, availSpace, aFloat);
+ // Record this float in the current-line list
+ mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
+ } else {
+ (*aLineLayout->GetLine())->SetHadFloatPushed();
+ }
+ }
+ else {
+ // Always claim to be placed; we don't know whether we fit yet, so we
+ // deal with this in PlaceBelowCurrentLineFloats
+ placed = true;
+ // This float will be placed after the line is done (it is a
+ // below-current-line float).
+ mBelowCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
+ }
+
+ // Restore coordinate system
+ mFloatManager->Translate(dI, dB);
+
+ return placed;
+}
+
+bool
+BlockReflowInput::CanPlaceFloat(nscoord aFloatISize,
+ const nsFlowAreaRect& aFloatAvailableSpace)
+{
+ // A float fits at a given block-dir position if there are no floats
+ // at its inline-dir position (no matter what its inline size) or if
+ // its inline size fits in the space remaining after prior floats have
+ // been placed.
+ // FIXME: We should allow overflow by up to half a pixel here (bug 21193).
+ return !aFloatAvailableSpace.mHasFloats ||
+ aFloatAvailableSpace.mRect.ISize(mReflowInput.GetWritingMode()) >=
+ aFloatISize;
+}
+
+// Return the inline-size that the float (including margins) will take up
+// in the writing mode of the containing block. If this returns
+// NS_UNCONSTRAINEDSIZE, we're dealing with an orthogonal block that
+// has block-size:auto, and we'll need to actually reflow it to find out
+// how much inline-size it will occupy in the containing block's mode.
+static nscoord
+FloatMarginISize(const ReflowInput& aCBReflowInput,
+ nscoord aFloatAvailableISize,
+ nsIFrame *aFloat,
+ const SizeComputationInput& aFloatOffsetState)
+{
+ AutoMaybeDisableFontInflation an(aFloat);
+ WritingMode wm = aFloatOffsetState.GetWritingMode();
+
+ LogicalSize floatSize =
+ aFloat->ComputeSize(
+ aCBReflowInput.mRenderingContext,
+ wm,
+ aCBReflowInput.ComputedSize(wm),
+ aFloatAvailableISize,
+ aFloatOffsetState.ComputedLogicalMargin().Size(wm),
+ aFloatOffsetState.ComputedLogicalBorderPadding().Size(wm) -
+ aFloatOffsetState.ComputedLogicalPadding().Size(wm),
+ aFloatOffsetState.ComputedLogicalPadding().Size(wm),
+ nsIFrame::ComputeSizeFlags::eShrinkWrap);
+
+ WritingMode cbwm = aCBReflowInput.GetWritingMode();
+ nscoord floatISize = floatSize.ConvertTo(cbwm, wm).ISize(cbwm);
+ if (floatISize == NS_UNCONSTRAINEDSIZE) {
+ return NS_UNCONSTRAINEDSIZE; // reflow is needed to get the true size
+ }
+
+ return floatISize +
+ aFloatOffsetState.ComputedLogicalMargin().Size(wm).
+ ConvertTo(cbwm, wm).ISize(cbwm) +
+ aFloatOffsetState.ComputedLogicalBorderPadding().Size(wm).
+ ConvertTo(cbwm, wm).ISize(cbwm);
+}
+
+bool
+BlockReflowInput::FlowAndPlaceFloat(nsIFrame* aFloat)
+{
+ WritingMode wm = mReflowInput.GetWritingMode();
+ // Save away the Y coordinate before placing the float. We will
+ // restore mBCoord at the end after placing the float. This is
+ // necessary because any adjustments to mBCoord during the float
+ // placement are for the float only, not for any non-floating
+ // content.
+ AutoRestore<nscoord> restoreBCoord(mBCoord);
+
+ // Grab the float's display information
+ const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay();
+
+ // The float's old region, so we can propagate damage.
+ LogicalRect oldRegion = nsFloatManager::GetRegionFor(wm, aFloat,
+ ContainerSize());
+
+ // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
+ // ``above'' another float that preceded it in the flow.
+ mBCoord = std::max(mFloatManager->GetLowestFloatTop(), mBCoord);
+
+ // See if the float should clear any preceding floats...
+ // XXX We need to mark this float somehow so that it gets reflowed
+ // when floats are inserted before it.
+ if (StyleClear::None != floatDisplay->mBreakType) {
+ // XXXldb Does this handle vertical margins correctly?
+ mBCoord = ClearFloats(mBCoord, floatDisplay->PhysicalBreakType(wm));
+ }
+ // Get the band of available space
+ nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
+ LogicalRect adjustedAvailableSpace =
+ mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat);
+
+ NS_ASSERTION(aFloat->GetParent() == mBlock,
+ "Float frame has wrong parent");
+
+ SizeComputationInput offsets(aFloat, mReflowInput.mRenderingContext,
+ wm, mReflowInput.ComputedISize());
+
+ nscoord floatMarginISize = FloatMarginISize(mReflowInput,
+ adjustedAvailableSpace.ISize(wm),
+ aFloat, offsets);
+
+ LogicalMargin floatMargin(wm); // computed margin
+ LogicalMargin floatOffsets(wm);
+ nsReflowStatus reflowStatus;
+
+ // If it's a floating first-letter, we need to reflow it before we
+ // know how wide it is (since we don't compute which letters are part
+ // of the first letter until reflow!).
+ // We also need to do this early reflow if FloatMarginISize returned
+ // an unconstrained inline-size, which can occur if the float had an
+ // orthogonal writing mode and 'auto' block-size (in its mode).
+ bool earlyFloatReflow =
+ aFloat->GetType() == nsGkAtoms::letterFrame ||
+ floatMarginISize == NS_UNCONSTRAINEDSIZE;
+ if (earlyFloatReflow) {
+ mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin,
+ floatOffsets, false, reflowStatus);
+ floatMarginISize = aFloat->ISize(wm) + floatMargin.IStartEnd(wm);
+ NS_ASSERTION(NS_FRAME_IS_COMPLETE(reflowStatus),
+ "letter frames and orthogonal floats with auto block-size "
+ "shouldn't break, and if they do now, then they're breaking "
+ "at the wrong point");
+ }
+
+ // Find a place to place the float. The CSS2 spec doesn't want
+ // floats overlapping each other or sticking out of the containing
+ // block if possible (CSS2 spec section 9.5.1, see the rule list).
+ StyleFloat floatStyle = floatDisplay->PhysicalFloats(wm);
+ MOZ_ASSERT(StyleFloat::Left == floatStyle || StyleFloat::Right == floatStyle,
+ "Invalid float type!");
+
+ // Can the float fit here?
+ bool keepFloatOnSameLine = false;
+
+ // Are we required to place at least part of the float because we're
+ // at the top of the page (to avoid an infinite loop of pushing and
+ // breaking).
+ bool mustPlaceFloat =
+ mReflowInput.mFlags.mIsTopOfPage && IsAdjacentWithTop();
+
+ for (;;) {
+ if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
+ floatAvailableSpace.mRect.BSize(wm) <= 0 &&
+ !mustPlaceFloat) {
+ // No space, nowhere to put anything.
+ PushFloatPastBreak(aFloat);
+ return false;
+ }
+
+ if (CanPlaceFloat(floatMarginISize, floatAvailableSpace)) {
+ // We found an appropriate place.
+ break;
+ }
+
+ // Nope. try to advance to the next band.
+ if (StyleDisplay::Table != floatDisplay->mDisplay ||
+ eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) {
+
+ mBCoord += floatAvailableSpace.mRect.BSize(wm);
+ if (adjustedAvailableSpace.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
+ adjustedAvailableSpace.BSize(wm) -= floatAvailableSpace.mRect.BSize(wm);
+ }
+ floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
+ } else {
+ // This quirk matches the one in nsBlockFrame::AdjustFloatAvailableSpace
+ // IE handles float tables in a very special way
+
+ // see if the previous float is also a table and has "align"
+ nsFloatCache* fc = mCurrentLineFloats.Head();
+ nsIFrame* prevFrame = nullptr;
+ while (fc) {
+ if (fc->mFloat == aFloat) {
+ break;
+ }
+ prevFrame = fc->mFloat;
+ fc = fc->Next();
+ }
+
+ if(prevFrame) {
+ //get the frame type
+ if (nsGkAtoms::tableWrapperFrame == prevFrame->GetType()) {
+ //see if it has "align="
+ // IE makes a difference between align and he float property
+ nsIContent* content = prevFrame->GetContent();
+ if (content) {
+ // we're interested only if previous frame is align=left
+ // IE messes things up when "right" (overlapping frames)
+ if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::align,
+ NS_LITERAL_STRING("left"), eIgnoreCase)) {
+ keepFloatOnSameLine = true;
+ // don't advance to next line (IE quirkie behaviour)
+ // it breaks rule CSS2/9.5.1/1, but what the hell
+ // since we cannot evangelize the world
+ break;
+ }
+ }
+ }
+ }
+
+ // the table does not fit anymore in this line so advance to next band
+ mBCoord += floatAvailableSpace.mRect.BSize(wm);
+ // To match nsBlockFrame::AdjustFloatAvailableSpace, we have to
+ // get a new width for the new band.
+ floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
+ adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this,
+ floatAvailableSpace.mRect, aFloat);
+ floatMarginISize = FloatMarginISize(mReflowInput,
+ adjustedAvailableSpace.ISize(wm),
+ aFloat, offsets);
+ }
+
+ mustPlaceFloat = false;
+ }
+
+ // If the float is continued, it will get the same absolute x value as its prev-in-flow
+
+ // We don't worry about the geometry of the prev in flow, let the continuation
+ // place and size itself as required.
+
+ // Assign inline and block dir coordinates to the float. We don't use
+ // LineLeft() and LineRight() here, because we would only have to
+ // convert the result back into this block's writing mode.
+ LogicalPoint floatPos(wm);
+ bool leftFloat = floatStyle == StyleFloat::Left;
+
+ if (leftFloat == wm.IsBidiLTR()) {
+ floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm);
+ }
+ else {
+ if (!keepFloatOnSameLine) {
+ floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize;
+ }
+ else {
+ // this is the IE quirk (see few lines above)
+ // the table is kept in the same line: don't let it overlap the
+ // previous float
+ floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm);
+ }
+ }
+ // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
+ // be higher than the top of its containing block." (Since the
+ // containing block is the content edge of the block box, this
+ // means the margin edge of the float can't be higher than the
+ // content edge of the block that contains it.)
+ floatPos.B(wm) = std::max(mBCoord, ContentBStart());
+
+ // Reflow the float after computing its vertical position so it knows
+ // where to break.
+ if (!earlyFloatReflow) {
+ bool pushedDown = mBCoord != restoreBCoord.SavedValue();
+ mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin,
+ floatOffsets, pushedDown, reflowStatus);
+ }
+ if (aFloat->GetPrevInFlow())
+ floatMargin.BStart(wm) = 0;
+ if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
+ floatMargin.BEnd(wm) = 0;
+
+ // In the case that we're in columns and not splitting floats, we need
+ // to check here that the float's height fit, and if it didn't, bail.
+ // (controlled by the pref "layout.float-fragments-inside-column.enabled")
+ //
+ // Likewise, if none of the float fit, and it needs to be pushed in
+ // its entirety to the next page (NS_FRAME_IS_TRUNCATED or
+ // NS_INLINE_IS_BREAK_BEFORE), we need to do the same.
+ if ((ContentBSize() != NS_UNCONSTRAINEDSIZE &&
+ !mFlags.mFloatFragmentsInsideColumnEnabled &&
+ adjustedAvailableSpace.BSize(wm) == NS_UNCONSTRAINEDSIZE &&
+ !mustPlaceFloat &&
+ aFloat->BSize(wm) + floatMargin.BStartEnd(wm) >
+ ContentBEnd() - floatPos.B(wm)) ||
+ NS_FRAME_IS_TRUNCATED(reflowStatus) ||
+ NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) {
+ PushFloatPastBreak(aFloat);
+ return false;
+ }
+
+ // We can't use aFloat->ShouldAvoidBreakInside(mReflowInput) here since
+ // its mIsTopOfPage may be true even though the float isn't at the
+ // top when floatPos.B(wm) > 0.
+ if (ContentBSize() != NS_UNCONSTRAINEDSIZE &&
+ !mustPlaceFloat &&
+ (!mReflowInput.mFlags.mIsTopOfPage || floatPos.B(wm) > 0) &&
+ NS_STYLE_PAGE_BREAK_AVOID == aFloat->StyleDisplay()->mBreakInside &&
+ (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus) ||
+ aFloat->BSize(wm) + floatMargin.BStartEnd(wm) >
+ ContentBEnd() - floatPos.B(wm)) &&
+ !aFloat->GetPrevInFlow()) {
+ PushFloatPastBreak(aFloat);
+ return false;
+ }
+
+ // Calculate the actual origin of the float frame's border rect
+ // relative to the parent block; the margin must be added in
+ // to get the border rect
+ LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm),
+ floatMargin.BStart(wm) + floatPos.B(wm));
+
+ // If float is relatively positioned, factor that in as well
+ ReflowInput::ApplyRelativePositioning(aFloat, wm, floatOffsets,
+ &origin, ContainerSize());
+
+ // Position the float and make sure and views are properly
+ // positioned. We need to explicitly position its child views as
+ // well, since we're moving the float after flowing it.
+ bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin;
+ if (moved) {
+ aFloat->SetPosition(wm, origin, ContainerSize());
+ nsContainerFrame::PositionFrameView(aFloat);
+ nsContainerFrame::PositionChildViews(aFloat);
+ }
+
+ // Update the float combined area state
+ // XXX Floats should really just get invalidated here if necessary
+ mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() +
+ aFloat->GetPosition());
+
+ // Place the float in the float manager
+ // calculate region
+ LogicalRect region =
+ nsFloatManager::CalculateRegionFor(wm, aFloat, floatMargin,
+ ContainerSize());
+ // if the float split, then take up all of the vertical height
+ if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) &&
+ (NS_UNCONSTRAINEDSIZE != ContentBSize())) {
+ region.BSize(wm) = std::max(region.BSize(wm),
+ ContentBSize() - floatPos.B(wm));
+ }
+ DebugOnly<nsresult> rv = mFloatManager->AddFloat(aFloat, region, wm,
+ ContainerSize());
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "bad float placement");
+ // store region
+ nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize());
+
+ // If the float's dimensions have changed, note the damage in the
+ // float manager.
+ if (!region.IsEqualEdges(oldRegion)) {
+ // XXXwaterson conservative: we could probably get away with noting
+ // less damage; e.g., if only height has changed, then only note the
+ // area into which the float has grown or from which the float has
+ // shrunk.
+ nscoord blockStart = std::min(region.BStart(wm), oldRegion.BStart(wm));
+ nscoord blockEnd = std::max(region.BEnd(wm), oldRegion.BEnd(wm));
+ mFloatManager->IncludeInDamage(blockStart, blockEnd);
+ }
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) {
+ mBlock->SplitFloat(*this, aFloat, reflowStatus);
+ } else {
+ MOZ_ASSERT(!aFloat->GetNextInFlow());
+ }
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyFloatManager) {
+ nscoord tI, tB;
+ mFloatManager->GetTranslation(tI, tB);
+ nsIFrame::ListTag(stdout, mBlock);
+ printf(": FlowAndPlaceFloat: AddFloat: tIB=%d,%d (%d,%d) {%d,%d,%d,%d}\n",
+ tI, tB, mFloatManagerI, mFloatManagerB,
+ region.IStart(wm), region.BStart(wm),
+ region.ISize(wm), region.BSize(wm));
+ }
+
+ if (nsBlockFrame::gNoisyReflow) {
+ nsRect r = aFloat->GetRect();
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("placed float: ");
+ nsFrame::ListTag(stdout, aFloat);
+ printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
+ }
+#endif
+
+ return true;
+}
+
+void
+BlockReflowInput::PushFloatPastBreak(nsIFrame *aFloat)
+{
+ // This ensures that we:
+ // * don't try to place later but smaller floats (which CSS says
+ // must have their tops below the top of this float)
+ // * don't waste much time trying to reflow this float again until
+ // after the break
+ StyleFloat floatStyle =
+ aFloat->StyleDisplay()->PhysicalFloats(mReflowInput.GetWritingMode());
+ if (floatStyle == StyleFloat::Left) {
+ mFloatManager->SetPushedLeftFloatPastBreak();
+ } else {
+ MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float value!");
+ mFloatManager->SetPushedRightFloatPastBreak();
+ }
+
+ // Put the float on the pushed floats list, even though it
+ // isn't actually a continuation.
+ DebugOnly<nsresult> rv = mBlock->StealFrame(aFloat);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame should succeed");
+ AppendPushedFloatChain(aFloat);
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(mReflowStatus);
+}
+
+/**
+ * Place below-current-line floats.
+ */
+void
+BlockReflowInput::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList,
+ nsLineBox* aLine)
+{
+ nsFloatCache* fc = aList.Head();
+ while (fc) {
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("placing bcl float: ");
+ nsFrame::ListTag(stdout, fc->mFloat);
+ printf("\n");
+ }
+#endif
+ // Place the float
+ bool placed = FlowAndPlaceFloat(fc->mFloat);
+ nsFloatCache *next = fc->Next();
+ if (!placed) {
+ aList.Remove(fc);
+ delete fc;
+ aLine->SetHadFloatPushed();
+ }
+ fc = next;
+ }
+}
+
+nscoord
+BlockReflowInput::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
+ nsIFrame *aReplacedBlock,
+ uint32_t aFlags)
+{
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("clear floats: in: aBCoord=%d\n", aBCoord);
+ }
+#endif
+
+#ifdef NOISY_FLOAT_CLEARING
+ printf("BlockReflowInput::ClearFloats: aBCoord=%d breakType=%s\n",
+ aBCoord, nsLineBox::BreakTypeToString(aBreakType));
+ mFloatManager->List(stdout);
+#endif
+
+ if (!mFloatManager->HasAnyFloats()) {
+ return aBCoord;
+ }
+
+ nscoord newBCoord = aBCoord;
+
+ if (aBreakType != StyleClear::None) {
+ newBCoord = mFloatManager->ClearFloats(newBCoord, aBreakType, aFlags);
+ }
+
+ if (aReplacedBlock) {
+ for (;;) {
+ nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newBCoord);
+ if (ReplacedBlockFitsInAvailSpace(aReplacedBlock, floatAvailableSpace)) {
+ break;
+ }
+ // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames
+ if (!AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
+ // Stop trying to clear here; we'll just get pushed to the
+ // next column or page and try again there.
+ break;
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
+ printf("clear floats: out: y=%d\n", newBCoord);
+ }
+#endif
+
+ return newBCoord;
+}
+
diff --git a/layout/generic/BlockReflowInput.h b/layout/generic/BlockReflowInput.h
new file mode 100644
index 000000000..d37c47d78
--- /dev/null
+++ b/layout/generic/BlockReflowInput.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* state used in reflow of block frames */
+
+#ifndef BlockReflowInput_h
+#define BlockReflowInput_h
+
+#include "nsFloatManager.h"
+#include "nsLineBox.h"
+#include "mozilla/ReflowInput.h"
+
+class nsBlockFrame;
+class nsFrameList;
+class nsOverflowContinuationTracker;
+
+namespace mozilla {
+
+// BlockReflowInput contains additional reflow input information that the
+// block frame uses along with ReflowInput. Like ReflowInput, this
+// is read-only data that is passed down from a parent frame to its children.
+class BlockReflowInput {
+
+ // Block reflow input flags.
+ struct Flags {
+ Flags()
+ : mHasUnconstrainedBSize(false)
+ , mIsBStartMarginRoot(false)
+ , mIsBEndMarginRoot(false)
+ , mShouldApplyBStartMargin(false)
+ , mIsFirstInflow(false)
+ , mHasLineAdjacentToTop(false)
+ , mBlockNeedsFloatManager(false)
+ , mIsLineLayoutEmpty(false)
+ , mIsOverflowContainer(false)
+ , mIsFloatListInBlockPropertyTable(false)
+ , mFloatFragmentsInsideColumnEnabled(false)
+ {}
+
+ // Set in the BlockReflowInput constructor when the frame being reflowed has
+ // been given NS_UNCONSTRAINEDSIZE as its available BSize in the
+ // ReflowInput. If set, NS_UNCONSTRAINEDSIZE is passed to nsLineLayout as
+ // the available BSize.
+ bool mHasUnconstrainedBSize : 1;
+
+ // Set in the BlockReflowInput constructor when reflowing a "block margin
+ // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for
+ // which margins apply by default).
+ //
+ // The flag is also set when reflowing a frame whose computed BStart border
+ // padding is non-zero.
+ bool mIsBStartMarginRoot : 1;
+
+ // Set in the BlockReflowInput constructor when reflowing a "block margin
+ // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for
+ // which margins apply by default).
+ //
+ // The flag is also set when reflowing a frame whose computed BEnd border
+ // padding is non-zero.
+ bool mIsBEndMarginRoot : 1;
+
+ // Set if the BStart margin should be considered when placing a linebox that
+ // contains a block frame. It may be set as a side-effect of calling
+ // nsBlockFrame::ShouldApplyBStartMargin(); once set,
+ // ShouldApplyBStartMargin() uses it as a fast-path way to return whether
+ // the BStart margin should apply.
+ //
+ // If the flag hasn't been set in the block reflow input, then
+ // ShouldApplyBStartMargin() will crawl the line list to see if a block frame
+ // precedes the specified frame. If so, the BStart margin should be applied, and
+ // the flag is set to cache the result. (If not, the BStart margin will be
+ // applied as a result of the generational margin collapsing logic in
+ // nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this case, the flag
+ // won't be set, so subsequent calls to ShouldApplyBStartMargin() will continue
+ // crawl the line list.)
+ //
+ // This flag is also set in the BlockReflowInput constructor if
+ // mIsBStartMarginRoot is set; that is, the frame being reflowed is a margin
+ // root by default.
+ bool mShouldApplyBStartMargin : 1;
+
+ bool mIsFirstInflow : 1;
+
+ // Set when mLineAdjacentToTop is valid.
+ bool mHasLineAdjacentToTop : 1;
+
+ // Set when the block has the equivalent of NS_BLOCK_FLOAT_MGR.
+ bool mBlockNeedsFloatManager : 1;
+
+ // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing
+ // the current line.
+ bool mIsLineLayoutEmpty : 1;
+
+ bool mIsOverflowContainer : 1;
+
+ // Set when our mPushedFloats list is stored on the block's property table.
+ bool mIsFloatListInBlockPropertyTable : 1;
+
+ // Set when the pref layout.float-fragments-inside-column.enabled is true.
+ bool mFloatFragmentsInsideColumnEnabled : 1;
+ };
+
+public:
+ BlockReflowInput(const ReflowInput& aReflowInput,
+ nsPresContext* aPresContext,
+ nsBlockFrame* aFrame,
+ bool aBStartMarginRoot, bool aBEndMarginRoot,
+ bool aBlockNeedsFloatManager,
+ nscoord aConsumedBSize = NS_INTRINSICSIZE);
+
+ /**
+ * Get the available reflow space (the area not occupied by floats)
+ * for the current y coordinate. The available space is relative to
+ * our coordinate system, which is the content box, with (0, 0) in the
+ * upper left.
+ *
+ * Returns whether there are floats present at the given block-direction
+ * coordinate and within the inline size of the content rect.
+ */
+ nsFlowAreaRect GetFloatAvailableSpace() const
+ { return GetFloatAvailableSpace(mBCoord); }
+ nsFlowAreaRect GetFloatAvailableSpace(nscoord aBCoord) const
+ { return GetFloatAvailableSpaceWithState(aBCoord, nullptr); }
+ nsFlowAreaRect
+ GetFloatAvailableSpaceWithState(nscoord aBCoord,
+ nsFloatManager::SavedState *aState) const;
+ nsFlowAreaRect
+ GetFloatAvailableSpaceForBSize(nscoord aBCoord, nscoord aBSize,
+ nsFloatManager::SavedState *aState) const;
+
+ /*
+ * The following functions all return true if they were able to
+ * place the float, false if the float did not fit in available
+ * space.
+ * aLineLayout is null when we are reflowing pushed floats (because
+ * they are not associated with a line box).
+ */
+ bool AddFloat(nsLineLayout* aLineLayout,
+ nsIFrame* aFloat,
+ nscoord aAvailableISize);
+
+ bool FlowAndPlaceFloat(nsIFrame* aFloat);
+
+ void PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aFloats,
+ nsLineBox* aLine);
+
+ // Returns the first coordinate >= aBCoord that clears the
+ // floats indicated by aBreakType and has enough inline size between floats
+ // (or no floats remaining) to accomodate aReplacedBlock.
+ nscoord ClearFloats(nscoord aBCoord, mozilla::StyleClear aBreakType,
+ nsIFrame *aReplacedBlock = nullptr,
+ uint32_t aFlags = 0);
+
+ // Advances to the next band, i.e., the next horizontal stripe in
+ // which there is a different set of floats.
+ // Return false if it did not advance, which only happens for
+ // constrained heights (and means that we should get pushed to the
+ // next column/page).
+ bool AdvanceToNextBand(const mozilla::LogicalRect& aFloatAvailableSpace,
+ nscoord *aBCoord) const {
+ mozilla::WritingMode wm = mReflowInput.GetWritingMode();
+ if (aFloatAvailableSpace.BSize(wm) > 0) {
+ // See if there's room in the next band.
+ *aBCoord += aFloatAvailableSpace.BSize(wm);
+ } else {
+ if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
+ // Stop trying to clear here; we'll just get pushed to the
+ // next column or page and try again there.
+ return false;
+ }
+ NS_NOTREACHED("avail space rect with zero height!");
+ *aBCoord += 1;
+ }
+ return true;
+ }
+
+ bool ReplacedBlockFitsInAvailSpace(nsIFrame* aReplacedBlock,
+ const nsFlowAreaRect& aFloatAvailableSpace) const;
+
+ bool IsAdjacentWithTop() const {
+ return mBCoord == mBorderPadding.BStart(mReflowInput.GetWritingMode());
+ }
+
+ /**
+ * Return mBlock's computed physical border+padding with GetSkipSides applied.
+ */
+ const mozilla::LogicalMargin& BorderPadding() const {
+ return mBorderPadding;
+ }
+
+ /**
+ * Retrieve the block-direction size "consumed" by any previous-in-flows.
+ */
+ nscoord GetConsumedBSize();
+
+ // Reconstruct the previous block-end margin that goes before |aLine|.
+ void ReconstructMarginBefore(nsLineList::iterator aLine);
+
+ // Caller must have called GetAvailableSpace for the correct position
+ // (which need not be the current mBCoord).
+ void ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame,
+ const mozilla::LogicalRect& aFloatAvailableSpace,
+ nscoord& aIStartResult,
+ nscoord& aIEndResult) const;
+
+ // Caller must have called GetAvailableSpace for the current mBCoord
+ void ComputeBlockAvailSpace(nsIFrame* aFrame,
+ const nsFlowAreaRect& aFloatAvailableSpace,
+ bool aBlockAvoidsFloats,
+ mozilla::LogicalRect& aResult);
+
+ void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord);
+
+ void AdvanceToNextLine() {
+ if (mFlags.mIsLineLayoutEmpty) {
+ mFlags.mIsLineLayoutEmpty = false;
+ } else {
+ mLineNumber++;
+ }
+ }
+
+ //----------------------------------------
+
+ // This state is the "global" state computed once for the reflow of
+ // the block.
+
+ // The block frame that is using this object
+ nsBlockFrame* mBlock;
+
+ nsPresContext* mPresContext;
+
+ const ReflowInput& mReflowInput;
+
+ nsFloatManager* mFloatManager;
+
+ // The coordinates within the float manager where the block is being
+ // placed <b>after</b> taking into account the blocks border and
+ // padding. This, therefore, represents the inner "content area" (in
+ // spacemanager coordinates) where child frames will be placed,
+ // including child blocks and floats.
+ nscoord mFloatManagerI, mFloatManagerB;
+
+ // XXX get rid of this
+ nsReflowStatus mReflowStatus;
+
+ // The float manager state as it was before the contents of this
+ // block. This is needed for positioning bullets, since we only want
+ // to move the bullet to flow around floats that were before this
+ // block, not floats inside of it.
+ nsFloatManager::SavedState mFloatManagerStateBefore;
+
+ nscoord mBEndEdge;
+
+ // The content area to reflow child frames within. This is within
+ // this frame's coordinate system and writing mode, which means
+ // mContentArea.IStart == BorderPadding().IStart and
+ // mContentArea.BStart == BorderPadding().BStart.
+ // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there
+ // is no page/column boundary below (the common case).
+ // mContentArea.BEnd() should only be called after checking that
+ // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise
+ // coordinate overflow may occur.
+ mozilla::LogicalRect mContentArea;
+ nscoord ContentIStart() const {
+ return mContentArea.IStart(mReflowInput.GetWritingMode());
+ }
+ nscoord ContentISize() const {
+ return mContentArea.ISize(mReflowInput.GetWritingMode());
+ }
+ nscoord ContentIEnd() const {
+ return mContentArea.IEnd(mReflowInput.GetWritingMode());
+ }
+ nscoord ContentBStart() const {
+ return mContentArea.BStart(mReflowInput.GetWritingMode());
+ }
+ nscoord ContentBSize() const {
+ return mContentArea.BSize(mReflowInput.GetWritingMode());
+ }
+ nscoord ContentBEnd() const {
+ return mContentArea.BEnd(mReflowInput.GetWritingMode());
+ }
+ mozilla::LogicalSize ContentSize(mozilla::WritingMode aWM) const {
+ mozilla::WritingMode wm = mReflowInput.GetWritingMode();
+ return mContentArea.Size(wm).ConvertTo(aWM, wm);
+ }
+
+ // Physical size. Use only for physical <-> logical coordinate conversion.
+ nsSize mContainerSize;
+ const nsSize& ContainerSize() const { return mContainerSize; }
+
+ // Continuation out-of-flow float frames that need to move to our
+ // next in flow are placed here during reflow. It's a pointer to
+ // a frame list stored in the block's property table.
+ nsFrameList *mPushedFloats;
+ // This method makes sure pushed floats are accessible to
+ // StealFrame. Call it before adding any frames to mPushedFloats.
+ void SetupPushedFloatList();
+ /**
+ * Append aFloatCont and its next-in-flows within the same block to
+ * mPushedFloats. aFloatCont should not be on any child list when
+ * making this call. Its next-in-flows will be removed from
+ * mBlock using StealFrame() before being added to mPushedFloats.
+ * All appended frames will be marked NS_FRAME_IS_PUSHED_FLOAT.
+ */
+ void AppendPushedFloatChain(nsIFrame* aFloatCont);
+
+ // Track child overflow continuations.
+ nsOverflowContinuationTracker* mOverflowTracker;
+
+ //----------------------------------------
+
+ // This state is "running" state updated by the reflow of each line
+ // in the block. This same state is "recovered" when a line is not
+ // dirty and is passed over during incremental reflow.
+
+ // The current line being reflowed
+ // If it is mBlock->end_lines(), then it is invalid.
+ nsLineList::iterator mCurrentLine;
+
+ // When mHasLineAdjacentToTop is set, this refers to a line
+ // which we know is adjacent to the top of the block (in other words,
+ // all lines before it are empty and do not have clearance. This line is
+ // always before the current line.
+ nsLineList::iterator mLineAdjacentToTop;
+
+ // The current block-direction coordinate in the block
+ nscoord mBCoord;
+
+ // mBlock's computed physical border+padding with GetSkipSides applied.
+ mozilla::LogicalMargin mBorderPadding;
+
+ // The overflow areas of all floats placed so far
+ nsOverflowAreas mFloatOverflowAreas;
+
+ nsFloatCacheFreeList mFloatCacheFreeList;
+
+ // Previous child. This is used when pulling up a frame to update
+ // the sibling list.
+ nsIFrame* mPrevChild;
+
+ // The previous child frames collapsed bottom margin value.
+ nsCollapsingMargin mPrevBEndMargin;
+
+ // The current next-in-flow for the block. When lines are pulled
+ // from a next-in-flow, this is used to know which next-in-flow to
+ // pull from. When a next-in-flow is emptied of lines, we advance
+ // this to the next next-in-flow.
+ nsBlockFrame* mNextInFlow;
+
+ //----------------------------------------
+
+ // Temporary line-reflow state. This state is used during the reflow
+ // of a given line, but doesn't have meaning before or after.
+
+ // The list of floats that are "current-line" floats. These are
+ // added to the line after the line has been reflowed, to keep the
+ // list fiddling from being N^2.
+ nsFloatCacheFreeList mCurrentLineFloats;
+
+ // The list of floats which are "below current-line"
+ // floats. These are reflowed/placed after the line is reflowed
+ // and placed. Again, this is done to keep the list fiddling from
+ // being N^2.
+ nsFloatCacheFreeList mBelowCurrentLineFloats;
+
+ nscoord mMinLineHeight;
+
+ int32_t mLineNumber;
+
+ Flags mFlags;
+
+ StyleClear mFloatBreakType;
+
+ // The amount of computed block-direction size "consumed" by previous-in-flows.
+ nscoord mConsumedBSize;
+
+ // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to
+ // place the line. When redoing the line, it will be used to query the
+ // accurate float available space in AddFloat() and
+ // nsBlockFrame::PlaceLine().
+ mozilla::Maybe<nscoord> mLineBSize;
+
+private:
+ bool CanPlaceFloat(nscoord aFloatISize,
+ const nsFlowAreaRect& aFloatAvailableSpace);
+
+ void PushFloatPastBreak(nsIFrame* aFloat);
+
+ void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord);
+};
+
+}; // namespace mozilla
+
+#endif // BlockReflowInput_h
diff --git a/layout/generic/CSSAlignUtils.cpp b/layout/generic/CSSAlignUtils.cpp
new file mode 100644
index 000000000..4cbaccd52
--- /dev/null
+++ b/layout/generic/CSSAlignUtils.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Utility code for performing CSS Box Alignment */
+
+#include "CSSAlignUtils.h"
+
+namespace mozilla {
+
+static nscoord
+SpaceToFill(WritingMode aWM, const LogicalSize& aSize, nscoord aMargin,
+ LogicalAxis aAxis, nscoord aCBSize)
+{
+ nscoord size = aAxis == eLogicalAxisBlock ? aSize.BSize(aWM)
+ : aSize.ISize(aWM);
+ return aCBSize - (size + aMargin);
+}
+
+nscoord
+CSSAlignUtils::AlignJustifySelf(uint8_t aAlignment, LogicalAxis aAxis,
+ AlignJustifyFlags aFlags,
+ nscoord aBaselineAdjust, nscoord aCBSize,
+ const ReflowInput& aRI,
+ const LogicalSize& aChildSize)
+{
+ MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_AUTO,
+ "auto values should have resolved already");
+ MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_LEFT &&
+ aAlignment != NS_STYLE_ALIGN_RIGHT,
+ "caller should map that to the corresponding START/END");
+
+ // Promote aFlags to convenience bools:
+ const bool isOverflowSafe = !!(aFlags & AlignJustifyFlags::eOverflowSafe);
+ const bool isSameSide = !!(aFlags & AlignJustifyFlags::eSameSide);
+
+ // Map some alignment values to 'start' / 'end'.
+ switch (aAlignment) {
+ case NS_STYLE_ALIGN_SELF_START: // align/justify-self: self-start
+ aAlignment = MOZ_LIKELY(isSameSide) ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END;
+ break;
+ case NS_STYLE_ALIGN_SELF_END: // align/justify-self: self-end
+ aAlignment = MOZ_LIKELY(isSameSide) ? NS_STYLE_ALIGN_END
+ : NS_STYLE_ALIGN_START;
+ break;
+ // flex-start/flex-end are the same as start/end, in most contexts.
+ // (They have special behavior in flex containers, so flex containers
+ // should map them to some other value before calling this method.)
+ case NS_STYLE_ALIGN_FLEX_START:
+ aAlignment = NS_STYLE_ALIGN_START;
+ break;
+ case NS_STYLE_ALIGN_FLEX_END:
+ aAlignment = NS_STYLE_ALIGN_END;
+ break;
+ }
+
+ // XXX try to condense this code a bit by adding the necessary convenience
+ // methods? (bug 1209710)
+
+ // Get the item's margin corresponding to the container's start/end side.
+ const LogicalMargin margin = aRI.ComputedLogicalMargin();
+ WritingMode wm = aRI.GetWritingMode();
+ nscoord marginStart, marginEnd;
+ if (aAxis == eLogicalAxisBlock) {
+ if (MOZ_LIKELY(isSameSide)) {
+ marginStart = margin.BStart(wm);
+ marginEnd = margin.BEnd(wm);
+ } else {
+ marginStart = margin.BEnd(wm);
+ marginEnd = margin.BStart(wm);
+ }
+ } else {
+ if (MOZ_LIKELY(isSameSide)) {
+ marginStart = margin.IStart(wm);
+ marginEnd = margin.IEnd(wm);
+ } else {
+ marginStart = margin.IEnd(wm);
+ marginEnd = margin.IStart(wm);
+ }
+ }
+
+ const auto& styleMargin = aRI.mStyleMargin->mMargin;
+ bool hasAutoMarginStart;
+ bool hasAutoMarginEnd;
+ if (aFlags & AlignJustifyFlags::eIgnoreAutoMargins) {
+ // (Note: ReflowInput will have treated "auto" margins as 0, so we
+ // don't need to do anything special to avoid expanding them.)
+ hasAutoMarginStart = hasAutoMarginEnd = false;
+ } else if (aAxis == eLogicalAxisBlock) {
+ hasAutoMarginStart = styleMargin.GetBStartUnit(wm) == eStyleUnit_Auto;
+ hasAutoMarginEnd = styleMargin.GetBEndUnit(wm) == eStyleUnit_Auto;
+ } else { /* aAxis == eLogicalAxisInline */
+ hasAutoMarginStart = styleMargin.GetIStartUnit(wm) == eStyleUnit_Auto;
+ hasAutoMarginEnd = styleMargin.GetIEndUnit(wm) == eStyleUnit_Auto;
+ }
+
+ // https://drafts.csswg.org/css-align-3/#overflow-values
+ // This implements <overflow-position> = 'safe'.
+ // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins
+ if ((MOZ_UNLIKELY(isOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) ||
+ hasAutoMarginStart || hasAutoMarginEnd) {
+ nscoord space = SpaceToFill(wm, aChildSize, marginStart + marginEnd,
+ aAxis, aCBSize);
+ // XXX we might want to include == 0 here as an optimization -
+ // I need to see what the baseline/last baseline code looks like first.
+ if (space < 0) {
+ // "Overflowing elements ignore their auto margins and overflow
+ // in the end directions"
+ aAlignment = NS_STYLE_ALIGN_START;
+ } else if (hasAutoMarginEnd) {
+ aAlignment = hasAutoMarginStart ? NS_STYLE_ALIGN_CENTER
+ : (isSameSide ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END);
+ } else if (hasAutoMarginStart) {
+ aAlignment = isSameSide ? NS_STYLE_ALIGN_END : NS_STYLE_ALIGN_START;
+ }
+ }
+
+ // Determine the offset for the child frame (its border-box) which will
+ // achieve the requested alignment.
+ nscoord offset = 0;
+ switch (aAlignment) {
+ case NS_STYLE_ALIGN_BASELINE:
+ case NS_STYLE_ALIGN_LAST_BASELINE:
+ if (MOZ_LIKELY(isSameSide == (aAlignment == NS_STYLE_ALIGN_BASELINE))) {
+ offset = marginStart + aBaselineAdjust;
+ } else {
+ nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
+ : aChildSize.ISize(wm);
+ offset = aCBSize - (size + marginEnd) - aBaselineAdjust;
+ }
+ break;
+ case NS_STYLE_ALIGN_STRETCH:
+ MOZ_FALLTHROUGH; // ComputeSize() deals with it
+ case NS_STYLE_ALIGN_START:
+ offset = marginStart;
+ break;
+ case NS_STYLE_ALIGN_END: {
+ nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
+ : aChildSize.ISize(wm);
+ offset = aCBSize - (size + marginEnd);
+ break;
+ }
+ case NS_STYLE_ALIGN_CENTER: {
+ nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
+ : aChildSize.ISize(wm);
+ offset = (aCBSize - size + marginStart - marginEnd) / 2;
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown align-/justify-self value");
+ }
+
+ return offset;
+}
+
+} // namespace mozilla
diff --git a/layout/generic/CSSAlignUtils.h b/layout/generic/CSSAlignUtils.h
new file mode 100644
index 000000000..3693038b2
--- /dev/null
+++ b/layout/generic/CSSAlignUtils.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Utility code for performing CSS Box Alignment */
+
+#ifndef mozilla_CSSAlignUtils_h
+#define mozilla_CSSAlignUtils_h
+
+#include "mozilla/WritingModes.h"
+
+namespace mozilla {
+
+class CSSAlignUtils {
+public:
+ /**
+ * Flags to customize the behavior of AlignJustifySelf:
+ */
+ enum class AlignJustifyFlags {
+ eNoFlags = 0,
+ // Indicates that we have <overflow-position> = safe.
+ eOverflowSafe = 1 << 0,
+ // Indicates that the container's start side in aAxis is the same
+ // as the child's start side in the child's parallel axis.
+ eSameSide = 1 << 1,
+ // Indicates that AlignJustifySelf() shouldn't expand "auto" margins.
+ // (By default, AlignJustifySelf() *will* expand such margins, to fill the
+ // available space before any alignment is done.)
+ eIgnoreAutoMargins = 1 << 2,
+ };
+
+ /**
+ * This computes the aligned offset of a CSS-aligned child within its
+ * alignment container. The returned offset is distance between the
+ * logical "start" edge of the alignment container & the logical "start" edge
+ * of the aligned child (in terms of the alignment container's writing mode).
+ *
+ * @param aAlignment An enumerated value representing a keyword for
+ * "align-self" or "justify-self". The values
+ * NS_STYLE_ALIGN_{AUTO,LEFT,RIGHT} must *not* be
+ * passed here; this method expects the caller to have
+ * already resolved those to 'start', 'end', or 'stretch'.
+ * @param aAxis The container's axis in which we're doing alignment.
+ * @param aBaselineAdjust The amount to offset baseline-aligned children.
+ * @param aCBSize The size of the alignment container, in its aAxis.
+ * @param aRI A ReflowInput for the child.
+ * @param aChildSize The child's LogicalSize (in its own writing mode).
+ */
+ static nscoord AlignJustifySelf(uint8_t aAlignment, LogicalAxis aAxis,
+ AlignJustifyFlags aFlags,
+ nscoord aBaselineAdjust, nscoord aCBSize,
+ const ReflowInput& aRI,
+ const LogicalSize& aChildSize);
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSAlignUtils::AlignJustifyFlags)
+
+} // namespace mozilla
+
+#endif // mozilla_CSSAlignUtils_h
diff --git a/layout/generic/DetailsFrame.cpp b/layout/generic/DetailsFrame.cpp
new file mode 100644
index 000000000..1d28bbe96
--- /dev/null
+++ b/layout/generic/DetailsFrame.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DetailsFrame.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/HTMLDetailsElement.h"
+#include "mozilla/dom/HTMLSummaryElement.h"
+#include "nsContentUtils.h"
+#include "nsPlaceholderFrame.h"
+#include "nsTextNode.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_FRAMEARENA_HELPERS(DetailsFrame)
+
+NS_QUERYFRAME_HEAD(DetailsFrame)
+ NS_QUERYFRAME_ENTRY(DetailsFrame)
+ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
+
+nsBlockFrame*
+NS_NewDetailsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) DetailsFrame(aContext);
+}
+
+DetailsFrame::DetailsFrame(nsStyleContext* aContext)
+ : nsBlockFrame(aContext)
+{
+}
+
+DetailsFrame::~DetailsFrame()
+{
+}
+
+nsIAtom*
+DetailsFrame::GetType() const
+{
+ return nsGkAtoms::detailsFrame;
+}
+
+void
+DetailsFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList)
+{
+#ifdef DEBUG
+ if (aListID == kPrincipalList) {
+ CheckValidMainSummary(aChildList);
+ }
+#endif
+
+ nsBlockFrame::SetInitialChildList(aListID, aChildList);
+}
+
+#ifdef DEBUG
+bool
+DetailsFrame::CheckValidMainSummary(const nsFrameList& aFrameList) const
+{
+ for (nsIFrame* child : aFrameList) {
+ HTMLSummaryElement* summary =
+ HTMLSummaryElement::FromContent(child->GetContent());
+
+ if (child == aFrameList.FirstChild()) {
+ if (summary && summary->IsMainSummary()) {
+ return true;
+ } else if (child->GetContent() == GetContent()) {
+ // The child frame's content is the same as our content, which means
+ // it's a kind of wrapper frame. Descend into its child list to find
+ // main summary.
+ if (CheckValidMainSummary(child->PrincipalChildList())) {
+ return true;
+ }
+ }
+ } else {
+ NS_ASSERTION(!summary || !summary->IsMainSummary(),
+ "Rest of the children are either not summary element "
+ "or are not the main summary!");
+ }
+ }
+ return false;
+}
+#endif
+
+void
+DetailsFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ nsContentUtils::DestroyAnonymousContent(&mDefaultSummary);
+ nsBlockFrame::DestroyFrom(aDestructRoot);
+}
+
+nsresult
+DetailsFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+ auto* details = HTMLDetailsElement::FromContent(GetContent());
+ if (details->GetFirstSummary()) {
+ return NS_OK;
+ }
+
+ // The <details> element lacks any direct <summary> child. Create a default
+ // <summary> element as an anonymous content.
+ nsNodeInfoManager* nodeInfoManager =
+ GetContent()->NodeInfo()->NodeInfoManager();
+
+ already_AddRefed<NodeInfo> nodeInfo =
+ nodeInfoManager->GetNodeInfo(nsGkAtoms::summary, nullptr, kNameSpaceID_XHTML,
+ nsIDOMNode::ELEMENT_NODE);
+ mDefaultSummary = new HTMLSummaryElement(nodeInfo);
+
+ nsXPIDLString defaultSummaryText;
+ nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+ "DefaultSummary", defaultSummaryText);
+ RefPtr<nsTextNode> description = new nsTextNode(nodeInfoManager);
+ description->SetText(defaultSummaryText, false);
+ mDefaultSummary->AppendChildTo(description, false);
+
+ aElements.AppendElement(mDefaultSummary);
+
+ return NS_OK;
+}
+
+void
+DetailsFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter)
+{
+ if (mDefaultSummary) {
+ aElements.AppendElement(mDefaultSummary);
+ }
+}
diff --git a/layout/generic/DetailsFrame.h b/layout/generic/DetailsFrame.h
new file mode 100644
index 000000000..1eb4ea87b
--- /dev/null
+++ b/layout/generic/DetailsFrame.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DetailsFrame_h
+#define DetailsFrame_h
+
+#include "nsBlockFrame.h"
+#include "nsIAnonymousContentCreator.h"
+
+class nsContainerFrame;
+class nsStyleContext;
+
+// DetailsFrame is generated by HTMLDetailsElement. See
+// nsCSSFrameConstructor::ConstructDetailsFrame for the structure of a
+// DetailsFrame.
+//
+class DetailsFrame final : public nsBlockFrame
+ , public nsIAnonymousContentCreator
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(DetailsFrame)
+ NS_DECL_QUERYFRAME
+
+ explicit DetailsFrame(nsStyleContext* aContext);
+
+ virtual ~DetailsFrame();
+
+ nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ nsresult GetFrameName(nsAString& aResult) const override
+ {
+ return MakeFrameName(NS_LITERAL_STRING("Details"), aResult);
+ }
+#endif
+
+#ifdef DEBUG
+ // Check the frame of the main summary element is the first child in the frame
+ // list. Returns true if we found the main summary frame; false otherwise.
+ bool CheckValidMainSummary(const nsFrameList& aFrameList) const;
+#endif
+
+ void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+
+ void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ // nsIAnonymousContentCreator
+ nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
+
+ void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter) override;
+
+private:
+ nsCOMPtr<nsIContent> mDefaultSummary;
+};
+
+#endif // DetailsFrame_h
diff --git a/layout/generic/FrameChildList.cpp b/layout/generic/FrameChildList.cpp
new file mode 100644
index 000000000..1d2fe25c0
--- /dev/null
+++ b/layout/generic/FrameChildList.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layout/FrameChildList.h"
+
+#include "nsIFrame.h"
+
+namespace mozilla {
+namespace layout {
+
+FrameChildListIterator::FrameChildListIterator(const nsIFrame* aFrame)
+ : FrameChildListArrayIterator(mLists)
+{
+ aFrame->GetChildLists(&mLists);
+#ifdef DEBUG
+ // Make sure that there are no duplicate list IDs.
+ FrameChildListIDs ids;
+ uint32_t count = mLists.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ NS_ASSERTION(!ids.Contains(mLists[i].mID),
+ "Duplicate item found!");
+ ids |= mLists[i].mID;
+ }
+#endif
+}
+
+#ifdef DEBUG_FRAME_DUMP
+const char*
+ChildListName(FrameChildListID aListID)
+{
+ switch (aListID) {
+ case kPrincipalList: return "";
+ case kPopupList: return "PopupList";
+ case kCaptionList: return "CaptionList";
+ case kColGroupList: return "ColGroupList";
+ case kSelectPopupList: return "SelectPopupList";
+ case kAbsoluteList: return "AbsoluteList";
+ case kFixedList: return "FixedList";
+ case kOverflowList: return "OverflowList";
+ case kOverflowContainersList: return "OverflowContainersList";
+ case kExcessOverflowContainersList: return "ExcessOverflowContainersList";
+ case kOverflowOutOfFlowList: return "OverflowOutOfFlowList";
+ case kFloatList: return "FloatList";
+ case kBulletList: return "BulletList";
+ case kPushedFloatsList: return "PushedFloatsList";
+ case kBackdropList: return "BackdropList";
+ case kNoReflowPrincipalList: return "NoReflowPrincipalList";
+ }
+
+ NS_NOTREACHED("unknown list");
+ return "UNKNOWN_FRAME_CHILD_LIST";
+}
+#endif
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/generic/FrameChildList.h b/layout/generic/FrameChildList.h
new file mode 100644
index 000000000..8ccfa2ff8
--- /dev/null
+++ b/layout/generic/FrameChildList.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef FrameChildList_h_
+#define FrameChildList_h_
+
+#include "nsFrameList.h"
+#include "nsTArray.h"
+
+class nsIFrame;
+
+namespace mozilla {
+namespace layout {
+
+// enum FrameChildListID lives in nsFrameList.h to solve circular dependencies.
+
+#ifdef DEBUG_FRAME_DUMP
+extern const char* ChildListName(FrameChildListID aListID);
+#endif
+
+class FrameChildListIDs {
+friend class FrameChildListIterator;
+ public:
+ FrameChildListIDs() : mIDs(0) {}
+ FrameChildListIDs(const FrameChildListIDs& aOther) : mIDs(aOther.mIDs) {}
+ MOZ_IMPLICIT FrameChildListIDs(FrameChildListID aListID) : mIDs(aListID) {}
+
+ FrameChildListIDs operator|(FrameChildListIDs aOther) const {
+ return FrameChildListIDs(mIDs | aOther.mIDs);
+ }
+ FrameChildListIDs& operator|=(FrameChildListIDs aOther) {
+ mIDs |= aOther.mIDs;
+ return *this;
+ }
+ bool operator==(FrameChildListIDs aOther) const {
+ return mIDs == aOther.mIDs;
+ }
+ bool operator!=(FrameChildListIDs aOther) const {
+ return !(*this == aOther);
+ }
+ bool Contains(FrameChildListIDs aOther) const {
+ return (mIDs & aOther.mIDs) == aOther.mIDs;
+ }
+
+ protected:
+ explicit FrameChildListIDs(uint32_t aIDs) : mIDs(aIDs) {}
+ uint32_t mIDs;
+};
+
+class FrameChildList {
+ public:
+ FrameChildList(const nsFrameList& aList, FrameChildListID aID)
+ : mList(aList), mID(aID) {}
+ nsFrameList mList;
+ FrameChildListID mID;
+};
+
+/**
+ * A class to iterate frame child lists.
+ */
+class MOZ_STACK_CLASS FrameChildListArrayIterator {
+ public:
+ explicit FrameChildListArrayIterator(const nsTArray<FrameChildList>& aLists)
+ : mLists(aLists), mCurrentIndex(0) {}
+ bool IsDone() const { return mCurrentIndex >= mLists.Length(); }
+ FrameChildListID CurrentID() const {
+ NS_ASSERTION(!IsDone(), "CurrentID(): iterator at end");
+ return mLists[mCurrentIndex].mID;
+ }
+ const nsFrameList& CurrentList() const {
+ NS_ASSERTION(!IsDone(), "CurrentList(): iterator at end");
+ return mLists[mCurrentIndex].mList;
+ }
+ void Next() {
+ NS_ASSERTION(!IsDone(), "Next(): iterator at end");
+ ++mCurrentIndex;
+ }
+
+protected:
+ const nsTArray<FrameChildList>& mLists;
+ uint32_t mCurrentIndex;
+};
+
+/**
+ * A class for retrieving a frame's child lists and iterate them.
+ */
+class MOZ_STACK_CLASS FrameChildListIterator
+ : public FrameChildListArrayIterator {
+ public:
+ explicit FrameChildListIterator(const nsIFrame* aFrame);
+
+protected:
+ AutoTArray<FrameChildList,4> mLists;
+};
+
+inline mozilla::layout::FrameChildListIDs
+operator|(mozilla::layout::FrameChildListID aLeftOp,
+ mozilla::layout::FrameChildListID aRightOp)
+{
+ return mozilla::layout::FrameChildListIDs(aLeftOp) |
+ mozilla::layout::FrameChildListIDs(aRightOp);
+}
+
+inline mozilla::layout::FrameChildListIDs
+operator|(mozilla::layout::FrameChildListID aLeftOp,
+ mozilla::layout::FrameChildListIDs aRightOp)
+{
+ return mozilla::layout::FrameChildListIDs(aLeftOp) | aRightOp;
+}
+
+} // namespace layout
+} // namespace mozilla
+
+inline void nsFrameList::AppendIfNonempty(
+ nsTArray<mozilla::layout::FrameChildList>* aLists,
+ mozilla::layout::FrameChildListID aListID) const
+{
+ if (NotEmpty()) {
+ aLists->AppendElement(mozilla::layout::FrameChildList(*this, aListID));
+ }
+}
+
+#endif /* !defined(FrameChildList_h_) */
diff --git a/layout/generic/JustificationUtils.h b/layout/generic/JustificationUtils.h
new file mode 100644
index 000000000..1be36956b
--- /dev/null
+++ b/layout/generic/JustificationUtils.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_JustificationUtils_h_
+#define mozilla_JustificationUtils_h_
+
+#include "mozilla/Attributes.h"
+#include "nsCoord.h"
+
+namespace mozilla {
+
+/**
+ * Jutification Algorithm
+ *
+ * The justification algorithm is based on expansion opportunities
+ * between justifiable clusters. By this algorithm, there is one
+ * expansion opportunity at each side of a justifiable cluster, and
+ * at most one opportunity between two clusters. For example, if there
+ * is a line in a Chinese document is: "你好世界hello world", then
+ * the expansion opportunities (marked as '*') would be:
+ *
+ * 你*好*世*界*hello*' '*world
+ *
+ * The spacing left in a line will then be distributed equally to each
+ * opportunities. Because we want that, only justifiable clusters get
+ * expanded, and the split point between two justifiable clusters would
+ * be at the middle of the spacing, each expansion opportunities will be
+ * filled by two justification gaps. The example above would be:
+ *
+ * 你 | 好 | 世 | 界 |hello| ' ' |world
+ *
+ * In the algorithm, information about expansion opportunities is stored
+ * in structure JustificationInfo, and the assignment of justification
+ * gaps is in structure JustificationAssignment.
+ */
+
+struct JustificationInfo
+{
+ // Number of expansion opportunities inside a span. It doesn't include
+ // any opportunities between this span and the one before or after.
+ int32_t mInnerOpportunities;
+ // The justifiability of the start and end sides of the span.
+ bool mIsStartJustifiable;
+ bool mIsEndJustifiable;
+
+ constexpr JustificationInfo()
+ : mInnerOpportunities(0)
+ , mIsStartJustifiable(false)
+ , mIsEndJustifiable(false)
+ {
+ }
+
+ // Claim that the last opportunity should be cancelled
+ // because the trailing space just gets trimmed.
+ void CancelOpportunityForTrimmedSpace()
+ {
+ if (mInnerOpportunities > 0) {
+ mInnerOpportunities--;
+ } else {
+ // There is no inner opportunities, hence the whole frame must
+ // contain only the trimmed space, because any content before
+ // space would cause an inner opportunity. The space made each
+ // side justifiable, which should be cancelled now.
+ mIsStartJustifiable = false;
+ mIsEndJustifiable = false;
+ }
+ }
+};
+
+struct JustificationAssignment
+{
+ // There are at most 2 gaps per end, so it is enough to use 2 bits.
+ uint8_t mGapsAtStart : 2;
+ uint8_t mGapsAtEnd : 2;
+
+ constexpr JustificationAssignment()
+ : mGapsAtStart(0)
+ , mGapsAtEnd(0)
+ {
+ }
+
+ int32_t TotalGaps() const { return mGapsAtStart + mGapsAtEnd; }
+};
+
+struct JustificationApplicationState
+{
+ struct
+ {
+ // The total number of justification gaps to be processed.
+ int32_t mCount;
+ // The number of justification gaps which have been handled.
+ int32_t mHandled;
+ } mGaps;
+
+ struct
+ {
+ // The total spacing left in a line before justification.
+ nscoord mAvailable;
+ // The spacing has been consumed by handled justification gaps.
+ nscoord mConsumed;
+ } mWidth;
+
+ JustificationApplicationState(int32_t aGaps, nscoord aWidth)
+ {
+ mGaps.mCount = aGaps;
+ mGaps.mHandled = 0;
+ mWidth.mAvailable = aWidth;
+ mWidth.mConsumed = 0;
+ }
+
+ bool IsJustifiable() const
+ {
+ return mGaps.mCount > 0 && mWidth.mAvailable > 0;
+ }
+
+ nscoord Consume(int32_t aGaps)
+ {
+ mGaps.mHandled += aGaps;
+ nscoord newAllocate = (mWidth.mAvailable * mGaps.mHandled) / mGaps.mCount;
+ nscoord deltaWidth = newAllocate - mWidth.mConsumed;
+ mWidth.mConsumed = newAllocate;
+ return deltaWidth;
+ }
+};
+
+class JustificationUtils
+{
+public:
+ // Compute justification gaps should be applied on a unit.
+ static int32_t CountGaps(const JustificationInfo& aInfo,
+ const JustificationAssignment& aAssign)
+ {
+ // Justification gaps include two gaps for each inner opportunities
+ // and the gaps given assigned to the ends.
+ return aInfo.mInnerOpportunities * 2 + aAssign.TotalGaps();
+ }
+};
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_JustificationUtils_h_) */
diff --git a/layout/generic/MathMLTextRunFactory.cpp b/layout/generic/MathMLTextRunFactory.cpp
new file mode 100644
index 000000000..b96aa921b
--- /dev/null
+++ b/layout/generic/MathMLTextRunFactory.cpp
@@ -0,0 +1,801 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MathMLTextRunFactory.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/BinarySearch.h"
+
+#include "nsStyleConsts.h"
+#include "nsTextFrameUtils.h"
+#include "nsFontMetrics.h"
+#include "nsDeviceContext.h"
+#include "nsUnicodeScriptCodes.h"
+
+using namespace mozilla;
+
+/*
+ Entries for the mathvariant lookup tables. mKey represents the Unicode
+ character to be transformed and is used for searching the tables.
+ mReplacement represents the mapped mathvariant Unicode character.
+*/
+typedef struct
+{
+ uint32_t mKey;
+ uint32_t mReplacement;
+} MathVarMapping;
+
+/*
+ Lookup tables for use with mathvariant mappings to transform a unicode
+ character point to another unicode character that indicates the proper output.
+ mKey represents one of two concepts.
+ 1. In the Latin table it represents a hole in the mathematical alphanumeric
+ block, where the character that should occupy that position is located
+ elsewhere.
+ 2. It represents an Arabic letter.
+
+ As a replacement, 0 is reserved to indicate no mapping was found.
+*/
+static const MathVarMapping gArabicInitialMapTable[] = {
+ { 0x628, 0x1EE21 },
+ { 0x62A, 0x1EE35 },
+ { 0x62B, 0x1EE36 },
+ { 0x62C, 0x1EE22 },
+ { 0x62D, 0x1EE27 },
+ { 0x62E, 0x1EE37 },
+ { 0x633, 0x1EE2E },
+ { 0x634, 0x1EE34 },
+ { 0x635, 0x1EE31 },
+ { 0x636, 0x1EE39 },
+ { 0x639, 0x1EE2F },
+ { 0x63A, 0x1EE3B },
+ { 0x641, 0x1EE30 },
+ { 0x642, 0x1EE32 },
+ { 0x643, 0x1EE2A },
+ { 0x644, 0x1EE2B },
+ { 0x645, 0x1EE2C },
+ { 0x646, 0x1EE2D },
+ { 0x647, 0x1EE24 },
+ { 0x64A, 0x1EE29 }
+};
+
+static const MathVarMapping gArabicTailedMapTable[] = {
+ { 0x62C, 0x1EE42 },
+ { 0x62D, 0x1EE47 },
+ { 0x62E, 0x1EE57 },
+ { 0x633, 0x1EE4E },
+ { 0x634, 0x1EE54 },
+ { 0x635, 0x1EE51 },
+ { 0x636, 0x1EE59 },
+ { 0x639, 0x1EE4F },
+ { 0x63A, 0x1EE5B },
+ { 0x642, 0x1EE52 },
+ { 0x644, 0x1EE4B },
+ { 0x646, 0x1EE4D },
+ { 0x64A, 0x1EE49 },
+ { 0x66F, 0x1EE5F },
+ { 0x6BA, 0x1EE5D }
+};
+
+static const MathVarMapping gArabicStretchedMapTable[] = {
+ { 0x628, 0x1EE61 },
+ { 0x62A, 0x1EE75 },
+ { 0x62B, 0x1EE76 },
+ { 0x62C, 0x1EE62 },
+ { 0x62D, 0x1EE67 },
+ { 0x62E, 0x1EE77 },
+ { 0x633, 0x1EE6E },
+ { 0x634, 0x1EE74 },
+ { 0x635, 0x1EE71 },
+ { 0x636, 0x1EE79 },
+ { 0x637, 0x1EE68 },
+ { 0x638, 0x1EE7A },
+ { 0x639, 0x1EE6F },
+ { 0x63A, 0x1EE7B },
+ { 0x641, 0x1EE70 },
+ { 0x642, 0x1EE72 },
+ { 0x643, 0x1EE6A },
+ { 0x645, 0x1EE6C },
+ { 0x646, 0x1EE6D },
+ { 0x647, 0x1EE64 },
+ { 0x64A, 0x1EE69 },
+ { 0x66E, 0x1EE7C },
+ { 0x6A1, 0x1EE7E }
+};
+
+static const MathVarMapping gArabicLoopedMapTable[] = {
+ { 0x627, 0x1EE80 },
+ { 0x628, 0x1EE81 },
+ { 0x62A, 0x1EE95 },
+ { 0x62B, 0x1EE96 },
+ { 0x62C, 0x1EE82 },
+ { 0x62D, 0x1EE87 },
+ { 0x62E, 0x1EE97 },
+ { 0x62F, 0x1EE83 },
+ { 0x630, 0x1EE98 },
+ { 0x631, 0x1EE93 },
+ { 0x632, 0x1EE86 },
+ { 0x633, 0x1EE8E },
+ { 0x634, 0x1EE94 },
+ { 0x635, 0x1EE91 },
+ { 0x636, 0x1EE99 },
+ { 0x637, 0x1EE88 },
+ { 0x638, 0x1EE9A },
+ { 0x639, 0x1EE8F },
+ { 0x63A, 0x1EE9B },
+ { 0x641, 0x1EE90 },
+ { 0x642, 0x1EE92 },
+ { 0x644, 0x1EE8B },
+ { 0x645, 0x1EE8C },
+ { 0x646, 0x1EE8D },
+ { 0x647, 0x1EE84 },
+ { 0x648, 0x1EE85 },
+ { 0x64A, 0x1EE89 }
+};
+
+static const MathVarMapping gArabicDoubleMapTable[] = {
+ { 0x628, 0x1EEA1 },
+ { 0x62A, 0x1EEB5 },
+ { 0x62B, 0x1EEB6 },
+ { 0x62C, 0x1EEA2 },
+ { 0x62D, 0x1EEA7 },
+ { 0x62E, 0x1EEB7 },
+ { 0x62F, 0x1EEA3 },
+ { 0x630, 0x1EEB8 },
+ { 0x631, 0x1EEB3 },
+ { 0x632, 0x1EEA6 },
+ { 0x633, 0x1EEAE },
+ { 0x634, 0x1EEB4 },
+ { 0x635, 0x1EEB1 },
+ { 0x636, 0x1EEB9 },
+ { 0x637, 0x1EEA8 },
+ { 0x638, 0x1EEBA },
+ { 0x639, 0x1EEAF },
+ { 0x63A, 0x1EEBB },
+ { 0x641, 0x1EEB0 },
+ { 0x642, 0x1EEB2 },
+ { 0x644, 0x1EEAB },
+ { 0x645, 0x1EEAC },
+ { 0x646, 0x1EEAD },
+ { 0x648, 0x1EEA5 },
+ { 0x64A, 0x1EEA9 }
+};
+
+static const MathVarMapping gLatinExceptionMapTable[] = {
+ { 0x1D455, 0x210E },
+ { 0x1D49D, 0x212C },
+ { 0x1D4A0, 0x2130 },
+ { 0x1D4A1, 0x2131 },
+ { 0x1D4A3, 0x210B },
+ { 0x1D4A4, 0x2110 },
+ { 0x1D4A7, 0x2112 },
+ { 0x1D4A8, 0x2133 },
+ { 0x1D4AD, 0x211B },
+ { 0x1D4BA, 0x212F },
+ { 0x1D4BC, 0x210A },
+ { 0x1D4C4, 0x2134 },
+ { 0x1D506, 0x212D },
+ { 0x1D50B, 0x210C },
+ { 0x1D50C, 0x2111 },
+ { 0x1D515, 0x211C },
+ { 0x1D51D, 0x2128 },
+ { 0x1D53A, 0x2102 },
+ { 0x1D53F, 0x210D },
+ { 0x1D545, 0x2115 },
+ { 0x1D547, 0x2119 },
+ { 0x1D548, 0x211A },
+ { 0x1D549, 0x211D },
+ { 0x1D551, 0x2124 }
+};
+
+namespace {
+
+struct MathVarMappingWrapper
+{
+ const MathVarMapping* const mTable;
+ explicit MathVarMappingWrapper(const MathVarMapping* aTable) : mTable(aTable) {}
+ uint32_t operator[](size_t index) const {
+ return mTable[index].mKey;
+ }
+};
+
+} // namespace
+
+// Finds a MathVarMapping struct with the specified key (aKey) within aTable.
+// aTable must be an array, whose length is specified by aNumElements
+static uint32_t
+MathvarMappingSearch(uint32_t aKey, const MathVarMapping* aTable, uint32_t aNumElements)
+{
+ size_t index;
+ if (BinarySearch(MathVarMappingWrapper(aTable), 0, aNumElements, aKey, &index)) {
+ return aTable[index].mReplacement;
+ }
+
+ return 0;
+}
+
+#define GREEK_UPPER_THETA 0x03F4
+#define HOLE_GREEK_UPPER_THETA 0x03A2
+#define NABLA 0x2207
+#define PARTIAL_DIFFERENTIAL 0x2202
+#define GREEK_UPPER_ALPHA 0x0391
+#define GREEK_UPPER_OMEGA 0x03A9
+#define GREEK_LOWER_ALPHA 0x03B1
+#define GREEK_LOWER_OMEGA 0x03C9
+#define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5
+#define GREEK_THETA_SYMBOL 0x03D1
+#define GREEK_KAPPA_SYMBOL 0x03F0
+#define GREEK_PHI_SYMBOL 0x03D5
+#define GREEK_RHO_SYMBOL 0x03F1
+#define GREEK_PI_SYMBOL 0x03D6
+#define GREEK_LETTER_DIGAMMA 0x03DC
+#define GREEK_SMALL_LETTER_DIGAMMA 0x03DD
+#define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA
+#define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB
+
+#define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
+#define LATIN_SMALL_LETTER_DOTLESS_J 0x0237
+
+#define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4
+#define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5
+
+#define MATH_BOLD_UPPER_A 0x1D400
+#define MATH_ITALIC_UPPER_A 0x1D434
+#define MATH_BOLD_SMALL_A 0x1D41A
+#define MATH_BOLD_UPPER_ALPHA 0x1D6A8
+#define MATH_BOLD_SMALL_ALPHA 0x1D6C2
+#define MATH_ITALIC_UPPER_ALPHA 0x1D6E2
+#define MATH_BOLD_DIGIT_ZERO 0x1D7CE
+#define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8
+
+#define MATH_BOLD_UPPER_THETA 0x1D6B9
+#define MATH_BOLD_NABLA 0x1D6C1
+#define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB
+#define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC
+#define MATH_BOLD_THETA_SYMBOL 0x1D6DD
+#define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE
+#define MATH_BOLD_PHI_SYMBOL 0x1D6DF
+#define MATH_BOLD_RHO_SYMBOL 0x1D6E0
+#define MATH_BOLD_PI_SYMBOL 0x1D6E1
+
+/*
+ Performs the character mapping needed to implement MathML's mathvariant
+ attribute. It takes a unicode character and maps it to its appropriate
+ mathvariant counterpart specified by aMathVar. The mapped character is
+ typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but
+ there are exceptions which this function accounts for.
+ Characters without a valid mapping or valid aMathvar value are returned
+ unaltered. Characters already in the mathematical blocks (or are one of the
+ exceptions) are never transformed.
+ Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h.
+ The transformable characters can be found at:
+ http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
+ https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
+*/
+static uint32_t
+MathVariant(uint32_t aCh, uint8_t aMathVar)
+{
+ uint32_t baseChar;
+ enum CharacterType {
+ kIsLatin,
+ kIsGreekish,
+ kIsNumber,
+ kIsArabic,
+ };
+ CharacterType varType;
+
+ int8_t multiplier;
+
+ if (aMathVar <= NS_MATHML_MATHVARIANT_NORMAL) {
+ // nothing to do here
+ return aCh;
+ }
+ if (aMathVar > NS_MATHML_MATHVARIANT_STRETCHED) {
+ NS_ASSERTION(false, "Illegal mathvariant value");
+ return aCh;
+ }
+
+ // Exceptional characters with at most one possible transformation
+ if (aCh == HOLE_GREEK_UPPER_THETA) {
+ // Nothing at this code point is transformed
+ return aCh;
+ }
+ if (aCh == GREEK_LETTER_DIGAMMA) {
+ if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) {
+ return MATH_BOLD_CAPITAL_DIGAMMA;
+ }
+ return aCh;
+ }
+ if (aCh == GREEK_SMALL_LETTER_DIGAMMA) {
+ if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) {
+ return MATH_BOLD_SMALL_DIGAMMA;
+ }
+ return aCh;
+ }
+ if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) {
+ if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) {
+ return MATH_ITALIC_SMALL_DOTLESS_I;
+ }
+ return aCh;
+ }
+ if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) {
+ if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) {
+ return MATH_ITALIC_SMALL_DOTLESS_J;
+ }
+ return aCh;
+ }
+
+ // The Unicode mathematical blocks are divided into four segments: Latin,
+ // Greek, numbers and Arabic. In the case of the first three
+ // baseChar represents the relative order in which the characters are
+ // encoded in the Unicode mathematical block, normalised to the first
+ // character of that sequence.
+ //
+ if ('A' <= aCh && aCh <= 'Z') {
+ baseChar = aCh - 'A';
+ varType = kIsLatin;
+ } else if ('a' <= aCh && aCh <= 'z') {
+ // Lowercase characters are placed immediately after the uppercase
+ // characters in the Unicode mathematical block. The constant subtraction
+ // represents the number of characters between the start of the sequence
+ // (capital A) and the first lowercase letter.
+ baseChar = MATH_BOLD_SMALL_A-MATH_BOLD_UPPER_A + aCh - 'a';
+ varType = kIsLatin;
+ } else if ('0' <= aCh && aCh <= '9') {
+ baseChar = aCh - '0';
+ varType = kIsNumber;
+ } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) {
+ baseChar = aCh-GREEK_UPPER_ALPHA;
+ varType = kIsGreekish;
+ } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) {
+ // Lowercase Greek comes after uppercase Greek.
+ // Note in this instance the presence of an additional character (Nabla)
+ // between the end of the uppercase Greek characters and the lowercase
+ // ones.
+ baseChar = MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA
+ + aCh-GREEK_LOWER_ALPHA;
+ varType = kIsGreekish;
+ } else if (0x0600 <= aCh && aCh <= 0x06FF) {
+ // Arabic characters are defined within this range
+ varType = kIsArabic;
+ } else {
+ switch (aCh) {
+ case GREEK_UPPER_THETA:
+ baseChar = MATH_BOLD_UPPER_THETA-MATH_BOLD_UPPER_ALPHA;
+ break;
+ case NABLA:
+ baseChar = MATH_BOLD_NABLA-MATH_BOLD_UPPER_ALPHA;
+ break;
+ case PARTIAL_DIFFERENTIAL:
+ baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ case GREEK_LUNATE_EPSILON_SYMBOL:
+ baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ case GREEK_THETA_SYMBOL:
+ baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ case GREEK_KAPPA_SYMBOL:
+ baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ case GREEK_PHI_SYMBOL:
+ baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ case GREEK_RHO_SYMBOL:
+ baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ case GREEK_PI_SYMBOL:
+ baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+ break;
+ default:
+ return aCh;
+ }
+
+ varType = kIsGreekish;
+ }
+
+ if (varType == kIsNumber) {
+ switch (aMathVar) {
+ // Each possible number mathvariant is encoded in a single, contiguous
+ // block. For example the beginning of the double struck number range
+ // follows immediately after the end of the bold number range.
+ // multiplier represents the order of the sequences relative to the first
+ // one.
+ case NS_MATHML_MATHVARIANT_BOLD:
+ multiplier = 0;
+ break;
+ case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK:
+ multiplier = 1;
+ break;
+ case NS_MATHML_MATHVARIANT_SANS_SERIF:
+ multiplier = 2;
+ break;
+ case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF:
+ multiplier = 3;
+ break;
+ case NS_MATHML_MATHVARIANT_MONOSPACE:
+ multiplier = 4;
+ break;
+ default:
+ // This mathvariant isn't defined for numbers or is otherwise normal
+ return aCh;
+ }
+ // As the ranges are contiguous, to find the desired mathvariant range it
+ // is sufficient to multiply the position within the sequence order
+ // (multiplier) with the period of the sequence (which is constant for all
+ // number sequences) and to add the character point of the first character
+ // within the number mathvariant range.
+ // To this the baseChar calculated earlier is added to obtain the final
+ // code point.
+ return baseChar+multiplier*(MATH_DOUBLE_STRUCK_ZERO-MATH_BOLD_DIGIT_ZERO)
+ +MATH_BOLD_DIGIT_ZERO;
+ } else if (varType == kIsGreekish) {
+ switch (aMathVar) {
+ case NS_MATHML_MATHVARIANT_BOLD:
+ multiplier = 0;
+ break;
+ case NS_MATHML_MATHVARIANT_ITALIC:
+ multiplier = 1;
+ break;
+ case NS_MATHML_MATHVARIANT_BOLD_ITALIC:
+ multiplier = 2;
+ break;
+ case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF:
+ multiplier = 3;
+ break;
+ case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC:
+ multiplier = 4;
+ break;
+ default:
+ // This mathvariant isn't defined for Greek or is otherwise normal
+ return aCh;
+ }
+ // See the kIsNumber case for an explanation of the following calculation
+ return baseChar + MATH_BOLD_UPPER_ALPHA +
+ multiplier*(MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA);
+ }
+
+ uint32_t tempChar;
+ uint32_t newChar;
+ if (varType == kIsArabic) {
+ const MathVarMapping* mapTable;
+ uint32_t tableLength;
+ switch (aMathVar) {
+ /* The Arabic mathematical block is not continuous, nor does it have a
+ * monotonic mapping to the unencoded characters, requiring the use of a
+ * lookup table.
+ */
+ case NS_MATHML_MATHVARIANT_INITIAL:
+ mapTable = gArabicInitialMapTable;
+ tableLength = ArrayLength(gArabicInitialMapTable);
+ break;
+ case NS_MATHML_MATHVARIANT_TAILED:
+ mapTable = gArabicTailedMapTable;
+ tableLength = ArrayLength(gArabicTailedMapTable);
+ break;
+ case NS_MATHML_MATHVARIANT_STRETCHED:
+ mapTable = gArabicStretchedMapTable;
+ tableLength = ArrayLength(gArabicStretchedMapTable);
+ break;
+ case NS_MATHML_MATHVARIANT_LOOPED:
+ mapTable = gArabicLoopedMapTable;
+ tableLength = ArrayLength(gArabicLoopedMapTable);
+ break;
+ case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK:
+ mapTable = gArabicDoubleMapTable;
+ tableLength = ArrayLength(gArabicDoubleMapTable);
+ break;
+ default:
+ // No valid transformations exist
+ return aCh;
+ }
+ newChar = MathvarMappingSearch(aCh, mapTable, tableLength);
+ } else {
+ // Must be Latin
+ if (aMathVar > NS_MATHML_MATHVARIANT_MONOSPACE) {
+ // Latin doesn't support the Arabic mathvariants
+ return aCh;
+ }
+ multiplier = aMathVar - 2;
+ // This is possible because the values for NS_MATHML_MATHVARIANT_* are
+ // chosen to coincide with the order in which the encoded mathvariant
+ // characters are located within their unicode block (less an offset to
+ // avoid _NONE and _NORMAL variants)
+ // See the kIsNumber case for an explanation of the following calculation
+ tempChar = baseChar + MATH_BOLD_UPPER_A +
+ multiplier*(MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A);
+ // There are roughly twenty characters that are located outside of the
+ // mathematical block, so the spaces where they ought to be are used
+ // as keys for a lookup table containing the correct character mappings.
+ newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable,
+ ArrayLength(gLatinExceptionMapTable));
+ }
+
+ if (newChar) {
+ return newChar;
+ } else if (varType == kIsLatin) {
+ return tempChar;
+ } else {
+ // An Arabic character without a corresponding mapping
+ return aCh;
+ }
+
+}
+
+#define TT_SSTY TRUETYPE_TAG('s', 's', 't', 'y')
+#define TT_DTLS TRUETYPE_TAG('d', 't', 'l', 's')
+
+void
+MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
+ mozilla::gfx::DrawTarget* aRefDrawTarget,
+ gfxMissingFontRecorder* aMFR)
+{
+ gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
+
+ nsAutoString convertedString;
+ AutoTArray<bool,50> charsToMergeArray;
+ AutoTArray<bool,50> deletedCharsArray;
+ AutoTArray<RefPtr<nsTransformedCharStyle>,50> styleArray;
+ AutoTArray<uint8_t,50> canBreakBeforeArray;
+ bool mergeNeeded = false;
+
+ bool singleCharMI =
+ aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI;
+
+ uint32_t length = aTextRun->GetLength();
+ const char16_t* str = aTextRun->mString.BeginReading();
+ const nsTArray<RefPtr<nsTransformedCharStyle>>& styles = aTextRun->mStyles;
+ nsFont font;
+ if (length) {
+ font = styles[0]->mFont;
+
+ if (mSSTYScriptLevel || (mFlags & MATH_FONT_FEATURE_DTLS)) {
+ bool foundSSTY = false;
+ bool foundDTLS = false;
+ // We respect ssty settings explicitly set by the user
+ for (uint32_t i = 0; i < font.fontFeatureSettings.Length(); i++) {
+ if (font.fontFeatureSettings[i].mTag == TT_SSTY) {
+ foundSSTY = true;
+ } else if (font.fontFeatureSettings[i].mTag == TT_DTLS) {
+ foundDTLS = true;
+ }
+ }
+ if (mSSTYScriptLevel && !foundSSTY) {
+ uint8_t sstyLevel = 0;
+ float scriptScaling = pow(styles[0]->mScriptSizeMultiplier,
+ mSSTYScriptLevel);
+ static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1,
+ "Shouldn't it make things smaller?");
+ /*
+ An SSTY level of 2 is set if the scaling factor is less than or equal
+ to halfway between that for a scriptlevel of 1 (0.71) and that of a
+ scriptlevel of 2 (0.71^2), assuming the default script size multiplier.
+ An SSTY level of 1 is set if the script scaling factor is less than
+ or equal that for a scriptlevel of 1 assuming the default script size
+ multiplier.
+
+ User specified values of script size multiplier will change the scaling
+ factor which mSSTYScriptLevel values correspond to.
+
+ In the event that the script size multiplier actually makes things
+ larger, no change is made.
+
+ To opt out of this change, add the following to the stylesheet:
+ "font-feature-settings: 'ssty' 0"
+ */
+ if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER +
+ (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER *
+ NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) {
+ // Currently only the first two ssty settings are used, so two is large
+ // as we go
+ sstyLevel = 2;
+ } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) {
+ sstyLevel = 1;
+ }
+ if (sstyLevel) {
+ gfxFontFeature settingSSTY;
+ settingSSTY.mTag = TT_SSTY;
+ settingSSTY.mValue = sstyLevel;
+ font.fontFeatureSettings.AppendElement(settingSSTY);
+ }
+ }
+ /*
+ Apply the dtls font feature setting (dotless).
+ This gets applied to the base frame and all descendants of the base
+ frame of certain <mover> and <munderover> frames.
+
+ See nsMathMLmunderoverFrame.cpp for a full description.
+
+ To opt out of this change, add the following to the stylesheet:
+ "font-feature-settings: 'dtls' 0"
+ */
+ if ((mFlags & MATH_FONT_FEATURE_DTLS) && !foundDTLS) {
+ gfxFontFeature settingDTLS;
+ settingDTLS.mTag = TT_DTLS;
+ settingDTLS.mValue = 1;
+ font.fontFeatureSettings.AppendElement(settingDTLS);
+ }
+ }
+ }
+
+ uint8_t mathVar = NS_MATHML_MATHVARIANT_NONE;
+ bool doMathvariantStyling = true;
+
+ for (uint32_t i = 0; i < length; ++i) {
+ int extraChars = 0;
+ mathVar = styles[i]->mMathVariant;
+
+ if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) {
+ // If the user has explicitly set a non-default value for fontstyle or
+ // fontweight, the italic mathvariant behaviour of <mi> is disabled
+ // This overrides the initial values specified in fontStyle, to avoid
+ // inconsistencies in which attributes allow CSS changes and which do not.
+ if (mFlags & MATH_FONT_WEIGHT_BOLD) {
+ font.weight = NS_FONT_WEIGHT_BOLD;
+ if (mFlags & MATH_FONT_STYLING_NORMAL) {
+ font.style = NS_FONT_STYLE_NORMAL;
+ } else {
+ font.style = NS_FONT_STYLE_ITALIC;
+ }
+ } else if (mFlags & MATH_FONT_STYLING_NORMAL) {
+ font.style = NS_FONT_STYLE_NORMAL;
+ font.weight = NS_FONT_WEIGHT_NORMAL;
+ } else {
+ mathVar = NS_MATHML_MATHVARIANT_ITALIC;
+ }
+ }
+
+ uint32_t ch = str[i];
+ if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
+ NS_IS_LOW_SURROGATE(str[i + 1])) {
+ ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
+ }
+ uint32_t ch2 = MathVariant(ch, mathVar);
+
+ if (mathVar == NS_MATHML_MATHVARIANT_BOLD ||
+ mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC ||
+ mathVar == NS_MATHML_MATHVARIANT_ITALIC) {
+ if (ch == ch2 && ch != 0x20 && ch != 0xA0) {
+ // Don't apply the CSS style if a character cannot be
+ // transformed. There is an exception for whitespace as it is both
+ // common and innocuous.
+ doMathvariantStyling = false;
+ }
+ if (ch2 != ch) {
+ // Bug 930504. Some platforms do not have fonts for Mathematical
+ // Alphanumeric Symbols. Hence we check whether the transformed
+ // character is actually available.
+ uint8_t matchType;
+ RefPtr<gfxFont> mathFont = fontGroup->
+ FindFontForChar(ch2, 0, 0, unicode::Script::COMMON, nullptr, &matchType);
+ if (mathFont) {
+ // Don't apply the CSS style if there is a math font for at least one
+ // of the transformed character in this text run.
+ doMathvariantStyling = false;
+ } else {
+ // We fallback to the original character.
+ ch2 = ch;
+ if (aMFR) {
+ aMFR->RecordScript(unicode::Script::MATHEMATICAL_NOTATION);
+ }
+ }
+ }
+ }
+
+ deletedCharsArray.AppendElement(false);
+ charsToMergeArray.AppendElement(false);
+ styleArray.AppendElement(styles[i]);
+ canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
+
+ if (IS_IN_BMP(ch2)) {
+ convertedString.Append(ch2);
+ } else {
+ convertedString.Append(H_SURROGATE(ch2));
+ convertedString.Append(L_SURROGATE(ch2));
+ ++extraChars;
+ if (!IS_IN_BMP(ch)) {
+ deletedCharsArray.AppendElement(true); // not exactly deleted, but
+ // the trailing surrogate is skipped
+ ++i;
+ }
+ }
+
+ while (extraChars-- > 0) {
+ mergeNeeded = true;
+ charsToMergeArray.AppendElement(true);
+ styleArray.AppendElement(styles[i]);
+ canBreakBeforeArray.AppendElement(false);
+ }
+ }
+
+ uint32_t flags;
+ gfxTextRunFactory::Parameters innerParams =
+ GetParametersForInner(aTextRun, &flags, aRefDrawTarget);
+
+ RefPtr<nsTransformedTextRun> transformedChild;
+ RefPtr<gfxTextRun> cachedChild;
+ gfxTextRun* child;
+
+ if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) {
+ font.style = NS_FONT_STYLE_NORMAL;
+ font.weight = NS_FONT_WEIGHT_BOLD;
+ } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) {
+ font.style = NS_FONT_STYLE_ITALIC;
+ font.weight = NS_FONT_WEIGHT_NORMAL;
+ } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC &&
+ doMathvariantStyling) {
+ font.style = NS_FONT_STYLE_ITALIC;
+ font.weight = NS_FONT_WEIGHT_BOLD;
+ } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) {
+ // Mathvariant overrides fontstyle and fontweight
+ // Need to check to see if mathvariant is actually applied as this function
+ // is used for other purposes.
+ font.style = NS_FONT_STYLE_NORMAL;
+ font.weight = NS_FONT_WEIGHT_NORMAL;
+ }
+ gfxFontGroup* newFontGroup = nullptr;
+
+ // Get the correct gfxFontGroup that corresponds to the earlier font changes.
+ if (length) {
+ font.size = NSToCoordRound(font.size * mFontInflation);
+ nsPresContext* pc = styles[0]->mPresContext;
+ nsFontMetrics::Params params;
+ params.language = styles[0]->mLanguage;
+ params.explicitLanguage = styles[0]->mExplicitLanguage;
+ params.userFontSet = pc->GetUserFontSet();
+ params.textPerf = pc->GetTextPerfMetrics();
+ RefPtr<nsFontMetrics> metrics =
+ pc->DeviceContext()->GetMetricsFor(font, params);
+ newFontGroup = metrics->GetThebesFontGroup();
+ }
+
+ if (!newFontGroup) {
+ // If we can't get a new font group, fall back to the old one. Rendering
+ // will be incorrect, but not significantly so.
+ newFontGroup = fontGroup;
+ }
+
+ if (mInnerTransformingTextRunFactory) {
+ transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
+ convertedString.BeginReading(), convertedString.Length(),
+ &innerParams, newFontGroup, flags, Move(styleArray), false);
+ child = transformedChild.get();
+ } else {
+ cachedChild = newFontGroup->MakeTextRun(
+ convertedString.BeginReading(), convertedString.Length(),
+ &innerParams, flags, aMFR);
+ child = cachedChild.get();
+ }
+ if (!child)
+ return;
+
+ typedef gfxTextRun::Range Range;
+
+ // Copy potential linebreaks into child so they're preserved
+ // (and also child will be shaped appropriately)
+ NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
+ "Dropped characters or break-before values somewhere!");
+ Range range(0, uint32_t(canBreakBeforeArray.Length()));
+ child->SetPotentialLineBreaks(range, canBreakBeforeArray.Elements());
+ if (transformedChild) {
+ transformedChild->FinishSettingProperties(aRefDrawTarget, aMFR);
+ }
+
+ if (mergeNeeded) {
+ // Now merge multiple characters into one multi-glyph character as required
+ NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
+ "source length mismatch");
+ NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
+ "destination length mismatch");
+ MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
+ deletedCharsArray.Elements());
+ } else {
+ // No merging to do, so just copy; this produces a more optimized textrun.
+ // We can't steal the data because the child may be cached and stealing
+ // the data would break the cache.
+ aTextRun->ResetGlyphRuns();
+ aTextRun->CopyGlyphDataFrom(child, Range(child), 0);
+ }
+}
diff --git a/layout/generic/MathMLTextRunFactory.h b/layout/generic/MathMLTextRunFactory.h
new file mode 100644
index 000000000..6ebb710f5
--- /dev/null
+++ b/layout/generic/MathMLTextRunFactory.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MATHMLTEXTRUNFACTORY_H_
+#define MATHMLTEXTRUNFACTORY_H_
+
+#include "mozilla/UniquePtr.h"
+#include "nsTextRunTransformations.h"
+
+/**
+ * Builds textruns that render their text with MathML specific renderings.
+ */
+class MathMLTextRunFactory : public nsTransformingTextRunFactory {
+public:
+ MathMLTextRunFactory(UniquePtr<nsTransformingTextRunFactory> aInnerTransformingTextRunFactory,
+ uint32_t aFlags, uint8_t aSSTYScriptLevel,
+ float aFontInflation)
+ : mInnerTransformingTextRunFactory(Move(aInnerTransformingTextRunFactory)),
+ mFlags(aFlags),
+ mFontInflation(aFontInflation),
+ mSSTYScriptLevel(aSSTYScriptLevel) {}
+
+ virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
+ mozilla::gfx::DrawTarget* aRefDrawTarget,
+ gfxMissingFontRecorder* aMFR) override;
+ enum {
+ // Style effects which may override single character <mi> behaviour
+ MATH_FONT_STYLING_NORMAL = 0x1, // fontstyle="normal" has been set.
+ MATH_FONT_WEIGHT_BOLD = 0x2, // fontweight="bold" has been set.
+ MATH_FONT_FEATURE_DTLS = 0x4, // font feature dtls should be set
+ };
+
+protected:
+ UniquePtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
+ uint32_t mFlags;
+ float mFontInflation;
+ uint8_t mSSTYScriptLevel;
+};
+
+#endif /*MATHMLTEXTRUNFACTORY_H_*/
diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp
new file mode 100644
index 000000000..42f4a24b5
--- /dev/null
+++ b/layout/generic/ReflowInput.cpp
@@ -0,0 +1,3071 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* struct containing the input to nsIFrame::Reflow */
+
+#include "mozilla/ReflowInput.h"
+
+#include "LayoutLogging.h"
+#include "nsStyleConsts.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsFrame.h"
+#include "nsIContent.h"
+#include "nsGkAtoms.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsFontMetrics.h"
+#include "nsBlockFrame.h"
+#include "nsLineBox.h"
+#include "nsImageFrame.h"
+#include "nsTableFrame.h"
+#include "nsTableCellFrame.h"
+#include "nsIPercentBSizeObserver.h"
+#include "nsLayoutUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsFontInflationData.h"
+#include "StickyScrollContainer.h"
+#include "nsIFrameInlines.h"
+#include "CounterStyleManager.h"
+#include <algorithm>
+#include "mozilla/dom/HTMLInputElement.h"
+
+#ifdef DEBUG
+#undef NOISY_VERTICAL_ALIGN
+#else
+#undef NOISY_VERTICAL_ALIGN
+#endif
+
+using namespace mozilla;
+using namespace mozilla::css;
+using namespace mozilla::dom;
+using namespace mozilla::layout;
+
+enum eNormalLineHeightControl {
+ eUninitialized = -1,
+ eNoExternalLeading = 0, // does not include external leading
+ eIncludeExternalLeading, // use whatever value font vendor provides
+ eCompensateLeading // compensate leading if leading provided by font vendor is not enough
+};
+
+static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
+
+// Initialize a <b>root</b> reflow state with a rendering context to
+// use for measuring things.
+ReflowInput::ReflowInput(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsRenderingContext* aRenderingContext,
+ const LogicalSize& aAvailableSpace,
+ uint32_t aFlags)
+ : SizeComputationInput(aFrame, aRenderingContext)
+ , mBlockDelta(0)
+ , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE)
+ , mReflowDepth(0)
+{
+ NS_PRECONDITION(aRenderingContext, "no rendering context");
+ MOZ_ASSERT(aPresContext, "no pres context");
+ MOZ_ASSERT(aFrame, "no frame");
+ MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
+ mParentReflowInput = nullptr;
+ AvailableISize() = aAvailableSpace.ISize(mWritingMode);
+ AvailableBSize() = aAvailableSpace.BSize(mWritingMode);
+ mFloatManager = nullptr;
+ mLineLayout = nullptr;
+ mDiscoveredClearance = nullptr;
+ mPercentBSizeObserver = nullptr;
+
+ if (aFlags & DUMMY_PARENT_REFLOW_STATE) {
+ mFlags.mDummyParentReflowInput = true;
+ }
+ if (aFlags & COMPUTE_SIZE_SHRINK_WRAP) {
+ mFlags.mShrinkWrap = true;
+ }
+ if (aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE) {
+ mFlags.mUseAutoBSize = true;
+ }
+ if (aFlags & STATIC_POS_IS_CB_ORIGIN) {
+ mFlags.mStaticPosIsCBOrigin = true;
+ }
+ if (aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE) {
+ mFlags.mIClampMarginBoxMinSize = true;
+ }
+ if (aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE) {
+ mFlags.mBClampMarginBoxMinSize = true;
+ }
+
+ if (!(aFlags & CALLER_WILL_INIT)) {
+ Init(aPresContext);
+ }
+}
+
+static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
+{
+ nsIFrame* frameNext = aFrame->GetNextInFlow();
+ nsIFrame* parentNext = aParent->GetNextInFlow();
+ return frameNext && parentNext && frameNext->GetParent() == parentNext;
+}
+
+/**
+ * Adjusts the margin for a list (ol, ul), if necessary, depending on
+ * font inflation settings. Unfortunately, because bullets from a list are
+ * placed in the margin area, we only have ~40px in which to place the
+ * bullets. When they are inflated, however, this causes problems, since
+ * the text takes up more space than is available in the margin.
+ *
+ * This method will return a small amount (in app units) by which the
+ * margin can be adjusted, so that the space is available for list
+ * bullets to be rendered with font inflation enabled.
+ */
+static nscoord
+FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame)
+{
+ float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
+ if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
+ const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame);
+
+ // We only want to adjust the margins if we're dealing with an ordered
+ // list.
+ if (inflation > 1.0f &&
+ blockFrame->HasBullet() &&
+ inflation > 1.0f) {
+
+ auto listStyleType = aFrame->StyleList()->GetCounterStyle()->GetStyle();
+ if (listStyleType != NS_STYLE_LIST_STYLE_NONE &&
+ listStyleType != NS_STYLE_LIST_STYLE_DISC &&
+ listStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
+ listStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
+ listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
+ listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN) {
+ // The HTML spec states that the default padding for ordered lists
+ // begins at 40px, indicating that we have 40px of space to place a
+ // bullet. When performing font inflation calculations, we add space
+ // equivalent to this, but simply inflated at the same amount as the
+ // text, in app units.
+ return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1);
+ }
+
+ }
+ }
+
+ return 0;
+}
+
+// NOTE: If we ever want to use SizeComputationInput for a flex item or a
+// grid item, we need to make it take the containing-block block-size as
+// well as the inline-size, since flex items and grid items resolve
+// block-direction percent margins and padding against the
+// containing-block block-size, rather than its inline-size.
+SizeComputationInput::SizeComputationInput(nsIFrame *aFrame,
+ nsRenderingContext *aRenderingContext,
+ WritingMode aContainingBlockWritingMode,
+ nscoord aContainingBlockISize)
+ : mFrame(aFrame)
+ , mRenderingContext(aRenderingContext)
+ , mWritingMode(aFrame->GetWritingMode())
+{
+ MOZ_ASSERT(!aFrame->IsFlexOrGridItem(),
+ "We're about to resolve percent margin & padding "
+ "values against CB inline size, which is incorrect for "
+ "flex/grid items. "
+ "Additionally for grid items, this path doesn't handle baseline "
+ "padding contribution - see SizeComputationInput::InitOffsets");
+ LogicalSize cbSize(aContainingBlockWritingMode, aContainingBlockISize,
+ aContainingBlockISize);
+ ReflowInputFlags flags;
+ InitOffsets(aContainingBlockWritingMode, cbSize, mFrame->GetType(), flags);
+}
+
+// Initialize a reflow state for a child frame's reflow. Some state
+// is copied from the parent reflow state; the remaining state is
+// computed.
+ReflowInput::ReflowInput(
+ nsPresContext* aPresContext,
+ const ReflowInput& aParentReflowInput,
+ nsIFrame* aFrame,
+ const LogicalSize& aAvailableSpace,
+ const LogicalSize* aContainingBlockSize,
+ uint32_t aFlags)
+ : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext)
+ , mBlockDelta(0)
+ , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE)
+ , mFlags(aParentReflowInput.mFlags)
+ , mReflowDepth(aParentReflowInput.mReflowDepth + 1)
+{
+ MOZ_ASSERT(aPresContext, "no pres context");
+ MOZ_ASSERT(aFrame, "no frame");
+ MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
+ NS_PRECONDITION(!mFlags.mSpecialBSizeReflow ||
+ !NS_SUBTREE_DIRTY(aFrame),
+ "frame should be clean when getting special bsize reflow");
+
+ mParentReflowInput = &aParentReflowInput;
+
+ // If the parent is dirty, then the child is as well.
+ // XXX Are the other cases where the parent reflows a child a second
+ // time, as a resize?
+ if (!mFlags.mSpecialBSizeReflow)
+ mFrame->AddStateBits(mParentReflowInput->mFrame->GetStateBits() &
+ NS_FRAME_IS_DIRTY);
+
+ AvailableISize() = aAvailableSpace.ISize(mWritingMode);
+ AvailableBSize() = aAvailableSpace.BSize(mWritingMode);
+
+ if (mWritingMode.IsOrthogonalTo(aParentReflowInput.GetWritingMode())) {
+ // If we're setting up for an orthogonal flow, and the parent reflow state
+ // had a constrained ComputedBSize, we can use that as our AvailableISize
+ // in preference to leaving it unconstrained.
+ if (AvailableISize() == NS_UNCONSTRAINEDSIZE &&
+ aParentReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
+ AvailableISize() = aParentReflowInput.ComputedBSize();
+ }
+ }
+
+ mFloatManager = aParentReflowInput.mFloatManager;
+ if (mFrame->IsFrameOfType(nsIFrame::eLineParticipant))
+ mLineLayout = aParentReflowInput.mLineLayout;
+ else
+ mLineLayout = nullptr;
+
+ // Note: mFlags was initialized as a copy of aParentReflowInput.mFlags up in
+ // this constructor's init list, so the only flags that we need to explicitly
+ // initialize here are those that may need a value other than our parent's.
+ mFlags.mNextInFlowUntouched = aParentReflowInput.mFlags.mNextInFlowUntouched &&
+ CheckNextInFlowParenthood(aFrame, aParentReflowInput.mFrame);
+ mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false;
+ mFlags.mIsColumnBalancing = false;
+ mFlags.mIsFlexContainerMeasuringHeight = false;
+ mFlags.mDummyParentReflowInput = false;
+ mFlags.mShrinkWrap = !!(aFlags & COMPUTE_SIZE_SHRINK_WRAP);
+ mFlags.mUseAutoBSize = !!(aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE);
+ mFlags.mStaticPosIsCBOrigin = !!(aFlags & STATIC_POS_IS_CB_ORIGIN);
+ mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false;
+ mFlags.mIClampMarginBoxMinSize = !!(aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE);
+ mFlags.mBClampMarginBoxMinSize = !!(aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE);
+
+ mDiscoveredClearance = nullptr;
+ mPercentBSizeObserver = (aParentReflowInput.mPercentBSizeObserver &&
+ aParentReflowInput.mPercentBSizeObserver->NeedsToObserve(*this))
+ ? aParentReflowInput.mPercentBSizeObserver : nullptr;
+
+ if ((aFlags & DUMMY_PARENT_REFLOW_STATE) ||
+ (mParentReflowInput->mFlags.mDummyParentReflowInput &&
+ mFrame->GetType() == nsGkAtoms::tableFrame)) {
+ mFlags.mDummyParentReflowInput = true;
+ }
+
+ if (!(aFlags & CALLER_WILL_INIT)) {
+ Init(aPresContext, aContainingBlockSize);
+ }
+}
+
+inline nscoord
+SizeComputationInput::ComputeISizeValue(nscoord aContainingBlockISize,
+ nscoord aContentEdgeToBoxSizing,
+ nscoord aBoxSizingToMarginEdge,
+ const nsStyleCoord& aCoord) const
+{
+ return mFrame->ComputeISizeValue(mRenderingContext,
+ aContainingBlockISize,
+ aContentEdgeToBoxSizing,
+ aBoxSizingToMarginEdge,
+ aCoord);
+}
+
+nscoord
+SizeComputationInput::ComputeISizeValue(nscoord aContainingBlockISize,
+ StyleBoxSizing aBoxSizing,
+ const nsStyleCoord& aCoord) const
+{
+ WritingMode wm = GetWritingMode();
+ nscoord inside = 0, outside = ComputedLogicalBorderPadding().IStartEnd(wm) +
+ ComputedLogicalMargin().IStartEnd(wm);
+ if (aBoxSizing == StyleBoxSizing::Border) {
+ inside = ComputedLogicalBorderPadding().IStartEnd(wm);
+ }
+ outside -= inside;
+
+ return ComputeISizeValue(aContainingBlockISize, inside,
+ outside, aCoord);
+}
+
+nscoord
+SizeComputationInput::ComputeBSizeValue(nscoord aContainingBlockBSize,
+ StyleBoxSizing aBoxSizing,
+ const nsStyleCoord& aCoord) const
+{
+ WritingMode wm = GetWritingMode();
+ nscoord inside = 0;
+ if (aBoxSizing == StyleBoxSizing::Border) {
+ inside = ComputedLogicalBorderPadding().BStartEnd(wm);
+ }
+ return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize,
+ inside, aCoord);
+}
+
+void
+ReflowInput::SetComputedWidth(nscoord aComputedWidth)
+{
+ NS_ASSERTION(mFrame, "Must have a frame!");
+ // It'd be nice to assert that |frame| is not in reflow, but this fails for
+ // two reasons:
+ //
+ // 1) Viewport frames reset the computed width on a copy of their reflow
+ // state when reflowing fixed-pos kids. In that case we actually don't
+ // want to mess with the resize flags, because comparing the frame's rect
+ // to the munged computed width is pointless.
+ // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow
+ // state is not used to reflow the parent, but just as a parent for the
+ // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
+ // (like a text control, for example), we'll end up creating a reflow
+ // state for the parent while the parent is reflowing.
+
+ NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width");
+ if (ComputedWidth() != aComputedWidth) {
+ ComputedWidth() = aComputedWidth;
+ nsIAtom* frameType = mFrame->GetType();
+ if (frameType != nsGkAtoms::viewportFrame || // Or check GetParent()?
+ mWritingMode.IsVertical()) {
+ InitResizeFlags(mFrame->PresContext(), frameType);
+ }
+ }
+}
+
+void
+ReflowInput::SetComputedHeight(nscoord aComputedHeight)
+{
+ NS_ASSERTION(mFrame, "Must have a frame!");
+ // It'd be nice to assert that |frame| is not in reflow, but this fails
+ // because:
+ //
+ // nsFrame::BoxReflow creates a reflow state for its parent. This reflow
+ // state is not used to reflow the parent, but just as a parent for the
+ // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
+ // (like a text control, for example), we'll end up creating a reflow
+ // state for the parent while the parent is reflowing.
+
+ NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height");
+ if (ComputedHeight() != aComputedHeight) {
+ ComputedHeight() = aComputedHeight;
+ nsIAtom* frameType = mFrame->GetType();
+ if (frameType != nsGkAtoms::viewportFrame || !mWritingMode.IsVertical()) {
+ InitResizeFlags(mFrame->PresContext(), frameType);
+ }
+ }
+}
+
+void
+ReflowInput::Init(nsPresContext* aPresContext,
+ const LogicalSize* aContainingBlockSize,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding)
+{
+ if (AvailableISize() == NS_UNCONSTRAINEDSIZE) {
+ // Look up the parent chain for an orthogonal inline limit,
+ // and reset AvailableISize() if found.
+ for (const ReflowInput *parent = mParentReflowInput;
+ parent != nullptr; parent = parent->mParentReflowInput) {
+ if (parent->GetWritingMode().IsOrthogonalTo(mWritingMode) &&
+ parent->mOrthogonalLimit != NS_UNCONSTRAINEDSIZE) {
+ AvailableISize() = parent->mOrthogonalLimit;
+ break;
+ }
+ }
+ }
+
+ LAYOUT_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained inline-size; this should only "
+ "result from very large sizes, not attempts at "
+ "intrinsic inline-size calculation");
+
+ mStylePosition = mFrame->StylePosition();
+ mStyleDisplay = mFrame->StyleDisplay();
+ mStyleVisibility = mFrame->StyleVisibility();
+ mStyleBorder = mFrame->StyleBorder();
+ mStyleMargin = mFrame->StyleMargin();
+ mStylePadding = mFrame->StylePadding();
+ mStyleText = mFrame->StyleText();
+
+ nsIAtom* type = mFrame->GetType();
+
+ InitFrameType(type);
+ InitCBReflowInput();
+
+ LogicalSize cbSize(mWritingMode, -1, -1);
+ if (aContainingBlockSize) {
+ cbSize = *aContainingBlockSize;
+ }
+
+ InitConstraints(aPresContext, cbSize, aBorder, aPadding, type);
+
+ InitResizeFlags(aPresContext, type);
+
+ nsIFrame *parent = mFrame->GetParent();
+ if (parent &&
+ (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_BSIZE) &&
+ !(parent->GetType() == nsGkAtoms::scrollFrame &&
+ parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) {
+ mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
+ } else if (type == nsGkAtoms::svgForeignObjectFrame) {
+ // An SVG foreignObject frame is inherently constrained block-size.
+ mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
+ } else {
+ const nsStyleCoord& bSizeCoord = mStylePosition->BSize(mWritingMode);
+ const nsStyleCoord& maxBSizeCoord = mStylePosition->MaxBSize(mWritingMode);
+ if ((bSizeCoord.GetUnit() != eStyleUnit_Auto ||
+ maxBSizeCoord.GetUnit() != eStyleUnit_None) &&
+ // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements.
+ (mFrame->GetContent() &&
+ !(mFrame->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::body,
+ nsGkAtoms::html)))) {
+
+ // If our block-size was specified as a percentage, then this could
+ // actually resolve to 'auto', based on:
+ // http://www.w3.org/TR/CSS21/visudet.html#the-height-property
+ nsIFrame* containingBlk = mFrame;
+ while (containingBlk) {
+ const nsStylePosition* stylePos = containingBlk->StylePosition();
+ const nsStyleCoord& bSizeCoord = stylePos->BSize(mWritingMode);
+ const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(mWritingMode);
+ if ((bSizeCoord.IsCoordPercentCalcUnit() &&
+ !bSizeCoord.HasPercent()) ||
+ (maxBSizeCoord.IsCoordPercentCalcUnit() &&
+ !maxBSizeCoord.HasPercent())) {
+ mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
+ break;
+ } else if ((bSizeCoord.IsCoordPercentCalcUnit() &&
+ bSizeCoord.HasPercent()) ||
+ (maxBSizeCoord.IsCoordPercentCalcUnit() &&
+ maxBSizeCoord.HasPercent())) {
+ if (!(containingBlk = containingBlk->GetContainingBlock())) {
+ // If we've reached the top of the tree, then we don't have
+ // a constrained block-size.
+ mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
+ break;
+ }
+
+ continue;
+ } else {
+ mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
+ break;
+ }
+ }
+ } else {
+ mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
+ }
+ }
+
+ if (mParentReflowInput &&
+ mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) {
+ // Orthogonal frames are always reflowed with an unconstrained
+ // dimension to avoid incomplete reflow across an orthogonal
+ // boundary. Normally this is the block-size, but for column sets
+ // with auto-height it's the inline-size, so that they can add
+ // columns in the container's block direction
+ if (type == nsGkAtoms::columnSetFrame &&
+ eStyleUnit_Auto == mStylePosition->ISize(mWritingMode).GetUnit()) {
+ ComputedISize() = NS_UNCONSTRAINEDSIZE;
+ } else {
+ AvailableBSize() = NS_UNCONSTRAINEDSIZE;
+ }
+ }
+
+ LAYOUT_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE &&
+ !mFrame->IsFrameOfType(nsIFrame::eReplaced)) ||
+ type == nsGkAtoms::textFrame ||
+ ComputedISize() != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained inline-size; this should only "
+ "result from very large sizes, not attempts at "
+ "intrinsic inline-size calculation");
+}
+
+void ReflowInput::InitCBReflowInput()
+{
+ if (!mParentReflowInput) {
+ mCBReflowInput = nullptr;
+ return;
+ }
+ if (mParentReflowInput->mFlags.mDummyParentReflowInput) {
+ mCBReflowInput = mParentReflowInput;
+ return;
+ }
+
+ if (mParentReflowInput->mFrame == mFrame->GetContainingBlock()) {
+ // Inner table frames need to use the containing block of the outer
+ // table frame.
+ if (mFrame->GetType() == nsGkAtoms::tableFrame) {
+ mCBReflowInput = mParentReflowInput->mCBReflowInput;
+ } else {
+ mCBReflowInput = mParentReflowInput;
+ }
+ } else {
+ mCBReflowInput = mParentReflowInput->mCBReflowInput;
+ }
+}
+
+/* Check whether CalcQuirkContainingBlockHeight would stop on the
+ * given reflow state, using its block as a height. (essentially
+ * returns false for any case in which CalcQuirkContainingBlockHeight
+ * has a "continue" in its main loop.)
+ *
+ * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
+ * this function as well
+ */
+static bool
+IsQuirkContainingBlockHeight(const ReflowInput* rs, nsIAtom* aFrameType)
+{
+ if (nsGkAtoms::blockFrame == aFrameType ||
+#ifdef MOZ_XUL
+ nsGkAtoms::XULLabelFrame == aFrameType ||
+#endif
+ nsGkAtoms::scrollFrame == aFrameType) {
+ // Note: This next condition could change due to a style change,
+ // but that would cause a style reflow anyway, which means we're ok.
+ if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
+ if (!rs->mFrame->IsAbsolutelyPositioned()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+void
+ReflowInput::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType)
+{
+ const WritingMode wm = mWritingMode; // just a shorthand
+ // We should report that we have a resize in the inline dimension if
+ // *either* the border-box size or the content-box size in that
+ // dimension has changed. It might not actually be necessary to do
+ // this if the border-box size has changed and the content-box size
+ // has not changed, but since we've historically used the flag to mean
+ // border-box size change, continue to do that. (It's possible for
+ // the content-box size to change without a border-box size change or
+ // a style change given (1) a fixed width (possibly fixed by max-width
+ // or min-width), (2) box-sizing:border-box or padding-box, and
+ // (3) percentage padding.)
+ //
+ // However, we don't actually have the information at this point to
+ // tell whether the content-box size has changed, since both style
+ // data and the UsedPaddingProperty() have already been updated. So,
+ // instead, we explicitly check for the case where it's possible for
+ // the content-box size to have changed without either (a) a change in
+ // the border-box size or (b) an nsChangeHint_NeedDirtyReflow change
+ // hint due to change in border or padding. Thus we test using the
+ // conditions from the previous paragraph, except without testing (1)
+ // since it's complicated to test properly and less likely to help
+ // with optimizing cases away.
+ bool isIResize =
+ // is the border-box resizing?
+ mFrame->ISize(wm) !=
+ ComputedISize() + ComputedLogicalBorderPadding().IStartEnd(wm) ||
+ // or is the content-box resizing? (see comment above)
+ (mStylePosition->mBoxSizing != StyleBoxSizing::Content &&
+ mStylePadding->IsWidthDependent());
+
+ if ((mFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) &&
+ nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
+ // Create our font inflation data if we don't have it already, and
+ // give it our current width information.
+ bool dirty = nsFontInflationData::UpdateFontInflationDataISizeFor(*this) &&
+ // Avoid running this at the box-to-block interface
+ // (where we shouldn't be inflating anyway, and where
+ // reflow state construction is probably to construct a
+ // dummy parent reflow state anyway).
+ !mFlags.mDummyParentReflowInput;
+
+ if (dirty || (!mFrame->GetParent() && isIResize)) {
+ // When font size inflation is enabled, a change in either:
+ // * the effective width of a font inflation flow root
+ // * the width of the frame
+ // needs to cause a dirty reflow since they change the font size
+ // inflation calculations, which in turn change the size of text,
+ // line-heights, etc. This is relatively similar to a classic
+ // case of style change reflow, except that because inflation
+ // doesn't affect the intrinsic sizing codepath, there's no need
+ // to invalidate intrinsic sizes.
+ //
+ // Note that this makes horizontal resizing a good bit more
+ // expensive. However, font size inflation is targeted at a set of
+ // devices (zoom-and-pan devices) where the main use case for
+ // horizontal resizing needing to be efficient (window resizing) is
+ // not present. It does still increase the cost of dynamic changes
+ // caused by script where a style or content change in one place
+ // causes a resize in another (e.g., rebalancing a table).
+
+ // FIXME: This isn't so great for the cases where
+ // ReflowInput::SetComputedWidth is called, if the first time
+ // we go through InitResizeFlags we set IsHResize() to true, and then
+ // the second time we'd set it to false even without the
+ // NS_FRAME_IS_DIRTY bit already set.
+ if (mFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
+ // Foreign object frames use dirty bits in a special way.
+ mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ nsIFrame *kid = mFrame->PrincipalChildList().FirstChild();
+ if (kid) {
+ kid->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+ } else {
+ mFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+
+ // Mark intrinsic widths on all descendants dirty. We need to do
+ // this (1) since we're changing the size of text and need to
+ // clear text runs on text frames and (2) since we actually are
+ // changing some intrinsic widths, but only those that live inside
+ // of containers.
+
+ // It makes sense to do this for descendants but not ancestors
+ // (which is unusual) because we're only changing the unusual
+ // inflation-dependent intrinsic widths (i.e., ones computed with
+ // nsPresContext::mInflationDisabledForShrinkWrap set to false),
+ // which should never affect anything outside of their inflation
+ // flow root (or, for that matter, even their inflation
+ // container).
+
+ // This is also different from what PresShell::FrameNeedsReflow
+ // does because it doesn't go through placeholders. It doesn't
+ // need to because we're actually doing something that cares about
+ // frame tree geometry (the width on an ancestor) rather than
+ // style.
+
+ AutoTArray<nsIFrame*, 32> stack;
+ stack.AppendElement(mFrame);
+
+ do {
+ nsIFrame *f = stack.ElementAt(stack.Length() - 1);
+ stack.RemoveElementAt(stack.Length() - 1);
+
+ nsIFrame::ChildListIterator lists(f);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* kid = childFrames.get();
+ kid->MarkIntrinsicISizesDirty();
+ stack.AppendElement(kid);
+ }
+ }
+ } while (stack.Length() != 0);
+ }
+ }
+
+ SetIResize(!(mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
+ isIResize);
+
+ // XXX Should we really need to null check mCBReflowInput? (We do for
+ // at least nsBoxFrame).
+ if (IS_TABLE_CELL(aFrameType) &&
+ (mFlags.mSpecialBSizeReflow ||
+ (mFrame->FirstInFlow()->GetStateBits() &
+ NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) &&
+ (mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
+ // Need to set the bit on the cell so that
+ // mCBReflowInput->IsBResize() is set correctly below when
+ // reflowing descendant.
+ SetBResize(true);
+ } else if (mCBReflowInput && mFrame->IsBlockWrapper()) {
+ // XXX Is this problematic for relatively positioned inlines acting
+ // as containing block for absolutely positioned elements?
+ // Possibly; in that case we should at least be checking
+ // NS_SUBTREE_DIRTY, I'd think.
+ SetBResize(mCBReflowInput->IsBResizeForWM(wm));
+ } else if (mCBReflowInput && !nsLayoutUtils::GetAsBlock(mFrame)) {
+ // Some non-block frames (e.g. table frames) aggressively optimize out their
+ // BSize recomputation when they don't have the BResize flag set. This
+ // means that if they go from having a computed non-auto height to having an
+ // auto height and don't have that flag set, they will not actually compute
+ // their auto height and will just remain at whatever size they already
+ // were. We can end up in that situation if the child has a percentage
+ // specified height and the parent changes from non-auto height to auto
+ // height. When that happens, the parent will typically have the BResize
+ // flag set, and we want to propagate that flag to the kid.
+ //
+ // Ideally it seems like we'd do this for blocks too, of course... but we'd
+ // really want to restrict it to the percentage height case or something, to
+ // avoid extra reflows in common cases. Maybe we should be examining
+ // mStylePosition->BSize(wm).GetUnit() for that purpose?
+ //
+ // Note that we _also_ need to set the BResize flag if we have auto
+ // ComputedBSize() and a dirty subtree, since that might require us to
+ // change BSize due to kids having been added or removed.
+ SetBResize(mCBReflowInput->IsBResizeForWM(wm));
+ if (ComputedBSize() == NS_AUTOHEIGHT) {
+ SetBResize(IsBResize() || NS_SUBTREE_DIRTY(mFrame));
+ }
+ } else if (ComputedBSize() == NS_AUTOHEIGHT) {
+ if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
+ mCBReflowInput) {
+ SetBResize(mCBReflowInput->IsBResizeForWM(wm));
+ } else {
+ SetBResize(IsIResize());
+ }
+ SetBResize(IsBResize() || NS_SUBTREE_DIRTY(mFrame));
+ } else {
+ // not 'auto' block-size
+ SetBResize(mFrame->BSize(wm) !=
+ ComputedBSize() + ComputedLogicalBorderPadding().BStartEnd(wm));
+ }
+
+ bool dependsOnCBBSize =
+ (mStylePosition->BSizeDependsOnContainer(wm) &&
+ // FIXME: condition this on not-abspos?
+ mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto) ||
+ mStylePosition->MinBSizeDependsOnContainer(wm) ||
+ mStylePosition->MaxBSizeDependsOnContainer(wm) ||
+ mStylePosition->OffsetHasPercent(wm.PhysicalSide(eLogicalSideBStart)) ||
+ mStylePosition->mOffset.GetBEndUnit(wm) != eStyleUnit_Auto ||
+ mFrame->IsXULBoxFrame();
+
+ if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) {
+ NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() ==
+ NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
+ "bad line-height value");
+
+ // line-height depends on block bsize
+ mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ // but only on containing blocks if this frame is not a suitable block
+ dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(mFrame);
+ }
+
+ // If we're the descendant of a table cell that performs special bsize
+ // reflows and we could be the child that requires them, always set
+ // the block-axis resize in case this is the first pass before the
+ // special bsize reflow. However, don't do this if it actually is
+ // the special bsize reflow, since in that case it will already be
+ // set correctly above if we need it set.
+ if (!IsBResize() && mCBReflowInput &&
+ (IS_TABLE_CELL(mCBReflowInput->mFrame->GetType()) ||
+ mCBReflowInput->mFlags.mHeightDependsOnAncestorCell) &&
+ !mCBReflowInput->mFlags.mSpecialBSizeReflow &&
+ dependsOnCBBSize) {
+ SetBResize(true);
+ mFlags.mHeightDependsOnAncestorCell = true;
+ }
+
+ // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed.
+
+ // It would be nice to check that |ComputedBSize != NS_AUTOHEIGHT|
+ // &&ed with the percentage bsize check. However, this doesn't get
+ // along with table special bsize reflows, since a special bsize
+ // reflow (a quirk that makes such percentage height work on children
+ // of table cells) can cause not just a single percentage height to
+ // become fixed, but an entire descendant chain of percentage height
+ // to become fixed.
+ if (dependsOnCBBSize && mCBReflowInput) {
+ const ReflowInput *rs = this;
+ bool hitCBReflowInput = false;
+ do {
+ rs = rs->mParentReflowInput;
+ if (!rs) {
+ break;
+ }
+
+ if (rs->mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)
+ break; // no need to go further
+ rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+
+ // Keep track of whether we've hit the containing block, because
+ // we need to go at least that far.
+ if (rs == mCBReflowInput) {
+ hitCBReflowInput = true;
+ }
+
+ // XXX What about orthogonal flows? It doesn't make sense to
+ // keep propagating this bit across an orthogonal boundary,
+ // where the meaning of BSize changes. Bug 1175517.
+ } while (!hitCBReflowInput ||
+ (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
+ !IsQuirkContainingBlockHeight(rs, rs->mFrame->GetType())));
+ // Note: We actually don't need to set the
+ // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases
+ // where we hit the early break statements in
+ // CalcQuirkContainingBlockHeight. But it doesn't hurt
+ // us to set the bit in these cases.
+
+ }
+ if (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
+ // If we're reflowing everything, then we'll find out if we need
+ // to re-set this.
+ mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ }
+}
+
+nscoord
+ReflowInput::GetContainingBlockContentISize(WritingMode aWritingMode) const
+{
+ if (!mCBReflowInput) {
+ return 0;
+ }
+ return mCBReflowInput->GetWritingMode().IsOrthogonalTo(aWritingMode)
+ ? mCBReflowInput->ComputedBSize()
+ : mCBReflowInput->ComputedISize();
+}
+
+void
+ReflowInput::InitFrameType(nsIAtom* aFrameType)
+{
+ const nsStyleDisplay *disp = mStyleDisplay;
+ nsCSSFrameType frameType;
+
+ // Section 9.7 of the CSS2 spec indicates that absolute position
+ // takes precedence over float which takes precedence over display.
+ // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
+ // Make sure the frame was actually moved out of the flow, and don't
+ // just assume what the style says, because we might not have had a
+ // useful float/absolute containing block
+
+ DISPLAY_INIT_TYPE(mFrame, this);
+
+ if (aFrameType == nsGkAtoms::tableFrame) {
+ mFrameType = NS_CSS_FRAME_TYPE_BLOCK;
+ return;
+ }
+
+ NS_ASSERTION(mFrame->StyleDisplay()->IsAbsolutelyPositionedStyle() ==
+ disp->IsAbsolutelyPositionedStyle(),
+ "Unexpected position style");
+ NS_ASSERTION(mFrame->StyleDisplay()->IsFloatingStyle() ==
+ disp->IsFloatingStyle(), "Unexpected float style");
+ if (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+ if (disp->IsAbsolutelyPositioned(mFrame)) {
+ frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
+ //XXXfr hack for making frames behave properly when in overflow container lists
+ // see bug 154892; need to revisit later
+ if (mFrame->GetPrevInFlow())
+ frameType = NS_CSS_FRAME_TYPE_BLOCK;
+ }
+ else if (disp->IsFloating(mFrame)) {
+ frameType = NS_CSS_FRAME_TYPE_FLOATING;
+ } else {
+ NS_ASSERTION(disp->mDisplay == StyleDisplay::Popup,
+ "unknown out of flow frame type");
+ frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
+ }
+ }
+ else {
+ switch (GetDisplay()) {
+ case StyleDisplay::Block:
+ case StyleDisplay::ListItem:
+ case StyleDisplay::Table:
+ case StyleDisplay::TableCaption:
+ case StyleDisplay::Flex:
+ case StyleDisplay::WebkitBox:
+ case StyleDisplay::Grid:
+ case StyleDisplay::RubyTextContainer:
+ frameType = NS_CSS_FRAME_TYPE_BLOCK;
+ break;
+
+ case StyleDisplay::Inline:
+ case StyleDisplay::InlineBlock:
+ case StyleDisplay::InlineTable:
+ case StyleDisplay::InlineBox:
+ case StyleDisplay::InlineXulGrid:
+ case StyleDisplay::InlineStack:
+ case StyleDisplay::InlineFlex:
+ case StyleDisplay::WebkitInlineBox:
+ case StyleDisplay::InlineGrid:
+ case StyleDisplay::Ruby:
+ case StyleDisplay::RubyBase:
+ case StyleDisplay::RubyText:
+ case StyleDisplay::RubyBaseContainer:
+ frameType = NS_CSS_FRAME_TYPE_INLINE;
+ break;
+
+ case StyleDisplay::TableCell:
+ case StyleDisplay::TableRowGroup:
+ case StyleDisplay::TableColumn:
+ case StyleDisplay::TableColumnGroup:
+ case StyleDisplay::TableHeaderGroup:
+ case StyleDisplay::TableFooterGroup:
+ case StyleDisplay::TableRow:
+ frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
+ break;
+
+ case StyleDisplay::None:
+ default:
+ frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ // See if the frame is replaced
+ if (mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
+ frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType);
+ } else if (mFrame->IsFrameOfType(nsIFrame::eReplaced)) {
+ frameType = NS_FRAME_REPLACED(frameType);
+ }
+
+ mFrameType = frameType;
+}
+
+/* static */ void
+ReflowInput::ComputeRelativeOffsets(WritingMode aWM,
+ nsIFrame* aFrame,
+ const LogicalSize& aCBSize,
+ nsMargin& aComputedOffsets)
+{
+ LogicalMargin offsets(aWM);
+ mozilla::css::Side inlineStart = aWM.PhysicalSide(eLogicalSideIStart);
+ mozilla::css::Side inlineEnd = aWM.PhysicalSide(eLogicalSideIEnd);
+ mozilla::css::Side blockStart = aWM.PhysicalSide(eLogicalSideBStart);
+ mozilla::css::Side blockEnd = aWM.PhysicalSide(eLogicalSideBEnd);
+
+ const nsStylePosition* position = aFrame->StylePosition();
+
+ // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart'
+ // moves the boxes to the end of the line, and 'inlineEnd' moves the
+ // boxes to the start of the line. The computed values are always:
+ // inlineStart=-inlineEnd
+ bool inlineStartIsAuto =
+ eStyleUnit_Auto == position->mOffset.GetUnit(inlineStart);
+ bool inlineEndIsAuto =
+ eStyleUnit_Auto == position->mOffset.GetUnit(inlineEnd);
+
+ // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're
+ // over-constrained and we ignore one of them
+ if (!inlineStartIsAuto && !inlineEndIsAuto) {
+ inlineEndIsAuto = true;
+ }
+
+ if (inlineStartIsAuto) {
+ if (inlineEndIsAuto) {
+ // If both are 'auto' (their initial values), the computed values are 0
+ offsets.IStart(aWM) = offsets.IEnd(aWM) = 0;
+ } else {
+ // 'inlineEnd' isn't 'auto' so compute its value
+ offsets.IEnd(aWM) = nsLayoutUtils::
+ ComputeCBDependentValue(aCBSize.ISize(aWM),
+ position->mOffset.Get(inlineEnd));
+
+ // Computed value for 'inlineStart' is minus the value of 'inlineEnd'
+ offsets.IStart(aWM) = -offsets.IEnd(aWM);
+ }
+
+ } else {
+ NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint");
+
+ // 'InlineStart' isn't 'auto' so compute its value
+ offsets.IStart(aWM) = nsLayoutUtils::
+ ComputeCBDependentValue(aCBSize.ISize(aWM),
+ position->mOffset.Get(inlineStart));
+
+ // Computed value for 'inlineEnd' is minus the value of 'inlineStart'
+ offsets.IEnd(aWM) = -offsets.IStart(aWM);
+ }
+
+ // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart'
+ // and 'blockEnd' properties move relatively positioned elements in
+ // the block progression direction. They also must be each other's
+ // negative
+ bool blockStartIsAuto =
+ eStyleUnit_Auto == position->mOffset.GetUnit(blockStart);
+ bool blockEndIsAuto =
+ eStyleUnit_Auto == position->mOffset.GetUnit(blockEnd);
+
+ // Check for percentage based values and a containing block block-size
+ // that depends on the content block-size. Treat them like 'auto'
+ if (NS_AUTOHEIGHT == aCBSize.BSize(aWM)) {
+ if (position->OffsetHasPercent(blockStart)) {
+ blockStartIsAuto = true;
+ }
+ if (position->OffsetHasPercent(blockEnd)) {
+ blockEndIsAuto = true;
+ }
+ }
+
+ // If neither is 'auto', 'block-end' is ignored
+ if (!blockStartIsAuto && !blockEndIsAuto) {
+ blockEndIsAuto = true;
+ }
+
+ if (blockStartIsAuto) {
+ if (blockEndIsAuto) {
+ // If both are 'auto' (their initial values), the computed values are 0
+ offsets.BStart(aWM) = offsets.BEnd(aWM) = 0;
+ } else {
+ // 'blockEnd' isn't 'auto' so compute its value
+ offsets.BEnd(aWM) = nsLayoutUtils::
+ ComputeBSizeDependentValue(aCBSize.BSize(aWM),
+ position->mOffset.Get(blockEnd));
+
+ // Computed value for 'blockStart' is minus the value of 'blockEnd'
+ offsets.BStart(aWM) = -offsets.BEnd(aWM);
+ }
+
+ } else {
+ NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint");
+
+ // 'blockStart' isn't 'auto' so compute its value
+ offsets.BStart(aWM) = nsLayoutUtils::
+ ComputeBSizeDependentValue(aCBSize.BSize(aWM),
+ position->mOffset.Get(blockStart));
+
+ // Computed value for 'blockEnd' is minus the value of 'blockStart'
+ offsets.BEnd(aWM) = -offsets.BStart(aWM);
+ }
+
+ // Convert the offsets to physical coordinates and store them on the frame
+ aComputedOffsets = offsets.GetPhysicalMargin(aWM);
+ FrameProperties props = aFrame->Properties();
+ nsMargin* physicalOffsets = props.Get(nsIFrame::ComputedOffsetProperty());
+ if (physicalOffsets) {
+ *physicalOffsets = aComputedOffsets;
+ } else {
+ props.Set(nsIFrame::ComputedOffsetProperty(),
+ new nsMargin(aComputedOffsets));
+ }
+}
+
+/* static */ void
+ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame,
+ const nsMargin& aComputedOffsets,
+ nsPoint* aPosition)
+{
+ if (!aFrame->IsRelativelyPositioned()) {
+ NS_ASSERTION(!aFrame->Properties().Get(nsIFrame::NormalPositionProperty()),
+ "We assume that changing the 'position' property causes "
+ "frame reconstruction. If that ever changes, this code "
+ "should call "
+ "props.Delete(nsIFrame::NormalPositionProperty())");
+ return;
+ }
+
+ // Store the normal position
+ FrameProperties props = aFrame->Properties();
+ nsPoint* normalPosition = props.Get(nsIFrame::NormalPositionProperty());
+ if (normalPosition) {
+ *normalPosition = *aPosition;
+ } else {
+ props.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition));
+ }
+
+ const nsStyleDisplay* display = aFrame->StyleDisplay();
+ if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
+ *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top);
+ } else if (NS_STYLE_POSITION_STICKY == display->mPosition &&
+ !aFrame->GetNextContinuation() &&
+ !aFrame->GetPrevContinuation() &&
+ !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
+ // Sticky positioning for elements with multiple frames needs to be
+ // computed all at once. We can't safely do that here because we might be
+ // partway through (re)positioning the frames, so leave it until the scroll
+ // container reflows and calls StickyScrollContainer::UpdatePositions.
+ // For single-frame sticky positioned elements, though, go ahead and apply
+ // it now to avoid unnecessary overflow updates later.
+ StickyScrollContainer* ssc =
+ StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
+ if (ssc) {
+ *aPosition = ssc->ComputePosition(aFrame);
+ }
+ }
+}
+
+nsIFrame*
+ReflowInput::GetHypotheticalBoxContainer(nsIFrame* aFrame,
+ nscoord& aCBIStartEdge,
+ LogicalSize& aCBSize) const
+{
+ aFrame = aFrame->GetContainingBlock();
+ NS_ASSERTION(aFrame != mFrame, "How did that happen?");
+
+ /* Now aFrame is the containing block we want */
+
+ /* Check whether the containing block is currently being reflowed.
+ If so, use the info from the reflow state. */
+ const ReflowInput* state;
+ if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
+ for (state = mParentReflowInput; state && state->mFrame != aFrame;
+ state = state->mParentReflowInput) {
+ /* do nothing */
+ }
+ } else {
+ state = nullptr;
+ }
+
+ if (state) {
+ WritingMode wm = state->GetWritingMode();
+ NS_ASSERTION(wm == aFrame->GetWritingMode(), "unexpected writing mode");
+ aCBIStartEdge = state->ComputedLogicalBorderPadding().IStart(wm);
+ aCBSize = state->ComputedSize(wm);
+ } else {
+ /* Didn't find a reflow state for aFrame. Just compute the information we
+ want, on the assumption that aFrame already knows its size. This really
+ ought to be true by now. */
+ NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
+ "aFrame shouldn't be in reflow; we'll lie if it is");
+ WritingMode wm = aFrame->GetWritingMode();
+ LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm);
+ aCBIStartEdge = borderPadding.IStart(wm);
+ aCBSize = aFrame->GetLogicalSize(wm) - borderPadding.Size(wm);
+ }
+
+ return aFrame;
+}
+
+struct nsHypotheticalPosition {
+ // offset from inline-start edge of containing block (which is a padding edge)
+ nscoord mIStart;
+ // offset from block-start edge of containing block (which is a padding edge)
+ nscoord mBStart;
+ WritingMode mWritingMode;
+};
+
+static bool
+GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameType)
+{
+ // See if it is an image frame
+ bool success = false;
+
+ // Currently the only type of replaced frame that we can get the intrinsic
+ // size for is an image frame
+ // XXX We should add back the GetReflowOutput() function and one of the
+ // things should be the intrinsic size...
+ if (aFrameType == nsGkAtoms::imageFrame) {
+ nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
+
+ if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) {
+ success = (aIntrinsicSize != nsSize(0, 0));
+ }
+ }
+ return success;
+}
+
+/**
+ * aInsideBoxSizing returns the part of the padding, border, and margin
+ * in the aAxis dimension that goes inside the edge given by box-sizing;
+ * aOutsideBoxSizing returns the rest.
+ */
+void
+ReflowInput::CalculateBorderPaddingMargin(
+ LogicalAxis aAxis,
+ nscoord aContainingBlockSize,
+ nscoord* aInsideBoxSizing,
+ nscoord* aOutsideBoxSizing) const
+{
+ WritingMode wm = GetWritingMode();
+ mozilla::css::Side startSide =
+ wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart));
+ mozilla::css::Side endSide =
+ wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd));
+
+ nsMargin styleBorder = mStyleBorder->GetComputedBorder();
+ nscoord borderStartEnd =
+ styleBorder.Side(startSide) + styleBorder.Side(endSide);
+
+ nscoord paddingStartEnd, marginStartEnd;
+
+ // See if the style system can provide us the padding directly
+ nsMargin stylePadding;
+ if (mStylePadding->GetPadding(stylePadding)) {
+ paddingStartEnd =
+ stylePadding.Side(startSide) + stylePadding.Side(endSide);
+ } else {
+ // We have to compute the start and end values
+ nscoord start, end;
+ start = nsLayoutUtils::
+ ComputeCBDependentValue(aContainingBlockSize,
+ mStylePadding->mPadding.Get(startSide));
+ end = nsLayoutUtils::
+ ComputeCBDependentValue(aContainingBlockSize,
+ mStylePadding->mPadding.Get(endSide));
+ paddingStartEnd = start + end;
+ }
+
+ // See if the style system can provide us the margin directly
+ nsMargin styleMargin;
+ if (mStyleMargin->GetMargin(styleMargin)) {
+ marginStartEnd =
+ styleMargin.Side(startSide) + styleMargin.Side(endSide);
+ } else {
+ nscoord start, end;
+ // We have to compute the start and end values
+ if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(startSide)) {
+ // XXX FIXME (or does CalculateBlockSideMargins do this?)
+ start = 0; // just ignore
+ } else {
+ start = nsLayoutUtils::
+ ComputeCBDependentValue(aContainingBlockSize,
+ mStyleMargin->mMargin.Get(startSide));
+ }
+ if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(endSide)) {
+ // XXX FIXME (or does CalculateBlockSideMargins do this?)
+ end = 0; // just ignore
+ } else {
+ end = nsLayoutUtils::
+ ComputeCBDependentValue(aContainingBlockSize,
+ mStyleMargin->mMargin.Get(endSide));
+ }
+ marginStartEnd = start + end;
+ }
+
+ nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd;
+ nscoord inside = 0;
+ if (mStylePosition->mBoxSizing == StyleBoxSizing::Border) {
+ inside = borderStartEnd + paddingStartEnd;
+ }
+ outside -= inside;
+ *aInsideBoxSizing = inside;
+ *aOutsideBoxSizing = outside;
+ return;
+}
+
+/**
+ * Returns true iff a pre-order traversal of the normal child
+ * frames rooted at aFrame finds no non-empty frame before aDescendant.
+ */
+static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
+ nsIFrame* aDescendant, bool* aFound) {
+ if (aFrame == aDescendant) {
+ *aFound = true;
+ return true;
+ }
+ if (!aFrame->IsSelfEmpty()) {
+ *aFound = false;
+ return false;
+ }
+ for (nsIFrame* f : aFrame->PrincipalChildList()) {
+ bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
+ if (*aFound || !allEmpty) {
+ return allEmpty;
+ }
+ }
+ *aFound = false;
+ return true;
+}
+
+// Calculate the position of the hypothetical box that the element would have
+// if it were in the flow.
+// The values returned are relative to the padding edge of the absolute
+// containing block. The writing-mode of the hypothetical box position will
+// have the same block direction as the absolute containing block, but may
+// differ in inline-bidi direction.
+// In the code below, |cbrs->frame| is the absolute containing block, while
+// |containingBlock| is the nearest block container of the placeholder frame,
+// which may be different from the absolute containing block.
+void
+ReflowInput::CalculateHypotheticalPosition
+ (nsPresContext* aPresContext,
+ nsIFrame* aPlaceholderFrame,
+ const ReflowInput* cbrs,
+ nsHypotheticalPosition& aHypotheticalPos,
+ nsIAtom* aFrameType) const
+{
+ NS_ASSERTION(mStyleDisplay->mOriginalDisplay != StyleDisplay::None,
+ "mOriginalDisplay has not been properly initialized");
+
+ // Find the nearest containing block frame to the placeholder frame,
+ // and its inline-start edge and width.
+ nscoord blockIStartContentEdge;
+ // Dummy writing mode for blockContentSize, will be changed as needed by
+ // GetHypotheticalBoxContainer.
+ WritingMode cbwm = cbrs->GetWritingMode();
+ LogicalSize blockContentSize(cbwm);
+ nsIFrame* containingBlock =
+ GetHypotheticalBoxContainer(aPlaceholderFrame, blockIStartContentEdge,
+ blockContentSize);
+ // Now blockContentSize is in containingBlock's writing mode.
+
+ // If it's a replaced element and it has a 'auto' value for
+ //'inline size', see if we can get the intrinsic size. This will allow
+ // us to exactly determine both the inline edges
+ WritingMode wm = containingBlock->GetWritingMode();
+
+ nsStyleCoord styleISize = mStylePosition->ISize(wm);
+ bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto;
+ nsSize intrinsicSize;
+ bool knowIntrinsicSize = false;
+ if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
+ // See if we can get the intrinsic size of the element
+ knowIntrinsicSize = GetIntrinsicSizeFor(mFrame, intrinsicSize, aFrameType);
+ }
+
+ // See if we can calculate what the box inline size would have been if
+ // the element had been in the flow
+ nscoord boxISize;
+ bool knowBoxISize = false;
+ if ((StyleDisplay::Inline == mStyleDisplay->mOriginalDisplay) &&
+ !NS_FRAME_IS_REPLACED(mFrameType)) {
+ // For non-replaced inline-level elements the 'inline size' property
+ // doesn't apply, so we don't know what the inline size would have
+ // been without reflowing it
+
+ } else {
+ // It's either a replaced inline-level element or a block-level element
+
+ // Determine the total amount of inline direction
+ // border/padding/margin that the element would have had if it had
+ // been in the flow. Note that we ignore any 'auto' and 'inherit'
+ // values
+ nscoord insideBoxSizing, outsideBoxSizing;
+ CalculateBorderPaddingMargin(eLogicalAxisInline,
+ blockContentSize.ISize(wm),
+ &insideBoxSizing, &outsideBoxSizing);
+
+ if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
+ // It's a replaced element with an 'auto' inline size so the box
+ // inline size is its intrinsic size plus any border/padding/margin
+ if (knowIntrinsicSize) {
+ boxISize = LogicalSize(wm, intrinsicSize).ISize(wm) +
+ outsideBoxSizing + insideBoxSizing;
+ knowBoxISize = true;
+ }
+
+ } else if (isAutoISize) {
+ // The box inline size is the containing block inline size
+ boxISize = blockContentSize.ISize(wm);
+ knowBoxISize = true;
+
+ } else {
+ // We need to compute it. It's important we do this, because if it's
+ // percentage based this computed value may be different from the computed
+ // value calculated using the absolute containing block width
+ boxISize = ComputeISizeValue(blockContentSize.ISize(wm),
+ insideBoxSizing, outsideBoxSizing,
+ styleISize) +
+ insideBoxSizing + outsideBoxSizing;
+ knowBoxISize = true;
+ }
+ }
+
+ // Get the placeholder x-offset and y-offset in the coordinate
+ // space of its containing block
+ // XXXbz the placeholder is not fully reflowed yet if our containing block is
+ // relatively positioned...
+ nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW
+ ? cbrs->ComputedSizeAsContainerIfConstrained()
+ : containingBlock->GetSize();
+ LogicalPoint
+ placeholderOffset(wm, aPlaceholderFrame->GetOffsetTo(containingBlock),
+ containerSize);
+
+ // First, determine the hypothetical box's mBStart. We want to check the
+ // content insertion frame of containingBlock for block-ness, but make
+ // sure to compute all coordinates in the coordinate system of
+ // containingBlock.
+ nsBlockFrame* blockFrame =
+ nsLayoutUtils::GetAsBlock(containingBlock->GetContentInsertionFrame());
+ if (blockFrame) {
+ // Use a null containerSize to convert a LogicalPoint functioning as a
+ // vector into a physical nsPoint vector.
+ const nsSize nullContainerSize;
+ LogicalPoint blockOffset(wm, blockFrame->GetOffsetTo(containingBlock),
+ nullContainerSize);
+ bool isValid;
+ nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
+ if (!isValid) {
+ // Give up. We're probably dealing with somebody using
+ // position:absolute inside native-anonymous content anyway.
+ aHypotheticalPos.mBStart = placeholderOffset.B(wm);
+ } else {
+ NS_ASSERTION(iter.GetContainer() == blockFrame,
+ "Found placeholder in wrong block!");
+ nsBlockFrame::LineIterator lineBox = iter.GetLine();
+
+ // How we determine the hypothetical box depends on whether the element
+ // would have been inline-level or block-level
+ LogicalRect lineBounds =
+ lineBox->GetBounds().ConvertTo(wm, lineBox->mWritingMode,
+ lineBox->mContainerSize);
+ if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
+ // Use the block-start of the inline box which the placeholder lives in
+ // as the hypothetical box's block-start.
+ aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm);
+ } else {
+ // The element would have been block-level which means it would
+ // be below the line containing the placeholder frame, unless
+ // all the frames before it are empty. In that case, it would
+ // have been just before this line.
+ // XXXbz the line box is not fully reflowed yet if our
+ // containing block is relatively positioned...
+ if (lineBox != iter.End()) {
+ nsIFrame * firstFrame = lineBox->mFirstChild;
+ bool found = false;
+ bool allEmpty = true;
+ while (firstFrame) { // See bug 223064
+ allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
+ aPlaceholderFrame, &found);
+ if (found || !allEmpty)
+ break;
+ firstFrame = firstFrame->GetNextSibling();
+ }
+ NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
+
+ if (allEmpty) {
+ // The top of the hypothetical box is the top of the line
+ // containing the placeholder, since there is nothing in the
+ // line before our placeholder except empty frames.
+ aHypotheticalPos.mBStart =
+ lineBounds.BStart(wm) + blockOffset.B(wm);
+ } else {
+ // The top of the hypothetical box is just below the line
+ // containing the placeholder.
+ aHypotheticalPos.mBStart =
+ lineBounds.BEnd(wm) + blockOffset.B(wm);
+ }
+ } else {
+ // Just use the placeholder's block-offset wrt the containing block
+ aHypotheticalPos.mBStart = placeholderOffset.B(wm);
+ }
+ }
+ }
+ } else {
+ // The containing block is not a block, so it's probably something
+ // like a XUL box, etc.
+ // Just use the placeholder's block-offset
+ aHypotheticalPos.mBStart = placeholderOffset.B(wm);
+ }
+
+ // Second, determine the hypothetical box's mIStart.
+ // How we determine the hypothetical box depends on whether the element
+ // would have been inline-level or block-level
+ if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle() ||
+ mFlags.mIOffsetsNeedCSSAlign) {
+ // The placeholder represents the IStart edge of the hypothetical box.
+ // (Or if mFlags.mIOffsetsNeedCSSAlign is set, it represents the IStart
+ // edge of the Alignment Container.)
+ aHypotheticalPos.mIStart = placeholderOffset.I(wm);
+ } else {
+ aHypotheticalPos.mIStart = blockIStartContentEdge;
+ }
+
+ // The current coordinate space is that of the nearest block to the placeholder.
+ // Convert to the coordinate space of the absolute containing block
+ // One weird thing here is that for fixed-positioned elements we want to do
+ // the conversion incorrectly; specifically we want to ignore any scrolling
+ // that may have happened;
+ nsPoint cbOffset;
+ if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
+ // Exclude cases inside -moz-transform where fixed is like absolute.
+ nsLayoutUtils::IsReallyFixedPos(mFrame)) {
+ // In this case, cbrs->frame will likely be an ancestor of
+ // containingBlock, so can just walk our way up the frame tree.
+ // Make sure to not add positions of frames whose parent is a
+ // scrollFrame, since we're doing fixed positioning, which assumes
+ // everything is scrolled to (0,0).
+ cbOffset.MoveTo(0, 0);
+ do {
+ cbOffset += containingBlock->GetPositionIgnoringScrolling();
+ nsContainerFrame* parent = containingBlock->GetParent();
+ if (!parent) {
+ // Oops, our absolute containing block isn't an ancestor of the
+ // placeholder's containing block. This can happen if the placeholder
+ // is pushed to a different page in a printing context. 'cbOffset' is
+ // currently relative to the root frame (containingBlock) - so just
+ // subtract the offset to the absolute containing block to make it
+ // relative to that.
+ cbOffset -= containingBlock->GetOffsetTo(cbrs->mFrame);
+ break;
+ }
+ containingBlock = parent;
+ } while (containingBlock != cbrs->mFrame);
+ } else {
+ // XXXldb We need to either ignore scrolling for the absolute
+ // positioning case too (and take the incompatibility) or figure out
+ // how to make these positioned elements actually *move* when we
+ // scroll, and thus avoid the resulting incremental reflow bugs.
+ cbOffset = containingBlock->GetOffsetTo(cbrs->mFrame);
+ }
+ nsSize cbrsSize = cbrs->ComputedSizeAsContainerIfConstrained();
+ LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize);
+ aHypotheticalPos.mIStart += logCBOffs.I(wm);
+ aHypotheticalPos.mBStart += logCBOffs.B(wm);
+
+ // The specified offsets are relative to the absolute containing block's
+ // padding edge and our current values are relative to the border edge, so
+ // translate.
+ LogicalMargin border =
+ cbrs->ComputedLogicalBorderPadding() - cbrs->ComputedLogicalPadding();
+ border = border.ConvertTo(wm, cbrs->GetWritingMode());
+ aHypotheticalPos.mIStart -= border.IStart(wm);
+ aHypotheticalPos.mBStart -= border.BStart(wm);
+
+ // At this point, we have computed aHypotheticalPos using the writing mode
+ // of the placeholder's containing block.
+
+ if (cbwm.GetBlockDir() != wm.GetBlockDir()) {
+ // If the block direction we used in calculating aHypotheticalPos does not
+ // match the absolute containing block's, we need to convert here so that
+ // aHypotheticalPos is usable in relation to the absolute containing block.
+ // This requires computing or measuring the abspos frame's block-size,
+ // which is not otherwise required/used here (as aHypotheticalPos
+ // records only the block-start coordinate).
+
+ // This is similar to the inline-size calculation for a replaced
+ // inline-level element or a block-level element (above), except that
+ // 'auto' sizing is handled differently in the block direction for non-
+ // replaced elements and replaced elements lacking an intrinsic size.
+
+ // Determine the total amount of block direction
+ // border/padding/margin that the element would have had if it had
+ // been in the flow. Note that we ignore any 'auto' and 'inherit'
+ // values.
+ nscoord insideBoxSizing, outsideBoxSizing;
+ CalculateBorderPaddingMargin(eLogicalAxisBlock,
+ blockContentSize.BSize(wm),
+ &insideBoxSizing, &outsideBoxSizing);
+
+ nscoord boxBSize;
+ nsStyleCoord styleBSize = mStylePosition->BSize(wm);
+ bool isAutoBSize = styleBSize.GetUnit() == eStyleUnit_Auto;
+ if (isAutoBSize) {
+ if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) {
+ // It's a replaced element with an 'auto' block size so the box
+ // block size is its intrinsic size plus any border/padding/margin
+ boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) +
+ outsideBoxSizing + insideBoxSizing;
+ } else {
+ // XXX Bug 1191801
+ // Figure out how to get the correct boxBSize here (need to reflow the
+ // positioned frame?)
+ boxBSize = 0;
+ }
+ } else {
+ // We need to compute it. It's important we do this, because if it's
+ // percentage-based this computed value may be different from the
+ // computed value calculated using the absolute containing block height.
+ boxBSize = nsLayoutUtils::ComputeBSizeValue(blockContentSize.BSize(wm),
+ insideBoxSizing, styleBSize) +
+ insideBoxSizing + outsideBoxSizing;
+ }
+
+ LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize);
+
+ LogicalPoint origin(wm, aHypotheticalPos.mIStart,
+ aHypotheticalPos.mBStart);
+ origin = origin.ConvertTo(cbwm, wm, cbrsSize -
+ boxSize.GetPhysicalSize(wm));
+
+ aHypotheticalPos.mIStart = origin.I(cbwm);
+ aHypotheticalPos.mBStart = origin.B(cbwm);
+ aHypotheticalPos.mWritingMode = cbwm;
+ } else {
+ aHypotheticalPos.mWritingMode = wm;
+ }
+}
+
+void
+ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext,
+ const ReflowInput* cbrs,
+ const LogicalSize& aCBSize,
+ nsIAtom* aFrameType)
+{
+ WritingMode wm = GetWritingMode();
+ WritingMode cbwm = cbrs->GetWritingMode();
+ NS_PRECONDITION(aCBSize.BSize(cbwm) != NS_AUTOHEIGHT,
+ "containing block bsize must be constrained");
+
+ NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame,
+ "InitAbsoluteConstraints should not be called on table frames");
+ NS_ASSERTION(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "Why are we here?");
+
+ const auto& styleOffset = mStylePosition->mOffset;
+ bool iStartIsAuto = styleOffset.GetIStartUnit(cbwm) == eStyleUnit_Auto;
+ bool iEndIsAuto = styleOffset.GetIEndUnit(cbwm) == eStyleUnit_Auto;
+ bool bStartIsAuto = styleOffset.GetBStartUnit(cbwm) == eStyleUnit_Auto;
+ bool bEndIsAuto = styleOffset.GetBEndUnit(cbwm) == eStyleUnit_Auto;
+
+ // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
+ // 'auto', then compute the hypothetical box position where the element would
+ // have been if it had been in the flow
+ nsHypotheticalPosition hypotheticalPos;
+ if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) {
+ nsIFrame* placeholderFrame =
+ aPresContext->PresShell()->GetPlaceholderFrameFor(mFrame);
+ NS_ASSERTION(placeholderFrame, "no placeholder frame");
+
+ if (placeholderFrame->HasAnyStateBits(
+ PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
+ DebugOnly<nsIFrame*> placeholderParent = placeholderFrame->GetParent();
+ MOZ_ASSERT(placeholderParent, "shouldn't have unparented placeholders");
+ MOZ_ASSERT(placeholderParent->IsFlexOrGridContainer(),
+ "This flag should only be set on grid/flex children");
+
+ // If the (as-yet unknown) static position will determine the inline
+ // and/or block offsets, set flags to note those offsets aren't valid
+ // until we can do CSS Box Alignment on the OOF frame.
+ mFlags.mIOffsetsNeedCSSAlign = (iStartIsAuto && iEndIsAuto);
+ mFlags.mBOffsetsNeedCSSAlign = (bStartIsAuto && bEndIsAuto);
+ }
+
+ if (mFlags.mStaticPosIsCBOrigin) {
+ hypotheticalPos.mWritingMode = cbwm;
+ hypotheticalPos.mIStart = nscoord(0);
+ hypotheticalPos.mBStart = nscoord(0);
+ } else {
+ CalculateHypotheticalPosition(aPresContext, placeholderFrame, cbrs,
+ hypotheticalPos, aFrameType);
+ }
+ }
+
+ // Initialize the 'left' and 'right' computed offsets
+ // XXX Handle new 'static-position' value...
+
+ // Size of the containing block in its writing mode
+ LogicalSize cbSize = aCBSize;
+ LogicalMargin offsets = ComputedLogicalOffsets().ConvertTo(cbwm, wm);
+
+ if (iStartIsAuto) {
+ offsets.IStart(cbwm) = 0;
+ } else {
+ offsets.IStart(cbwm) = nsLayoutUtils::
+ ComputeCBDependentValue(cbSize.ISize(cbwm), styleOffset.GetIStart(cbwm));
+ }
+ if (iEndIsAuto) {
+ offsets.IEnd(cbwm) = 0;
+ } else {
+ offsets.IEnd(cbwm) = nsLayoutUtils::
+ ComputeCBDependentValue(cbSize.ISize(cbwm), styleOffset.GetIEnd(cbwm));
+ }
+
+ if (iStartIsAuto && iEndIsAuto) {
+ if (cbwm.IsBidiLTR() != hypotheticalPos.mWritingMode.IsBidiLTR()) {
+ offsets.IEnd(cbwm) = hypotheticalPos.mIStart;
+ iEndIsAuto = false;
+ } else {
+ offsets.IStart(cbwm) = hypotheticalPos.mIStart;
+ iStartIsAuto = false;
+ }
+ }
+
+ if (bStartIsAuto) {
+ offsets.BStart(cbwm) = 0;
+ } else {
+ offsets.BStart(cbwm) = nsLayoutUtils::
+ ComputeBSizeDependentValue(cbSize.BSize(cbwm),
+ styleOffset.GetBStart(cbwm));
+ }
+ if (bEndIsAuto) {
+ offsets.BEnd(cbwm) = 0;
+ } else {
+ offsets.BEnd(cbwm) = nsLayoutUtils::
+ ComputeBSizeDependentValue(cbSize.BSize(cbwm),
+ styleOffset.GetBEnd(cbwm));
+ }
+
+ if (bStartIsAuto && bEndIsAuto) {
+ // Treat 'top' like 'static-position'
+ offsets.BStart(cbwm) = hypotheticalPos.mBStart;
+ bStartIsAuto = false;
+ }
+
+ SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm));
+
+ typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags;
+ ComputeSizeFlags computeSizeFlags = ComputeSizeFlags::eDefault;
+ if (mFlags.mIClampMarginBoxMinSize) {
+ computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
+ ComputeSizeFlags::eIClampMarginBoxMinSize);
+ }
+ if (mFlags.mBClampMarginBoxMinSize) {
+ computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
+ ComputeSizeFlags::eBClampMarginBoxMinSize);
+ }
+ if (mFlags.mShrinkWrap) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+ }
+ if (mFlags.mUseAutoBSize) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize);
+ }
+ if (wm.IsOrthogonalTo(cbwm)) {
+ if (bStartIsAuto || bEndIsAuto) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+ }
+ } else {
+ if (iStartIsAuto || iEndIsAuto) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+ }
+ }
+
+ LogicalSize computedSize(wm);
+ {
+ AutoMaybeDisableFontInflation an(mFrame);
+
+ computedSize =
+ mFrame->ComputeSize(mRenderingContext, wm, cbSize.ConvertTo(wm, cbwm),
+ cbSize.ConvertTo(wm, cbwm).ISize(wm), // XXX or AvailableISize()?
+ ComputedLogicalMargin().Size(wm) +
+ ComputedLogicalOffsets().Size(wm),
+ ComputedLogicalBorderPadding().Size(wm) -
+ ComputedLogicalPadding().Size(wm),
+ ComputedLogicalPadding().Size(wm),
+ computeSizeFlags);
+ ComputedISize() = computedSize.ISize(wm);
+ ComputedBSize() = computedSize.BSize(wm);
+ NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
+ NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
+ ComputedBSize() >= 0, "Bogus block-size");
+ }
+ computedSize = computedSize.ConvertTo(cbwm, wm);
+
+ // XXX Now that we have ComputeSize, can we condense many of the
+ // branches off of widthIsAuto?
+
+ LogicalMargin margin = ComputedLogicalMargin().ConvertTo(cbwm, wm);
+ const LogicalMargin borderPadding =
+ ComputedLogicalBorderPadding().ConvertTo(cbwm, wm);
+
+ bool iSizeIsAuto = eStyleUnit_Auto == mStylePosition->ISize(cbwm).GetUnit();
+ if (iStartIsAuto) {
+ // We know 'right' is not 'auto' anymore thanks to the hypothetical
+ // box code above.
+ // Solve for 'left'.
+ if (iSizeIsAuto) {
+ // XXXldb This, and the corresponding code in
+ // nsAbsoluteContainingBlock.cpp, could probably go away now that
+ // we always compute widths.
+ offsets.IStart(cbwm) = NS_AUTOOFFSET;
+ } else {
+ offsets.IStart(cbwm) =
+ cbSize.ISize(cbwm) - offsets.IEnd(cbwm) -
+ computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
+ borderPadding.IStartEnd(cbwm);
+ }
+ } else if (iEndIsAuto) {
+ // We know 'left' is not 'auto' anymore thanks to the hypothetical
+ // box code above.
+ // Solve for 'right'.
+ if (iSizeIsAuto) {
+ // XXXldb This, and the corresponding code in
+ // nsAbsoluteContainingBlock.cpp, could probably go away now that
+ // we always compute widths.
+ offsets.IEnd(cbwm) = NS_AUTOOFFSET;
+ } else {
+ offsets.IEnd(cbwm) =
+ cbSize.ISize(cbwm) - offsets.IStart(cbwm) -
+ computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
+ borderPadding.IStartEnd(cbwm);
+ }
+ } else {
+ // Neither 'inline-start' nor 'inline-end' is 'auto'.
+
+ if (wm.IsOrthogonalTo(cbwm)) {
+ // For orthogonal blocks, we need to handle the case where the block had
+ // unconstrained block-size, which mapped to unconstrained inline-size
+ // in the containing block's writing mode.
+ nscoord autoISize = cbSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
+ borderPadding.IStartEnd(cbwm) - offsets.IStartEnd(cbwm);
+ if (autoISize < 0) {
+ autoISize = 0;
+ }
+
+ if (computedSize.ISize(cbwm) == NS_UNCONSTRAINEDSIZE) {
+ // For non-replaced elements with block-size auto, the block-size
+ // fills the remaining space.
+ computedSize.ISize(cbwm) = autoISize;
+
+ // XXX Do these need box-sizing adjustments?
+ LogicalSize maxSize = ComputedMaxSize(cbwm);
+ LogicalSize minSize = ComputedMinSize(cbwm);
+ if (computedSize.ISize(cbwm) > maxSize.ISize(cbwm)) {
+ computedSize.ISize(cbwm) = maxSize.ISize(cbwm);
+ }
+ if (computedSize.ISize(cbwm) < minSize.ISize(cbwm)) {
+ computedSize.ISize(cbwm) = minSize.ISize(cbwm);
+ }
+ }
+ }
+
+ // However, the inline-size might
+ // still not fill all the available space (even though we didn't
+ // shrink-wrap) in case:
+ // * inline-size was specified
+ // * we're dealing with a replaced element
+ // * width was constrained by min- or max-inline-size.
+
+ nscoord availMarginSpace =
+ aCBSize.ISize(cbwm) - offsets.IStartEnd(cbwm) - margin.IStartEnd(cbwm) -
+ borderPadding.IStartEnd(cbwm) - computedSize.ISize(cbwm);
+ bool marginIStartIsAuto =
+ eStyleUnit_Auto == mStyleMargin->mMargin.GetIStartUnit(cbwm);
+ bool marginIEndIsAuto =
+ eStyleUnit_Auto == mStyleMargin->mMargin.GetIEndUnit(cbwm);
+
+ if (marginIStartIsAuto) {
+ if (marginIEndIsAuto) {
+ if (availMarginSpace < 0) {
+ // Note that this case is different from the neither-'auto'
+ // case below, where the spec says to ignore 'left'/'right'.
+ // Ignore the specified value for 'margin-right'.
+ margin.IEnd(cbwm) = availMarginSpace;
+ } else {
+ // Both 'margin-left' and 'margin-right' are 'auto', so they get
+ // equal values
+ margin.IStart(cbwm) = availMarginSpace / 2;
+ margin.IEnd(cbwm) = availMarginSpace - margin.IStart(cbwm);
+ }
+ } else {
+ // Just 'margin-left' is 'auto'
+ margin.IStart(cbwm) = availMarginSpace;
+ }
+ } else {
+ if (marginIEndIsAuto) {
+ // Just 'margin-right' is 'auto'
+ margin.IEnd(cbwm) = availMarginSpace;
+ } else {
+ // We're over-constrained so use the direction of the containing
+ // block to dictate which value to ignore. (And note that the
+ // spec says to ignore 'left' or 'right' rather than
+ // 'margin-left' or 'margin-right'.)
+ // Note that this case is different from the both-'auto' case
+ // above, where the spec says to ignore
+ // 'margin-left'/'margin-right'.
+ // Ignore the specified value for 'right'.
+ offsets.IEnd(cbwm) += availMarginSpace;
+ }
+ }
+ }
+
+ bool bSizeIsAuto = eStyleUnit_Auto == mStylePosition->BSize(cbwm).GetUnit();
+ if (bStartIsAuto) {
+ // solve for block-start
+ if (bSizeIsAuto) {
+ offsets.BStart(cbwm) = NS_AUTOOFFSET;
+ } else {
+ offsets.BStart(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
+ borderPadding.BStartEnd(cbwm) - computedSize.BSize(cbwm) -
+ offsets.BEnd(cbwm);
+ }
+ } else if (bEndIsAuto) {
+ // solve for block-end
+ if (bSizeIsAuto) {
+ offsets.BEnd(cbwm) = NS_AUTOOFFSET;
+ } else {
+ offsets.BEnd(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
+ borderPadding.BStartEnd(cbwm) - computedSize.BSize(cbwm) -
+ offsets.BStart(cbwm);
+ }
+ } else {
+ // Neither block-start nor -end is 'auto'.
+ nscoord autoBSize = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
+ borderPadding.BStartEnd(cbwm) - offsets.BStartEnd(cbwm);
+ if (autoBSize < 0) {
+ autoBSize = 0;
+ }
+
+ if (computedSize.BSize(cbwm) == NS_UNCONSTRAINEDSIZE) {
+ // For non-replaced elements with block-size auto, the block-size
+ // fills the remaining space.
+ computedSize.BSize(cbwm) = autoBSize;
+
+ // XXX Do these need box-sizing adjustments?
+ LogicalSize maxSize = ComputedMaxSize(cbwm);
+ LogicalSize minSize = ComputedMinSize(cbwm);
+ if (computedSize.BSize(cbwm) > maxSize.BSize(cbwm)) {
+ computedSize.BSize(cbwm) = maxSize.BSize(cbwm);
+ }
+ if (computedSize.BSize(cbwm) < minSize.BSize(cbwm)) {
+ computedSize.BSize(cbwm) = minSize.BSize(cbwm);
+ }
+ }
+
+ // The block-size might still not fill all the available space in case:
+ // * bsize was specified
+ // * we're dealing with a replaced element
+ // * bsize was constrained by min- or max-bsize.
+ nscoord availMarginSpace = autoBSize - computedSize.BSize(cbwm);
+ bool marginBStartIsAuto =
+ eStyleUnit_Auto == mStyleMargin->mMargin.GetBStartUnit(cbwm);
+ bool marginBEndIsAuto =
+ eStyleUnit_Auto == mStyleMargin->mMargin.GetBEndUnit(cbwm);
+
+ if (marginBStartIsAuto) {
+ if (marginBEndIsAuto) {
+ // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
+ // equal values
+ margin.BStart(cbwm) = availMarginSpace / 2;
+ margin.BEnd(cbwm) = availMarginSpace - margin.BStart(cbwm);
+ } else {
+ // Just margin-block-start is 'auto'
+ margin.BStart(cbwm) = availMarginSpace;
+ }
+ } else {
+ if (marginBEndIsAuto) {
+ // Just margin-block-end is 'auto'
+ margin.BEnd(cbwm) = availMarginSpace;
+ } else {
+ // We're over-constrained so ignore the specified value for
+ // block-end. (And note that the spec says to ignore 'bottom'
+ // rather than 'margin-bottom'.)
+ offsets.BEnd(cbwm) += availMarginSpace;
+ }
+ }
+ }
+ ComputedBSize() = computedSize.ConvertTo(wm, cbwm).BSize(wm);
+ ComputedISize() = computedSize.ConvertTo(wm, cbwm).ISize(wm);
+
+ SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm));
+ SetComputedLogicalMargin(margin.ConvertTo(wm, cbwm));
+}
+
+// This will not be converted to abstract coordinates because it's only
+// used in CalcQuirkContainingBlockHeight
+nscoord
+GetBlockMarginBorderPadding(const ReflowInput* aReflowInput)
+{
+ nscoord result = 0;
+ if (!aReflowInput) return result;
+
+ // zero auto margins
+ nsMargin margin = aReflowInput->ComputedPhysicalMargin();
+ if (NS_AUTOMARGIN == margin.top)
+ margin.top = 0;
+ if (NS_AUTOMARGIN == margin.bottom)
+ margin.bottom = 0;
+
+ result += margin.top + margin.bottom;
+ result += aReflowInput->ComputedPhysicalBorderPadding().top +
+ aReflowInput->ComputedPhysicalBorderPadding().bottom;
+
+ return result;
+}
+
+/* Get the height based on the viewport of the containing block specified
+ * in aReflowInput when the containing block has mComputedHeight == NS_AUTOHEIGHT
+ * This will walk up the chain of containing blocks looking for a computed height
+ * until it finds the canvas frame, or it encounters a frame that is not a block,
+ * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
+ *
+ * When we encounter scrolledContent block frames, we skip over them,
+ * since they are guaranteed to not be useful for computing the containing block.
+ *
+ * See also IsQuirkContainingBlockHeight.
+ */
+static nscoord
+CalcQuirkContainingBlockHeight(const ReflowInput* aCBReflowInput)
+{
+ const ReflowInput* firstAncestorRI = nullptr; // a candidate for html frame
+ const ReflowInput* secondAncestorRI = nullptr; // a candidate for body frame
+
+ // initialize the default to NS_AUTOHEIGHT as this is the containings block
+ // computed height when this function is called. It is possible that we
+ // don't alter this height especially if we are restricted to one level
+ nscoord result = NS_AUTOHEIGHT;
+
+ const ReflowInput* rs = aCBReflowInput;
+ for (; rs; rs = rs->mParentReflowInput) {
+ nsIAtom* frameType = rs->mFrame->GetType();
+ // if the ancestor is auto height then skip it and continue up if it
+ // is the first block frame and possibly the body/html
+ if (nsGkAtoms::blockFrame == frameType ||
+#ifdef MOZ_XUL
+ nsGkAtoms::XULLabelFrame == frameType ||
+#endif
+ nsGkAtoms::scrollFrame == frameType) {
+
+ secondAncestorRI = firstAncestorRI;
+ firstAncestorRI = rs;
+
+ // If the current frame we're looking at is positioned, we don't want to
+ // go any further (see bug 221784). The behavior we want here is: 1) If
+ // not auto-height, use this as the percentage base. 2) If auto-height,
+ // keep looking, unless the frame is positioned.
+ if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
+ if (rs->mFrame->IsAbsolutelyPositioned()) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+ else if (nsGkAtoms::canvasFrame == frameType) {
+ // Always continue on to the height calculation
+ }
+ else if (nsGkAtoms::pageContentFrame == frameType) {
+ nsIFrame* prevInFlow = rs->mFrame->GetPrevInFlow();
+ // only use the page content frame for a height basis if it is the first in flow
+ if (prevInFlow)
+ break;
+ }
+ else {
+ break;
+ }
+
+ // if the ancestor is the page content frame then the percent base is
+ // the avail height, otherwise it is the computed height
+ result = (nsGkAtoms::pageContentFrame == frameType)
+ ? rs->AvailableHeight() : rs->ComputedHeight();
+ // if unconstrained - don't sutract borders - would result in huge height
+ if (NS_AUTOHEIGHT == result) return result;
+
+ // if we got to the canvas or page content frame, then subtract out
+ // margin/border/padding for the BODY and HTML elements
+ if ((nsGkAtoms::canvasFrame == frameType) ||
+ (nsGkAtoms::pageContentFrame == frameType)) {
+
+ result -= GetBlockMarginBorderPadding(firstAncestorRI);
+ result -= GetBlockMarginBorderPadding(secondAncestorRI);
+
+#ifdef DEBUG
+ // make sure the first ancestor is the HTML and the second is the BODY
+ if (firstAncestorRI) {
+ nsIContent* frameContent = firstAncestorRI->mFrame->GetContent();
+ if (frameContent) {
+ NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::html),
+ "First ancestor is not HTML");
+ }
+ }
+ if (secondAncestorRI) {
+ nsIContent* frameContent = secondAncestorRI->mFrame->GetContent();
+ if (frameContent) {
+ NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::body),
+ "Second ancestor is not BODY");
+ }
+ }
+#endif
+
+ }
+ // if we got to the html frame (a block child of the canvas) ...
+ else if (nsGkAtoms::blockFrame == frameType &&
+ rs->mParentReflowInput &&
+ nsGkAtoms::canvasFrame ==
+ rs->mParentReflowInput->mFrame->GetType()) {
+ // ... then subtract out margin/border/padding for the BODY element
+ result -= GetBlockMarginBorderPadding(secondAncestorRI);
+ }
+ break;
+ }
+
+ // Make sure not to return a negative height here!
+ return std::max(result, 0);
+}
+
+// Called by InitConstraints() to compute the containing block rectangle for
+// the element. Handles the special logic for absolutely positioned elements
+LogicalSize
+ReflowInput::ComputeContainingBlockRectangle(
+ nsPresContext* aPresContext,
+ const ReflowInput* aContainingBlockRI) const
+{
+ // Unless the element is absolutely positioned, the containing block is
+ // formed by the content edge of the nearest block-level ancestor
+ LogicalSize cbSize = aContainingBlockRI->ComputedSize();
+
+ WritingMode wm = aContainingBlockRI->GetWritingMode();
+
+ // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to
+ // special case them here.
+ if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE ||
+ (mFrame->GetType() == nsGkAtoms::tableFrame &&
+ mFrame->IsAbsolutelyPositioned() &&
+ (mFrame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) {
+ // See if the ancestor is block-level or inline-level
+ if (NS_FRAME_GET_TYPE(aContainingBlockRI->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
+ // Base our size on the actual size of the frame. In cases when this is
+ // completely bogus (eg initial reflow), this code shouldn't even be
+ // called, since the code in nsInlineFrame::Reflow will pass in
+ // the containing block dimensions to our constructor.
+ // XXXbz we should be taking the in-flows into account too, but
+ // that's very hard.
+
+ LogicalMargin computedBorder =
+ aContainingBlockRI->ComputedLogicalBorderPadding() -
+ aContainingBlockRI->ComputedLogicalPadding();
+ cbSize.ISize(wm) = aContainingBlockRI->mFrame->ISize(wm) -
+ computedBorder.IStartEnd(wm);
+ NS_ASSERTION(cbSize.ISize(wm) >= 0,
+ "Negative containing block isize!");
+ cbSize.BSize(wm) = aContainingBlockRI->mFrame->BSize(wm) -
+ computedBorder.BStartEnd(wm);
+ NS_ASSERTION(cbSize.BSize(wm) >= 0,
+ "Negative containing block bsize!");
+ } else {
+ // If the ancestor is block-level, the containing block is formed by the
+ // padding edge of the ancestor
+ cbSize.ISize(wm) +=
+ aContainingBlockRI->ComputedLogicalPadding().IStartEnd(wm);
+ cbSize.BSize(wm) +=
+ aContainingBlockRI->ComputedLogicalPadding().BStartEnd(wm);
+ }
+ } else {
+ // an element in quirks mode gets a containing block based on looking for a
+ // parent with a non-auto height if the element has a percent height
+ // Note: We don't emulate this quirk for percents in calc() or in
+ // vertical writing modes.
+ if (!wm.IsVertical() &&
+ NS_AUTOHEIGHT == cbSize.BSize(wm)) {
+ if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
+ mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
+ cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI);
+ }
+ }
+ }
+
+ return cbSize.ConvertTo(GetWritingMode(), wm);
+}
+
+static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
+{
+ if (sNormalLineHeightControl == eUninitialized) {
+ // browser.display.normal_lineheight_calc_control is not user
+ // changeable, so no need to register callback for it.
+ int32_t val =
+ Preferences::GetInt("browser.display.normal_lineheight_calc_control",
+ eNoExternalLeading);
+ sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val);
+ }
+ return sNormalLineHeightControl;
+}
+
+static inline bool
+IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay,
+ WritingMode aWM)
+{
+ if (aStyleDisplay->mDisplay != StyleDisplay::TableCaption) {
+ return false;
+ }
+ uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide;
+ return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
+ captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
+}
+
+// Flex/grid items resolve block-axis percentage margin & padding against the
+// containing block block-size (also for abs/fixed-pos child frames).
+// For everything else: the CSS21 spec requires that margin and padding
+// percentage values are calculated with respect to the inline-size of the
+// containing block, even for margin & padding in the block axis.
+static LogicalSize
+OffsetPercentBasis(const nsIFrame* aFrame,
+ WritingMode aWM,
+ const LogicalSize& aContainingBlockSize)
+{
+ LogicalSize offsetPercentBasis = aContainingBlockSize;
+ if (MOZ_LIKELY(!aFrame->GetParent() ||
+ !aFrame->GetParent()->IsFlexOrGridContainer())) {
+ offsetPercentBasis.BSize(aWM) = offsetPercentBasis.ISize(aWM);
+ } else if (offsetPercentBasis.BSize(aWM) == NS_AUTOHEIGHT) {
+ offsetPercentBasis.BSize(aWM) = 0;
+ }
+
+ return offsetPercentBasis;
+}
+
+// XXX refactor this code to have methods for each set of properties
+// we are computing: width,height,line-height; margin; offsets
+
+void
+ReflowInput::InitConstraints(nsPresContext* aPresContext,
+ const LogicalSize& aContainingBlockSize,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding,
+ nsIAtom* aFrameType)
+{
+ WritingMode wm = GetWritingMode();
+ DISPLAY_INIT_CONSTRAINTS(mFrame, this,
+ aContainingBlockSize.ISize(wm),
+ aContainingBlockSize.BSize(wm),
+ aBorder, aPadding);
+
+ // If this is a reflow root, then set the computed width and
+ // height equal to the available space
+ if (nullptr == mParentReflowInput || mFlags.mDummyParentReflowInput) {
+ // XXXldb This doesn't mean what it used to!
+ InitOffsets(wm, OffsetPercentBasis(mFrame, wm, aContainingBlockSize),
+ aFrameType, mFlags, aBorder, aPadding);
+ // Override mComputedMargin since reflow roots start from the
+ // frame's boundary, which is inside the margin.
+ ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
+ ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
+
+ ComputedISize() =
+ AvailableISize() - ComputedLogicalBorderPadding().IStartEnd(wm);
+ if (ComputedISize() < 0) {
+ ComputedISize() = 0;
+ }
+ if (AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
+ ComputedBSize() =
+ AvailableBSize() - ComputedLogicalBorderPadding().BStartEnd(wm);
+ if (ComputedBSize() < 0) {
+ ComputedBSize() = 0;
+ }
+ } else {
+ ComputedBSize() = NS_UNCONSTRAINEDSIZE;
+ }
+
+ ComputedMinWidth() = ComputedMinHeight() = 0;
+ ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
+ } else {
+ // Get the containing block reflow state
+ const ReflowInput* cbrs = mCBReflowInput;
+ NS_ASSERTION(nullptr != cbrs, "no containing block");
+
+ // If we weren't given a containing block width and height, then
+ // compute one
+ LogicalSize cbSize = (aContainingBlockSize == LogicalSize(wm, -1, -1))
+ ? ComputeContainingBlockRectangle(aPresContext, cbrs)
+ : aContainingBlockSize;
+
+ // See if the containing block height is based on the size of its
+ // content
+ nsIAtom* fType;
+ if (NS_AUTOHEIGHT == cbSize.BSize(wm)) {
+ // See if the containing block is a cell frame which needs
+ // to use the mComputedHeight of the cell instead of what the cell block passed in.
+ // XXX It seems like this could lead to bugs with min-height and friends
+ if (cbrs->mParentReflowInput) {
+ fType = cbrs->mFrame->GetType();
+ if (IS_TABLE_CELL(fType)) {
+ // use the cell's computed block size
+ cbSize.BSize(wm) = cbrs->ComputedSize(wm).BSize(wm);
+ }
+ }
+ }
+
+ // XXX Might need to also pass the CB height (not width) for page boxes,
+ // too, if we implement them.
+
+ // For calculating positioning offsets, margins, borders and
+ // padding, we use the writing mode of the containing block
+ WritingMode cbwm = cbrs->GetWritingMode();
+ InitOffsets(cbwm, OffsetPercentBasis(mFrame, cbwm,
+ cbSize.ConvertTo(cbwm, wm)),
+ aFrameType, mFlags, aBorder, aPadding);
+
+ // For calculating the size of this box, we use its own writing mode
+ const nsStyleCoord &blockSize = mStylePosition->BSize(wm);
+ nsStyleUnit blockSizeUnit = blockSize.GetUnit();
+
+ // Check for a percentage based block size and a containing block
+ // block size that depends on the content block size
+ // XXX twiddling blockSizeUnit doesn't help anymore
+ // FIXME Shouldn't we fix that?
+ if (blockSize.HasPercent()) {
+ if (NS_AUTOHEIGHT == cbSize.BSize(wm)) {
+ // this if clause enables %-blockSize on replaced inline frames,
+ // such as images. See bug 54119. The else clause "blockSizeUnit = eStyleUnit_Auto;"
+ // used to be called exclusively.
+ if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType ||
+ NS_FRAME_REPLACED_CONTAINS_BLOCK(
+ NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
+ // Get the containing block reflow state
+ NS_ASSERTION(nullptr != cbrs, "no containing block");
+ // in quirks mode, get the cb height using the special quirk method
+ if (!wm.IsVertical() &&
+ eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
+ if (!IS_TABLE_CELL(fType)) {
+ cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbrs);
+ if (cbSize.BSize(wm) == NS_AUTOHEIGHT) {
+ blockSizeUnit = eStyleUnit_Auto;
+ }
+ }
+ else {
+ blockSizeUnit = eStyleUnit_Auto;
+ }
+ }
+ // in standard mode, use the cb block size. if it's "auto",
+ // as will be the case by default in BODY, use auto block size
+ // as per CSS2 spec.
+ else
+ {
+ nscoord computedBSize = cbrs->ComputedSize(wm).BSize(wm);
+ if (NS_AUTOHEIGHT != computedBSize) {
+ cbSize.BSize(wm) = computedBSize;
+ }
+ else {
+ blockSizeUnit = eStyleUnit_Auto;
+ }
+ }
+ }
+ else {
+ // default to interpreting the blockSize like 'auto'
+ blockSizeUnit = eStyleUnit_Auto;
+ }
+ }
+ }
+
+ // Compute our offsets if the element is relatively positioned. We
+ // need the correct containing block inline-size and block-size
+ // here, which is why we need to do it after all the quirks-n-such
+ // above. (If the element is sticky positioned, we need to wait
+ // until the scroll container knows its size, so we compute offsets
+ // from StickyScrollContainer::UpdatePositions.)
+ if (mStyleDisplay->IsRelativelyPositioned(mFrame) &&
+ NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
+ ComputeRelativeOffsets(cbwm, mFrame, cbSize.ConvertTo(cbwm, wm),
+ ComputedPhysicalOffsets());
+ } else {
+ // Initialize offsets to 0
+ ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
+ }
+
+ // Calculate the computed values for min and max properties. Note that
+ // this MUST come after we've computed our border and padding.
+ ComputeMinMaxValues(cbSize);
+
+ // Calculate the computed inlineSize and blockSize.
+ // This varies by frame type.
+
+ if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
+ // Internal table elements. The rules vary depending on the type.
+ // Calculate the computed isize
+ bool rowOrRowGroup = false;
+ const nsStyleCoord &inlineSize = mStylePosition->ISize(wm);
+ nsStyleUnit inlineSizeUnit = inlineSize.GetUnit();
+ if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) ||
+ (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) {
+ // 'inlineSize' property doesn't apply to table rows and row groups
+ inlineSizeUnit = eStyleUnit_Auto;
+ rowOrRowGroup = true;
+ }
+
+ // calc() with percentages acts like auto on internal table elements
+ if (eStyleUnit_Auto == inlineSizeUnit ||
+ (inlineSize.IsCalcUnit() && inlineSize.CalcHasPercent())) {
+ ComputedISize() = AvailableISize();
+
+ if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
+ // Internal table elements don't have margins. Only tables and
+ // cells have border and padding
+ ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm);
+ if (ComputedISize() < 0)
+ ComputedISize() = 0;
+ }
+ NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize");
+
+ } else {
+ NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(),
+ "unexpected inline size unit change");
+ ComputedISize() = ComputeISizeValue(cbSize.ISize(wm),
+ mStylePosition->mBoxSizing,
+ inlineSize);
+ }
+
+ // Calculate the computed block size
+ if ((StyleDisplay::TableColumn == mStyleDisplay->mDisplay) ||
+ (StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay)) {
+ // 'blockSize' property doesn't apply to table columns and column groups
+ blockSizeUnit = eStyleUnit_Auto;
+ }
+ // calc() with percentages acts like 'auto' on internal table elements
+ if (eStyleUnit_Auto == blockSizeUnit ||
+ (blockSize.IsCalcUnit() && blockSize.CalcHasPercent())) {
+ ComputedBSize() = NS_AUTOHEIGHT;
+ } else {
+ NS_ASSERTION(blockSizeUnit == blockSize.GetUnit(),
+ "unexpected block size unit change");
+ ComputedBSize() = ComputeBSizeValue(cbSize.BSize(wm),
+ mStylePosition->mBoxSizing,
+ blockSize);
+ }
+
+ // Doesn't apply to table elements
+ ComputedMinWidth() = ComputedMinHeight() = 0;
+ ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
+
+ } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
+ // XXX not sure if this belongs here or somewhere else - cwk
+ InitAbsoluteConstraints(aPresContext, cbrs, cbSize.ConvertTo(cbrs->GetWritingMode(), wm), aFrameType);
+ } else {
+ AutoMaybeDisableFontInflation an(mFrame);
+
+ bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType);
+ typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags;
+ ComputeSizeFlags computeSizeFlags =
+ isBlock ? ComputeSizeFlags::eDefault : ComputeSizeFlags::eShrinkWrap;
+ if (mFlags.mIClampMarginBoxMinSize) {
+ computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
+ ComputeSizeFlags::eIClampMarginBoxMinSize);
+ }
+ if (mFlags.mBClampMarginBoxMinSize) {
+ computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
+ ComputeSizeFlags::eBClampMarginBoxMinSize);
+ }
+ if (mFlags.mShrinkWrap) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+ }
+ if (mFlags.mUseAutoBSize) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize);
+ }
+
+ nsIFrame* alignCB = mFrame->GetParent();
+ nsIAtom* alignCBType = alignCB ? alignCB->GetType() : nullptr;
+ if (alignCBType == nsGkAtoms::tableWrapperFrame &&
+ alignCB->GetParent()) {
+ auto parentCBType = alignCB->GetParent()->GetType();
+ // XXX grid-specific for now; maybe remove this check after we address bug 799725
+ if (parentCBType == nsGkAtoms::gridContainerFrame) {
+ alignCB = alignCB->GetParent();
+ alignCBType = parentCBType;
+ }
+ }
+ if (alignCBType == nsGkAtoms::gridContainerFrame) {
+ // Shrink-wrap grid items that will be aligned (rather than stretched)
+ // in its inline axis.
+ auto inlineAxisAlignment = wm.IsOrthogonalTo(cbwm) ?
+ mStylePosition->UsedAlignSelf(mFrame->StyleContext()->GetParent()) :
+ mStylePosition->UsedJustifySelf(mFrame->StyleContext()->GetParent());
+ if ((inlineAxisAlignment != NS_STYLE_ALIGN_STRETCH &&
+ inlineAxisAlignment != NS_STYLE_ALIGN_NORMAL) ||
+ mStyleMargin->mMargin.GetIStartUnit(wm) == eStyleUnit_Auto ||
+ mStyleMargin->mMargin.GetIEndUnit(wm) == eStyleUnit_Auto) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+ }
+ } else {
+ // Make sure legend frames with display:block and width:auto still
+ // shrink-wrap.
+ // Also shrink-wrap blocks that are orthogonal to their container.
+ if (isBlock &&
+ ((aFrameType == nsGkAtoms::legendFrame &&
+ mFrame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) ||
+ (aFrameType == nsGkAtoms::scrollFrame &&
+ mFrame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) ||
+ (mCBReflowInput &&
+ mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)))) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+ }
+
+ if (alignCBType == nsGkAtoms::flexContainerFrame) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
+
+ // If we're inside of a flex container that needs to measure our
+ // auto height, pass that information along to ComputeSize().
+ if (mFlags.mIsFlexContainerMeasuringHeight) {
+ computeSizeFlags =
+ ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize);
+ }
+ } else {
+ MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight,
+ "We're not in a flex container, so the flag "
+ "'mIsFlexContainerMeasuringHeight' shouldn't be set");
+ }
+ }
+
+ if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) {
+ // For orthogonal flows, where we found a parent orthogonal-limit
+ // for AvailableISize() in Init(), we'll use the same here as well.
+ cbSize.ISize(wm) = AvailableISize();
+ }
+
+ LogicalSize size =
+ mFrame->ComputeSize(mRenderingContext, wm, cbSize, AvailableISize(),
+ ComputedLogicalMargin().Size(wm),
+ ComputedLogicalBorderPadding().Size(wm) -
+ ComputedLogicalPadding().Size(wm),
+ ComputedLogicalPadding().Size(wm),
+ computeSizeFlags);
+
+ ComputedISize() = size.ISize(wm);
+ ComputedBSize() = size.BSize(wm);
+ NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
+ NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
+ ComputedBSize() >= 0, "Bogus block-size");
+
+ // Exclude inline tables, side captions, flex and grid items from block
+ // margin calculations.
+ if (isBlock &&
+ !IsSideCaption(mFrame, mStyleDisplay, cbwm) &&
+ mStyleDisplay->mDisplay != StyleDisplay::InlineTable &&
+ alignCBType != nsGkAtoms::flexContainerFrame &&
+ alignCBType != nsGkAtoms::gridContainerFrame) {
+ CalculateBlockSideMargins(aFrameType);
+ }
+ }
+ }
+}
+
+static void
+UpdateProp(FrameProperties& aProps,
+ const FramePropertyDescriptor<nsMargin>* aProperty,
+ bool aNeeded,
+ nsMargin& aNewValue)
+{
+ if (aNeeded) {
+ nsMargin* propValue = aProps.Get(aProperty);
+ if (propValue) {
+ *propValue = aNewValue;
+ } else {
+ aProps.Set(aProperty, new nsMargin(aNewValue));
+ }
+ } else {
+ aProps.Delete(aProperty);
+ }
+}
+
+void
+SizeComputationInput::InitOffsets(WritingMode aWM,
+ const LogicalSize& aPercentBasis,
+ nsIAtom* aFrameType,
+ ReflowInputFlags aFlags,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding)
+{
+ DISPLAY_INIT_OFFSETS(mFrame, this, aPercentBasis, aBorder, aPadding);
+
+ // Since we are in reflow, we don't need to store these properties anymore
+ // unless they are dependent on width, in which case we store the new value.
+ nsPresContext *presContext = mFrame->PresContext();
+ FrameProperties props(presContext->PropertyTable(), mFrame);
+ props.Delete(nsIFrame::UsedBorderProperty());
+
+ // Compute margins from the specified margin style information. These
+ // become the default computed values, and may be adjusted below
+ // XXX fix to provide 0,0 for the top&bottom margins for
+ // inline-non-replaced elements
+ bool needMarginProp = ComputeMargin(aWM, aPercentBasis);
+ // XXX We need to include 'auto' horizontal margins in this too!
+ // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin
+ // to use it even when the margins are all zero (since sometimes
+ // they get treated as auto)
+ ::UpdateProp(props, nsIFrame::UsedMarginProperty(), needMarginProp,
+ ComputedPhysicalMargin());
+
+
+ const nsStyleDisplay *disp = mFrame->StyleDisplay();
+ bool isThemed = mFrame->IsThemed(disp);
+ bool needPaddingProp;
+ nsIntMargin widget;
+ if (isThemed &&
+ presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
+ mFrame, disp->mAppearance,
+ &widget)) {
+ ComputedPhysicalPadding().top = presContext->DevPixelsToAppUnits(widget.top);
+ ComputedPhysicalPadding().right = presContext->DevPixelsToAppUnits(widget.right);
+ ComputedPhysicalPadding().bottom = presContext->DevPixelsToAppUnits(widget.bottom);
+ ComputedPhysicalPadding().left = presContext->DevPixelsToAppUnits(widget.left);
+ needPaddingProp = false;
+ }
+ else if (mFrame->IsSVGText()) {
+ ComputedPhysicalPadding().SizeTo(0, 0, 0, 0);
+ needPaddingProp = false;
+ }
+ else if (aPadding) { // padding is an input arg
+ ComputedPhysicalPadding() = *aPadding;
+ needPaddingProp = mFrame->StylePadding()->IsWidthDependent() ||
+ (mFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT);
+ }
+ else {
+ needPaddingProp = ComputePadding(aWM, aPercentBasis, aFrameType);
+ }
+
+ // Add [align|justify]-content:baseline padding contribution.
+ typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
+ auto ApplyBaselinePadding = [this, &needPaddingProp]
+ (LogicalAxis aAxis, Prop aProp) {
+ bool found;
+ nscoord val = mFrame->Properties().Get(aProp, &found);
+ if (found) {
+ NS_ASSERTION(val != nscoord(0), "zero in this property is useless");
+ WritingMode wm = GetWritingMode();
+ LogicalSide side;
+ if (val > 0) {
+ side = MakeLogicalSide(aAxis, eLogicalEdgeStart);
+ } else {
+ side = MakeLogicalSide(aAxis, eLogicalEdgeEnd);
+ val = -val;
+ }
+ mComputedPadding.Side(wm.PhysicalSide(side)) += val;
+ needPaddingProp = true;
+ }
+ };
+ if (!aFlags.mUseAutoBSize) {
+ ApplyBaselinePadding(eLogicalAxisBlock, nsIFrame::BBaselinePadProperty());
+ }
+ if (!aFlags.mShrinkWrap) {
+ ApplyBaselinePadding(eLogicalAxisInline, nsIFrame::IBaselinePadProperty());
+ }
+
+ if (isThemed) {
+ nsIntMargin widget;
+ presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
+ mFrame, disp->mAppearance,
+ &widget);
+ ComputedPhysicalBorderPadding().top =
+ presContext->DevPixelsToAppUnits(widget.top);
+ ComputedPhysicalBorderPadding().right =
+ presContext->DevPixelsToAppUnits(widget.right);
+ ComputedPhysicalBorderPadding().bottom =
+ presContext->DevPixelsToAppUnits(widget.bottom);
+ ComputedPhysicalBorderPadding().left =
+ presContext->DevPixelsToAppUnits(widget.left);
+ }
+ else if (mFrame->IsSVGText()) {
+ ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0);
+ }
+ else if (aBorder) { // border is an input arg
+ ComputedPhysicalBorderPadding() = *aBorder;
+ }
+ else {
+ ComputedPhysicalBorderPadding() = mFrame->StyleBorder()->GetComputedBorder();
+ }
+ ComputedPhysicalBorderPadding() += ComputedPhysicalPadding();
+
+ if (aFrameType == nsGkAtoms::tableFrame) {
+ nsTableFrame *tableFrame = static_cast<nsTableFrame*>(mFrame);
+
+ if (tableFrame->IsBorderCollapse()) {
+ // border-collapsed tables don't use any of their padding, and
+ // only part of their border. We need to do this here before we
+ // try to do anything like handling 'auto' widths,
+ // 'box-sizing', or 'auto' margins.
+ ComputedPhysicalPadding().SizeTo(0,0,0,0);
+ SetComputedLogicalBorderPadding(
+ tableFrame->GetIncludedOuterBCBorder(mWritingMode));
+ }
+
+ // The margin is inherited to the table wrapper frame via
+ // the ::-moz-table-wrapper rule in ua.css.
+ ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
+ } else if (aFrameType == nsGkAtoms::scrollbarFrame) {
+ // scrollbars may have had their width or height smashed to zero
+ // by the associated scrollframe, in which case we must not report
+ // any padding or border.
+ nsSize size(mFrame->GetSize());
+ if (size.width == 0 || size.height == 0) {
+ ComputedPhysicalPadding().SizeTo(0,0,0,0);
+ ComputedPhysicalBorderPadding().SizeTo(0,0,0,0);
+ }
+ }
+ ::UpdateProp(props, nsIFrame::UsedPaddingProperty(), needPaddingProp,
+ ComputedPhysicalPadding());
+}
+
+// This code enforces section 10.3.3 of the CSS2 spec for this formula:
+//
+// 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
+// 'padding-right' + 'border-right-width' + 'margin-right'
+// = width of containing block
+//
+// Note: the width unit is not auto when this is called
+void
+ReflowInput::CalculateBlockSideMargins(nsIAtom* aFrameType)
+{
+ // Calculations here are done in the containing block's writing mode,
+ // which is where margins will eventually be applied: we're calculating
+ // margins that will be used by the container in its inline direction,
+ // which in the case of an orthogonal contained block will correspond to
+ // the block direction of this reflow state. So in the orthogonal-flow
+ // case, "CalculateBlock*Side*Margins" will actually end up adjusting
+ // the BStart/BEnd margins; those are the "sides" of the block from its
+ // container's point of view.
+ WritingMode cbWM =
+ mCBReflowInput ? mCBReflowInput->GetWritingMode(): GetWritingMode();
+
+ nscoord availISizeCBWM = AvailableSize(cbWM).ISize(cbWM);
+ nscoord computedISizeCBWM = ComputedSize(cbWM).ISize(cbWM);
+ if (computedISizeCBWM == NS_UNCONSTRAINEDSIZE) {
+ // For orthogonal flows, where we found a parent orthogonal-limit
+ // for AvailableISize() in Init(), we'll use the same here as well.
+ computedISizeCBWM = availISizeCBWM;
+ }
+
+ LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != computedISizeCBWM &&
+ NS_UNCONSTRAINEDSIZE != availISizeCBWM,
+ "have unconstrained inline-size; this should only "
+ "result from very large sizes, not attempts at "
+ "intrinsic inline-size calculation");
+
+ LogicalMargin margin =
+ ComputedLogicalMargin().ConvertTo(cbWM, mWritingMode);
+ LogicalMargin borderPadding =
+ ComputedLogicalBorderPadding().ConvertTo(cbWM, mWritingMode);
+ nscoord sum = margin.IStartEnd(cbWM) +
+ borderPadding.IStartEnd(cbWM) + computedISizeCBWM;
+ if (sum == availISizeCBWM) {
+ // The sum is already correct
+ return;
+ }
+
+ // Determine the start and end margin values. The isize value
+ // remains constant while we do this.
+
+ // Calculate how much space is available for margins
+ nscoord availMarginSpace = availISizeCBWM - sum;
+
+ // If the available margin space is negative, then don't follow the
+ // usual overconstraint rules.
+ if (availMarginSpace < 0) {
+ margin.IEnd(cbWM) += availMarginSpace;
+ SetComputedLogicalMargin(margin.ConvertTo(mWritingMode, cbWM));
+ return;
+ }
+
+ // The css2 spec clearly defines how block elements should behave
+ // in section 10.3.3.
+ const nsStyleSides& styleSides = mStyleMargin->mMargin;
+ bool isAutoStartMargin = eStyleUnit_Auto == styleSides.GetIStartUnit(cbWM);
+ bool isAutoEndMargin = eStyleUnit_Auto == styleSides.GetIEndUnit(cbWM);
+ if (!isAutoStartMargin && !isAutoEndMargin) {
+ // Neither margin is 'auto' so we're over constrained. Use the
+ // 'direction' property of the parent to tell which margin to
+ // ignore
+ // First check if there is an HTML alignment that we should honor
+ const ReflowInput* prs = mParentReflowInput;
+ if (aFrameType == nsGkAtoms::tableFrame) {
+ NS_ASSERTION(prs->mFrame->GetType() == nsGkAtoms::tableWrapperFrame,
+ "table not inside table wrapper");
+ // Center the table within the table wrapper based on the alignment
+ // of the table wrapper's parent.
+ prs = prs->mParentReflowInput;
+ }
+ if (prs &&
+ (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
+ prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
+ prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
+ if (prs->mWritingMode.IsBidiLTR()) {
+ isAutoStartMargin =
+ prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
+ isAutoEndMargin =
+ prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
+ } else {
+ isAutoStartMargin =
+ prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
+ isAutoEndMargin =
+ prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
+ }
+ }
+ // Otherwise apply the CSS rules, and ignore one margin by forcing
+ // it to 'auto', depending on 'direction'.
+ else {
+ isAutoEndMargin = true;
+ }
+ }
+
+ // Logic which is common to blocks and tables
+ // The computed margins need not be zero because the 'auto' could come from
+ // overconstraint or from HTML alignment so values need to be accumulated
+
+ if (isAutoStartMargin) {
+ if (isAutoEndMargin) {
+ // Both margins are 'auto' so the computed addition should be equal
+ nscoord forStart = availMarginSpace / 2;
+ margin.IStart(cbWM) += forStart;
+ margin.IEnd(cbWM) += availMarginSpace - forStart;
+ } else {
+ margin.IStart(cbWM) += availMarginSpace;
+ }
+ } else if (isAutoEndMargin) {
+ margin.IEnd(cbWM) += availMarginSpace;
+ }
+ SetComputedLogicalMargin(margin.ConvertTo(mWritingMode, cbWM));
+}
+
+#define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight
+// For "normal" we use the font's normal line height (em height + leading).
+// If both internal leading and external leading specified by font itself
+// are zeros, we should compensate this by creating extra (external) leading
+// in eCompensateLeading mode. This is necessary because without this
+// compensation, normal line height might looks too tight.
+
+// For risk management, we use preference to control the behavior, and
+// eNoExternalLeading is the old behavior.
+static nscoord
+GetNormalLineHeight(nsFontMetrics* aFontMetrics)
+{
+ NS_PRECONDITION(nullptr != aFontMetrics, "no font metrics");
+
+ nscoord normalLineHeight;
+
+ nscoord externalLeading = aFontMetrics->ExternalLeading();
+ nscoord internalLeading = aFontMetrics->InternalLeading();
+ nscoord emHeight = aFontMetrics->EmHeight();
+ switch (GetNormalLineHeightCalcControl()) {
+ case eIncludeExternalLeading:
+ normalLineHeight = emHeight+ internalLeading + externalLeading;
+ break;
+ case eCompensateLeading:
+ if (!internalLeading && !externalLeading)
+ normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
+ else
+ normalLineHeight = emHeight+ internalLeading + externalLeading;
+ break;
+ default:
+ //case eNoExternalLeading:
+ normalLineHeight = emHeight + internalLeading;
+ }
+ return normalLineHeight;
+}
+
+static inline nscoord
+ComputeLineHeight(nsStyleContext* aStyleContext,
+ nscoord aBlockBSize,
+ float aFontSizeInflation)
+{
+ const nsStyleCoord& lhCoord = aStyleContext->StyleText()->mLineHeight;
+
+ if (lhCoord.GetUnit() == eStyleUnit_Coord) {
+ nscoord result = lhCoord.GetCoordValue();
+ if (aFontSizeInflation != 1.0f) {
+ result = NSToCoordRound(result * aFontSizeInflation);
+ }
+ return result;
+ }
+
+ if (lhCoord.GetUnit() == eStyleUnit_Factor)
+ // For factor units the computed value of the line-height property
+ // is found by multiplying the factor by the font's computed size
+ // (adjusted for min-size prefs and text zoom).
+ return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation *
+ aStyleContext->StyleFont()->mFont.size);
+
+ NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal ||
+ lhCoord.GetUnit() == eStyleUnit_Enumerated,
+ "bad line-height unit");
+
+ if (lhCoord.GetUnit() == eStyleUnit_Enumerated) {
+ NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
+ "bad line-height value");
+ if (aBlockBSize != NS_AUTOHEIGHT) {
+ return aBlockBSize;
+ }
+ }
+
+ RefPtr<nsFontMetrics> fm = nsLayoutUtils::
+ GetFontMetricsForStyleContext(aStyleContext, aFontSizeInflation);
+ return GetNormalLineHeight(fm);
+}
+
+nscoord
+ReflowInput::CalcLineHeight() const
+{
+ nscoord blockBSize =
+ nsLayoutUtils::IsNonWrapperBlock(mFrame) ? ComputedBSize() :
+ (mCBReflowInput ? mCBReflowInput->ComputedBSize() : NS_AUTOHEIGHT);
+
+ return CalcLineHeight(mFrame->GetContent(), mFrame->StyleContext(), blockBSize,
+ nsLayoutUtils::FontSizeInflationFor(mFrame));
+}
+
+/* static */ nscoord
+ReflowInput::CalcLineHeight(nsIContent* aContent,
+ nsStyleContext* aStyleContext,
+ nscoord aBlockBSize,
+ float aFontSizeInflation)
+{
+ NS_PRECONDITION(aStyleContext, "Must have a style context");
+
+ nscoord lineHeight =
+ ComputeLineHeight(aStyleContext, aBlockBSize, aFontSizeInflation);
+
+ NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
+
+ HTMLInputElement* input = HTMLInputElement::FromContentOrNull(aContent);
+ if (input && input->IsSingleLineTextControl()) {
+ // For Web-compatibility, single-line text input elements cannot
+ // have a line-height smaller than one.
+ nscoord lineHeightOne =
+ aFontSizeInflation * aStyleContext->StyleFont()->mFont.size;
+ if (lineHeight < lineHeightOne) {
+ lineHeight = lineHeightOne;
+ }
+ }
+
+ return lineHeight;
+}
+
+bool
+SizeComputationInput::ComputeMargin(WritingMode aWM,
+ const LogicalSize& aPercentBasis)
+{
+ // SVG text frames have no margin.
+ if (mFrame->IsSVGText()) {
+ return false;
+ }
+
+ // If style style can provide us the margin directly, then use it.
+ const nsStyleMargin *styleMargin = mFrame->StyleMargin();
+
+ bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin());
+ if (isCBDependent) {
+ // We have to compute the value. Note that this calculation is
+ // performed according to the writing mode of the containing block
+ // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
+ LogicalMargin m(aWM);
+ m.IStart(aWM) = nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.ISize(aWM),
+ styleMargin->mMargin.GetIStart(aWM));
+ m.IEnd(aWM) = nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.ISize(aWM),
+ styleMargin->mMargin.GetIEnd(aWM));
+
+ m.BStart(aWM) = nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.BSize(aWM),
+ styleMargin->mMargin.GetBStart(aWM));
+ m.BEnd(aWM) = nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.BSize(aWM),
+ styleMargin->mMargin.GetBEnd(aWM));
+
+ SetComputedLogicalMargin(aWM, m);
+ }
+
+ // ... but font-size-inflation-based margin adjustment uses the
+ // frame's writing mode
+ nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(mFrame);
+
+ if (marginAdjustment > 0) {
+ LogicalMargin m = ComputedLogicalMargin();
+ m.IStart(mWritingMode) += marginAdjustment;
+ SetComputedLogicalMargin(m);
+ }
+
+ return isCBDependent;
+}
+
+bool
+SizeComputationInput::ComputePadding(WritingMode aWM,
+ const LogicalSize& aPercentBasis,
+ nsIAtom* aFrameType)
+{
+ // If style can provide us the padding directly, then use it.
+ const nsStylePadding *stylePadding = mFrame->StylePadding();
+ bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding());
+ // a table row/col group, row/col doesn't have padding
+ // XXXldb Neither do border-collapse tables.
+ if (nsGkAtoms::tableRowGroupFrame == aFrameType ||
+ nsGkAtoms::tableColGroupFrame == aFrameType ||
+ nsGkAtoms::tableRowFrame == aFrameType ||
+ nsGkAtoms::tableColFrame == aFrameType) {
+ ComputedPhysicalPadding().SizeTo(0,0,0,0);
+ }
+ else if (isCBDependent) {
+ // We have to compute the value. This calculation is performed
+ // according to the writing mode of the containing block
+ // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
+ // clamp negative calc() results to 0
+ LogicalMargin p(aWM);
+ p.IStart(aWM) = std::max(0, nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.ISize(aWM),
+ stylePadding->mPadding.GetIStart(aWM)));
+ p.IEnd(aWM) = std::max(0, nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.ISize(aWM),
+ stylePadding->mPadding.GetIEnd(aWM)));
+
+ p.BStart(aWM) = std::max(0, nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.BSize(aWM),
+ stylePadding->mPadding.GetBStart(aWM)));
+ p.BEnd(aWM) = std::max(0, nsLayoutUtils::
+ ComputeCBDependentValue(aPercentBasis.BSize(aWM),
+ stylePadding->mPadding.GetBEnd(aWM)));
+
+ SetComputedLogicalPadding(aWM, p);
+ }
+ return isCBDependent;
+}
+
+void
+ReflowInput::ComputeMinMaxValues(const LogicalSize&aCBSize)
+{
+ WritingMode wm = GetWritingMode();
+
+ const nsStyleCoord& minISize = mStylePosition->MinISize(wm);
+ const nsStyleCoord& maxISize = mStylePosition->MaxISize(wm);
+ const nsStyleCoord& minBSize = mStylePosition->MinBSize(wm);
+ const nsStyleCoord& maxBSize = mStylePosition->MaxBSize(wm);
+
+ // NOTE: min-width:auto resolves to 0, except on a flex item. (But
+ // even there, it's supposed to be ignored (i.e. treated as 0) until
+ // the flex container explicitly resolves & considers it.)
+ if (eStyleUnit_Auto == minISize.GetUnit()) {
+ ComputedMinISize() = 0;
+ } else {
+ ComputedMinISize() = ComputeISizeValue(aCBSize.ISize(wm),
+ mStylePosition->mBoxSizing,
+ minISize);
+ }
+
+ if (eStyleUnit_None == maxISize.GetUnit()) {
+ // Specified value of 'none'
+ ComputedMaxISize() = NS_UNCONSTRAINEDSIZE; // no limit
+ } else {
+ ComputedMaxISize() = ComputeISizeValue(aCBSize.ISize(wm),
+ mStylePosition->mBoxSizing,
+ maxISize);
+ }
+
+ // If the computed value of 'min-width' is greater than the value of
+ // 'max-width', 'max-width' is set to the value of 'min-width'
+ if (ComputedMinISize() > ComputedMaxISize()) {
+ ComputedMaxISize() = ComputedMinISize();
+ }
+
+ // Check for percentage based values and a containing block height that
+ // depends on the content height. Treat them like 'auto'
+ // Likewise, check for calc() with percentages on internal table elements;
+ // that's treated as 'auto' too.
+ // Likewise, if we're a child of a flex container who's measuring our
+ // intrinsic height, then we want to disregard our min-height.
+
+ // NOTE: min-height:auto resolves to 0, except on a flex item. (But
+ // even there, it's supposed to be ignored (i.e. treated as 0) until
+ // the flex container explicitly resolves & considers it.)
+ if (eStyleUnit_Auto == minBSize.GetUnit() ||
+ (NS_AUTOHEIGHT == aCBSize.BSize(wm) &&
+ minBSize.HasPercent()) ||
+ (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
+ minBSize.IsCalcUnit() && minBSize.CalcHasPercent()) ||
+ mFlags.mIsFlexContainerMeasuringHeight) {
+ ComputedMinBSize() = 0;
+ } else {
+ ComputedMinBSize() = ComputeBSizeValue(aCBSize.BSize(wm),
+ mStylePosition->mBoxSizing,
+ minBSize);
+ }
+ nsStyleUnit maxBSizeUnit = maxBSize.GetUnit();
+ if (eStyleUnit_None == maxBSizeUnit) {
+ // Specified value of 'none'
+ ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE; // no limit
+ } else {
+ // Check for percentage based values and a containing block height that
+ // depends on the content height. Treat them like 'none'
+ // Likewise, check for calc() with percentages on internal table elements;
+ // that's treated as 'auto' too.
+ // Likewise, if we're a child of a flex container who's measuring our
+ // intrinsic height, then we want to disregard our max-height.
+ if ((NS_AUTOHEIGHT == aCBSize.BSize(wm) &&
+ maxBSize.HasPercent()) ||
+ (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
+ maxBSize.IsCalcUnit() && maxBSize.CalcHasPercent()) ||
+ mFlags.mIsFlexContainerMeasuringHeight) {
+ ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE;
+ } else {
+ ComputedMaxBSize() = ComputeBSizeValue(aCBSize.BSize(wm),
+ mStylePosition->mBoxSizing,
+ maxBSize);
+ }
+ }
+
+ // If the computed value of 'min-height' is greater than the value of
+ // 'max-height', 'max-height' is set to the value of 'min-height'
+ if (ComputedMinBSize() > ComputedMaxBSize()) {
+ ComputedMaxBSize() = ComputedMinBSize();
+ }
+}
+
+void
+ReflowInput::SetTruncated(const ReflowOutput& aMetrics,
+ nsReflowStatus* aStatus) const
+{
+ const WritingMode containerWM = aMetrics.GetWritingMode();
+ if (GetWritingMode().IsOrthogonalTo(containerWM)) {
+ // Orthogonal flows are always reflowed with an unconstrained dimension,
+ // so should never end up truncated (see ReflowInput::Init()).
+ *aStatus &= ~NS_FRAME_TRUNCATED;
+ } else if (AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
+ AvailableBSize() < aMetrics.BSize(containerWM) &&
+ !mFlags.mIsTopOfPage) {
+ *aStatus |= NS_FRAME_TRUNCATED;
+ } else {
+ *aStatus &= ~NS_FRAME_TRUNCATED;
+ }
+}
+
+bool
+ReflowInput::IsFloating() const
+{
+ return mStyleDisplay->IsFloating(mFrame);
+}
+
+mozilla::StyleDisplay
+ReflowInput::GetDisplay() const
+{
+ return mStyleDisplay->GetDisplay(mFrame);
+}
diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h
new file mode 100644
index 000000000..e42508646
--- /dev/null
+++ b/layout/generic/ReflowInput.h
@@ -0,0 +1,1017 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* struct containing the input to nsIFrame::Reflow */
+
+#ifndef mozilla_ReflowInput_h
+#define mozilla_ReflowInput_h
+
+#include "nsMargin.h"
+#include "nsStyleCoord.h"
+#include "nsIFrame.h"
+#include "mozilla/Assertions.h"
+#include <algorithm>
+
+class nsPresContext;
+class nsRenderingContext;
+class nsFloatManager;
+class nsLineLayout;
+class nsIPercentBSizeObserver;
+struct nsHypotheticalPosition;
+
+/**
+ * @return aValue clamped to [aMinValue, aMaxValue].
+ *
+ * @note This function needs to handle aMinValue > aMaxValue. In that case,
+ * aMinValue is returned.
+ * @see http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
+ * @see http://www.w3.org/TR/CSS21/visudet.html#min-max-heights
+ */
+template <class NumericType>
+NumericType
+NS_CSS_MINMAX(NumericType aValue, NumericType aMinValue, NumericType aMaxValue)
+{
+ NumericType result = aValue;
+ if (aMaxValue < result)
+ result = aMaxValue;
+ if (aMinValue > result)
+ result = aMinValue;
+ return result;
+}
+
+/**
+ * CSS Frame type. Included as part of the reflow state.
+ */
+typedef uint32_t nsCSSFrameType;
+
+#define NS_CSS_FRAME_TYPE_UNKNOWN 0
+#define NS_CSS_FRAME_TYPE_INLINE 1
+#define NS_CSS_FRAME_TYPE_BLOCK 2 /* block-level in normal flow */
+#define NS_CSS_FRAME_TYPE_FLOATING 3
+#define NS_CSS_FRAME_TYPE_ABSOLUTE 4
+#define NS_CSS_FRAME_TYPE_INTERNAL_TABLE 5 /* row group frame, row frame, cell frame, ... */
+
+/**
+ * Bit-flag that indicates whether the element is replaced. Applies to inline,
+ * block-level, floating, and absolutely positioned elements
+ */
+#define NS_CSS_FRAME_TYPE_REPLACED 0x08000
+
+/**
+ * Bit-flag that indicates that the element is replaced and contains a block
+ * (eg some form controls). Applies to inline, block-level, floating, and
+ * absolutely positioned elements. Mutually exclusive with
+ * NS_CSS_FRAME_TYPE_REPLACED.
+ */
+#define NS_CSS_FRAME_TYPE_REPLACED_CONTAINS_BLOCK 0x10000
+
+/**
+ * Helper macros for telling whether items are replaced
+ */
+#define NS_FRAME_IS_REPLACED_NOBLOCK(_ft) \
+ (NS_CSS_FRAME_TYPE_REPLACED == ((_ft) & NS_CSS_FRAME_TYPE_REPLACED))
+
+#define NS_FRAME_IS_REPLACED(_ft) \
+ (NS_FRAME_IS_REPLACED_NOBLOCK(_ft) || \
+ NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(_ft))
+
+#define NS_FRAME_REPLACED(_ft) \
+ (NS_CSS_FRAME_TYPE_REPLACED | (_ft))
+
+#define NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(_ft) \
+ (NS_CSS_FRAME_TYPE_REPLACED_CONTAINS_BLOCK == \
+ ((_ft) & NS_CSS_FRAME_TYPE_REPLACED_CONTAINS_BLOCK))
+
+#define NS_FRAME_REPLACED_CONTAINS_BLOCK(_ft) \
+ (NS_CSS_FRAME_TYPE_REPLACED_CONTAINS_BLOCK | (_ft))
+
+/**
+ * A macro to extract the type. Masks off the 'replaced' bit-flag
+ */
+#define NS_FRAME_GET_TYPE(_ft) \
+ ((_ft) & ~(NS_CSS_FRAME_TYPE_REPLACED | \
+ NS_CSS_FRAME_TYPE_REPLACED_CONTAINS_BLOCK))
+
+namespace mozilla {
+
+// A base class of ReflowInput that computes only the padding,
+// border, and margin, since those values are needed more often.
+struct SizeComputationInput {
+public:
+ typedef mozilla::WritingMode WritingMode;
+ typedef mozilla::LogicalMargin LogicalMargin;
+
+ // The frame being reflowed.
+ nsIFrame* mFrame;
+
+ // Rendering context to use for measurement.
+ nsRenderingContext* mRenderingContext;
+
+ const nsMargin& ComputedPhysicalMargin() const { return mComputedMargin; }
+ const nsMargin& ComputedPhysicalBorderPadding() const { return mComputedBorderPadding; }
+ const nsMargin& ComputedPhysicalPadding() const { return mComputedPadding; }
+
+ // We may need to eliminate the (few) users of these writable-reference accessors
+ // as part of migrating to logical coordinates.
+ nsMargin& ComputedPhysicalMargin() { return mComputedMargin; }
+ nsMargin& ComputedPhysicalBorderPadding() { return mComputedBorderPadding; }
+ nsMargin& ComputedPhysicalPadding() { return mComputedPadding; }
+
+ const LogicalMargin ComputedLogicalMargin() const
+ { return LogicalMargin(mWritingMode, mComputedMargin); }
+ const LogicalMargin ComputedLogicalBorderPadding() const
+ { return LogicalMargin(mWritingMode, mComputedBorderPadding); }
+ const LogicalMargin ComputedLogicalPadding() const
+ { return LogicalMargin(mWritingMode, mComputedPadding); }
+
+ void SetComputedLogicalMargin(mozilla::WritingMode aWM,
+ const LogicalMargin& aMargin)
+ { mComputedMargin = aMargin.GetPhysicalMargin(aWM); }
+ void SetComputedLogicalMargin(const LogicalMargin& aMargin)
+ { SetComputedLogicalMargin(mWritingMode, aMargin); }
+
+ void SetComputedLogicalBorderPadding(mozilla::WritingMode aWM,
+ const LogicalMargin& aMargin)
+ { mComputedBorderPadding = aMargin.GetPhysicalMargin(aWM); }
+ void SetComputedLogicalBorderPadding(const LogicalMargin& aMargin)
+ { SetComputedLogicalBorderPadding(mWritingMode, aMargin); }
+
+ void SetComputedLogicalPadding(mozilla::WritingMode aWM,
+ const LogicalMargin& aMargin)
+ { mComputedPadding = aMargin.GetPhysicalMargin(aWM); }
+ void SetComputedLogicalPadding(const LogicalMargin& aMargin)
+ { SetComputedLogicalPadding(mWritingMode, aMargin); }
+
+ WritingMode GetWritingMode() const { return mWritingMode; }
+
+protected:
+ // cached copy of the frame's writing-mode, for logical coordinates
+ WritingMode mWritingMode;
+
+ // These are PHYSICAL coordinates (for now).
+ // Will probably become logical in due course.
+
+ // Computed margin values
+ nsMargin mComputedMargin;
+
+ // Cached copy of the border + padding values
+ nsMargin mComputedBorderPadding;
+
+ // Computed padding values
+ nsMargin mComputedPadding;
+
+public:
+ // Callers using this constructor must call InitOffsets on their own.
+ SizeComputationInput(nsIFrame *aFrame, nsRenderingContext *aRenderingContext)
+ : mFrame(aFrame)
+ , mRenderingContext(aRenderingContext)
+ , mWritingMode(aFrame->GetWritingMode())
+ {
+ }
+
+ SizeComputationInput(nsIFrame *aFrame, nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aContainingBlockWritingMode,
+ nscoord aContainingBlockISize);
+
+ struct ReflowInputFlags {
+ ReflowInputFlags() { memset(this, 0, sizeof(*this)); }
+ uint32_t mSpecialBSizeReflow:1; // used by tables to communicate special reflow (in process) to handle
+ // percent bsize frames inside cells which may not have computed bsizes
+ uint32_t mNextInFlowUntouched:1; // nothing in the frame's next-in-flow (or its descendants)
+ // is changing
+ uint32_t mIsTopOfPage:1; // Is the current context at the top of a
+ // page? When true, we force something
+ // that's too tall for a page/column to
+ // fit anyway to avoid infinite loops.
+ uint32_t mAssumingHScrollbar:1; // parent frame is an nsIScrollableFrame and it
+ // is assuming a horizontal scrollbar
+ uint32_t mAssumingVScrollbar:1; // parent frame is an nsIScrollableFrame and it
+ // is assuming a vertical scrollbar
+
+ uint32_t mIsIResize:1; // Is frame (a) not dirty and (b) a
+ // different inline-size than before?
+
+ uint32_t mIsBResize:1; // Is frame (a) not dirty and (b) a
+ // different block-size than before or
+ // (potentially) in a context where
+ // percent block-sizes have a different
+ // basis?
+ uint32_t mTableIsSplittable:1; // tables are splittable, this should happen only inside a page
+ // and never insider a column frame
+ uint32_t mHeightDependsOnAncestorCell:1; // Does frame height depend on
+ // an ancestor table-cell?
+ uint32_t mIsColumnBalancing:1; // nsColumnSetFrame is balancing columns
+ uint32_t mIsFlexContainerMeasuringHeight:1; // nsFlexContainerFrame is
+ // reflowing this child to
+ // measure its intrinsic height.
+ uint32_t mDummyParentReflowInput:1; // a "fake" reflow state made
+ // in order to be the parent
+ // of a real one
+ uint32_t mMustReflowPlaceholders:1; // Should this frame reflow its place-
+ // holder children? If the available
+ // height of this frame didn't change,
+ // but its in a paginated environment
+ // (e.g. columns), it should always
+ // reflow its placeholder children.
+ uint32_t mShrinkWrap:1; // stores the COMPUTE_SIZE_SHRINK_WRAP ctor flag
+ uint32_t mUseAutoBSize:1; // stores the COMPUTE_SIZE_USE_AUTO_BSIZE ctor flag
+ uint32_t mStaticPosIsCBOrigin:1; // the STATIC_POS_IS_CB_ORIGIN ctor flag
+ uint32_t mIClampMarginBoxMinSize:1; // the I_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag
+ uint32_t mBClampMarginBoxMinSize:1; // the B_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag
+
+ // If set, the following two flags indicate that:
+ // (1) this frame is absolutely-positioned (or fixed-positioned).
+ // (2) this frame's static position depends on the CSS Box Alignment.
+ // (3) we do need to compute the static position, because the frame's
+ // {Inline and/or Block} offsets actually depend on it.
+ // When these bits are set, the offset values (IStart/IEnd, BStart/BEnd)
+ // represent the "start" edge of the frame's CSS Box Alignment container
+ // area, in that axis -- and these offsets need to be further-resolved
+ // (with CSS Box Alignment) after we know the OOF frame's size.
+ // NOTE: The "I" and "B" (for "Inline" and "Block") refer the axes of the
+ // *containing block's writing-mode*, NOT mFrame's own writing-mode. This
+ // is purely for convenience, since that's the writing-mode we're dealing
+ // with when we set & react to these bits.
+ uint32_t mIOffsetsNeedCSSAlign:1;
+ uint32_t mBOffsetsNeedCSSAlign:1;
+ };
+
+#ifdef DEBUG
+ // Reflow trace methods. Defined in nsFrame.cpp so they have access
+ // to the display-reflow infrastructure.
+ static void* DisplayInitOffsetsEnter(
+ nsIFrame* aFrame,
+ SizeComputationInput* aState,
+ const mozilla::LogicalSize& aPercentBasis,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding);
+ static void DisplayInitOffsetsExit(nsIFrame* aFrame,
+ SizeComputationInput* aState,
+ void* aValue);
+#endif
+
+private:
+ /**
+ * Computes margin values from the specified margin style information, and
+ * fills in the mComputedMargin member.
+ *
+ * @param aWM Writing mode of the containing block
+ * @param aPercentBasis
+ * Logical size in the writing mode of the containing block to use
+ * for resolving percentage margin values in the inline and block
+ * axes.
+ * The inline size is usually the containing block inline-size
+ * (width if writing mode is horizontal, and height if vertical).
+ * The block size is usually the containing block inline-size, per
+ * CSS21 sec 8.3 (read in conjunction with CSS Writing Modes sec
+ * 7.2), but may be the containing block block-size, e.g. in CSS3
+ * Flexbox and Grid.
+ * @return true if the margin is dependent on the containing block size.
+ */
+ bool ComputeMargin(mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aPercentBasis);
+
+ /**
+ * Computes padding values from the specified padding style information, and
+ * fills in the mComputedPadding member.
+ *
+ * @param aWM Writing mode of the containing block
+ * @param aPercentBasis
+ * Logical size in the writing mode of the containing block to use
+ * for resolving percentage padding values in the inline and block
+ * axes.
+ * The inline size is usually the containing block inline-size
+ * (width if writing mode is horizontal, and height if vertical).
+ * The block size is usually the containing block inline-size, per
+ * CSS21 sec 8.3 (read in conjunction with CSS Writing Modes sec
+ * 7.2), but may be the containing block block-size, e.g. in CSS3
+ * Flexbox and Grid.
+ * @return true if the padding is dependent on the containing block size.
+ */
+ bool ComputePadding(mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aPercentBasis,
+ nsIAtom* aFrameType);
+
+protected:
+
+ void InitOffsets(mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aPercentBasis,
+ nsIAtom* aFrameType,
+ ReflowInputFlags aFlags,
+ const nsMargin* aBorder = nullptr,
+ const nsMargin* aPadding = nullptr);
+
+ /*
+ * Convert nsStyleCoord to nscoord when percentages depend on the
+ * inline size of the containing block, and enumerated values are for
+ * inline size, min-inline-size, or max-inline-size. Does not handle
+ * auto inline sizes.
+ */
+ inline nscoord ComputeISizeValue(nscoord aContainingBlockISize,
+ nscoord aContentEdgeToBoxSizing,
+ nscoord aBoxSizingToMarginEdge,
+ const nsStyleCoord& aCoord) const;
+ // same as previous, but using mComputedBorderPadding, mComputedPadding,
+ // and mComputedMargin
+ nscoord ComputeISizeValue(nscoord aContainingBlockISize,
+ mozilla::StyleBoxSizing aBoxSizing,
+ const nsStyleCoord& aCoord) const;
+
+ nscoord ComputeBSizeValue(nscoord aContainingBlockBSize,
+ mozilla::StyleBoxSizing aBoxSizing,
+ const nsStyleCoord& aCoord) const;
+};
+
+/**
+ * State passed to a frame during reflow or intrinsic size calculation.
+ *
+ * XXX Refactor so only a base class (nsSizingState?) is used for intrinsic
+ * size calculation.
+ *
+ * @see nsIFrame#Reflow()
+ */
+struct ReflowInput : public SizeComputationInput {
+ // the reflow states are linked together. this is the pointer to the
+ // parent's reflow state
+ const ReflowInput* mParentReflowInput;
+
+ // pointer to the float manager associated with this area
+ nsFloatManager* mFloatManager;
+
+ // LineLayout object (only for inline reflow; set to nullptr otherwise)
+ nsLineLayout* mLineLayout;
+
+ // The appropriate reflow state for the containing block (for
+ // percentage widths, etc.) of this reflow state's frame.
+ MOZ_INIT_OUTSIDE_CTOR
+ const ReflowInput *mCBReflowInput;
+
+ // The type of frame, from css's perspective. This value is
+ // initialized by the Init method below.
+ MOZ_INIT_OUTSIDE_CTOR
+ nsCSSFrameType mFrameType;
+
+ // The amount the in-flow position of the block is moving vertically relative
+ // to its previous in-flow position (i.e. the amount the line containing the
+ // block is moving).
+ // This should be zero for anything which is not a block outside, and it
+ // should be zero for anything which has a non-block parent.
+ // The intended use of this value is to allow the accurate determination
+ // of the potential impact of a float
+ // This takes on an arbitrary value the first time a block is reflowed
+ nscoord mBlockDelta;
+
+ // If an ReflowInput finds itself initialized with an unconstrained
+ // inline-size, it will look up its parentReflowInput chain for a state
+ // with an orthogonal writing mode and a non-NS_UNCONSTRAINEDSIZE value for
+ // orthogonal limit; when it finds such a reflow-state, it will use its
+ // orthogonal-limit value to constrain inline-size.
+ // This is initialized to NS_UNCONSTRAINEDSIZE (so it will be ignored),
+ // but reset to a suitable value for the reflow root by nsPresShell.
+ nscoord mOrthogonalLimit;
+
+ // Accessors for the private fields below. Forcing all callers to use these
+ // will allow us to introduce logical-coordinate versions and gradually
+ // change clients from physical to logical as needed; and potentially switch
+ // the internal fields from physical to logical coordinates in due course,
+ // while maintaining compatibility with not-yet-updated code.
+ nscoord AvailableWidth() const { return mAvailableWidth; }
+ nscoord AvailableHeight() const { return mAvailableHeight; }
+ nscoord ComputedWidth() const { return mComputedWidth; }
+ nscoord ComputedHeight() const { return mComputedHeight; }
+ nscoord ComputedMinWidth() const { return mComputedMinWidth; }
+ nscoord ComputedMaxWidth() const { return mComputedMaxWidth; }
+ nscoord ComputedMinHeight() const { return mComputedMinHeight; }
+ nscoord ComputedMaxHeight() const { return mComputedMaxHeight; }
+
+ nscoord& AvailableWidth() { return mAvailableWidth; }
+ nscoord& AvailableHeight() { return mAvailableHeight; }
+ nscoord& ComputedWidth() { return mComputedWidth; }
+ nscoord& ComputedHeight() { return mComputedHeight; }
+ nscoord& ComputedMinWidth() { return mComputedMinWidth; }
+ nscoord& ComputedMaxWidth() { return mComputedMaxWidth; }
+ nscoord& ComputedMinHeight() { return mComputedMinHeight; }
+ nscoord& ComputedMaxHeight() { return mComputedMaxHeight; }
+
+ // ISize and BSize are logical-coordinate dimensions:
+ // ISize is the size in the writing mode's inline direction (which equates to
+ // width in horizontal writing modes, height in vertical ones), and BSize is
+ // the size in the block-progression direction.
+ nscoord AvailableISize() const
+ { return mWritingMode.IsVertical() ? mAvailableHeight : mAvailableWidth; }
+ nscoord AvailableBSize() const
+ { return mWritingMode.IsVertical() ? mAvailableWidth : mAvailableHeight; }
+ nscoord ComputedISize() const
+ { return mWritingMode.IsVertical() ? mComputedHeight : mComputedWidth; }
+ nscoord ComputedBSize() const
+ { return mWritingMode.IsVertical() ? mComputedWidth : mComputedHeight; }
+ nscoord ComputedMinISize() const
+ { return mWritingMode.IsVertical() ? mComputedMinHeight : mComputedMinWidth; }
+ nscoord ComputedMaxISize() const
+ { return mWritingMode.IsVertical() ? mComputedMaxHeight : mComputedMaxWidth; }
+ nscoord ComputedMinBSize() const
+ { return mWritingMode.IsVertical() ? mComputedMinWidth : mComputedMinHeight; }
+ nscoord ComputedMaxBSize() const
+ { return mWritingMode.IsVertical() ? mComputedMaxWidth : mComputedMaxHeight; }
+
+ nscoord& AvailableISize()
+ { return mWritingMode.IsVertical() ? mAvailableHeight : mAvailableWidth; }
+ nscoord& AvailableBSize()
+ { return mWritingMode.IsVertical() ? mAvailableWidth : mAvailableHeight; }
+ nscoord& ComputedISize()
+ { return mWritingMode.IsVertical() ? mComputedHeight : mComputedWidth; }
+ nscoord& ComputedBSize()
+ { return mWritingMode.IsVertical() ? mComputedWidth : mComputedHeight; }
+ nscoord& ComputedMinISize()
+ { return mWritingMode.IsVertical() ? mComputedMinHeight : mComputedMinWidth; }
+ nscoord& ComputedMaxISize()
+ { return mWritingMode.IsVertical() ? mComputedMaxHeight : mComputedMaxWidth; }
+ nscoord& ComputedMinBSize()
+ { return mWritingMode.IsVertical() ? mComputedMinWidth : mComputedMinHeight; }
+ nscoord& ComputedMaxBSize()
+ { return mWritingMode.IsVertical() ? mComputedMaxWidth : mComputedMaxHeight; }
+
+ mozilla::LogicalSize AvailableSize() const {
+ return mozilla::LogicalSize(mWritingMode,
+ AvailableISize(), AvailableBSize());
+ }
+ mozilla::LogicalSize ComputedSize() const {
+ return mozilla::LogicalSize(mWritingMode,
+ ComputedISize(), ComputedBSize());
+ }
+ mozilla::LogicalSize ComputedMinSize() const {
+ return mozilla::LogicalSize(mWritingMode,
+ ComputedMinISize(), ComputedMinBSize());
+ }
+ mozilla::LogicalSize ComputedMaxSize() const {
+ return mozilla::LogicalSize(mWritingMode,
+ ComputedMaxISize(), ComputedMaxBSize());
+ }
+
+ mozilla::LogicalSize AvailableSize(mozilla::WritingMode aWM) const
+ { return AvailableSize().ConvertTo(aWM, mWritingMode); }
+ mozilla::LogicalSize ComputedSize(mozilla::WritingMode aWM) const
+ { return ComputedSize().ConvertTo(aWM, mWritingMode); }
+ mozilla::LogicalSize ComputedMinSize(mozilla::WritingMode aWM) const
+ { return ComputedMinSize().ConvertTo(aWM, mWritingMode); }
+ mozilla::LogicalSize ComputedMaxSize(mozilla::WritingMode aWM) const
+ { return ComputedMaxSize().ConvertTo(aWM, mWritingMode); }
+
+ mozilla::LogicalSize ComputedSizeWithPadding() const {
+ mozilla::WritingMode wm = GetWritingMode();
+ return mozilla::LogicalSize(wm,
+ ComputedISize() +
+ ComputedLogicalPadding().IStartEnd(wm),
+ ComputedBSize() +
+ ComputedLogicalPadding().BStartEnd(wm));
+ }
+
+ mozilla::LogicalSize ComputedSizeWithPadding(mozilla::WritingMode aWM) const {
+ return ComputedSizeWithPadding().ConvertTo(aWM, GetWritingMode());
+ }
+
+ mozilla::LogicalSize ComputedSizeWithBorderPadding() const {
+ mozilla::WritingMode wm = GetWritingMode();
+ return mozilla::LogicalSize(wm,
+ ComputedISize() +
+ ComputedLogicalBorderPadding().IStartEnd(wm),
+ ComputedBSize() +
+ ComputedLogicalBorderPadding().BStartEnd(wm));
+ }
+
+ mozilla::LogicalSize
+ ComputedSizeWithBorderPadding(mozilla::WritingMode aWM) const {
+ return ComputedSizeWithBorderPadding().ConvertTo(aWM, GetWritingMode());
+ }
+
+ mozilla::LogicalSize
+ ComputedSizeWithMarginBorderPadding() const {
+ mozilla::WritingMode wm = GetWritingMode();
+ return mozilla::LogicalSize(wm,
+ ComputedISize() +
+ ComputedLogicalMargin().IStartEnd(wm) +
+ ComputedLogicalBorderPadding().IStartEnd(wm),
+ ComputedBSize() +
+ ComputedLogicalMargin().BStartEnd(wm) +
+ ComputedLogicalBorderPadding().BStartEnd(wm));
+ }
+
+ mozilla::LogicalSize
+ ComputedSizeWithMarginBorderPadding(mozilla::WritingMode aWM) const {
+ return ComputedSizeWithMarginBorderPadding().ConvertTo(aWM,
+ GetWritingMode());
+ }
+
+ nsSize
+ ComputedPhysicalSize() const {
+ return nsSize(ComputedWidth(), ComputedHeight());
+ }
+
+ // XXX this will need to change when we make mComputedOffsets logical;
+ // we won't be able to return a reference for the physical offsets
+ const nsMargin& ComputedPhysicalOffsets() const { return mComputedOffsets; }
+ nsMargin& ComputedPhysicalOffsets() { return mComputedOffsets; }
+
+ const LogicalMargin ComputedLogicalOffsets() const
+ { return LogicalMargin(mWritingMode, mComputedOffsets); }
+
+ void SetComputedLogicalOffsets(const LogicalMargin& aOffsets)
+ { mComputedOffsets = aOffsets.GetPhysicalMargin(mWritingMode); }
+
+ // Return the state's computed size including border-padding, with
+ // unconstrained dimensions replaced by zero.
+ nsSize ComputedSizeAsContainerIfConstrained() const {
+ const nscoord wd = ComputedWidth();
+ const nscoord ht = ComputedHeight();
+ return nsSize(wd == NS_UNCONSTRAINEDSIZE
+ ? 0 : wd + ComputedPhysicalBorderPadding().LeftRight(),
+ ht == NS_UNCONSTRAINEDSIZE
+ ? 0 : ht + ComputedPhysicalBorderPadding().TopBottom());
+ }
+
+private:
+ // the available width in which to reflow the frame. The space
+ // represents the amount of room for the frame's margin, border,
+ // padding, and content area. The frame size you choose should fit
+ // within the available width.
+ nscoord mAvailableWidth;
+
+ // A value of NS_UNCONSTRAINEDSIZE for the available height means
+ // you can choose whatever size you want. In galley mode the
+ // available height is always NS_UNCONSTRAINEDSIZE, and only page
+ // mode or multi-column layout involves a constrained height. The
+ // element's the top border and padding, and content, must fit. If the
+ // element is complete after reflow then its bottom border, padding
+ // and margin (and similar for its complete ancestors) will need to
+ // fit in this height.
+ nscoord mAvailableHeight;
+
+ // The computed width specifies the frame's content area width, and it does
+ // not apply to inline non-replaced elements
+ //
+ // For replaced inline frames, a value of NS_INTRINSICSIZE means you should
+ // use your intrinsic width as the computed width
+ //
+ // For block-level frames, the computed width is based on the width of the
+ // containing block, the margin/border/padding areas, and the min/max width.
+ MOZ_INIT_OUTSIDE_CTOR
+ nscoord mComputedWidth;
+
+ // The computed height specifies the frame's content height, and it does
+ // not apply to inline non-replaced elements
+ //
+ // For replaced inline frames, a value of NS_INTRINSICSIZE means you should
+ // use your intrinsic height as the computed height
+ //
+ // For non-replaced block-level frames in the flow and floated, a value of
+ // NS_AUTOHEIGHT means you choose a height to shrink wrap around the normal
+ // flow child frames. The height must be within the limit of the min/max
+ // height if there is such a limit
+ //
+ // For replaced block-level frames, a value of NS_INTRINSICSIZE
+ // means you use your intrinsic height as the computed height
+ MOZ_INIT_OUTSIDE_CTOR
+ nscoord mComputedHeight;
+
+ // Computed values for 'left/top/right/bottom' offsets. Only applies to
+ // 'positioned' elements. These are PHYSICAL coordinates (for now).
+ nsMargin mComputedOffsets;
+
+ // Computed values for 'min-width/max-width' and 'min-height/max-height'
+ // XXXldb The width ones here should go; they should be needed only
+ // internally.
+ MOZ_INIT_OUTSIDE_CTOR
+ nscoord mComputedMinWidth, mComputedMaxWidth;
+ MOZ_INIT_OUTSIDE_CTOR
+ nscoord mComputedMinHeight, mComputedMaxHeight;
+
+public:
+ // Cached pointers to the various style structs used during intialization
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStyleDisplay* mStyleDisplay;
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStyleVisibility* mStyleVisibility;
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStylePosition* mStylePosition;
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStyleBorder* mStyleBorder;
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStyleMargin* mStyleMargin;
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStylePadding* mStylePadding;
+ MOZ_INIT_OUTSIDE_CTOR
+ const nsStyleText* mStyleText;
+
+ bool IsFloating() const;
+
+ mozilla::StyleDisplay GetDisplay() const;
+
+ // a frame (e.g. nsTableCellFrame) which may need to generate a special
+ // reflow for percent bsize calculations
+ nsIPercentBSizeObserver* mPercentBSizeObserver;
+
+ // CSS margin collapsing sometimes requires us to reflow
+ // optimistically assuming that margins collapse to see if clearance
+ // is required. When we discover that clearance is required, we
+ // store the frame in which clearance was discovered to the location
+ // requested here.
+ nsIFrame** mDiscoveredClearance;
+
+ ReflowInputFlags mFlags;
+
+ // This value keeps track of how deeply nested a given reflow state
+ // is from the top of the frame tree.
+ int16_t mReflowDepth;
+
+ // Logical and physical accessors for the resize flags. All users should go
+ // via these accessors, so that in due course we can change the storage from
+ // physical to logical.
+ bool IsHResize() const {
+ return mWritingMode.IsVertical() ? mFlags.mIsBResize : mFlags.mIsIResize;
+ }
+ bool IsVResize() const {
+ return mWritingMode.IsVertical() ? mFlags.mIsIResize : mFlags.mIsBResize;
+ }
+ bool IsIResize() const {
+ return mFlags.mIsIResize;
+ }
+ bool IsBResize() const {
+ return mFlags.mIsBResize;
+ }
+ bool IsBResizeForWM(mozilla::WritingMode aWM) const {
+ return aWM.IsOrthogonalTo(mWritingMode) ? mFlags.mIsIResize
+ : mFlags.mIsBResize;
+ }
+ void SetHResize(bool aValue) {
+ if (mWritingMode.IsVertical()) {
+ mFlags.mIsBResize = aValue;
+ } else {
+ mFlags.mIsIResize = aValue;
+ }
+ }
+ void SetVResize(bool aValue) {
+ if (mWritingMode.IsVertical()) {
+ mFlags.mIsIResize = aValue;
+ } else {
+ mFlags.mIsBResize = aValue;
+ }
+ }
+ void SetIResize(bool aValue) {
+ mFlags.mIsIResize = aValue;
+ }
+ void SetBResize(bool aValue) {
+ mFlags.mIsBResize = aValue;
+ }
+
+ // Note: The copy constructor is written by the compiler automatically. You
+ // can use that and then override specific values if you want, or you can
+ // call Init as desired...
+
+ /**
+ * Initialize a ROOT reflow state.
+ *
+ * @param aPresContext Must be equal to aFrame->PresContext().
+ * @param aFrame The frame for whose reflow state is being constructed.
+ * @param aRenderingContext The rendering context to be used for measurements.
+ * @param aAvailableSpace See comments for availableHeight and availableWidth
+ * members.
+ * @param aFlags A set of flags used for additional boolean parameters (see
+ * below).
+ */
+ ReflowInput(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsRenderingContext* aRenderingContext,
+ const mozilla::LogicalSize& aAvailableSpace,
+ uint32_t aFlags = 0);
+
+ /**
+ * Initialize a reflow state for a child frame's reflow. Some parts of the
+ * state are copied from the parent's reflow state. The remainder is computed.
+ *
+ * @param aPresContext Must be equal to aFrame->PresContext().
+ * @param aParentReflowInput A reference to an ReflowInput object that
+ * is to be the parent of this object.
+ * @param aFrame The frame for whose reflow state is being constructed.
+ * @param aAvailableSpace See comments for availableHeight and availableWidth
+ * members.
+ * @param aContainingBlockSize An optional size, in app units, specifying
+ * the containing block size to use instead of the default which is
+ * to use the aAvailableSpace.
+ * @param aFlags A set of flags used for additional boolean parameters (see
+ * below).
+ */
+ ReflowInput(nsPresContext* aPresContext,
+ const ReflowInput& aParentReflowInput,
+ nsIFrame* aFrame,
+ const mozilla::LogicalSize& aAvailableSpace,
+ const mozilla::LogicalSize* aContainingBlockSize = nullptr,
+ uint32_t aFlags = 0);
+
+ // Values for |aFlags| passed to constructor
+ enum {
+ // Indicates that the parent of this reflow state is "fake" (see
+ // mDummyParentReflowInput in mFlags).
+ DUMMY_PARENT_REFLOW_STATE = (1<<0),
+
+ // Indicates that the calling function will initialize the reflow state, and
+ // that the constructor should not call Init().
+ CALLER_WILL_INIT = (1<<1),
+
+ // The caller wants shrink-wrap behavior (i.e. ComputeSizeFlags::eShrinkWrap
+ // will be passed to ComputeSize()).
+ COMPUTE_SIZE_SHRINK_WRAP = (1<<2),
+
+ // The caller wants 'auto' bsize behavior (ComputeSizeFlags::eUseAutoBSize
+ // will be be passed to ComputeSize()).
+ COMPUTE_SIZE_USE_AUTO_BSIZE = (1<<3),
+
+ // The caller wants the abs.pos. static-position resolved at the origin of
+ // the containing block, i.e. at LogicalPoint(0, 0). (Note that this
+ // doesn't necessarily mean that (0, 0) is the *correct* static position
+ // for the frame in question.)
+ STATIC_POS_IS_CB_ORIGIN = (1<<4),
+
+ // Pass ComputeSizeFlags::eIClampMarginBoxMinSize to ComputeSize().
+ I_CLAMP_MARGIN_BOX_MIN_SIZE = (1<<5),
+
+ // Pass ComputeSizeFlags::eBClampMarginBoxMinSize to ComputeSize().
+ B_CLAMP_MARGIN_BOX_MIN_SIZE = (1<<6),
+ };
+
+ // This method initializes various data members. It is automatically
+ // called by the various constructors
+ void Init(nsPresContext* aPresContext,
+ const mozilla::LogicalSize* aContainingBlockSize = nullptr,
+ const nsMargin* aBorder = nullptr,
+ const nsMargin* aPadding = nullptr);
+
+ /**
+ * Find the content isize of our containing block for the given writing mode,
+ * which need not be the same as the reflow state's mode.
+ */
+ nscoord GetContainingBlockContentISize(mozilla::WritingMode aWritingMode) const;
+
+ /**
+ * Calculate the used line-height property. The return value will be >= 0.
+ */
+ nscoord CalcLineHeight() const;
+
+ /**
+ * Same as CalcLineHeight() above, but doesn't need a reflow state.
+ *
+ * @param aBlockBSize The computed block size of the content rect of the block
+ * that the line should fill.
+ * Only used with line-height:-moz-block-height.
+ * NS_AUTOHEIGHT results in a normal line-height for
+ * line-height:-moz-block-height.
+ * @param aFontSizeInflation The result of the appropriate
+ * nsLayoutUtils::FontSizeInflationFor call,
+ * or 1.0 if during intrinsic size
+ * calculation.
+ */
+ static nscoord CalcLineHeight(nsIContent* aContent,
+ nsStyleContext* aStyleContext,
+ nscoord aBlockBSize,
+ float aFontSizeInflation);
+
+
+ mozilla::LogicalSize ComputeContainingBlockRectangle(
+ nsPresContext* aPresContext,
+ const ReflowInput* aContainingBlockRI) const;
+
+ /**
+ * Apply the mComputed(Min/Max)Width constraints to the content
+ * size computed so far.
+ */
+ nscoord ApplyMinMaxWidth(nscoord aWidth) const {
+ if (NS_UNCONSTRAINEDSIZE != ComputedMaxWidth()) {
+ aWidth = std::min(aWidth, ComputedMaxWidth());
+ }
+ return std::max(aWidth, ComputedMinWidth());
+ }
+
+ /**
+ * Apply the mComputed(Min/Max)ISize constraints to the content
+ * size computed so far.
+ */
+ nscoord ApplyMinMaxISize(nscoord aISize) const {
+ if (NS_UNCONSTRAINEDSIZE != ComputedMaxISize()) {
+ aISize = std::min(aISize, ComputedMaxISize());
+ }
+ return std::max(aISize, ComputedMinISize());
+ }
+
+ /**
+ * Apply the mComputed(Min/Max)Height constraints to the content
+ * size computed so far.
+ *
+ * @param aHeight The height that we've computed an to which we want to apply
+ * min/max constraints.
+ * @param aConsumed The amount of the computed height that was consumed by
+ * our prev-in-flows.
+ */
+ nscoord ApplyMinMaxHeight(nscoord aHeight, nscoord aConsumed = 0) const {
+ aHeight += aConsumed;
+
+ if (NS_UNCONSTRAINEDSIZE != ComputedMaxHeight()) {
+ aHeight = std::min(aHeight, ComputedMaxHeight());
+ }
+
+ if (NS_UNCONSTRAINEDSIZE != ComputedMinHeight()) {
+ aHeight = std::max(aHeight, ComputedMinHeight());
+ }
+
+ return aHeight - aConsumed;
+ }
+
+ /**
+ * Apply the mComputed(Min/Max)BSize constraints to the content
+ * size computed so far.
+ *
+ * @param aBSize The block-size that we've computed an to which we want to apply
+ * min/max constraints.
+ * @param aConsumed The amount of the computed block-size that was consumed by
+ * our prev-in-flows.
+ */
+ nscoord ApplyMinMaxBSize(nscoord aBSize, nscoord aConsumed = 0) const {
+ aBSize += aConsumed;
+
+ if (NS_UNCONSTRAINEDSIZE != ComputedMaxBSize()) {
+ aBSize = std::min(aBSize, ComputedMaxBSize());
+ }
+
+ if (NS_UNCONSTRAINEDSIZE != ComputedMinBSize()) {
+ aBSize = std::max(aBSize, ComputedMinBSize());
+ }
+
+ return aBSize - aConsumed;
+ }
+
+ bool ShouldReflowAllKids() const {
+ // Note that we could make a stronger optimization for IsBResize if
+ // we use it in a ShouldReflowChild test that replaces the current
+ // checks of NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN, if it
+ // were tested there along with NS_FRAME_CONTAINS_RELATIVE_BSIZE.
+ // This would need to be combined with a slight change in which
+ // frames NS_FRAME_CONTAINS_RELATIVE_BSIZE is marked on.
+ return (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) ||
+ IsIResize() ||
+ (IsBResize() &&
+ (mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE));
+ }
+
+ // This method doesn't apply min/max computed widths to the value passed in.
+ void SetComputedWidth(nscoord aComputedWidth);
+
+ // This method doesn't apply min/max computed heights to the value passed in.
+ void SetComputedHeight(nscoord aComputedHeight);
+
+ void SetComputedISize(nscoord aComputedISize) {
+ if (mWritingMode.IsVertical()) {
+ SetComputedHeight(aComputedISize);
+ } else {
+ SetComputedWidth(aComputedISize);
+ }
+ }
+
+ void SetComputedBSize(nscoord aComputedBSize) {
+ if (mWritingMode.IsVertical()) {
+ SetComputedWidth(aComputedBSize);
+ } else {
+ SetComputedHeight(aComputedBSize);
+ }
+ }
+
+ void SetComputedBSizeWithoutResettingResizeFlags(nscoord aComputedBSize) {
+ // Viewport frames reset the computed block size on a copy of their reflow
+ // state when reflowing fixed-pos kids. In that case we actually don't
+ // want to mess with the resize flags, because comparing the frame's rect
+ // to the munged computed isize is pointless.
+ ComputedBSize() = aComputedBSize;
+ }
+
+ void SetTruncated(const ReflowOutput& aMetrics, nsReflowStatus* aStatus) const;
+
+ bool WillReflowAgainForClearance() const {
+ return mDiscoveredClearance && *mDiscoveredClearance;
+ }
+
+ // Compute the offsets for a relative position element
+ static void ComputeRelativeOffsets(mozilla::WritingMode aWM,
+ nsIFrame* aFrame,
+ const mozilla::LogicalSize& aCBSize,
+ nsMargin& aComputedOffsets);
+
+ // If a relatively positioned element, adjust the position appropriately.
+ static void ApplyRelativePositioning(nsIFrame* aFrame,
+ const nsMargin& aComputedOffsets,
+ nsPoint* aPosition);
+
+ void ApplyRelativePositioning(nsPoint* aPosition) const {
+ ApplyRelativePositioning(mFrame, ComputedPhysicalOffsets(), aPosition);
+ }
+
+ static void
+ ApplyRelativePositioning(nsIFrame* aFrame,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalMargin& aComputedOffsets,
+ mozilla::LogicalPoint* aPosition,
+ const nsSize& aContainerSize) {
+ // Subtract the size of the frame from the container size that we
+ // use for converting between the logical and physical origins of
+ // the frame. This accounts for the fact that logical origins in RTL
+ // coordinate systems are at the top right of the frame instead of
+ // the top left.
+ nsSize frameSize = aFrame->GetSize();
+ nsPoint pos = aPosition->GetPhysicalPoint(aWritingMode,
+ aContainerSize - frameSize);
+ ApplyRelativePositioning(aFrame,
+ aComputedOffsets.GetPhysicalMargin(aWritingMode),
+ &pos);
+ *aPosition = mozilla::LogicalPoint(aWritingMode, pos,
+ aContainerSize - frameSize);
+ }
+
+ void ApplyRelativePositioning(mozilla::LogicalPoint* aPosition,
+ const nsSize& aContainerSize) const {
+ ApplyRelativePositioning(mFrame, mWritingMode,
+ ComputedLogicalOffsets(), aPosition,
+ aContainerSize);
+ }
+
+#ifdef DEBUG
+ // Reflow trace methods. Defined in nsFrame.cpp so they have access
+ // to the display-reflow infrastructure.
+ static void* DisplayInitConstraintsEnter(nsIFrame* aFrame,
+ ReflowInput* aState,
+ nscoord aCBISize,
+ nscoord aCBBSize,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding);
+ static void DisplayInitConstraintsExit(nsIFrame* aFrame,
+ ReflowInput* aState,
+ void* aValue);
+ static void* DisplayInitFrameTypeEnter(nsIFrame* aFrame,
+ ReflowInput* aState);
+ static void DisplayInitFrameTypeExit(nsIFrame* aFrame,
+ ReflowInput* aState,
+ void* aValue);
+#endif
+
+protected:
+ void InitFrameType(nsIAtom* aFrameType);
+ void InitCBReflowInput();
+ void InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType);
+
+ void InitConstraints(nsPresContext* aPresContext,
+ const mozilla::LogicalSize& aContainingBlockSize,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding,
+ nsIAtom* aFrameType);
+
+ // Returns the nearest containing block or block frame (whether or not
+ // it is a containing block) for the specified frame. Also returns
+ // the inline-start edge and logical size of the containing block's
+ // content area.
+ // These are returned in the coordinate space of the containing block.
+ nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame,
+ nscoord& aCBIStartEdge,
+ mozilla::LogicalSize& aCBSize) const;
+
+ // Calculate a "hypothetical box" position where the placeholder frame
+ // (for a position:fixed/absolute element) would have been placed if it were
+ // positioned statically. The hypothetical box position will have a writing
+ // mode with the same block direction as the absolute containing block
+ // (cbrs->frame), though it may differ in inline direction.
+ void CalculateHypotheticalPosition(nsPresContext* aPresContext,
+ nsIFrame* aPlaceholderFrame,
+ const ReflowInput* cbrs,
+ nsHypotheticalPosition& aHypotheticalPos,
+ nsIAtom* aFrameType) const;
+
+ void InitAbsoluteConstraints(nsPresContext* aPresContext,
+ const ReflowInput* cbrs,
+ const mozilla::LogicalSize& aContainingBlockSize,
+ nsIAtom* aFrameType);
+
+ // Calculates the computed values for the 'min-Width', 'max-Width',
+ // 'min-Height', and 'max-Height' properties, and stores them in the assorted
+ // data members
+ void ComputeMinMaxValues(const mozilla::LogicalSize& aContainingBlockSize);
+
+ // aInsideBoxSizing returns the part of the padding, border, and margin
+ // in the aAxis dimension that goes inside the edge given by box-sizing;
+ // aOutsideBoxSizing returns the rest.
+ void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis,
+ nscoord aContainingBlockSize,
+ nscoord* aInsideBoxSizing,
+ nscoord* aOutsideBoxSizing) const;
+
+ void CalculateBlockSideMargins(nsIAtom* aFrameType);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ReflowInput_h
diff --git a/layout/generic/ReflowOutput.cpp b/layout/generic/ReflowOutput.cpp
new file mode 100644
index 000000000..e64e41dc5
--- /dev/null
+++ b/layout/generic/ReflowOutput.cpp
@@ -0,0 +1,67 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* struct containing the output from nsIFrame::Reflow */
+
+#include "mozilla/ReflowOutput.h"
+#include "mozilla/ReflowInput.h"
+
+void
+nsOverflowAreas::UnionWith(const nsOverflowAreas& aOther)
+{
+ // FIXME: We should probably change scrollable overflow to use
+ // UnionRectIncludeEmpty (but leave visual overflow using UnionRect).
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ mRects[otype].UnionRect(mRects[otype], aOther.mRects[otype]);
+ }
+}
+
+void
+nsOverflowAreas::UnionAllWith(const nsRect& aRect)
+{
+ // FIXME: We should probably change scrollable overflow to use
+ // UnionRectIncludeEmpty (but leave visual overflow using UnionRect).
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ mRects[otype].UnionRect(mRects[otype], aRect);
+ }
+}
+
+void
+nsOverflowAreas::SetAllTo(const nsRect& aRect)
+{
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ mRects[otype] = aRect;
+ }
+}
+
+ReflowOutput::ReflowOutput(const ReflowInput& aState,
+ uint32_t aFlags)
+ : mISize(0)
+ , mBSize(0)
+ , mBlockStartAscent(ASK_FOR_BASELINE)
+ , mFlags(aFlags)
+ , mWritingMode(aState.GetWritingMode())
+{
+}
+
+void
+ReflowOutput::SetOverflowAreasToDesiredBounds()
+{
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ mOverflowAreas.Overflow(otype).SetRect(0, 0, Width(), Height());
+ }
+}
+
+void
+ReflowOutput::UnionOverflowAreasWithDesiredBounds()
+{
+ // FIXME: We should probably change scrollable overflow to use
+ // UnionRectIncludeEmpty (but leave visual overflow using UnionRect).
+ nsRect rect(0, 0, Width(), Height());
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = mOverflowAreas.Overflow(otype);
+ o.UnionRect(o, rect);
+ }
+}
diff --git a/layout/generic/ReflowOutput.h b/layout/generic/ReflowOutput.h
new file mode 100644
index 000000000..9877ed6fa
--- /dev/null
+++ b/layout/generic/ReflowOutput.h
@@ -0,0 +1,348 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* struct containing the output from nsIFrame::Reflow */
+
+#ifndef mozilla_ReflowOutput_h
+#define mozilla_ReflowOutput_h
+
+#include "mozilla/WritingModes.h"
+#include "nsBoundingMetrics.h"
+#include "nsRect.h"
+
+//----------------------------------------------------------------------
+
+namespace mozilla {
+struct ReflowInput;
+} // namespace mozilla
+
+// Option flags
+#define NS_REFLOW_CALC_BOUNDING_METRICS 0x0001
+
+/**
+ * When we store overflow areas as an array of scrollable and visual
+ * overflow, we use these indices.
+ *
+ * eOverflowType_LENGTH is needed (for gcc 4.5.*, at least) to ensure
+ * that 2 is a valid value of nsOverflowType for use in
+ * NS_FOR_FRAME_OVERFLOW_TYPES.
+ */
+enum nsOverflowType { eVisualOverflow, eScrollableOverflow,
+ eOverflowType_LENGTH };
+
+#define NS_FOR_FRAME_OVERFLOW_TYPES(var_) \
+ for (nsOverflowType var_ = nsOverflowType(0); var_ < 2; \
+ var_ = nsOverflowType(var_ + 1))
+
+struct nsOverflowAreas {
+private:
+ nsRect mRects[2];
+public:
+ nsRect& Overflow(size_t aIndex) {
+ NS_ASSERTION(aIndex < 2, "index out of range");
+ return mRects[aIndex];
+ }
+ const nsRect& Overflow(size_t aIndex) const {
+ NS_ASSERTION(aIndex < 2, "index out of range");
+ return mRects[aIndex];
+ }
+
+ nsRect& VisualOverflow() { return mRects[eVisualOverflow]; }
+ const nsRect& VisualOverflow() const { return mRects[eVisualOverflow]; }
+
+ nsRect& ScrollableOverflow() { return mRects[eScrollableOverflow]; }
+ const nsRect& ScrollableOverflow() const { return mRects[eScrollableOverflow]; }
+
+ nsOverflowAreas() {
+ // default-initializes to zero due to nsRect's default constructor
+ }
+
+ nsOverflowAreas(const nsRect& aVisualOverflow,
+ const nsRect& aScrollableOverflow)
+ {
+ mRects[eVisualOverflow] = aVisualOverflow;
+ mRects[eScrollableOverflow] = aScrollableOverflow;
+ }
+
+ nsOverflowAreas(const nsOverflowAreas& aOther) {
+ *this = aOther;
+ }
+
+ nsOverflowAreas& operator=(const nsOverflowAreas& aOther) {
+ mRects[0] = aOther.mRects[0];
+ mRects[1] = aOther.mRects[1];
+ return *this;
+ }
+
+ bool operator==(const nsOverflowAreas& aOther) const {
+ // Scrollable overflow is a point-set rectangle and visual overflow
+ // is a pixel-set rectangle.
+ return VisualOverflow().IsEqualInterior(aOther.VisualOverflow()) &&
+ ScrollableOverflow().IsEqualEdges(aOther.ScrollableOverflow());
+ }
+
+ bool operator!=(const nsOverflowAreas& aOther) const {
+ return !(*this == aOther);
+ }
+
+ nsOverflowAreas operator+(const nsPoint& aPoint) const {
+ nsOverflowAreas result(*this);
+ result += aPoint;
+ return result;
+ }
+
+ nsOverflowAreas& operator+=(const nsPoint& aPoint) {
+ mRects[0] += aPoint;
+ mRects[1] += aPoint;
+ return *this;
+ }
+
+ void Clear() {
+ mRects[0].SetRect(0, 0, 0, 0);
+ mRects[1].SetRect(0, 0, 0, 0);
+ }
+
+ // Mutates |this| by unioning both overflow areas with |aOther|.
+ void UnionWith(const nsOverflowAreas& aOther);
+
+ // Mutates |this| by unioning both overflow areas with |aRect|.
+ void UnionAllWith(const nsRect& aRect);
+
+ // Mutates |this| by setting both overflow areas to |aRect|.
+ void SetAllTo(const nsRect& aRect);
+};
+
+/**
+ * An nsCollapsingMargin represents a vertical collapsing margin between
+ * blocks as described in section 8.3.1 of CSS2,
+ * <URL: http://www.w3.org/TR/REC-CSS2/box.html#collapsing-margins >.
+ *
+ * All adjacent vertical margins collapse, and the resulting margin is
+ * the sum of the largest positive margin included and the smallest (most
+ * negative) negative margin included.
+ */
+struct nsCollapsingMargin {
+ private:
+ nscoord mMostPos; // the largest positive margin included
+ nscoord mMostNeg; // the smallest negative margin included
+
+ public:
+ nsCollapsingMargin()
+ : mMostPos(0),
+ mMostNeg(0)
+ {
+ }
+
+ nsCollapsingMargin(const nsCollapsingMargin& aOther)
+ : mMostPos(aOther.mMostPos),
+ mMostNeg(aOther.mMostNeg)
+ {
+ }
+
+ bool operator==(const nsCollapsingMargin& aOther)
+ {
+ return mMostPos == aOther.mMostPos &&
+ mMostNeg == aOther.mMostNeg;
+ }
+
+ bool operator!=(const nsCollapsingMargin& aOther)
+ {
+ return !(*this == aOther);
+ }
+
+ nsCollapsingMargin& operator=(const nsCollapsingMargin& aOther)
+ {
+ mMostPos = aOther.mMostPos;
+ mMostNeg = aOther.mMostNeg;
+ return *this;
+ }
+
+ void Include(nscoord aCoord)
+ {
+ if (aCoord > mMostPos)
+ mMostPos = aCoord;
+ else if (aCoord < mMostNeg)
+ mMostNeg = aCoord;
+ }
+
+ void Include(const nsCollapsingMargin& aOther)
+ {
+ if (aOther.mMostPos > mMostPos)
+ mMostPos = aOther.mMostPos;
+ if (aOther.mMostNeg < mMostNeg)
+ mMostNeg = aOther.mMostNeg;
+ }
+
+ void Zero()
+ {
+ mMostPos = 0;
+ mMostNeg = 0;
+ }
+
+ bool IsZero() const
+ {
+ return (mMostPos == 0) && (mMostNeg == 0);
+ }
+
+ nscoord get() const
+ {
+ return mMostPos + mMostNeg;
+ }
+};
+
+namespace mozilla {
+
+/**
+ * Reflow metrics used to return the frame's desired size and alignment
+ * information.
+ *
+ * @see #Reflow()
+ */
+class ReflowOutput {
+public:
+ // XXXldb Should |aFlags| generally be passed from parent to child?
+ // Some places do it, and some don't. |aFlags| should perhaps go away
+ // entirely.
+ // XXX width/height/ascent are OUT parameters and so they shouldn't
+ // have to be initialized, but there are some bad frame classes that
+ // aren't properly setting them when returning from Reflow()...
+ explicit ReflowOutput(mozilla::WritingMode aWritingMode, uint32_t aFlags = 0)
+ : mISize(0)
+ , mBSize(0)
+ , mBlockStartAscent(ASK_FOR_BASELINE)
+ , mFlags(aFlags)
+ , mWritingMode(aWritingMode)
+ {}
+
+ explicit ReflowOutput(const ReflowInput& aState, uint32_t aFlags = 0);
+
+ // ISize and BSize are logical-coordinate dimensions:
+ // ISize is the size in the writing mode's inline direction (which equates to
+ // width in horizontal writing modes, height in vertical ones), and BSize is
+ // the size in the block-progression direction.
+ nscoord ISize(mozilla::WritingMode aWritingMode) const {
+ NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+ "mismatched writing mode");
+ return mISize;
+ }
+ nscoord BSize(mozilla::WritingMode aWritingMode) const {
+ NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+ "mismatched writing mode");
+ return mBSize;
+ }
+ mozilla::LogicalSize Size(mozilla::WritingMode aWritingMode) const {
+ NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+ "mismatched writing mode");
+ return mozilla::LogicalSize(aWritingMode, mISize, mBSize);
+ }
+
+ nscoord& ISize(mozilla::WritingMode aWritingMode) {
+ NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+ "mismatched writing mode");
+ return mISize;
+ }
+ nscoord& BSize(mozilla::WritingMode aWritingMode) {
+ NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+ "mismatched writing mode");
+ return mBSize;
+ }
+
+ // Set inline and block size from a LogicalSize, converting to our
+ // writing mode as necessary.
+ void SetSize(mozilla::WritingMode aWM, mozilla::LogicalSize aSize)
+ {
+ mozilla::LogicalSize convertedSize = aSize.ConvertTo(mWritingMode, aWM);
+ mBSize = convertedSize.BSize(mWritingMode);
+ mISize = convertedSize.ISize(mWritingMode);
+ }
+
+ // Set both inline and block size to zero -- no need for a writing mode!
+ void ClearSize()
+ {
+ mISize = mBSize = 0;
+ }
+
+ // Width and Height are physical dimensions, independent of writing mode.
+ // Accessing these is slightly more expensive than accessing the logical
+ // dimensions (once vertical writing mode support is enabled); as far as
+ // possible, client code should work purely with logical dimensions.
+ nscoord Width() const { return mWritingMode.IsVertical() ? mBSize : mISize; }
+ nscoord Height() const { return mWritingMode.IsVertical() ? mISize : mBSize; }
+
+ // It's only meaningful to consider "ascent" on the block-start side of the
+ // frame, so no need to pass a writing mode argument
+ nscoord BlockStartAscent() const
+ {
+ return mBlockStartAscent;
+ }
+
+ nscoord& Width() { return mWritingMode.IsVertical() ? mBSize : mISize; }
+ nscoord& Height() { return mWritingMode.IsVertical() ? mISize : mBSize; }
+
+ nsSize PhysicalSize()
+ {
+ return Size(mWritingMode).GetPhysicalSize(mWritingMode);
+ }
+
+ void SetBlockStartAscent(nscoord aAscent)
+ {
+ mBlockStartAscent = aAscent;
+ }
+
+ enum { ASK_FOR_BASELINE = nscoord_MAX };
+
+ // Metrics that _exactly_ enclose the text to allow precise MathML placements.
+ // If the NS_REFLOW_CALC_BOUNDING_METRICS flag is set, then the caller is
+ // requesting that you also compute additional details about your inner
+ // bounding box and italic correction. For example, the bounding box of
+ // msup is the smallest rectangle that _exactly_ encloses both the text
+ // of the base and the text of the superscript.
+ nsBoundingMetrics mBoundingMetrics; // [OUT]
+
+ // Carried out block-end margin values. This is the collapsed
+ // (generational) block-end margin value.
+ nsCollapsingMargin mCarriedOutBEndMargin;
+
+ // For frames that have content that overflow their content area
+ // (HasOverflowAreas() is true) these rectangles represent the total
+ // area of the frame including visible overflow, i.e., don't include
+ // overflowing content that is hidden. The rects are in the local
+ // coordinate space of the frame, and should be at least as big as the
+ // desired size. If there is no content that overflows, then the
+ // overflow area is identical to the desired size and should be {0, 0,
+ // width, height}.
+ nsOverflowAreas mOverflowAreas;
+
+ nsRect& VisualOverflow()
+ { return mOverflowAreas.VisualOverflow(); }
+ const nsRect& VisualOverflow() const
+ { return mOverflowAreas.VisualOverflow(); }
+ nsRect& ScrollableOverflow()
+ { return mOverflowAreas.ScrollableOverflow(); }
+ const nsRect& ScrollableOverflow() const
+ { return mOverflowAreas.ScrollableOverflow(); }
+
+ // Set all of mOverflowAreas to (0, 0, width, height).
+ void SetOverflowAreasToDesiredBounds();
+
+ // Union all of mOverflowAreas with (0, 0, width, height).
+ void UnionOverflowAreasWithDesiredBounds();
+
+ mozilla::WritingMode GetWritingMode() const { return mWritingMode; }
+
+private:
+ nscoord mISize, mBSize; // [OUT] desired width and height (border-box)
+ nscoord mBlockStartAscent; // [OUT] baseline (in Block direction), or ASK_FOR_BASELINE
+
+public:
+ uint32_t mFlags;
+
+private:
+ mozilla::WritingMode mWritingMode;
+};
+
+} // mozilla namespace
+
+#endif // mozilla_ReflowOutput_h
diff --git a/layout/generic/RubyUtils.cpp b/layout/generic/RubyUtils.cpp
new file mode 100644
index 000000000..f340663bc
--- /dev/null
+++ b/layout/generic/RubyUtils.cpp
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RubyUtils.h"
+#include "nsRubyFrame.h"
+#include "nsRubyBaseFrame.h"
+#include "nsRubyTextFrame.h"
+#include "nsRubyBaseContainerFrame.h"
+#include "nsRubyTextContainerFrame.h"
+
+using namespace mozilla;
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord)
+
+/* static */ void
+RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize)
+{
+ MOZ_ASSERT(IsExpandableRubyBox(aFrame));
+ aFrame->Properties().Set(ReservedISize(), aISize);
+}
+
+/* static */ void
+RubyUtils::ClearReservedISize(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(IsExpandableRubyBox(aFrame));
+ aFrame->Properties().Remove(ReservedISize());
+}
+
+/* static */ nscoord
+RubyUtils::GetReservedISize(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(IsExpandableRubyBox(aFrame));
+ return aFrame->Properties().Get(ReservedISize());
+}
+
+AutoRubyTextContainerArray::AutoRubyTextContainerArray(
+ nsRubyBaseContainerFrame* aBaseContainer)
+{
+ for (nsIFrame* frame = aBaseContainer->GetNextSibling();
+ frame && frame->GetType() == nsGkAtoms::rubyTextContainerFrame;
+ frame = frame->GetNextSibling()) {
+ AppendElement(static_cast<nsRubyTextContainerFrame*>(frame));
+ }
+}
+
+nsIFrame*
+RubyColumn::Iterator::operator*() const
+{
+ nsIFrame* frame;
+ if (mIndex == -1) {
+ frame = mColumn.mBaseFrame;
+ } else {
+ frame = mColumn.mTextFrames[mIndex];
+ }
+ MOZ_ASSERT(frame, "Frame here cannot be null");
+ return frame;
+}
+
+void
+RubyColumn::Iterator::SkipUntilExistingFrame()
+{
+ if (mIndex == -1) {
+ if (mColumn.mBaseFrame) {
+ return;
+ }
+ ++mIndex;
+ }
+ int32_t numTextFrames = mColumn.mTextFrames.Length();
+ for (; mIndex < numTextFrames; ++mIndex) {
+ if (mColumn.mTextFrames[mIndex]) {
+ break;
+ }
+ }
+}
+
+RubySegmentEnumerator::RubySegmentEnumerator(nsRubyFrame* aRubyFrame)
+{
+ nsIFrame* frame = aRubyFrame->PrincipalChildList().FirstChild();
+ MOZ_ASSERT(!frame ||
+ frame->GetType() == nsGkAtoms::rubyBaseContainerFrame);
+ mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
+}
+
+void
+RubySegmentEnumerator::Next()
+{
+ MOZ_ASSERT(mBaseContainer);
+ nsIFrame* frame = mBaseContainer->GetNextSibling();
+ while (frame && frame->GetType() != nsGkAtoms::rubyBaseContainerFrame) {
+ frame = frame->GetNextSibling();
+ }
+ mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
+}
+
+RubyColumnEnumerator::RubyColumnEnumerator(
+ nsRubyBaseContainerFrame* aBaseContainer,
+ const AutoRubyTextContainerArray& aTextContainers)
+ : mAtIntraLevelWhitespace(false)
+{
+ const uint32_t rtcCount = aTextContainers.Length();
+ mFrames.SetCapacity(rtcCount + 1);
+
+ nsIFrame* rbFrame = aBaseContainer->PrincipalChildList().FirstChild();
+ MOZ_ASSERT(!rbFrame || rbFrame->GetType() == nsGkAtoms::rubyBaseFrame);
+ mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rbFrame));
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsRubyTextContainerFrame* container = aTextContainers[i];
+ // If the container is for span, leave a nullptr here.
+ // Spans do not take part in pairing.
+ nsIFrame* rtFrame = !container->IsSpanContainer() ?
+ container->PrincipalChildList().FirstChild() : nullptr;
+ MOZ_ASSERT(!rtFrame || rtFrame->GetType() == nsGkAtoms::rubyTextFrame);
+ mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rtFrame));
+ }
+
+ // We have to init mAtIntraLevelWhitespace to be correct for the
+ // first column. There are two ways we could end up with intra-level
+ // whitespace in our first colum:
+ // 1. The current segment itself is an inter-segment whitespace;
+ // 2. If our ruby segment is split across multiple lines, and some
+ // intra-level whitespace happens to fall right after a line-break.
+ // Each line will get its own nsRubyBaseContainerFrame, and the
+ // container right after the line-break will end up with its first
+ // column containing that intra-level whitespace.
+ for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
+ nsRubyContentFrame* frame = mFrames[i];
+ if (frame && frame->IsIntraLevelWhitespace()) {
+ mAtIntraLevelWhitespace = true;
+ break;
+ }
+ }
+}
+
+void
+RubyColumnEnumerator::Next()
+{
+ bool advancingToIntraLevelWhitespace = false;
+ for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
+ nsRubyContentFrame* frame = mFrames[i];
+ // If we've got intra-level whitespace frames at some levels in the
+ // current ruby column, we "faked" an anonymous box for all other
+ // levels for this column. So when we advance off this column, we
+ // don't advance any of the frames in those levels, because we're
+ // just advancing across the "fake" frames.
+ if (frame && (!mAtIntraLevelWhitespace ||
+ frame->IsIntraLevelWhitespace())) {
+ nsIFrame* nextSibling = frame->GetNextSibling();
+ MOZ_ASSERT(!nextSibling || nextSibling->GetType() == frame->GetType(),
+ "Frame type should be identical among a level");
+ mFrames[i] = frame = static_cast<nsRubyContentFrame*>(nextSibling);
+ if (!advancingToIntraLevelWhitespace &&
+ frame && frame->IsIntraLevelWhitespace()) {
+ advancingToIntraLevelWhitespace = true;
+ }
+ }
+ }
+ MOZ_ASSERT(!advancingToIntraLevelWhitespace || !mAtIntraLevelWhitespace,
+ "Should never have adjacent intra-level whitespace columns");
+ mAtIntraLevelWhitespace = advancingToIntraLevelWhitespace;
+}
+
+bool
+RubyColumnEnumerator::AtEnd() const
+{
+ for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
+ if (mFrames[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+nsRubyContentFrame*
+RubyColumnEnumerator::GetFrameAtLevel(uint32_t aIndex) const
+{
+ // If the current ruby column is for intra-level whitespaces, we
+ // return nullptr for any levels that do not have an actual intra-
+ // level whitespace frame in this column. This nullptr represents
+ // an anonymous empty intra-level whitespace box. (In this case,
+ // it's important that we NOT return mFrames[aIndex], because it's
+ // really part of the next column, not the current one.)
+ nsRubyContentFrame* frame = mFrames[aIndex];
+ return !mAtIntraLevelWhitespace ||
+ (frame && frame->IsIntraLevelWhitespace()) ? frame : nullptr;
+}
+
+void
+RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const
+{
+ nsRubyContentFrame* rbFrame = GetFrameAtLevel(0);
+ MOZ_ASSERT(!rbFrame || rbFrame->GetType() == nsGkAtoms::rubyBaseFrame);
+ aColumn.mBaseFrame = static_cast<nsRubyBaseFrame*>(rbFrame);
+ aColumn.mTextFrames.ClearAndRetainStorage();
+ for (uint32_t i = 1, iend = mFrames.Length(); i < iend; i++) {
+ nsRubyContentFrame* rtFrame = GetFrameAtLevel(i);
+ MOZ_ASSERT(!rtFrame || rtFrame->GetType() == nsGkAtoms::rubyTextFrame);
+ aColumn.mTextFrames.AppendElement(static_cast<nsRubyTextFrame*>(rtFrame));
+ }
+ aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace;
+}
diff --git a/layout/generic/RubyUtils.h b/layout/generic/RubyUtils.h
new file mode 100644
index 000000000..8c9545f70
--- /dev/null
+++ b/layout/generic/RubyUtils.h
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_RubyUtils_h_
+#define mozilla_RubyUtils_h_
+
+#include "nsTArray.h"
+#include "nsGkAtoms.h"
+#include "nsCSSAnonBoxes.h"
+
+#define RTC_ARRAY_SIZE 1
+
+class nsRubyFrame;
+class nsRubyBaseFrame;
+class nsRubyTextFrame;
+class nsRubyContentFrame;
+class nsRubyBaseContainerFrame;
+class nsRubyTextContainerFrame;
+
+namespace mozilla {
+
+/**
+ * Reserved ISize
+ *
+ * With some exceptions, each ruby internal box has two isizes, which
+ * are the reflowed isize and the final isize. The reflowed isize is
+ * what a box itself needs. It is determined when the box gets reflowed.
+ *
+ * The final isize is what a box should be as the final result. For a
+ * ruby base/text box, the final isize is the size of its ruby column.
+ * For a ruby base/text container, the final isize is the size of its
+ * ruby segment. The final isize is never smaller than the reflowed
+ * isize. It is initially determined when a ruby column/segment gets
+ * fully reflowed, and may be advanced when a box is expanded, e.g.
+ * for justification.
+ *
+ * The difference between the reflowed isize and the final isize is
+ * reserved in the line layout after reflowing a box, hence it is called
+ * "Reserved ISize" here. It is used to expand the ruby boxes from their
+ * reflowed isize to the final isize during alignment of the line.
+ *
+ * There are three exceptions for the final isize:
+ * 1. A ruby text container has a larger final isize only if it is for
+ * a span or collapsed annotations.
+ * 2. A ruby base container has a larger final isize only if at least
+ * one of its ruby text containers does.
+ * 3. If a ruby text container has a larger final isize, its children
+ * must not have.
+ */
+
+class RubyUtils
+{
+public:
+ static inline bool IsRubyContentBox(nsIAtom* aFrameType)
+ {
+ return aFrameType == nsGkAtoms::rubyBaseFrame ||
+ aFrameType == nsGkAtoms::rubyTextFrame;
+ }
+
+ static inline bool IsRubyContainerBox(nsIAtom* aFrameType)
+ {
+ return aFrameType == nsGkAtoms::rubyBaseContainerFrame ||
+ aFrameType == nsGkAtoms::rubyTextContainerFrame;
+ }
+
+ static inline bool IsRubyBox(nsIAtom* aFrameType)
+ {
+ return aFrameType == nsGkAtoms::rubyFrame ||
+ IsRubyContentBox(aFrameType) || IsRubyContainerBox(aFrameType);
+ }
+
+ static inline bool IsExpandableRubyBox(nsIFrame* aFrame)
+ {
+ nsIAtom* type = aFrame->GetType();
+ return IsRubyContentBox(type) || IsRubyContainerBox(type);
+ }
+
+ static inline bool IsRubyPseudo(nsIAtom* aPseudo)
+ {
+ return aPseudo == nsCSSAnonBoxes::ruby ||
+ aPseudo == nsCSSAnonBoxes::rubyBase ||
+ aPseudo == nsCSSAnonBoxes::rubyText ||
+ aPseudo == nsCSSAnonBoxes::rubyBaseContainer ||
+ aPseudo == nsCSSAnonBoxes::rubyTextContainer;
+ }
+
+ static void SetReservedISize(nsIFrame* aFrame, nscoord aISize);
+ static void ClearReservedISize(nsIFrame* aFrame);
+ static nscoord GetReservedISize(nsIFrame* aFrame);
+};
+
+/**
+ * This array stores all ruby text containers of the ruby segment
+ * of the given ruby base container.
+ */
+class MOZ_RAII AutoRubyTextContainerArray final
+ : public AutoTArray<nsRubyTextContainerFrame*, RTC_ARRAY_SIZE>
+{
+public:
+ explicit AutoRubyTextContainerArray(nsRubyBaseContainerFrame* aBaseContainer);
+};
+
+/**
+ * This enumerator enumerates each ruby segment.
+ */
+class MOZ_STACK_CLASS RubySegmentEnumerator
+{
+public:
+ explicit RubySegmentEnumerator(nsRubyFrame* aRubyFrame);
+
+ void Next();
+ bool AtEnd() const { return !mBaseContainer; }
+
+ nsRubyBaseContainerFrame* GetBaseContainer() const
+ {
+ return mBaseContainer;
+ }
+
+private:
+ nsRubyBaseContainerFrame* mBaseContainer;
+};
+
+/**
+ * Ruby column is a unit consists of one ruby base and all ruby
+ * annotations paired with it.
+ * See http://dev.w3.org/csswg/css-ruby/#ruby-pairing
+ */
+struct MOZ_STACK_CLASS RubyColumn
+{
+ nsRubyBaseFrame* mBaseFrame;
+ AutoTArray<nsRubyTextFrame*, RTC_ARRAY_SIZE> mTextFrames;
+ bool mIsIntraLevelWhitespace;
+
+ RubyColumn() : mBaseFrame(nullptr), mIsIntraLevelWhitespace(false) { }
+
+ // Helper class to support iteration across the frames within a single
+ // RubyColumn (the column's ruby base and its annotations).
+ class MOZ_STACK_CLASS Iterator
+ {
+ public:
+ nsIFrame* operator*() const;
+
+ Iterator& operator++() { ++mIndex; SkipUntilExistingFrame(); return *this; }
+ Iterator operator++(int) { auto ret = *this; ++*this; return ret; }
+
+ friend bool operator==(const Iterator& aIter1, const Iterator& aIter2)
+ {
+ MOZ_ASSERT(&aIter1.mColumn == &aIter2.mColumn,
+ "Should only compare iterators of the same ruby column");
+ return aIter1.mIndex == aIter2.mIndex;
+ }
+ friend bool operator!=(const Iterator& aIter1, const Iterator& aIter2)
+ {
+ return !(aIter1 == aIter2);
+ }
+
+ private:
+ Iterator(const RubyColumn& aColumn, int32_t aIndex)
+ : mColumn(aColumn)
+ , mIndex(aIndex)
+ {
+ MOZ_ASSERT(aIndex == -1 ||
+ (aIndex >= 0 &&
+ aIndex <= int32_t(aColumn.mTextFrames.Length())));
+ SkipUntilExistingFrame();
+ }
+ friend struct RubyColumn; // for the constructor
+
+ void SkipUntilExistingFrame();
+
+ const RubyColumn& mColumn;
+ // -1 means the ruby base frame,
+ // non-negative means the index of ruby text frame
+ // a value of mTextFrames.Length() means we're done iterating
+ int32_t mIndex = -1;
+ };
+
+ Iterator begin() const { return Iterator(*this, -1); }
+ Iterator end() const { return Iterator(*this, mTextFrames.Length()); }
+ Iterator cbegin() const { return begin(); }
+ Iterator cend() const { return end(); }
+};
+
+/**
+ * This enumerator enumerates ruby columns in a segment.
+ */
+class MOZ_STACK_CLASS RubyColumnEnumerator
+{
+public:
+ RubyColumnEnumerator(nsRubyBaseContainerFrame* aRBCFrame,
+ const AutoRubyTextContainerArray& aRTCFrames);
+
+ void Next();
+ bool AtEnd() const;
+
+ uint32_t GetLevelCount() const { return mFrames.Length(); }
+ nsRubyContentFrame* GetFrameAtLevel(uint32_t aIndex) const;
+ void GetColumn(RubyColumn& aColumn) const;
+
+private:
+ // Frames in this array are NOT necessary part of the current column.
+ // When in doubt, use GetFrameAtLevel to access it.
+ // See GetFrameAtLevel() and Next() for more info.
+ AutoTArray<nsRubyContentFrame*, RTC_ARRAY_SIZE + 1> mFrames;
+ // Whether we are on a column for intra-level whitespaces
+ bool mAtIntraLevelWhitespace;
+};
+
+/**
+ * Stores block-axis leadings produced from ruby annotations.
+ */
+struct RubyBlockLeadings
+{
+ nscoord mStart = 0;
+ nscoord mEnd = 0;
+
+ void Reset() {
+ mStart = mEnd = 0;
+ }
+ void Update(nscoord aStart, nscoord aEnd) {
+ mStart = std::max(mStart, aStart);
+ mEnd = std::max(mEnd, aEnd);
+ }
+ void Update(const RubyBlockLeadings& aOther) {
+ Update(aOther.mStart, aOther.mEnd);
+ }
+};
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_RubyUtils_h_) */
diff --git a/layout/generic/ScrollSnap.cpp b/layout/generic/ScrollSnap.cpp
new file mode 100644
index 000000000..96eb72925
--- /dev/null
+++ b/layout/generic/ScrollSnap.cpp
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FrameMetrics.h"
+#include "ScrollSnap.h"
+#include "gfxPrefs.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "nsLineLayout.h"
+
+namespace mozilla {
+
+using layers::ScrollSnapInfo;
+
+/**
+ * Stores candidate snapping edges.
+ */
+class SnappingEdgeCallback {
+public:
+ virtual void AddHorizontalEdge(nscoord aEdge) = 0;
+ virtual void AddVerticalEdge(nscoord aEdge) = 0;
+ virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange,
+ nscoord aInterval,
+ nscoord aOffset) = 0;
+ virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange,
+ nscoord aInterval,
+ nscoord aOffset) = 0;
+};
+
+/**
+ * Keeps track of the current best edge to snap to. The criteria for
+ * adding an edge depends on the scrolling unit.
+ */
+class CalcSnapPoints : public SnappingEdgeCallback {
+public:
+ CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit,
+ const nsPoint& aDestination,
+ const nsPoint& aStartPos);
+ virtual void AddHorizontalEdge(nscoord aEdge) override;
+ virtual void AddVerticalEdge(nscoord aEdge) override;
+ virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange,
+ nscoord aInterval, nscoord aOffset)
+ override;
+ virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange,
+ nscoord aInterval, nscoord aOffset)
+ override;
+ void AddEdge(nscoord aEdge,
+ nscoord aDestination,
+ nscoord aStartPos,
+ nscoord aScrollingDirection,
+ nscoord* aBestEdge,
+ bool* aEdgeFound);
+ void AddEdgeInterval(nscoord aInterval,
+ nscoord aMinPos,
+ nscoord aMaxPos,
+ nscoord aOffset,
+ nscoord aDestination,
+ nscoord aStartPos,
+ nscoord aScrollingDirection,
+ nscoord* aBestEdge,
+ bool* aEdgeFound);
+ nsPoint GetBestEdge() const;
+protected:
+ nsIScrollableFrame::ScrollUnit mUnit;
+ nsPoint mDestination; // gives the position after scrolling but before snapping
+ nsPoint mStartPos; // gives the position before scrolling
+ nsIntPoint mScrollingDirection; // always -1, 0, or 1
+ nsPoint mBestEdge; // keeps track of the position of the current best edge
+ bool mHorizontalEdgeFound; // true if mBestEdge.x is storing a valid horizontal edge
+ bool mVerticalEdgeFound; // true if mBestEdge.y is storing a valid vertical edge
+};
+
+CalcSnapPoints::CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit,
+ const nsPoint& aDestination,
+ const nsPoint& aStartPos)
+{
+ mUnit = aUnit;
+ mDestination = aDestination;
+ mStartPos = aStartPos;
+
+ nsPoint direction = aDestination - aStartPos;
+ mScrollingDirection = nsIntPoint(0,0);
+ if (direction.x < 0) {
+ mScrollingDirection.x = -1;
+ }
+ if (direction.x > 0) {
+ mScrollingDirection.x = 1;
+ }
+ if (direction.y < 0) {
+ mScrollingDirection.y = -1;
+ }
+ if (direction.y > 0) {
+ mScrollingDirection.y = 1;
+ }
+ mBestEdge = aDestination;
+ mHorizontalEdgeFound = false;
+ mVerticalEdgeFound = false;
+}
+
+nsPoint
+CalcSnapPoints::GetBestEdge() const
+{
+ return nsPoint(mVerticalEdgeFound ? mBestEdge.x : mStartPos.x,
+ mHorizontalEdgeFound ? mBestEdge.y : mStartPos.y);
+}
+
+void
+CalcSnapPoints::AddHorizontalEdge(nscoord aEdge)
+{
+ AddEdge(aEdge, mDestination.y, mStartPos.y, mScrollingDirection.y, &mBestEdge.y,
+ &mHorizontalEdgeFound);
+}
+
+void
+CalcSnapPoints::AddVerticalEdge(nscoord aEdge)
+{
+ AddEdge(aEdge, mDestination.x, mStartPos.x, mScrollingDirection.x, &mBestEdge.x,
+ &mVerticalEdgeFound);
+}
+
+void
+CalcSnapPoints::AddHorizontalEdgeInterval(const nsRect &aScrollRange,
+ nscoord aInterval, nscoord aOffset)
+{
+ AddEdgeInterval(aInterval, aScrollRange.y, aScrollRange.YMost(), aOffset,
+ mDestination.y, mStartPos.y, mScrollingDirection.y,
+ &mBestEdge.y, &mHorizontalEdgeFound);
+}
+
+void
+CalcSnapPoints::AddVerticalEdgeInterval(const nsRect &aScrollRange,
+ nscoord aInterval, nscoord aOffset)
+{
+ AddEdgeInterval(aInterval, aScrollRange.x, aScrollRange.XMost(), aOffset,
+ mDestination.x, mStartPos.x, mScrollingDirection.x,
+ &mBestEdge.x, &mVerticalEdgeFound);
+}
+
+void
+CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination, nscoord aStartPos,
+ nscoord aScrollingDirection, nscoord* aBestEdge,
+ bool *aEdgeFound)
+{
+ // nsIScrollableFrame::DEVICE_PIXELS indicates that we are releasing a drag
+ // gesture or any other user input event that sets an absolute scroll
+ // position. In this case, scroll snapping is expected to travel in any
+ // direction. Otherwise, we will restrict the direction of the scroll
+ // snapping movement based on aScrollingDirection.
+ if (mUnit != nsIScrollableFrame::DEVICE_PIXELS) {
+ // Unless DEVICE_PIXELS, we only want to snap to points ahead of the
+ // direction we are scrolling
+ if (aScrollingDirection == 0) {
+ // The scroll direction is neutral - will not hit a snap point.
+ return;
+ }
+ // nsIScrollableFrame::WHOLE indicates that we are navigating to "home" or
+ // "end". In this case, we will always select the first or last snap point
+ // regardless of the direction of the scroll. Otherwise, we will select
+ // scroll snapping points only in the direction specified by
+ // aScrollingDirection.
+ if (mUnit != nsIScrollableFrame::WHOLE) {
+ // Direction of the edge from the current position (before scrolling) in
+ // the direction of scrolling
+ nscoord direction = (aEdge - aStartPos) * aScrollingDirection;
+ if (direction <= 0) {
+ // The edge is not in the direction we are scrolling, skip it.
+ return;
+ }
+ }
+ }
+ if (!*aEdgeFound) {
+ *aBestEdge = aEdge;
+ *aEdgeFound = true;
+ return;
+ }
+ if (mUnit == nsIScrollableFrame::DEVICE_PIXELS ||
+ mUnit == nsIScrollableFrame::LINES) {
+ if (std::abs(aEdge - aDestination) < std::abs(*aBestEdge - aDestination)) {
+ *aBestEdge = aEdge;
+ }
+ } else if (mUnit == nsIScrollableFrame::PAGES) {
+ // distance to the edge from the scrolling destination in the direction of scrolling
+ nscoord overshoot = (aEdge - aDestination) * aScrollingDirection;
+ // distance to the current best edge from the scrolling destination in the direction of scrolling
+ nscoord curOvershoot = (*aBestEdge - aDestination) * aScrollingDirection;
+
+ // edges between the current position and the scrolling destination are favoured
+ // to preserve context
+ if (overshoot < 0 && (overshoot > curOvershoot || curOvershoot >= 0)) {
+ *aBestEdge = aEdge;
+ }
+ // if there are no edges between the current position and the scrolling destination
+ // the closest edge beyond the destination is used
+ if (overshoot > 0 && overshoot < curOvershoot) {
+ *aBestEdge = aEdge;
+ }
+ } else if (mUnit == nsIScrollableFrame::WHOLE) {
+ // the edge closest to the top/bottom/left/right is used, depending on scrolling direction
+ if (aScrollingDirection > 0 && aEdge > *aBestEdge) {
+ *aBestEdge = aEdge;
+ } else if (aScrollingDirection < 0 && aEdge < *aBestEdge) {
+ *aBestEdge = aEdge;
+ }
+ } else {
+ NS_ERROR("Invalid scroll mode");
+ return;
+ }
+}
+
+void
+CalcSnapPoints::AddEdgeInterval(nscoord aInterval, nscoord aMinPos,
+ nscoord aMaxPos, nscoord aOffset,
+ nscoord aDestination, nscoord aStartPos,
+ nscoord aScrollingDirection,
+ nscoord* aBestEdge, bool *aEdgeFound)
+{
+ if (aInterval == 0) {
+ // When interval is 0, there are no scroll snap points.
+ // Avoid division by zero and bail.
+ return;
+ }
+
+ // The only possible candidate interval snap points are the edges immediately
+ // surrounding aDestination.
+
+ // aDestination must be clamped to the scroll
+ // range in order to handle cases where the best matching snap point would
+ // result in scrolling out of bounds. This clamping must be prior to
+ // selecting the two interval edges.
+ nscoord clamped = std::max(std::min(aDestination, aMaxPos), aMinPos);
+
+ // Add each edge in the interval immediately before aTarget and after aTarget
+ // Do not add edges that are out of range.
+ nscoord r = (clamped + aOffset) % aInterval;
+ if (r < aMinPos) {
+ r += aInterval;
+ }
+ nscoord edge = clamped - r;
+ if (edge >= aMinPos && edge <= aMaxPos) {
+ AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge,
+ aEdgeFound);
+ }
+ edge += aInterval;
+ if (edge >= aMinPos && edge <= aMaxPos) {
+ AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge,
+ aEdgeFound);
+ }
+}
+
+static void
+ProcessScrollSnapCoordinates(SnappingEdgeCallback& aCallback,
+ const nsTArray<nsPoint>& aScrollSnapCoordinates,
+ const nsPoint& aScrollSnapDestination) {
+ for (nsPoint snapCoords : aScrollSnapCoordinates) {
+ // Make them relative to the scroll snap destination.
+ snapCoords -= aScrollSnapDestination;
+
+ aCallback.AddVerticalEdge(snapCoords.x);
+ aCallback.AddHorizontalEdge(snapCoords.y);
+ }
+}
+
+Maybe<nsPoint> ScrollSnapUtils::GetSnapPointForDestination(
+ const ScrollSnapInfo& aSnapInfo,
+ nsIScrollableFrame::ScrollUnit aUnit,
+ const nsSize& aScrollPortSize,
+ const nsRect& aScrollRange,
+ const nsPoint& aStartPos,
+ const nsPoint& aDestination)
+{
+ if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
+ aSnapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
+ return Nothing();
+ }
+
+ nsPoint destPos = aSnapInfo.mScrollSnapDestination;
+
+ CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos);
+
+ if (aSnapInfo.mScrollSnapIntervalX.isSome()) {
+ nscoord interval = aSnapInfo.mScrollSnapIntervalX.value();
+ calcSnapPoints.AddVerticalEdgeInterval(aScrollRange, interval, destPos.x);
+ }
+ if (aSnapInfo.mScrollSnapIntervalY.isSome()) {
+ nscoord interval = aSnapInfo.mScrollSnapIntervalY.value();
+ calcSnapPoints.AddHorizontalEdgeInterval(aScrollRange, interval, destPos.y);
+ }
+
+ ProcessScrollSnapCoordinates(calcSnapPoints, aSnapInfo.mScrollSnapCoordinates, destPos);
+ bool snapped = false;
+ nsPoint finalPos = calcSnapPoints.GetBestEdge();
+ nscoord proximityThreshold = gfxPrefs::ScrollSnapProximityThreshold();
+ proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold);
+ if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
+ std::abs(aDestination.y - finalPos.y) > proximityThreshold) {
+ finalPos.y = aDestination.y;
+ } else {
+ snapped = true;
+ }
+ if (aSnapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
+ std::abs(aDestination.x - finalPos.x) > proximityThreshold) {
+ finalPos.x = aDestination.x;
+ } else {
+ snapped = true;
+ }
+ return snapped ? Some(finalPos) : Nothing();
+}
+
+} // namespace mozilla
diff --git a/layout/generic/ScrollSnap.h b/layout/generic/ScrollSnap.h
new file mode 100644
index 000000000..a2877564d
--- /dev/null
+++ b/layout/generic/ScrollSnap.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layout_ScrollSnap_h_
+#define mozilla_layout_ScrollSnap_h_
+
+namespace mozilla {
+
+namespace layers {
+struct ScrollSnapInfo;
+}
+
+struct ScrollSnapUtils {
+ /**
+ * GetSnapPointForDestination determines which point to snap to after
+ * scrolling. |aStartPos| gives the position before scrolling and
+ * |aDestination| gives the position after scrolling, with no snapping.
+ * Behaviour is dependent on the value of |aUnit|.
+ * |aSnapInfo|, |aScrollPortSize|, and |aScrollRange| are characteristics
+ * of the scroll frame for which snapping is being performed.
+ * If a suitable snap point could be found, it is returned. Otherwise, an
+ * empty Maybe is returned.
+ * IMPORTANT NOTE: This function is designed to be called both on and off
+ * the main thread. If modifying its implementation, be sure
+ * not to touch main-thread-only data structures without
+ * appropriate locking.
+ */
+ static Maybe<nsPoint> GetSnapPointForDestination(
+ const layers::ScrollSnapInfo& aSnapInfo,
+ nsIScrollableFrame::ScrollUnit aUnit,
+ const nsSize& aScrollPortSize,
+ const nsRect& aScrollRange,
+ const nsPoint& aStartPos,
+ const nsPoint& aDestination);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_layout_ScrollSnap_h_
diff --git a/layout/generic/ScrollVelocityQueue.cpp b/layout/generic/ScrollVelocityQueue.cpp
new file mode 100644
index 000000000..820e6cb5c
--- /dev/null
+++ b/layout/generic/ScrollVelocityQueue.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScrollVelocityQueue.h"
+
+#include "gfxPrefs.h"
+#include "nsPresContext.h"
+#include "nsRefreshDriver.h"
+
+namespace mozilla {
+namespace layout {
+
+void
+ScrollVelocityQueue::Sample(const nsPoint& aScrollPosition)
+{
+ float flingSensitivity = gfxPrefs::ScrollSnapPredictionSensitivity();
+ int maxVelocity = gfxPrefs::ScrollSnapPredictionMaxVelocity();
+ maxVelocity = nsPresContext::CSSPixelsToAppUnits(maxVelocity);
+ int maxOffset = maxVelocity * flingSensitivity;
+ TimeStamp currentRefreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
+ if (mSampleTime.IsNull()) {
+ mAccumulator = nsPoint();
+ } else {
+ uint32_t durationMs = (currentRefreshTime - mSampleTime).ToMilliseconds();
+ if (durationMs > gfxPrefs::APZVelocityRelevanceTime()) {
+ mAccumulator = nsPoint();
+ mQueue.Clear();
+ } else if (durationMs == 0) {
+ mAccumulator += aScrollPosition - mLastPosition;
+ } else {
+ nsPoint velocity = mAccumulator * 1000 / durationMs;
+ velocity.Clamp(maxVelocity);
+ mQueue.AppendElement(std::make_pair(durationMs, velocity));
+ mAccumulator = aScrollPosition - mLastPosition;
+ }
+ }
+ mAccumulator.Clamp(maxOffset);
+ mSampleTime = currentRefreshTime;
+ mLastPosition = aScrollPosition;
+ TrimQueue();
+}
+
+void
+ScrollVelocityQueue::TrimQueue()
+{
+ if (mSampleTime.IsNull()) {
+ // There are no samples, nothing to do here.
+ return;
+ }
+
+ TimeStamp currentRefreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
+ nsPoint velocity;
+ uint32_t timeDelta = (currentRefreshTime - mSampleTime).ToMilliseconds();
+ for (int i = mQueue.Length() - 1; i >= 0; i--) {
+ timeDelta += mQueue[i].first;
+ if (timeDelta >= gfxPrefs::APZVelocityRelevanceTime()) {
+ // The rest of the samples have expired and should be dropped
+ for (; i >= 0; i--) {
+ mQueue.RemoveElementAt(0);
+ }
+ }
+ }
+}
+
+void
+ScrollVelocityQueue::Reset()
+{
+ mAccumulator = nsPoint();
+ mSampleTime = TimeStamp();
+ mQueue.Clear();
+}
+
+/**
+ Calculate the velocity of the scroll frame, in appunits / second.
+*/
+nsPoint
+ScrollVelocityQueue::GetVelocity()
+{
+ TrimQueue();
+ if (mQueue.Length() == 0) {
+ // If getting the scroll velocity before any scrolling has occurred,
+ // the velocity must be (0, 0)
+ return nsPoint();
+ }
+ nsPoint velocity;
+ for (int i = mQueue.Length() - 1; i >= 0; i--) {
+ velocity += mQueue[i].second;
+ }
+ return velocity / mQueue.Length();;
+}
+
+} // namespace layout
+} // namespace mozilla
+
diff --git a/layout/generic/ScrollVelocityQueue.h b/layout/generic/ScrollVelocityQueue.h
new file mode 100644
index 000000000..ca0e5a83a
--- /dev/null
+++ b/layout/generic/ScrollVelocityQueue.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ScrollVelocityQueue_h_
+#define ScrollVelocityQueue_h_
+
+#include "nsTArray.h"
+#include "nsPoint.h"
+#include "mozilla/TimeStamp.h"
+
+class nsPresContext;
+
+namespace mozilla {
+namespace layout {
+
+/**
+ * ScrollVelocityQueue is used to determine the current velocity of a
+ * scroll frame, derived from scroll position samples.
+ *
+ * Using the last iteration's scroll position, stored in mLastPosition, a
+ * delta of the scroll position is calculated and accumulated in mAccumulator
+ * until the refresh driver returns a new timestamp for MostRecentRefresh().
+ *
+ * When there is a new timestamp from the refresh driver, the accumulated
+ * change in scroll position is divided by the delta of the timestamp to
+ * get an average velocity over that period. This velocity is pushed into
+ * mQueue as a std::pair associating each velocity with the
+ * duration over which it was sampled.
+ *
+ * Samples are removed from mQueue, leaving only those necessary to determine
+ * the average velocity over the recent relevant period, which has a duration
+ * set by the apz.velocity_relevance_time_ms preference.
+ *
+ * The velocity of each sample is clamped to a value set by the
+ * layout.css.scroll-snap.prediction-max-velocity.
+ *
+ * As the average velocity will later be integrated over a duration set by
+ * the layout.css.scroll-snap.prediction-sensitivity preference and the
+ * velocity samples are clamped to a set value, the maximum expected scroll
+ * offset can be calculated. This maximum offset is used to clamp
+ * mAccumulator, eliminating samples that would otherwise result in scroll
+ * snap position selection that is not consistent with the user's perception
+ * of scroll velocity.
+ */
+
+class ScrollVelocityQueue final {
+public:
+ explicit ScrollVelocityQueue(nsPresContext *aPresContext)
+ : mPresContext(aPresContext) {}
+
+ // Sample() is to be called periodically when scroll movement occurs, to
+ // record samples of scroll position used later by GetVelocity().
+ void Sample(const nsPoint& aScrollPosition);
+
+ // Discards velocity samples, resulting in velocity of 0 returned by
+ // GetVelocity until move scroll position updates.
+ void Reset();
+
+ // Get scroll velocity averaged from recent movement, in appunits / second
+ nsPoint GetVelocity();
+private:
+ // A queue of (duration, velocity) pairs; these are the historical average
+ // velocities over the given durations. Durations are in milliseconds,
+ // velocities are in app units per second.
+ nsTArray<std::pair<uint32_t, nsPoint> > mQueue;
+
+ // Accumulates the distance and direction travelled by the scroll frame since
+ // mSampleTime.
+ nsPoint mAccumulator;
+
+ // Time that mAccumulator was last reset and began accumulating.
+ TimeStamp mSampleTime;
+
+ // Scroll offset at the mAccumulator was last reset and began
+ // accumulating.
+ nsPoint mLastPosition;
+
+ // PresContext of the containing frame, used to get timebase
+ nsPresContext* mPresContext;
+
+ // Remove samples from mQueue that no longer contribute to GetVelocity()
+ // due to their age
+ void TrimQueue();
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif /* !defined(ScrollVelocityQueue_h_) */
diff --git a/layout/generic/ScrollbarActivity.cpp b/layout/generic/ScrollbarActivity.cpp
new file mode 100644
index 000000000..78859bc2b
--- /dev/null
+++ b/layout/generic/ScrollbarActivity.cpp
@@ -0,0 +1,469 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScrollbarActivity.h"
+#include "nsIScrollbarMediator.h"
+#include "nsIContent.h"
+#include "nsICSSDeclaration.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMCSSStyleDeclaration.h"
+#include "nsIFrame.h"
+#include "nsContentUtils.h"
+#include "nsAString.h"
+#include "nsQueryFrame.h"
+#include "nsComponentManagerUtils.h"
+#include "nsStyledElement.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace layout {
+
+NS_IMPL_ISUPPORTS(ScrollbarActivity, nsIDOMEventListener)
+
+static bool
+GetForceAlwaysVisiblePref()
+{
+ static bool sForceAlwaysVisible;
+ static bool sForceAlwaysVisiblePrefCached = false;
+ if (!sForceAlwaysVisiblePrefCached) {
+ Preferences::AddBoolVarCache(&sForceAlwaysVisible,
+ "layout.testing.overlay-scrollbars.always-visible");
+ sForceAlwaysVisiblePrefCached = true;
+ }
+ return sForceAlwaysVisible;
+}
+
+void
+ScrollbarActivity::QueryLookAndFeelVals()
+{
+ // Fade animation constants
+ mScrollbarFadeBeginDelay =
+ LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarFadeBeginDelay);
+ mScrollbarFadeDuration =
+ LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarFadeDuration);
+ // Controls whether we keep the mouse move listener so we can display the
+ // scrollbars whenever the user moves the mouse within the scroll area.
+ mDisplayOnMouseMove =
+ LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollbarDisplayOnMouseMove);
+}
+
+void
+ScrollbarActivity::Destroy()
+{
+ StopListeningForScrollbarEvents();
+ StopListeningForScrollAreaEvents();
+ UnregisterFromRefreshDriver();
+ CancelFadeBeginTimer();
+}
+
+void
+ScrollbarActivity::ActivityOccurred()
+{
+ ActivityStarted();
+ ActivityStopped();
+}
+
+void
+ScrollbarActivity::ActivityStarted()
+{
+ mNestedActivityCounter++;
+ CancelFadeBeginTimer();
+ if (!SetIsFading(false)) {
+ return;
+ }
+ UnregisterFromRefreshDriver();
+ StartListeningForScrollbarEvents();
+ StartListeningForScrollAreaEvents();
+ SetIsActive(true);
+
+ NS_ASSERTION(mIsActive, "need to be active during activity");
+ NS_ASSERTION(!mIsFading, "must not be fading during activity");
+}
+
+void
+ScrollbarActivity::ActivityStopped()
+{
+ if (!IsActivityOngoing()) {
+ // This can happen if there was a frame reconstruction while the activity
+ // was ongoing. In this case we just do nothing. We should probably handle
+ // this case better.
+ return;
+ }
+ NS_ASSERTION(mIsActive, "need to be active during activity");
+ NS_ASSERTION(!mIsFading, "must not be fading during ongoing activity");
+
+ mNestedActivityCounter--;
+
+ if (!IsActivityOngoing()) {
+ StartFadeBeginTimer();
+
+ NS_ASSERTION(mIsActive, "need to be active right after activity");
+ NS_ASSERTION(!mIsFading, "must not be fading right after activity");
+ }
+}
+
+NS_IMETHODIMP
+ScrollbarActivity::HandleEvent(nsIDOMEvent* aEvent)
+{
+ if (!mDisplayOnMouseMove && !mIsActive)
+ return NS_OK;
+
+ nsAutoString type;
+ aEvent->GetType(type);
+
+ if (type.EqualsLiteral("mousemove")) {
+ // Mouse motions anywhere in the scrollable frame should keep the
+ // scrollbars visible.
+ ActivityOccurred();
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMEventTarget> target;
+ aEvent->GetOriginalTarget(getter_AddRefs(target));
+ nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
+
+ HandleEventForScrollbar(type, targetContent, GetHorizontalScrollbar(),
+ &mHScrollbarHovered);
+ HandleEventForScrollbar(type, targetContent, GetVerticalScrollbar(),
+ &mVScrollbarHovered);
+
+ return NS_OK;
+}
+
+void
+ScrollbarActivity::WillRefresh(TimeStamp aTime)
+{
+ NS_ASSERTION(mIsActive, "should only fade while scrollbars are visible");
+ NS_ASSERTION(!IsActivityOngoing(), "why weren't we unregistered from the refresh driver when scrollbar activity started?");
+ NS_ASSERTION(mIsFading, "should only animate fading during fade");
+
+ if (!UpdateOpacity(aTime)) {
+ return;
+ }
+
+ if (!IsStillFading(aTime)) {
+ EndFade();
+ }
+}
+
+bool
+ScrollbarActivity::IsStillFading(TimeStamp aTime)
+{
+ return !mFadeBeginTime.IsNull() && (aTime - mFadeBeginTime < FadeDuration());
+}
+
+void
+ScrollbarActivity::HandleEventForScrollbar(const nsAString& aType,
+ nsIContent* aTarget,
+ nsIContent* aScrollbar,
+ bool* aStoredHoverState)
+{
+ if (!aTarget || !aScrollbar ||
+ !nsContentUtils::ContentIsDescendantOf(aTarget, aScrollbar))
+ return;
+
+ if (aType.EqualsLiteral("mousedown")) {
+ ActivityStarted();
+ } else if (aType.EqualsLiteral("mouseup")) {
+ ActivityStopped();
+ } else if (aType.EqualsLiteral("mouseover") ||
+ aType.EqualsLiteral("mouseout")) {
+ bool newHoveredState = aType.EqualsLiteral("mouseover");
+ if (newHoveredState && !*aStoredHoverState) {
+ ActivityStarted();
+ HoveredScrollbar(aScrollbar);
+ } else if (*aStoredHoverState && !newHoveredState) {
+ ActivityStopped();
+ // Don't call HoveredScrollbar(nullptr) here because we want the hover
+ // attribute to stick until the scrollbars are hidden.
+ }
+ *aStoredHoverState = newHoveredState;
+ }
+}
+
+void
+ScrollbarActivity::StartListeningForScrollbarEvents()
+{
+ if (mListeningForScrollbarEvents)
+ return;
+
+ mHorizontalScrollbar = do_QueryInterface(GetHorizontalScrollbar());
+ mVerticalScrollbar = do_QueryInterface(GetVerticalScrollbar());
+
+ AddScrollbarEventListeners(mHorizontalScrollbar);
+ AddScrollbarEventListeners(mVerticalScrollbar);
+
+ mListeningForScrollbarEvents = true;
+}
+
+void
+ScrollbarActivity::StopListeningForScrollbarEvents()
+{
+ if (!mListeningForScrollbarEvents)
+ return;
+
+ RemoveScrollbarEventListeners(mHorizontalScrollbar);
+ RemoveScrollbarEventListeners(mVerticalScrollbar);
+
+ mHorizontalScrollbar = nullptr;
+ mVerticalScrollbar = nullptr;
+ mListeningForScrollbarEvents = false;
+}
+
+void
+ScrollbarActivity::StartListeningForScrollAreaEvents()
+{
+ if (mListeningForScrollAreaEvents)
+ return;
+
+ nsIFrame* scrollArea = do_QueryFrame(mScrollableFrame);
+ nsCOMPtr<nsIDOMEventTarget> scrollAreaTarget
+ = do_QueryInterface(scrollArea->GetContent());
+ if (scrollAreaTarget) {
+ scrollAreaTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), this,
+ true);
+ }
+ mListeningForScrollAreaEvents = true;
+}
+
+void
+ScrollbarActivity::StopListeningForScrollAreaEvents()
+{
+ if (!mListeningForScrollAreaEvents)
+ return;
+
+ nsIFrame* scrollArea = do_QueryFrame(mScrollableFrame);
+ nsCOMPtr<nsIDOMEventTarget> scrollAreaTarget = do_QueryInterface(scrollArea->GetContent());
+ if (scrollAreaTarget) {
+ scrollAreaTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, true);
+ }
+ mListeningForScrollAreaEvents = false;
+}
+
+void
+ScrollbarActivity::AddScrollbarEventListeners(nsIDOMEventTarget* aScrollbar)
+{
+ if (aScrollbar) {
+ aScrollbar->AddEventListener(NS_LITERAL_STRING("mousedown"), this, true);
+ aScrollbar->AddEventListener(NS_LITERAL_STRING("mouseup"), this, true);
+ aScrollbar->AddEventListener(NS_LITERAL_STRING("mouseover"), this, true);
+ aScrollbar->AddEventListener(NS_LITERAL_STRING("mouseout"), this, true);
+ }
+}
+
+void
+ScrollbarActivity::RemoveScrollbarEventListeners(nsIDOMEventTarget* aScrollbar)
+{
+ if (aScrollbar) {
+ aScrollbar->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true);
+ aScrollbar->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, true);
+ aScrollbar->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, true);
+ aScrollbar->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, true);
+ }
+}
+
+void
+ScrollbarActivity::BeginFade()
+{
+ NS_ASSERTION(mIsActive, "can't begin fade when we're already inactive");
+ NS_ASSERTION(!IsActivityOngoing(), "why wasn't the fade begin timer cancelled when scrollbar activity started?");
+ NS_ASSERTION(!mIsFading, "shouldn't be fading just yet");
+
+ CancelFadeBeginTimer();
+ mFadeBeginTime = TimeStamp::Now();
+ if (!SetIsFading(true)) {
+ return;
+ }
+ RegisterWithRefreshDriver();
+
+ NS_ASSERTION(mIsActive, "only fade while scrollbars are visible");
+ NS_ASSERTION(mIsFading, "should be fading now");
+}
+
+void
+ScrollbarActivity::EndFade()
+{
+ NS_ASSERTION(mIsActive, "still need to be active at this point");
+ NS_ASSERTION(!IsActivityOngoing(), "why wasn't the fade end timer cancelled when scrollbar activity started?");
+
+ if (!SetIsFading(false)) {
+ return;
+ }
+ SetIsActive(false);
+ UnregisterFromRefreshDriver();
+ StopListeningForScrollbarEvents();
+ if (!mDisplayOnMouseMove) {
+ StopListeningForScrollAreaEvents();
+ }
+
+ NS_ASSERTION(!mIsActive, "should have gone inactive after fade end");
+ NS_ASSERTION(!mIsFading, "shouldn't be fading anymore");
+}
+
+void
+ScrollbarActivity::RegisterWithRefreshDriver()
+{
+ nsRefreshDriver* refreshDriver = GetRefreshDriver();
+ if (refreshDriver) {
+ refreshDriver->AddRefreshObserver(this, Flush_Style);
+ }
+}
+
+void
+ScrollbarActivity::UnregisterFromRefreshDriver()
+{
+ nsRefreshDriver* refreshDriver = GetRefreshDriver();
+ if (refreshDriver) {
+ refreshDriver->RemoveRefreshObserver(this, Flush_Style);
+ }
+}
+
+static void
+SetBooleanAttribute(nsIContent* aContent, nsIAtom* aAttribute, bool aValue)
+{
+ if (aContent) {
+ if (aValue) {
+ aContent->SetAttr(kNameSpaceID_None, aAttribute,
+ NS_LITERAL_STRING("true"), true);
+ } else {
+ aContent->UnsetAttr(kNameSpaceID_None, aAttribute, true);
+ }
+ }
+}
+
+void
+ScrollbarActivity::SetIsActive(bool aNewActive)
+{
+ if (mIsActive == aNewActive)
+ return;
+
+ mIsActive = aNewActive;
+ if (!mIsActive) {
+ // Clear sticky scrollbar hover status.
+ HoveredScrollbar(nullptr);
+ }
+
+ SetBooleanAttribute(GetHorizontalScrollbar(), nsGkAtoms::active, mIsActive);
+ SetBooleanAttribute(GetVerticalScrollbar(), nsGkAtoms::active, mIsActive);
+}
+
+static void
+SetOpacityOnElement(nsIContent* aContent, double aOpacity)
+{
+ nsCOMPtr<nsStyledElement> inlineStyleContent =
+ do_QueryInterface(aContent);
+ if (inlineStyleContent) {
+ nsICSSDeclaration* decl = inlineStyleContent->Style();
+ nsAutoString str;
+ str.AppendFloat(aOpacity);
+ decl->SetProperty(NS_LITERAL_STRING("opacity"), str, EmptyString());
+ }
+}
+
+bool
+ScrollbarActivity::UpdateOpacity(TimeStamp aTime)
+{
+ // Avoid division by zero if mScrollbarFadeDuration is zero, just jump
+ // to the end of the fade animation
+ double progress = mScrollbarFadeDuration
+ ? ((aTime - mFadeBeginTime) / FadeDuration())
+ : 1.0;
+ double opacity = 1.0 - std::max(0.0, std::min(1.0, progress));
+
+ // 'this' may be getting destroyed during SetOpacityOnElement calls.
+ nsWeakFrame weakFrame((do_QueryFrame(mScrollableFrame)));
+ SetOpacityOnElement(GetHorizontalScrollbar(), opacity);
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+ SetOpacityOnElement(GetVerticalScrollbar(), opacity);
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+ return true;
+}
+
+static void
+UnsetOpacityOnElement(nsIContent* aContent)
+{
+ nsCOMPtr<nsStyledElement> inlineStyleContent =
+ do_QueryInterface(aContent);
+ if (inlineStyleContent) {
+ nsICSSDeclaration* decl = inlineStyleContent->Style();
+ nsAutoString dummy;
+ decl->RemoveProperty(NS_LITERAL_STRING("opacity"), dummy);
+ }
+}
+
+bool
+ScrollbarActivity::SetIsFading(bool aNewFading)
+{
+ if (mIsFading == aNewFading)
+ return true;
+
+ mIsFading = aNewFading;
+ if (!mIsFading) {
+ mFadeBeginTime = TimeStamp();
+ // 'this' may be getting destroyed during UnsetOpacityOnElement calls.
+ nsWeakFrame weakFrame((do_QueryFrame(mScrollableFrame)));
+ UnsetOpacityOnElement(GetHorizontalScrollbar());
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+ UnsetOpacityOnElement(GetVerticalScrollbar());
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+ScrollbarActivity::StartFadeBeginTimer()
+{
+ if (GetForceAlwaysVisiblePref()) {
+ return;
+ }
+ if (!mFadeBeginTimer) {
+ mFadeBeginTimer = do_CreateInstance("@mozilla.org/timer;1");
+ }
+ mFadeBeginTimer->InitWithNamedFuncCallback(
+ FadeBeginTimerFired, this, mScrollbarFadeBeginDelay,
+ nsITimer::TYPE_ONE_SHOT, "ScrollbarActivity::FadeBeginTimerFired");
+}
+
+void
+ScrollbarActivity::CancelFadeBeginTimer()
+{
+ if (mFadeBeginTimer) {
+ mFadeBeginTimer->Cancel();
+ }
+}
+
+void
+ScrollbarActivity::HoveredScrollbar(nsIContent* aScrollbar)
+{
+ SetBooleanAttribute(GetHorizontalScrollbar(), nsGkAtoms::hover, false);
+ SetBooleanAttribute(GetVerticalScrollbar(), nsGkAtoms::hover, false);
+ SetBooleanAttribute(aScrollbar, nsGkAtoms::hover, true);
+}
+
+nsRefreshDriver*
+ScrollbarActivity::GetRefreshDriver()
+{
+ nsIFrame* scrollableFrame = do_QueryFrame(mScrollableFrame);
+ return scrollableFrame->PresContext()->RefreshDriver();
+}
+
+nsIContent*
+ScrollbarActivity::GetScrollbarContent(bool aVertical)
+{
+ nsIFrame* box = mScrollableFrame->GetScrollbarBox(aVertical);
+ return box ? box->GetContent() : nullptr;
+}
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/generic/ScrollbarActivity.h b/layout/generic/ScrollbarActivity.h
new file mode 100644
index 000000000..54f0a3577
--- /dev/null
+++ b/layout/generic/ScrollbarActivity.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ScrollbarActivity_h___
+#define ScrollbarActivity_h___
+
+#include "mozilla/Attributes.h"
+#include "nsCOMPtr.h"
+#include "nsIDOMEventListener.h"
+#include "mozilla/TimeStamp.h"
+#include "nsRefreshDriver.h"
+
+class nsIContent;
+class nsIScrollbarMediator;
+class nsITimer;
+
+namespace mozilla {
+namespace layout {
+
+/**
+ * ScrollbarActivity
+ *
+ * This class manages scrollbar behavior that imitates the native Mac OS X
+ * Lion overlay scrollbar behavior: Scrollbars are only shown while "scrollbar
+ * activity" occurs, and they're hidden with a fade animation after a short
+ * delay.
+ *
+ * Scrollbar activity has these states:
+ * - inactive:
+ * Scrollbars are hidden.
+ * - ongoing activity:
+ * Scrollbars are visible and being operated on in some way, for example
+ * because they're hovered or pressed.
+ * - active, but waiting for fade out
+ * Scrollbars are still completely visible but are about to fade away.
+ * - fading out
+ * Scrollbars are subject to a fade-out animation.
+ *
+ * Initial scrollbar activity needs to be reported by the scrollbar holder that
+ * owns the ScrollbarActivity instance. This needs to happen via a call to
+ * ActivityOccurred(), for example when the current scroll position or the size
+ * of the scroll area changes.
+ *
+ * As soon as scrollbars are visible, the ScrollbarActivity class manages the
+ * rest of the activity behavior: It ensures that mouse motions inside the
+ * scroll area keep the scrollbars visible, and that scrollbars don't fade away
+ * while they're being hovered / dragged. It also sets a sticky hover attribute
+ * on the most recently hovered scrollbar.
+ *
+ * ScrollbarActivity falls into hibernation after the scrollbars have faded
+ * out. It only starts acting after the next call to ActivityOccurred() /
+ * ActivityStarted().
+ */
+
+class ScrollbarActivity final : public nsIDOMEventListener,
+ public nsARefreshObserver
+{
+public:
+ explicit ScrollbarActivity(nsIScrollbarMediator* aScrollableFrame)
+ : mScrollableFrame(aScrollableFrame)
+ , mNestedActivityCounter(0)
+ , mIsActive(false)
+ , mIsFading(false)
+ , mListeningForScrollbarEvents(false)
+ , mListeningForScrollAreaEvents(false)
+ , mHScrollbarHovered(false)
+ , mVScrollbarHovered(false)
+ , mDisplayOnMouseMove(false)
+ , mScrollbarFadeBeginDelay(0)
+ , mScrollbarFadeDuration(0)
+ {
+ QueryLookAndFeelVals();
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ void Destroy();
+
+ void ActivityOccurred();
+ void ActivityStarted();
+ void ActivityStopped();
+
+ virtual void WillRefresh(TimeStamp aTime) override;
+
+ static void FadeBeginTimerFired(nsITimer* aTimer, void* aSelf) {
+ RefPtr<ScrollbarActivity> scrollbarActivity(
+ reinterpret_cast<ScrollbarActivity*>(aSelf));
+ scrollbarActivity->BeginFade();
+ }
+
+protected:
+ virtual ~ScrollbarActivity() {}
+
+ bool IsActivityOngoing()
+ { return mNestedActivityCounter > 0; }
+ bool IsStillFading(TimeStamp aTime);
+ void QueryLookAndFeelVals();
+
+ void HandleEventForScrollbar(const nsAString& aType,
+ nsIContent* aTarget,
+ nsIContent* aScrollbar,
+ bool* aStoredHoverState);
+
+ void SetIsActive(bool aNewActive);
+ bool SetIsFading(bool aNewFading); // returns false if 'this' was destroyed
+
+ void BeginFade();
+ void EndFade();
+
+ void StartFadeBeginTimer();
+ void CancelFadeBeginTimer();
+
+ void StartListeningForScrollbarEvents();
+ void StartListeningForScrollAreaEvents();
+ void StopListeningForScrollbarEvents();
+ void StopListeningForScrollAreaEvents();
+ void AddScrollbarEventListeners(nsIDOMEventTarget* aScrollbar);
+ void RemoveScrollbarEventListeners(nsIDOMEventTarget* aScrollbar);
+
+ void RegisterWithRefreshDriver();
+ void UnregisterFromRefreshDriver();
+
+ bool UpdateOpacity(TimeStamp aTime); // returns false if 'this' was destroyed
+ void HoveredScrollbar(nsIContent* aScrollbar);
+
+ nsRefreshDriver* GetRefreshDriver();
+ nsIContent* GetScrollbarContent(bool aVertical);
+ nsIContent* GetHorizontalScrollbar() { return GetScrollbarContent(false); }
+ nsIContent* GetVerticalScrollbar() { return GetScrollbarContent(true); }
+
+ const TimeDuration FadeDuration() {
+ return TimeDuration::FromMilliseconds(mScrollbarFadeDuration);
+ }
+
+ nsIScrollbarMediator* mScrollableFrame;
+ TimeStamp mFadeBeginTime;
+ nsCOMPtr<nsITimer> mFadeBeginTimer;
+ nsCOMPtr<nsIDOMEventTarget> mHorizontalScrollbar; // null while inactive
+ nsCOMPtr<nsIDOMEventTarget> mVerticalScrollbar; // null while inactive
+ int mNestedActivityCounter;
+ bool mIsActive;
+ bool mIsFading;
+ bool mListeningForScrollbarEvents;
+ bool mListeningForScrollAreaEvents;
+ bool mHScrollbarHovered;
+ bool mVScrollbarHovered;
+
+ // LookAndFeel values we load on creation
+ bool mDisplayOnMouseMove;
+ int mScrollbarFadeBeginDelay;
+ int mScrollbarFadeDuration;
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif /* ScrollbarActivity_h___ */
diff --git a/layout/generic/Selection.h b/layout/generic/Selection.h
new file mode 100644
index 000000000..6f94303ca
--- /dev/null
+++ b/layout/generic/Selection.h
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Selection_h__
+#define mozilla_Selection_h__
+
+#include "nsIWeakReference.h"
+
+#include "mozilla/AutoRestore.h"
+#include "mozilla/TextRange.h"
+#include "nsISelection.h"
+#include "nsISelectionController.h"
+#include "nsISelectionListener.h"
+#include "nsISelectionPrivate.h"
+#include "nsRange.h"
+#include "nsThreadUtils.h"
+#include "nsWrapperCache.h"
+
+struct CachedOffsetForFrame;
+class nsAutoScrollTimer;
+class nsIContentIterator;
+class nsIFrame;
+class nsFrameSelection;
+struct SelectionDetails;
+class nsCopySupport;
+class nsHTMLCopyEncoder;
+
+namespace mozilla {
+class ErrorResult;
+struct AutoPrepareFocusRange;
+} // namespace mozilla
+
+struct RangeData
+{
+ explicit RangeData(nsRange* aRange)
+ : mRange(aRange)
+ {}
+
+ RefPtr<nsRange> mRange;
+ mozilla::TextRangeStyle mTextRangeStyle;
+};
+
+// Note, the ownership of mozilla::dom::Selection depends on which way the
+// object is created. When nsFrameSelection has created Selection,
+// addreffing/releasing the Selection object is aggregated to nsFrameSelection.
+// Otherwise normal addref/release is used. This ensures that nsFrameSelection
+// is never deleted before its Selections.
+namespace mozilla {
+namespace dom {
+
+class Selection final : public nsISelectionPrivate,
+ public nsWrapperCache,
+ public nsSupportsWeakReference
+{
+protected:
+ virtual ~Selection();
+
+public:
+ Selection();
+ explicit Selection(nsFrameSelection *aList);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Selection, nsISelectionPrivate)
+ NS_DECL_NSISELECTION
+ NS_DECL_NSISELECTIONPRIVATE
+
+ virtual Selection* AsSelection() override { return this; }
+
+ nsresult EndBatchChangesInternal(int16_t aReason = nsISelectionListener::NO_REASON);
+
+ nsIDocument* GetParentObject() const;
+
+ // utility methods for scrolling the selection into view
+ nsPresContext* GetPresContext() const;
+ nsIPresShell* GetPresShell() const;
+ nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
+ // Returns a rect containing the selection region, and frame that that
+ // position is relative to. For SELECTION_ANCHOR_REGION or
+ // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
+ // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
+ // region rects.
+ nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect *aRect);
+ // Returns the position of the region (SELECTION_ANCHOR_REGION or
+ // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
+ // The 'position' is a zero-width rectangle.
+ nsIFrame* GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect *aRect);
+
+ nsresult PostScrollSelectionIntoViewEvent(
+ SelectionRegion aRegion,
+ int32_t aFlags,
+ nsIPresShell::ScrollAxis aVertical,
+ nsIPresShell::ScrollAxis aHorizontal);
+ enum {
+ SCROLL_SYNCHRONOUS = 1<<1,
+ SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
+ SCROLL_DO_FLUSH = 1<<3, // only matters if SCROLL_SYNCHRONOUS is passed too
+ SCROLL_OVERFLOW_HIDDEN = 1<<5,
+ SCROLL_FOR_CARET_MOVE = 1<<6
+ };
+ // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
+ // the scroll event fires so we make sure to scroll to the right place.
+ // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
+ // flush layout and you MUST hold a strong ref on 'this' for the duration
+ // of this call. This might destroy arbitrary layout objects.
+ nsresult ScrollIntoView(SelectionRegion aRegion,
+ nsIPresShell::ScrollAxis aVertical =
+ nsIPresShell::ScrollAxis(),
+ nsIPresShell::ScrollAxis aHorizontal =
+ nsIPresShell::ScrollAxis(),
+ int32_t aFlags = 0);
+ nsresult SubtractRange(RangeData* aRange, nsRange* aSubtract,
+ nsTArray<RangeData>* aOutput);
+ /**
+ * AddItem adds aRange to this Selection. If mUserInitiated is true,
+ * then aRange is first scanned for -moz-user-select:none nodes and split up
+ * into multiple ranges to exclude those before adding the resulting ranges
+ * to this Selection.
+ */
+ nsresult AddItem(nsRange* aRange, int32_t* aOutIndex, bool aNoStartSelect = false);
+ nsresult RemoveItem(nsRange* aRange);
+ nsresult RemoveCollapsedRanges();
+ nsresult Clear(nsPresContext* aPresContext);
+ nsresult Collapse(nsINode* aParentNode, int32_t aOffset);
+ nsresult Extend(nsINode* aParentNode, int32_t aOffset);
+ nsRange* GetRangeAt(int32_t aIndex) const;
+
+ // Get the anchor-to-focus range if we don't care which end is
+ // anchor and which end is focus.
+ const nsRange* GetAnchorFocusRange() const {
+ return mAnchorFocusRange;
+ }
+
+ nsDirection GetDirection(){return mDirection;}
+ void SetDirection(nsDirection aDir){mDirection = aDir;}
+ nsresult SetAnchorFocusToRange(nsRange *aRange);
+ void ReplaceAnchorFocusRange(nsRange *aRange);
+ void AdjustAnchorFocusForMultiRange(nsDirection aDirection);
+
+ // NS_IMETHOD GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, int32_t aOffset, bool aIsEndNode, nsIFrame **aResultFrame);
+ NS_IMETHOD GetPrimaryFrameForAnchorNode(nsIFrame **aResultFrame);
+ NS_IMETHOD GetPrimaryFrameForFocusNode(nsIFrame **aResultFrame, int32_t *aOffset, bool aVisual);
+ NS_IMETHOD LookUpSelection(nsIContent *aContent,
+ int32_t aContentOffset,
+ int32_t aContentLength,
+ SelectionDetails** aReturnDetails,
+ SelectionType aSelectionType,
+ bool aSlowCheck);
+ NS_IMETHOD Repaint(nsPresContext* aPresContext);
+
+ // Note: StartAutoScrollTimer might destroy arbitrary frames etc.
+ nsresult StartAutoScrollTimer(nsIFrame *aFrame,
+ nsPoint& aPoint,
+ uint32_t aDelay);
+
+ nsresult StopAutoScrollTimer();
+
+ JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ // WebIDL methods
+ nsINode* GetAnchorNode();
+ uint32_t AnchorOffset();
+ nsINode* GetFocusNode();
+ uint32_t FocusOffset();
+
+ bool IsCollapsed() const;
+ void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
+ void CollapseToStart(mozilla::ErrorResult& aRv);
+ void CollapseToEnd(mozilla::ErrorResult& aRv);
+
+ void Extend(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
+
+ void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
+ void DeleteFromDocument(mozilla::ErrorResult& aRv);
+
+ uint32_t RangeCount() const
+ {
+ return mRanges.Length();
+ }
+ nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
+ void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
+ void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv);
+ void RemoveAllRanges(mozilla::ErrorResult& aRv);
+
+ void Stringify(nsAString& aResult);
+
+ bool ContainsNode(nsINode& aNode, bool aPartlyContained, mozilla::ErrorResult& aRv);
+
+ /**
+ * Check to see if the given point is contained within the selection area. In
+ * particular, this iterates through all the rects that make up the selection,
+ * not just the bounding box, and checks to see if the given point is contained
+ * in any one of them.
+ * @param aPoint The point to check, relative to the root frame.
+ */
+ bool ContainsPoint(const nsPoint& aPoint);
+
+ void Modify(const nsAString& aAlter, const nsAString& aDirection,
+ const nsAString& aGranularity, mozilla::ErrorResult& aRv);
+
+ bool GetInterlinePosition(mozilla::ErrorResult& aRv);
+ void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
+
+ Nullable<int16_t> GetCaretBidiLevel(mozilla::ErrorResult& aRv) const;
+ void SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv);
+
+ void ToStringWithFormat(const nsAString& aFormatType,
+ uint32_t aFlags,
+ int32_t aWrapColumn,
+ nsAString& aReturn,
+ mozilla::ErrorResult& aRv);
+ void AddSelectionListener(nsISelectionListener* aListener,
+ mozilla::ErrorResult& aRv);
+ void RemoveSelectionListener(nsISelectionListener* aListener,
+ mozilla::ErrorResult& aRv);
+
+ RawSelectionType RawType() const
+ {
+ return ToRawSelectionType(mSelectionType);
+ }
+ SelectionType Type() const { return mSelectionType; }
+
+ void GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
+ nsINode& aEndNode, int32_t aEndOffset,
+ bool aAllowAdjacent,
+ nsTArray<RefPtr<nsRange>>& aReturn,
+ mozilla::ErrorResult& aRv);
+
+ void ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
+ int16_t aVPercent, int16_t aHPercent,
+ mozilla::ErrorResult& aRv);
+
+ void AddSelectionChangeBlocker();
+ void RemoveSelectionChangeBlocker();
+ bool IsBlockingSelectionChangeEvents() const;
+private:
+ friend class ::nsAutoScrollTimer;
+
+ // Note: DoAutoScroll might destroy arbitrary frames etc.
+ nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
+
+ // XXX Please don't add additional uses of this method, it's only for
+ // XXX supporting broken code (bug 1245883) in the following classes:
+ friend class ::nsCopySupport;
+ friend class ::nsHTMLCopyEncoder;
+ void AddRangeInternal(nsRange& aRange, nsIDocument* aDocument, ErrorResult&);
+
+public:
+ SelectionType GetType() const { return mSelectionType; }
+ void SetType(SelectionType aSelectionType)
+ {
+ mSelectionType = aSelectionType;
+ }
+
+ nsresult NotifySelectionListeners();
+
+ friend struct AutoUserInitiated;
+ struct MOZ_RAII AutoUserInitiated
+ {
+ explicit AutoUserInitiated(Selection* aSelection
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mSavedValue(aSelection->mUserInitiated)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ aSelection->mUserInitiated = true;
+ }
+ AutoRestore<bool> mSavedValue;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ };
+
+private:
+ friend struct mozilla::AutoPrepareFocusRange;
+ class ScrollSelectionIntoViewEvent;
+ friend class ScrollSelectionIntoViewEvent;
+
+ class ScrollSelectionIntoViewEvent : public Runnable {
+ public:
+ NS_DECL_NSIRUNNABLE
+ ScrollSelectionIntoViewEvent(Selection* aSelection,
+ SelectionRegion aRegion,
+ nsIPresShell::ScrollAxis aVertical,
+ nsIPresShell::ScrollAxis aHorizontal,
+ int32_t aFlags)
+ : mSelection(aSelection),
+ mRegion(aRegion),
+ mVerticalScroll(aVertical),
+ mHorizontalScroll(aHorizontal),
+ mFlags(aFlags) {
+ NS_ASSERTION(aSelection, "null parameter");
+ }
+ void Revoke() { mSelection = nullptr; }
+ private:
+ Selection *mSelection;
+ SelectionRegion mRegion;
+ nsIPresShell::ScrollAxis mVerticalScroll;
+ nsIPresShell::ScrollAxis mHorizontalScroll;
+ int32_t mFlags;
+ };
+
+ void setAnchorFocusRange(int32_t aIndex); // pass in index into mRanges;
+ // negative value clears
+ // mAnchorFocusRange
+ nsresult SelectAllFramesForContent(nsIContentIterator *aInnerIter,
+ nsIContent *aContent,
+ bool aSelected);
+ nsresult selectFrames(nsPresContext* aPresContext, nsRange *aRange, bool aSelect);
+ nsresult getTableCellLocationFromRange(nsRange *aRange, int32_t *aSelectionType, int32_t *aRow, int32_t *aCol);
+ nsresult addTableCellRange(nsRange *aRange, bool *aDidAddRange, int32_t *aOutIndex);
+
+ nsresult FindInsertionPoint(
+ nsTArray<RangeData>* aElementArray,
+ nsINode* aPointNode, int32_t aPointOffset,
+ nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
+ int32_t* aPoint);
+ bool EqualsRangeAtPoint(nsINode* aBeginNode, int32_t aBeginOffset,
+ nsINode* aEndNode, int32_t aEndOffset,
+ int32_t aRangeIndex);
+ nsresult GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
+ nsINode* aEndNode, int32_t aEndOffset,
+ bool aAllowAdjacent,
+ int32_t* aStartIndex, int32_t* aEndIndex);
+ RangeData* FindRangeData(nsIDOMRange* aRange);
+
+ void UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange> >& rangesToAdd);
+
+ /**
+ * Helper method for AddItem.
+ */
+ nsresult AddItemInternal(nsRange* aRange, int32_t* aOutIndex);
+
+ // These are the ranges inside this selection. They are kept sorted in order
+ // of DOM start position.
+ //
+ // This data structure is sorted by the range beginnings. As the ranges are
+ // disjoint, it is also implicitly sorted by the range endings. This allows
+ // us to perform binary searches when searching for existence of a range,
+ // giving us O(log n) search time.
+ //
+ // Inserting a new range requires finding the overlapping interval, requiring
+ // two binary searches plus up to an additional 6 DOM comparisons. If this
+ // proves to be a performance concern, then an interval tree may be a
+ // possible solution, allowing the calculation of the overlap interval in
+ // O(log n) time, though this would require rebalancing and other overhead.
+ nsTArray<RangeData> mRanges;
+
+ RefPtr<nsRange> mAnchorFocusRange;
+ RefPtr<nsFrameSelection> mFrameSelection;
+ RefPtr<nsAutoScrollTimer> mAutoScrollTimer;
+ nsCOMArray<nsISelectionListener> mSelectionListeners;
+ nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
+ CachedOffsetForFrame *mCachedOffsetForFrame;
+ nsDirection mDirection;
+ SelectionType mSelectionType;
+ /**
+ * True if the current selection operation was initiated by user action.
+ * It determines whether we exclude -moz-user-select:none nodes or not,
+ * as well as whether selectstart events will be fired.
+ */
+ bool mUserInitiated;
+
+ // Non-zero if we don't want any changes we make to the selection to be
+ // visible to content. If non-zero, content won't be notified about changes.
+ uint32_t mSelectionChangeBlockerCount;
+};
+
+// Stack-class to turn on/off selection batching.
+class MOZ_STACK_CLASS SelectionBatcher final
+{
+private:
+ RefPtr<Selection> mSelection;
+public:
+ explicit SelectionBatcher(Selection* aSelection)
+ {
+ mSelection = aSelection;
+ if (mSelection) {
+ mSelection->StartBatchChanges();
+ }
+ }
+
+ ~SelectionBatcher()
+ {
+ if (mSelection) {
+ mSelection->EndBatchChangesInternal();
+ }
+ }
+};
+
+class MOZ_RAII AutoHideSelectionChanges final
+{
+private:
+ RefPtr<Selection> mSelection;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+public:
+ explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame);
+
+ explicit AutoHideSelectionChanges(Selection* aSelection
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mSelection(aSelection)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ mSelection = aSelection;
+ if (mSelection) {
+ mSelection->AddSelectionChangeBlocker();
+ }
+ }
+
+ ~AutoHideSelectionChanges()
+ {
+ if (mSelection) {
+ mSelection->RemoveSelectionChangeBlocker();
+ }
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_Selection_h__
diff --git a/layout/generic/SelectionChangeListener.h b/layout/generic/SelectionChangeListener.h
new file mode 100644
index 000000000..81d66aeb4
--- /dev/null
+++ b/layout/generic/SelectionChangeListener.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SelectionChangeListener_h_
+#define mozilla_SelectionChangeListener_h_
+
+#include "nsISelectionListener.h"
+#include "nsISelectionPrivate.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace dom {
+
+class SelectionChangeListener final : public nsISelectionListener
+{
+public:
+ // SelectionChangeListener has to participate in cycle collection because
+ // it holds strong references to nsINodes in its mOldRanges array.
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(SelectionChangeListener)
+ NS_DECL_NSISELECTIONLISTENER
+
+ // This field is used to keep track of the ranges which were present in the
+ // selection when the selectionchange event was previously fired. This allows
+ // for the selectionchange event to only be fired when a selection is actually
+ // changed.
+ struct RawRangeData
+ {
+ // These properties are not void*s to avoid the potential situation where the
+ // nsINode is freed, and a new nsINode is allocated with the same address, which
+ // could potentially break the comparison logic. In reality, this is extremely
+ // unlikely to occur (potentially impossible), but these nsCOMPtrs are safer.
+ // They are never dereferenced.
+ nsCOMPtr<nsINode> mStartParent;
+ nsCOMPtr<nsINode> mEndParent;
+
+ // XXX These are int32_ts on nsRange, but uint32_ts in the return value
+ // of GetStart_, so I use uint32_ts here. See bug 1194256.
+ uint32_t mStartOffset;
+ uint32_t mEndOffset;
+
+ explicit RawRangeData(const nsRange* aRange);
+ bool Equals(const nsRange* aRange);
+ };
+
+private:
+ nsTArray<RawRangeData> mOldRanges;
+
+ ~SelectionChangeListener() {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_SelectionChangeListener_h_
diff --git a/layout/generic/StickyScrollContainer.cpp b/layout/generic/StickyScrollContainer.cpp
new file mode 100644
index 000000000..d61a7e042
--- /dev/null
+++ b/layout/generic/StickyScrollContainer.cpp
@@ -0,0 +1,391 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * compute sticky positioning, both during reflow and when the scrolling
+ * container scrolls
+ */
+
+#include "StickyScrollContainer.h"
+#include "nsIFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+#include "RestyleTracker.h"
+
+namespace mozilla {
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(StickyScrollContainerProperty,
+ StickyScrollContainer)
+
+StickyScrollContainer::StickyScrollContainer(nsIScrollableFrame* aScrollFrame)
+ : mScrollFrame(aScrollFrame)
+ , mScrollPosition()
+{
+ mScrollFrame->AddScrollPositionListener(this);
+}
+
+StickyScrollContainer::~StickyScrollContainer()
+{
+ mScrollFrame->RemoveScrollPositionListener(this);
+}
+
+// static
+StickyScrollContainer*
+StickyScrollContainer::GetStickyScrollContainerForFrame(nsIFrame* aFrame)
+{
+ nsIScrollableFrame* scrollFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(aFrame->GetParent(),
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+ if (!scrollFrame) {
+ // We might not find any, for instance in the case of
+ // <html style="position: fixed">
+ return nullptr;
+ }
+ FrameProperties props = static_cast<nsIFrame*>(do_QueryFrame(scrollFrame))->
+ Properties();
+ StickyScrollContainer* s = props.Get(StickyScrollContainerProperty());
+ if (!s) {
+ s = new StickyScrollContainer(scrollFrame);
+ props.Set(StickyScrollContainerProperty(), s);
+ }
+ return s;
+}
+
+// static
+void
+StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(nsIFrame* aFrame,
+ nsIFrame* aOldParent)
+{
+ nsIScrollableFrame* oldScrollFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(aOldParent,
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+ if (!oldScrollFrame) {
+ // XXX maybe aFrame has sticky descendants that can be sticky now, but
+ // we aren't going to handle that.
+ return;
+ }
+ FrameProperties props = static_cast<nsIFrame*>(do_QueryFrame(oldScrollFrame))->
+ Properties();
+ StickyScrollContainer* oldSSC = props.Get(StickyScrollContainerProperty());
+ if (!oldSSC) {
+ // aOldParent had no sticky descendants, so aFrame doesn't have any sticky
+ // descendants, and we're done here.
+ return;
+ }
+
+ auto i = oldSSC->mFrames.Length();
+ while (i-- > 0) {
+ nsIFrame* f = oldSSC->mFrames[i];
+ StickyScrollContainer* newSSC = GetStickyScrollContainerForFrame(f);
+ if (newSSC != oldSSC) {
+ oldSSC->RemoveFrame(f);
+ if (newSSC) {
+ newSSC->AddFrame(f);
+ }
+ }
+ }
+}
+
+// static
+StickyScrollContainer*
+StickyScrollContainer::GetStickyScrollContainerForScrollFrame(nsIFrame* aFrame)
+{
+ FrameProperties props = aFrame->Properties();
+ return props.Get(StickyScrollContainerProperty());
+}
+
+static nscoord
+ComputeStickySideOffset(Side aSide, const nsStyleSides& aOffset,
+ nscoord aPercentBasis)
+{
+ if (eStyleUnit_Auto == aOffset.GetUnit(aSide)) {
+ return NS_AUTOOFFSET;
+ } else {
+ return nsLayoutUtils::ComputeCBDependentValue(aPercentBasis,
+ aOffset.Get(aSide));
+ }
+}
+
+// static
+void
+StickyScrollContainer::ComputeStickyOffsets(nsIFrame* aFrame)
+{
+ nsIScrollableFrame* scrollableFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(aFrame->GetParent(),
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+
+ if (!scrollableFrame) {
+ // Bail.
+ return;
+ }
+
+ nsSize scrollContainerSize = scrollableFrame->GetScrolledFrame()->
+ GetContentRectRelativeToSelf().Size();
+
+ nsMargin computedOffsets;
+ const nsStylePosition* position = aFrame->StylePosition();
+
+ computedOffsets.left = ComputeStickySideOffset(eSideLeft, position->mOffset,
+ scrollContainerSize.width);
+ computedOffsets.right = ComputeStickySideOffset(eSideRight, position->mOffset,
+ scrollContainerSize.width);
+ computedOffsets.top = ComputeStickySideOffset(eSideTop, position->mOffset,
+ scrollContainerSize.height);
+ computedOffsets.bottom = ComputeStickySideOffset(eSideBottom, position->mOffset,
+ scrollContainerSize.height);
+
+ // Store the offset
+ FrameProperties props = aFrame->Properties();
+ nsMargin* offsets = props.Get(nsIFrame::ComputedOffsetProperty());
+ if (offsets) {
+ *offsets = computedOffsets;
+ } else {
+ props.Set(nsIFrame::ComputedOffsetProperty(),
+ new nsMargin(computedOffsets));
+ }
+}
+
+void
+StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick,
+ nsRect* aContain) const
+{
+ NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame),
+ "Can't sticky position individual continuations");
+
+ aStick->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
+ aContain->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
+
+ const nsMargin* computedOffsets =
+ aFrame->Properties().Get(nsIFrame::ComputedOffsetProperty());
+ if (!computedOffsets) {
+ // We haven't reflowed the scroll frame yet, so offsets haven't been
+ // computed. Bail.
+ return;
+ }
+
+ nsIFrame* scrolledFrame = mScrollFrame->GetScrolledFrame();
+ nsIFrame* cbFrame = aFrame->GetContainingBlock();
+ NS_ASSERTION(cbFrame == scrolledFrame ||
+ nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame),
+ "Scroll frame should be an ancestor of the containing block");
+
+ nsRect rect =
+ nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame->GetParent());
+
+ // Containing block limits for the position of aFrame relative to its parent.
+ // The margin box of the sticky element stays within the content box of the
+ // contaning-block element.
+ if (cbFrame != scrolledFrame) {
+ *aContain = nsLayoutUtils::
+ GetAllInFlowRectsUnion(cbFrame, aFrame->GetParent(),
+ nsLayoutUtils::RECTS_USE_CONTENT_BOX);
+ nsRect marginRect = nsLayoutUtils::
+ GetAllInFlowRectsUnion(aFrame, aFrame->GetParent(),
+ nsLayoutUtils::RECTS_USE_MARGIN_BOX);
+
+ // Deflate aContain by the difference between the union of aFrame's
+ // continuations' margin boxes and the union of their border boxes, so that
+ // by keeping aFrame within aContain, we keep the union of the margin boxes
+ // within the containing block's content box.
+ aContain->Deflate(marginRect - rect);
+
+ // Deflate aContain by the border-box size, to form a constraint on the
+ // upper-left corner of aFrame and continuations.
+ aContain->Deflate(nsMargin(0, rect.width, rect.height, 0));
+ }
+
+ nsMargin sfPadding = scrolledFrame->GetUsedPadding();
+ nsPoint sfOffset = aFrame->GetParent()->GetOffsetTo(scrolledFrame);
+
+ // Top
+ if (computedOffsets->top != NS_AUTOOFFSET) {
+ aStick->SetTopEdge(mScrollPosition.y + sfPadding.top +
+ computedOffsets->top - sfOffset.y);
+ }
+
+ nsSize sfSize = scrolledFrame->GetContentRectRelativeToSelf().Size();
+
+ // Bottom
+ if (computedOffsets->bottom != NS_AUTOOFFSET &&
+ (computedOffsets->top == NS_AUTOOFFSET ||
+ rect.height <= sfSize.height - computedOffsets->TopBottom())) {
+ aStick->SetBottomEdge(mScrollPosition.y + sfPadding.top + sfSize.height -
+ computedOffsets->bottom - rect.height - sfOffset.y);
+ }
+
+ uint8_t direction = cbFrame->StyleVisibility()->mDirection;
+
+ // Left
+ if (computedOffsets->left != NS_AUTOOFFSET &&
+ (computedOffsets->right == NS_AUTOOFFSET ||
+ direction == NS_STYLE_DIRECTION_LTR ||
+ rect.width <= sfSize.width - computedOffsets->LeftRight())) {
+ aStick->SetLeftEdge(mScrollPosition.x + sfPadding.left +
+ computedOffsets->left - sfOffset.x);
+ }
+
+ // Right
+ if (computedOffsets->right != NS_AUTOOFFSET &&
+ (computedOffsets->left == NS_AUTOOFFSET ||
+ direction == NS_STYLE_DIRECTION_RTL ||
+ rect.width <= sfSize.width - computedOffsets->LeftRight())) {
+ aStick->SetRightEdge(mScrollPosition.x + sfPadding.left + sfSize.width -
+ computedOffsets->right - rect.width - sfOffset.x);
+ }
+
+ // These limits are for the bounding box of aFrame's continuations. Convert
+ // to limits for aFrame itself.
+ nsPoint frameOffset = aFrame->GetPosition() - rect.TopLeft();
+ aStick->MoveBy(frameOffset);
+ aContain->MoveBy(frameOffset);
+}
+
+nsPoint
+StickyScrollContainer::ComputePosition(nsIFrame* aFrame) const
+{
+ nsRect stick;
+ nsRect contain;
+ ComputeStickyLimits(aFrame, &stick, &contain);
+
+ nsPoint position = aFrame->GetNormalPosition();
+
+ // For each sticky direction (top, bottom, left, right), move the frame along
+ // the appropriate axis, based on the scroll position, but limit this to keep
+ // the element's margin box within the containing block.
+ position.y = std::max(position.y, std::min(stick.y, contain.YMost()));
+ position.y = std::min(position.y, std::max(stick.YMost(), contain.y));
+ position.x = std::max(position.x, std::min(stick.x, contain.XMost()));
+ position.x = std::min(position.x, std::max(stick.XMost(), contain.x));
+
+ return position;
+}
+
+void
+StickyScrollContainer::GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter,
+ nsRect* aInner) const
+{
+ // We need to use the first in flow; continuation frames should not move
+ // relative to each other and should get identical scroll ranges.
+ // Also, ComputeStickyLimits requires this.
+ nsIFrame *firstCont =
+ nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
+
+ nsRect stick;
+ nsRect contain;
+ ComputeStickyLimits(firstCont, &stick, &contain);
+
+ aOuter->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
+ aInner->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
+
+ const nsPoint normalPosition = firstCont->GetNormalPosition();
+
+ // Bottom and top
+ if (stick.YMost() != nscoord_MAX/2) {
+ aOuter->SetTopEdge(contain.y - stick.YMost());
+ aInner->SetTopEdge(normalPosition.y - stick.YMost());
+ }
+
+ if (stick.y != nscoord_MIN/2) {
+ aInner->SetBottomEdge(normalPosition.y - stick.y);
+ aOuter->SetBottomEdge(contain.YMost() - stick.y);
+ }
+
+ // Right and left
+ if (stick.XMost() != nscoord_MAX/2) {
+ aOuter->SetLeftEdge(contain.x - stick.XMost());
+ aInner->SetLeftEdge(normalPosition.x - stick.XMost());
+ }
+
+ if (stick.x != nscoord_MIN/2) {
+ aInner->SetRightEdge(normalPosition.x - stick.x);
+ aOuter->SetRightEdge(contain.XMost() - stick.x);
+ }
+
+ // Make sure |inner| does not extend outside of |outer|. (The consumers of
+ // the Layers API, to which this information is propagated, expect this
+ // invariant to hold.) The calculated value of |inner| can sometimes extend
+ // outside of |outer|, for example due to margin collapsing, since
+ // GetNormalPosition() returns the actual position after margin collapsing,
+ // while |contain| is calculated based on the frame's GetUsedMargin() which
+ // is pre-collapsing.
+ // Note that this doesn't necessarily solve all problems stemming from
+ // comparing pre- and post-collapsing margins (TODO: find a proper solution).
+ *aInner = aInner->Intersect(*aOuter);
+}
+
+void
+StickyScrollContainer::PositionContinuations(nsIFrame* aFrame)
+{
+ NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame),
+ "Should be starting from the first continuation");
+ nsPoint translation = ComputePosition(aFrame) - aFrame->GetNormalPosition();
+
+ // Move all continuation frames by the same amount.
+ for (nsIFrame* cont = aFrame; cont;
+ cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ cont->SetPosition(cont->GetNormalPosition() + translation);
+ }
+}
+
+void
+StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition,
+ nsIFrame* aSubtreeRoot)
+{
+#ifdef DEBUG
+ {
+ nsIFrame* scrollFrameAsFrame = do_QueryFrame(mScrollFrame);
+ NS_ASSERTION(!aSubtreeRoot || aSubtreeRoot == scrollFrameAsFrame,
+ "If reflowing, should be reflowing the scroll frame");
+ }
+#endif
+ mScrollPosition = aScrollPosition;
+
+ OverflowChangedTracker oct;
+ oct.SetSubtreeRoot(aSubtreeRoot);
+ for (nsTArray<nsIFrame*>::size_type i = 0; i < mFrames.Length(); i++) {
+ nsIFrame* f = mFrames[i];
+ if (!nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(f)) {
+ // This frame was added in nsFrame::Init before we knew it wasn't
+ // the first ib-split-sibling.
+ mFrames.RemoveElementAt(i);
+ --i;
+ continue;
+ }
+
+ if (aSubtreeRoot) {
+ // Reflowing the scroll frame, so recompute offsets.
+ ComputeStickyOffsets(f);
+ }
+ // mFrames will only contain first continuations, because we filter in
+ // nsIFrame::Init.
+ PositionContinuations(f);
+
+ f = f->GetParent();
+ if (f != aSubtreeRoot) {
+ for (nsIFrame* cont = f; cont;
+ cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ oct.AddFrame(cont, OverflowChangedTracker::CHILDREN_CHANGED);
+ }
+ }
+ }
+ oct.Flush();
+}
+
+void
+StickyScrollContainer::ScrollPositionWillChange(nscoord aX, nscoord aY)
+{
+}
+
+void
+StickyScrollContainer::ScrollPositionDidChange(nscoord aX, nscoord aY)
+{
+ UpdatePositions(nsPoint(aX, aY), nullptr);
+}
+
+} // namespace mozilla
diff --git a/layout/generic/StickyScrollContainer.h b/layout/generic/StickyScrollContainer.h
new file mode 100644
index 000000000..1d26982a9
--- /dev/null
+++ b/layout/generic/StickyScrollContainer.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * compute sticky positioning, both during reflow and when the scrolling
+ * container scrolls
+ */
+
+#ifndef StickyScrollContainer_h
+#define StickyScrollContainer_h
+
+#include "nsPoint.h"
+#include "nsTArray.h"
+#include "nsIScrollPositionListener.h"
+
+struct nsRect;
+class nsIFrame;
+class nsIScrollableFrame;
+
+namespace mozilla {
+
+class StickyScrollContainer final : public nsIScrollPositionListener
+{
+public:
+ /**
+ * Find (and create if necessary) the StickyScrollContainer associated with
+ * the scroll container of the given frame, if a scroll container exists.
+ */
+ static StickyScrollContainer* GetStickyScrollContainerForFrame(nsIFrame* aFrame);
+
+ /**
+ * Find the StickyScrollContainer associated with the given scroll frame,
+ * if it exists.
+ */
+ static StickyScrollContainer* GetStickyScrollContainerForScrollFrame(nsIFrame* aScrollFrame);
+
+ /**
+ * aFrame may have moved into or out of a scroll frame's frame subtree.
+ */
+ static void NotifyReparentedFrameAcrossScrollFrameBoundary(nsIFrame* aFrame,
+ nsIFrame* aOldParent);
+
+ void AddFrame(nsIFrame* aFrame) {
+ mFrames.AppendElement(aFrame);
+ }
+ void RemoveFrame(nsIFrame* aFrame) {
+ mFrames.RemoveElement(aFrame);
+ }
+
+ nsIScrollableFrame* ScrollFrame() const {
+ return mScrollFrame;
+ }
+
+ // Compute the offsets for a sticky position element
+ static void ComputeStickyOffsets(nsIFrame* aFrame);
+
+ /**
+ * Compute the position of a sticky positioned frame, based on information
+ * stored in its properties along with our scroll frame and scroll position.
+ */
+ nsPoint ComputePosition(nsIFrame* aFrame) const;
+
+ /**
+ * Compute where a frame should not scroll with the page, represented by the
+ * difference of two rectangles.
+ */
+ void GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter, nsRect* aInner) const;
+
+ /**
+ * Compute and set the position of a frame and its following continuations.
+ */
+ void PositionContinuations(nsIFrame* aFrame);
+
+ /**
+ * Compute and set the position of all sticky frames, given the current
+ * scroll position of the scroll frame. If not in reflow, aSubtreeRoot should
+ * be null; otherwise, overflow-area updates will be limited to not affect
+ * aSubtreeRoot or its ancestors.
+ */
+ void UpdatePositions(nsPoint aScrollPosition, nsIFrame* aSubtreeRoot);
+
+ // nsIScrollPositionListener
+ virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) override;
+ virtual void ScrollPositionDidChange(nscoord aX, nscoord aY) override;
+
+ ~StickyScrollContainer();
+
+private:
+ explicit StickyScrollContainer(nsIScrollableFrame* aScrollFrame);
+
+ /**
+ * Compute two rectangles that determine sticky positioning: |aStick|, based
+ * on the scroll container, and |aContain|, based on the containing block.
+ * Sticky positioning keeps the frame position (its upper-left corner) always
+ * within |aContain| and secondarily within |aStick|.
+ */
+ void ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick,
+ nsRect* aContain) const;
+
+ nsIScrollableFrame* const mScrollFrame;
+ nsTArray<nsIFrame*> mFrames;
+ nsPoint mScrollPosition;
+};
+
+} // namespace mozilla
+
+#endif /* StickyScrollContainer_h */
diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp
new file mode 100644
index 000000000..b4d935fbf
--- /dev/null
+++ b/layout/generic/TextOverflow.cpp
@@ -0,0 +1,837 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextOverflow.h"
+#include <algorithm>
+
+// Please maintain alphabetical order below
+#include "nsBlockFrame.h"
+#include "nsCaret.h"
+#include "nsContentUtils.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsFontMetrics.h"
+#include "nsGfxScrollFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsRect.h"
+#include "nsRenderingContext.h"
+#include "nsTextFrame.h"
+#include "nsIFrameInlines.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Likely.h"
+#include "nsISelection.h"
+
+namespace mozilla {
+namespace css {
+
+class LazyReferenceRenderingDrawTargetGetterFromFrame final :
+ public gfxFontGroup::LazyReferenceDrawTargetGetter {
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ explicit LazyReferenceRenderingDrawTargetGetterFromFrame(nsIFrame* aFrame)
+ : mFrame(aFrame) {}
+ virtual already_AddRefed<DrawTarget> GetRefDrawTarget() override
+ {
+ RefPtr<gfxContext> ctx =
+ mFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
+ RefPtr<DrawTarget> dt = ctx->GetDrawTarget();
+ return dt.forget();
+ }
+private:
+ nsIFrame* mFrame;
+};
+
+static gfxTextRun*
+GetEllipsisTextRun(nsIFrame* aFrame)
+{
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
+ LazyReferenceRenderingDrawTargetGetterFromFrame lazyRefDrawTargetGetter(aFrame);
+ return fm->GetThebesFontGroup()->GetEllipsisTextRun(
+ aFrame->PresContext()->AppUnitsPerDevPixel(),
+ nsLayoutUtils::GetTextRunOrientFlagsForStyle(aFrame->StyleContext()),
+ lazyRefDrawTargetGetter);
+}
+
+static nsIFrame*
+GetSelfOrNearestBlock(nsIFrame* aFrame)
+{
+ return nsLayoutUtils::GetAsBlock(aFrame) ? aFrame :
+ nsLayoutUtils::FindNearestBlockAncestor(aFrame);
+}
+
+// Return true if the frame is an atomic inline-level element.
+// It's not supposed to be called for block frames since we never
+// process block descendants for text-overflow.
+static bool
+IsAtomicElement(nsIFrame* aFrame, const nsIAtom* aFrameType)
+{
+ NS_PRECONDITION(!nsLayoutUtils::GetAsBlock(aFrame) ||
+ !aFrame->IsBlockOutside(),
+ "unexpected block frame");
+ NS_PRECONDITION(aFrameType != nsGkAtoms::placeholderFrame,
+ "unexpected placeholder frame");
+ return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
+}
+
+static bool
+IsFullyClipped(nsTextFrame* aFrame, nscoord aLeft, nscoord aRight,
+ nscoord* aSnappedLeft, nscoord* aSnappedRight)
+{
+ *aSnappedLeft = aLeft;
+ *aSnappedRight = aRight;
+ if (aLeft <= 0 && aRight <= 0) {
+ return false;
+ }
+ return !aFrame->MeasureCharClippedText(aLeft, aRight,
+ aSnappedLeft, aSnappedRight);
+}
+
+static bool
+IsInlineAxisOverflowVisible(nsIFrame* aFrame)
+{
+ NS_PRECONDITION(nsLayoutUtils::GetAsBlock(aFrame) != nullptr,
+ "expected a block frame");
+
+ nsIFrame* f = aFrame;
+ while (f && f->StyleContext()->GetPseudo() &&
+ f->GetType() != nsGkAtoms::scrollFrame) {
+ f = f->GetParent();
+ }
+ if (!f) {
+ return true;
+ }
+ auto overflow = aFrame->GetWritingMode().IsVertical() ?
+ f->StyleDisplay()->mOverflowY : f->StyleDisplay()->mOverflowX;
+ return overflow == NS_STYLE_OVERFLOW_VISIBLE;
+}
+
+static void
+ClipMarker(const nsRect& aContentArea,
+ const nsRect& aMarkerRect,
+ DisplayListClipState::AutoSaveRestore& aClipState)
+{
+ nscoord rightOverflow = aMarkerRect.XMost() - aContentArea.XMost();
+ nsRect markerRect = aMarkerRect;
+ if (rightOverflow > 0) {
+ // Marker overflows on the right side (content width < marker width).
+ markerRect.width -= rightOverflow;
+ aClipState.ClipContentDescendants(markerRect);
+ } else {
+ nscoord leftOverflow = aContentArea.x - aMarkerRect.x;
+ if (leftOverflow > 0) {
+ // Marker overflows on the left side
+ markerRect.width -= leftOverflow;
+ markerRect.x += leftOverflow;
+ aClipState.ClipContentDescendants(markerRect);
+ }
+ }
+}
+
+static void
+InflateIStart(WritingMode aWM, LogicalRect* aRect, nscoord aDelta)
+{
+ nscoord iend = aRect->IEnd(aWM);
+ aRect->IStart(aWM) -= aDelta;
+ aRect->ISize(aWM) = std::max(iend - aRect->IStart(aWM), 0);
+}
+
+static void
+InflateIEnd(WritingMode aWM, LogicalRect* aRect, nscoord aDelta)
+{
+ aRect->ISize(aWM) = std::max(aRect->ISize(aWM) + aDelta, 0);
+}
+
+static bool
+IsFrameDescendantOfAny(nsIFrame* aChild,
+ const TextOverflow::FrameHashtable& aSetOfFrames,
+ nsIFrame* aCommonAncestor)
+{
+ for (nsIFrame* f = aChild; f && f != aCommonAncestor;
+ f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
+ if (aSetOfFrames.GetEntry(f)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+class nsDisplayTextOverflowMarker : public nsDisplayItem
+{
+public:
+ nsDisplayTextOverflowMarker(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+ const nsRect& aRect, nscoord aAscent,
+ const nsStyleTextOverflowSide* aStyle,
+ uint32_t aIndex)
+ : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
+ mStyle(aStyle), mAscent(aAscent), mIndex(aIndex) {
+ MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayTextOverflowMarker() {
+ MOZ_COUNT_DTOR(nsDisplayTextOverflowMarker);
+ }
+#endif
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override {
+ *aSnap = false;
+ nsRect shadowRect =
+ nsLayoutUtils::GetTextShadowRectsUnion(mRect, mFrame);
+ return mRect.Union(shadowRect);
+ }
+
+ virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
+ {
+ if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
+ // On OS X, web authors can turn off subpixel text rendering using the
+ // CSS property -moz-osx-font-smoothing. If they do that, we don't need
+ // to use component alpha layers for the affected text.
+ if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
+ return nsRect();
+ }
+ }
+ bool snap;
+ return GetBounds(aBuilder, &snap);
+ }
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+
+ virtual uint32_t GetPerFrameKey() override {
+ return (mIndex << nsDisplayItem::TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
+ }
+ void PaintTextToContext(nsRenderingContext* aCtx,
+ nsPoint aOffsetFromRect);
+ NS_DISPLAY_DECL_NAME("TextOverflow", TYPE_TEXT_OVERFLOW)
+private:
+ nsRect mRect; // in reference frame coordinates
+ const nsStyleTextOverflowSide* mStyle;
+ nscoord mAscent; // baseline for the marker text in mRect
+ uint32_t mIndex;
+};
+
+static void
+PaintTextShadowCallback(nsRenderingContext* aCtx,
+ nsPoint aShadowOffset,
+ const nscolor& aShadowColor,
+ void* aData)
+{
+ reinterpret_cast<nsDisplayTextOverflowMarker*>(aData)->
+ PaintTextToContext(aCtx, aShadowOffset);
+}
+
+void
+nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ nscolor foregroundColor = nsLayoutUtils::
+ GetColor(mFrame, eCSSProperty__webkit_text_fill_color);
+
+ // Paint the text-shadows for the overflow marker
+ nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect, mVisibleRect,
+ foregroundColor, PaintTextShadowCallback,
+ (void*)this);
+ aCtx->ThebesContext()->SetColor(gfx::Color::FromABGR(foregroundColor));
+ PaintTextToContext(aCtx, nsPoint(0, 0));
+}
+
+void
+nsDisplayTextOverflowMarker::PaintTextToContext(nsRenderingContext* aCtx,
+ nsPoint aOffsetFromRect)
+{
+ WritingMode wm = mFrame->GetWritingMode();
+ nsPoint pt(mRect.x, mRect.y);
+ if (wm.IsVertical()) {
+ if (wm.IsVerticalLR()) {
+ pt.x = NSToCoordFloor(nsLayoutUtils::GetSnappedBaselineX(
+ mFrame, aCtx->ThebesContext(), pt.x, mAscent));
+ } else {
+ pt.x = NSToCoordFloor(nsLayoutUtils::GetSnappedBaselineX(
+ mFrame, aCtx->ThebesContext(), pt.x + mRect.width, -mAscent));
+ }
+ } else {
+ pt.y = NSToCoordFloor(nsLayoutUtils::GetSnappedBaselineY(
+ mFrame, aCtx->ThebesContext(), pt.y, mAscent));
+ }
+ pt += aOffsetFromRect;
+
+ if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
+ gfxTextRun* textRun = GetEllipsisTextRun(mFrame);
+ if (textRun) {
+ NS_ASSERTION(!textRun->IsRightToLeft(),
+ "Ellipsis textruns should always be LTR!");
+ gfxPoint gfxPt(pt.x, pt.y);
+ textRun->Draw(gfxTextRun::Range(textRun), gfxPt,
+ gfxTextRun::DrawParams(aCtx->ThebesContext()));
+ }
+ } else {
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(mFrame);
+ nsLayoutUtils::DrawString(mFrame, *fm, aCtx, mStyle->mString.get(),
+ mStyle->mString.Length(), pt);
+ }
+}
+
+TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aBlockFrame)
+ : mContentArea(aBlockFrame->GetWritingMode(),
+ aBlockFrame->GetContentRectRelativeToSelf(),
+ aBlockFrame->GetSize())
+ , mBuilder(aBuilder)
+ , mBlock(aBlockFrame)
+ , mScrollableFrame(nsLayoutUtils::GetScrollableFrameFor(aBlockFrame))
+ , mBlockSize(aBlockFrame->GetSize())
+ , mBlockWM(aBlockFrame->GetWritingMode())
+ , mAdjustForPixelSnapping(false)
+{
+#ifdef MOZ_XUL
+ if (!mScrollableFrame) {
+ nsIAtom* pseudoType = aBlockFrame->StyleContext()->GetPseudo();
+ if (pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
+ mScrollableFrame =
+ nsLayoutUtils::GetScrollableFrameFor(aBlockFrame->GetParent());
+ // nsXULScrollFrame::ClampAndSetBounds rounds to nearest pixels
+ // for RTL blocks (also for overflow:hidden), so we need to move
+ // the edges 1px outward in ExamineLineFrames to avoid triggering
+ // a text-overflow marker in this case.
+ mAdjustForPixelSnapping = !mBlockWM.IsBidiLTR();
+ }
+ }
+#endif
+ mCanHaveInlineAxisScrollbar = false;
+ if (mScrollableFrame) {
+ auto scrollbarStyle = mBlockWM.IsVertical() ?
+ mScrollableFrame->GetScrollbarStyles().mVertical :
+ mScrollableFrame->GetScrollbarStyles().mHorizontal;
+ mCanHaveInlineAxisScrollbar = scrollbarStyle != NS_STYLE_OVERFLOW_HIDDEN;
+ if (!mAdjustForPixelSnapping) {
+ // Scrolling to the end position can leave some text still overflowing due
+ // to pixel snapping behaviour in our scrolling code.
+ mAdjustForPixelSnapping = mCanHaveInlineAxisScrollbar;
+ }
+ // Use a null containerSize to convert a vector from logical to physical.
+ const nsSize nullContainerSize;
+ mContentArea.MoveBy(mBlockWM,
+ LogicalPoint(mBlockWM,
+ mScrollableFrame->GetScrollPosition(),
+ nullContainerSize));
+ nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
+ scrollFrame->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
+ }
+ uint8_t direction = aBlockFrame->StyleVisibility()->mDirection;
+ const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
+ if (mBlockWM.IsBidiLTR()) {
+ mIStart.Init(style->mTextOverflow.GetLeft(direction));
+ mIEnd.Init(style->mTextOverflow.GetRight(direction));
+ } else {
+ mIStart.Init(style->mTextOverflow.GetRight(direction));
+ mIEnd.Init(style->mTextOverflow.GetLeft(direction));
+ }
+ // The left/right marker string is setup in ExamineLineFrames when a line
+ // has overflow on that side.
+}
+
+/* static */ TextOverflow*
+TextOverflow::WillProcessLines(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aBlockFrame)
+{
+ if (!CanHaveTextOverflow(aBuilder, aBlockFrame)) {
+ return nullptr;
+ }
+ nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
+ if (scrollableFrame && scrollableFrame->IsTransformingByAPZ()) {
+ // If the APZ is actively scrolling this, don't bother with markers.
+ return nullptr;
+ }
+ return new TextOverflow(aBuilder, aBlockFrame);
+}
+
+void
+TextOverflow::ExamineFrameSubtree(nsIFrame* aFrame,
+ const LogicalRect& aContentArea,
+ const LogicalRect& aInsideMarkersArea,
+ FrameHashtable* aFramesToHide,
+ AlignmentEdges* aAlignmentEdges,
+ bool* aFoundVisibleTextOrAtomic,
+ InnerClipEdges* aClippedMarkerEdges)
+{
+ const nsIAtom* frameType = aFrame->GetType();
+ if (frameType == nsGkAtoms::brFrame ||
+ frameType == nsGkAtoms::placeholderFrame) {
+ return;
+ }
+ const bool isAtomic = IsAtomicElement(aFrame, frameType);
+ if (aFrame->StyleVisibility()->IsVisible()) {
+ LogicalRect childRect =
+ GetLogicalScrollableOverflowRectRelativeToBlock(aFrame);
+ bool overflowIStart =
+ childRect.IStart(mBlockWM) < aContentArea.IStart(mBlockWM);
+ bool overflowIEnd =
+ childRect.IEnd(mBlockWM) > aContentArea.IEnd(mBlockWM);
+ if (overflowIStart) {
+ mIStart.mHasOverflow = true;
+ }
+ if (overflowIEnd) {
+ mIEnd.mHasOverflow = true;
+ }
+ if (isAtomic && ((mIStart.mActive && overflowIStart) ||
+ (mIEnd.mActive && overflowIEnd))) {
+ aFramesToHide->PutEntry(aFrame);
+ } else if (isAtomic || frameType == nsGkAtoms::textFrame) {
+ AnalyzeMarkerEdges(aFrame, frameType, aInsideMarkersArea,
+ aFramesToHide, aAlignmentEdges,
+ aFoundVisibleTextOrAtomic,
+ aClippedMarkerEdges);
+ }
+ }
+ if (isAtomic) {
+ return;
+ }
+
+ for (nsIFrame* child : aFrame->PrincipalChildList()) {
+ ExamineFrameSubtree(child, aContentArea, aInsideMarkersArea,
+ aFramesToHide, aAlignmentEdges,
+ aFoundVisibleTextOrAtomic,
+ aClippedMarkerEdges);
+ }
+}
+
+void
+TextOverflow::AnalyzeMarkerEdges(nsIFrame* aFrame,
+ const nsIAtom* aFrameType,
+ const LogicalRect& aInsideMarkersArea,
+ FrameHashtable* aFramesToHide,
+ AlignmentEdges* aAlignmentEdges,
+ bool* aFoundVisibleTextOrAtomic,
+ InnerClipEdges* aClippedMarkerEdges)
+{
+ LogicalRect borderRect(mBlockWM,
+ nsRect(aFrame->GetOffsetTo(mBlock),
+ aFrame->GetSize()),
+ mBlockSize);
+ nscoord istartOverlap = std::max(
+ aInsideMarkersArea.IStart(mBlockWM) - borderRect.IStart(mBlockWM), 0);
+ nscoord iendOverlap = std::max(
+ borderRect.IEnd(mBlockWM) - aInsideMarkersArea.IEnd(mBlockWM), 0);
+ bool insideIStartEdge =
+ aInsideMarkersArea.IStart(mBlockWM) <= borderRect.IEnd(mBlockWM);
+ bool insideIEndEdge =
+ borderRect.IStart(mBlockWM) <= aInsideMarkersArea.IEnd(mBlockWM);
+
+ if (istartOverlap > 0) {
+ aClippedMarkerEdges->AccumulateIStart(mBlockWM, borderRect);
+ if (!mIStart.mActive) {
+ istartOverlap = 0;
+ }
+ }
+ if (iendOverlap > 0) {
+ aClippedMarkerEdges->AccumulateIEnd(mBlockWM, borderRect);
+ if (!mIEnd.mActive) {
+ iendOverlap = 0;
+ }
+ }
+
+ if ((istartOverlap > 0 && insideIStartEdge) ||
+ (iendOverlap > 0 && insideIEndEdge)) {
+ if (aFrameType == nsGkAtoms::textFrame) {
+ if (aInsideMarkersArea.IStart(mBlockWM) <
+ aInsideMarkersArea.IEnd(mBlockWM)) {
+ // a clipped text frame and there is some room between the markers
+ nscoord snappedIStart, snappedIEnd;
+ auto textFrame = static_cast<nsTextFrame*>(aFrame);
+ bool isFullyClipped = mBlockWM.IsBidiLTR() ?
+ IsFullyClipped(textFrame, istartOverlap, iendOverlap,
+ &snappedIStart, &snappedIEnd) :
+ IsFullyClipped(textFrame, iendOverlap, istartOverlap,
+ &snappedIEnd, &snappedIStart);
+ if (!isFullyClipped) {
+ LogicalRect snappedRect = borderRect;
+ if (istartOverlap > 0) {
+ snappedRect.IStart(mBlockWM) += snappedIStart;
+ snappedRect.ISize(mBlockWM) -= snappedIStart;
+ }
+ if (iendOverlap > 0) {
+ snappedRect.ISize(mBlockWM) -= snappedIEnd;
+ }
+ aAlignmentEdges->Accumulate(mBlockWM, snappedRect);
+ *aFoundVisibleTextOrAtomic = true;
+ }
+ }
+ } else {
+ aFramesToHide->PutEntry(aFrame);
+ }
+ } else if (!insideIStartEdge || !insideIEndEdge) {
+ // frame is outside
+ if (IsAtomicElement(aFrame, aFrameType)) {
+ aFramesToHide->PutEntry(aFrame);
+ }
+ } else {
+ // frame is inside
+ aAlignmentEdges->Accumulate(mBlockWM, borderRect);
+ *aFoundVisibleTextOrAtomic = true;
+ }
+}
+
+void
+TextOverflow::ExamineLineFrames(nsLineBox* aLine,
+ FrameHashtable* aFramesToHide,
+ AlignmentEdges* aAlignmentEdges)
+{
+ // No ellipsing for 'clip' style.
+ bool suppressIStart = mIStart.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+ bool suppressIEnd = mIEnd.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+ if (mCanHaveInlineAxisScrollbar) {
+ LogicalPoint pos(mBlockWM, mScrollableFrame->GetScrollPosition(),
+ mBlockSize);
+ LogicalRect scrollRange(mBlockWM, mScrollableFrame->GetScrollRange(),
+ mBlockSize);
+ // No ellipsing when nothing to scroll to on that side (this includes
+ // overflow:auto that doesn't trigger a horizontal scrollbar).
+ if (pos.I(mBlockWM) <= scrollRange.IStart(mBlockWM)) {
+ suppressIStart = true;
+ }
+ if (pos.I(mBlockWM) >= scrollRange.IEnd(mBlockWM)) {
+ suppressIEnd = true;
+ }
+ }
+
+ LogicalRect contentArea = mContentArea;
+ const nscoord scrollAdjust = mAdjustForPixelSnapping ?
+ mBlock->PresContext()->AppUnitsPerDevPixel() : 0;
+ InflateIStart(mBlockWM, &contentArea, scrollAdjust);
+ InflateIEnd(mBlockWM, &contentArea, scrollAdjust);
+ LogicalRect lineRect(mBlockWM, aLine->GetScrollableOverflowArea(),
+ mBlockSize);
+ const bool istartOverflow =
+ !suppressIStart && lineRect.IStart(mBlockWM) < contentArea.IStart(mBlockWM);
+ const bool iendOverflow =
+ !suppressIEnd && lineRect.IEnd(mBlockWM) > contentArea.IEnd(mBlockWM);
+ if (!istartOverflow && !iendOverflow) {
+ // The line does not overflow on a side we should ellipsize.
+ return;
+ }
+
+ int pass = 0;
+ bool retryEmptyLine = true;
+ bool guessIStart = istartOverflow;
+ bool guessIEnd = iendOverflow;
+ mIStart.mActive = istartOverflow;
+ mIEnd.mActive = iendOverflow;
+ bool clippedIStartMarker = false;
+ bool clippedIEndMarker = false;
+ do {
+ // Setup marker strings as needed.
+ if (guessIStart) {
+ mIStart.SetupString(mBlock);
+ }
+ if (guessIEnd) {
+ mIEnd.SetupString(mBlock);
+ }
+
+ // If there is insufficient space for both markers then keep the one on the
+ // end side per the block's 'direction'.
+ nscoord istartMarkerISize = mIStart.mActive ? mIStart.mISize : 0;
+ nscoord iendMarkerISize = mIEnd.mActive ? mIEnd.mISize : 0;
+ if (istartMarkerISize && iendMarkerISize &&
+ istartMarkerISize + iendMarkerISize > contentArea.ISize(mBlockWM)) {
+ istartMarkerISize = 0;
+ }
+
+ // Calculate the area between the potential markers aligned at the
+ // block's edge.
+ LogicalRect insideMarkersArea = mContentArea;
+ if (guessIStart) {
+ InflateIStart(mBlockWM, &insideMarkersArea, -istartMarkerISize);
+ }
+ if (guessIEnd) {
+ InflateIEnd(mBlockWM, &insideMarkersArea, -iendMarkerISize);
+ }
+
+ // Analyze the frames on aLine for the overflow situation at the content
+ // edges and at the edges of the area between the markers.
+ bool foundVisibleTextOrAtomic = false;
+ int32_t n = aLine->GetChildCount();
+ nsIFrame* child = aLine->mFirstChild;
+ InnerClipEdges clippedMarkerEdges;
+ for (; n-- > 0; child = child->GetNextSibling()) {
+ ExamineFrameSubtree(child, contentArea, insideMarkersArea,
+ aFramesToHide, aAlignmentEdges,
+ &foundVisibleTextOrAtomic,
+ &clippedMarkerEdges);
+ }
+ if (!foundVisibleTextOrAtomic && retryEmptyLine) {
+ aAlignmentEdges->mAssigned = false;
+ aFramesToHide->Clear();
+ pass = -1;
+ if (mIStart.IsNeeded() && mIStart.mActive && !clippedIStartMarker) {
+ if (clippedMarkerEdges.mAssignedIStart &&
+ clippedMarkerEdges.mIStart > mContentArea.IStart(mBlockWM)) {
+ mIStart.mISize =
+ clippedMarkerEdges.mIStart - mContentArea.IStart(mBlockWM);
+ NS_ASSERTION(mIStart.mISize < mIStart.mIntrinsicISize,
+ "clipping a marker should make it strictly smaller");
+ clippedIStartMarker = true;
+ } else {
+ mIStart.mActive = guessIStart = false;
+ }
+ continue;
+ }
+ if (mIEnd.IsNeeded() && mIEnd.mActive && !clippedIEndMarker) {
+ if (clippedMarkerEdges.mAssignedIEnd &&
+ mContentArea.IEnd(mBlockWM) > clippedMarkerEdges.mIEnd) {
+ mIEnd.mISize = mContentArea.IEnd(mBlockWM) - clippedMarkerEdges.mIEnd;
+ NS_ASSERTION(mIEnd.mISize < mIEnd.mIntrinsicISize,
+ "clipping a marker should make it strictly smaller");
+ clippedIEndMarker = true;
+ } else {
+ mIEnd.mActive = guessIEnd = false;
+ }
+ continue;
+ }
+ // The line simply has no visible content even without markers,
+ // so examine the line again without suppressing markers.
+ retryEmptyLine = false;
+ mIStart.mISize = mIStart.mIntrinsicISize;
+ mIStart.mActive = guessIStart = istartOverflow;
+ mIEnd.mISize = mIEnd.mIntrinsicISize;
+ mIEnd.mActive = guessIEnd = iendOverflow;
+ continue;
+ }
+ if (guessIStart == (mIStart.mActive && mIStart.IsNeeded()) &&
+ guessIEnd == (mIEnd.mActive && mIEnd.IsNeeded())) {
+ break;
+ } else {
+ guessIStart = mIStart.mActive && mIStart.IsNeeded();
+ guessIEnd = mIEnd.mActive && mIEnd.IsNeeded();
+ mIStart.Reset();
+ mIEnd.Reset();
+ aFramesToHide->Clear();
+ }
+ NS_ASSERTION(pass == 0, "2nd pass should never guess wrong");
+ } while (++pass != 2);
+ if (!istartOverflow || !mIStart.mActive) {
+ mIStart.Reset();
+ }
+ if (!iendOverflow || !mIEnd.mActive) {
+ mIEnd.Reset();
+ }
+}
+
+void
+TextOverflow::ProcessLine(const nsDisplayListSet& aLists,
+ nsLineBox* aLine)
+{
+ NS_ASSERTION(mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
+ mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP,
+ "TextOverflow with 'clip' for both sides");
+ mIStart.Reset();
+ mIStart.mActive = mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP;
+ mIEnd.Reset();
+ mIEnd.mActive = mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP;
+
+ FrameHashtable framesToHide(64);
+ AlignmentEdges alignmentEdges;
+ ExamineLineFrames(aLine, &framesToHide, &alignmentEdges);
+ bool needIStart = mIStart.IsNeeded();
+ bool needIEnd = mIEnd.IsNeeded();
+ if (!needIStart && !needIEnd) {
+ return;
+ }
+ NS_ASSERTION(mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
+ !needIStart, "left marker for 'clip'");
+ NS_ASSERTION(mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
+ !needIEnd, "right marker for 'clip'");
+
+ // If there is insufficient space for both markers then keep the one on the
+ // end side per the block's 'direction'.
+ if (needIStart && needIEnd &&
+ mIStart.mISize + mIEnd.mISize > mContentArea.ISize(mBlockWM)) {
+ needIStart = false;
+ }
+ LogicalRect insideMarkersArea = mContentArea;
+ if (needIStart) {
+ InflateIStart(mBlockWM, &insideMarkersArea, -mIStart.mISize);
+ }
+ if (needIEnd) {
+ InflateIEnd(mBlockWM, &insideMarkersArea, -mIEnd.mISize);
+ }
+ if (!mCanHaveInlineAxisScrollbar && alignmentEdges.mAssigned) {
+ LogicalRect alignmentRect(mBlockWM, alignmentEdges.mIStart,
+ insideMarkersArea.BStart(mBlockWM),
+ alignmentEdges.ISize(), 1);
+ insideMarkersArea.IntersectRect(insideMarkersArea, alignmentRect);
+ }
+
+ // Clip and remove display items as needed at the final marker edges.
+ nsDisplayList* lists[] = { aLists.Content(), aLists.PositionedDescendants() };
+ for (uint32_t i = 0; i < ArrayLength(lists); ++i) {
+ PruneDisplayListContents(lists[i], framesToHide, insideMarkersArea);
+ }
+ CreateMarkers(aLine, needIStart, needIEnd, insideMarkersArea);
+}
+
+void
+TextOverflow::PruneDisplayListContents(nsDisplayList* aList,
+ const FrameHashtable& aFramesToHide,
+ const LogicalRect& aInsideMarkersArea)
+{
+ nsDisplayList saved;
+ nsDisplayItem* item;
+ while ((item = aList->RemoveBottom())) {
+ nsIFrame* itemFrame = item->Frame();
+ if (IsFrameDescendantOfAny(itemFrame, aFramesToHide, mBlock)) {
+ item->~nsDisplayItem();
+ continue;
+ }
+
+ nsDisplayList* wrapper = item->GetSameCoordinateSystemChildren();
+ if (wrapper) {
+ if (!itemFrame || GetSelfOrNearestBlock(itemFrame) == mBlock) {
+ PruneDisplayListContents(wrapper, aFramesToHide, aInsideMarkersArea);
+ }
+ }
+
+ nsCharClipDisplayItem* charClip = itemFrame ?
+ nsCharClipDisplayItem::CheckCast(item) : nullptr;
+ if (charClip && GetSelfOrNearestBlock(itemFrame) == mBlock) {
+ LogicalRect rect =
+ GetLogicalScrollableOverflowRectRelativeToBlock(itemFrame);
+ if (mIStart.IsNeeded()) {
+ nscoord istart =
+ aInsideMarkersArea.IStart(mBlockWM) - rect.IStart(mBlockWM);
+ if (istart > 0) {
+ (mBlockWM.IsBidiLTR() ?
+ charClip->mVisIStartEdge : charClip->mVisIEndEdge) = istart;
+ }
+ }
+ if (mIEnd.IsNeeded()) {
+ nscoord iend = rect.IEnd(mBlockWM) - aInsideMarkersArea.IEnd(mBlockWM);
+ if (iend > 0) {
+ (mBlockWM.IsBidiLTR() ?
+ charClip->mVisIEndEdge : charClip->mVisIStartEdge) = iend;
+ }
+ }
+ }
+
+ saved.AppendToTop(item);
+ }
+ aList->AppendToTop(&saved);
+}
+
+/* static */ bool
+TextOverflow::HasClippedOverflow(nsIFrame* aBlockFrame)
+{
+ const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
+ return style->mTextOverflow.mLeft.mType == NS_STYLE_TEXT_OVERFLOW_CLIP &&
+ style->mTextOverflow.mRight.mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+}
+
+/* static */ bool
+TextOverflow::CanHaveTextOverflow(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aBlockFrame)
+{
+ // Nothing to do for text-overflow:clip or if 'overflow-x/y:visible' or if
+ // we're just building items for event processing or frame visibility.
+ if (HasClippedOverflow(aBlockFrame) ||
+ IsInlineAxisOverflowVisible(aBlockFrame) ||
+ aBuilder->IsForEventDelivery() ||
+ aBuilder->IsForFrameVisibility()) {
+ return false;
+ }
+
+ // Skip ComboboxControlFrame because it would clip the drop-down arrow.
+ // Its anon block inherits 'text-overflow' and does what is expected.
+ if (aBlockFrame->GetType() == nsGkAtoms::comboboxControlFrame) {
+ return false;
+ }
+
+ // Inhibit the markers if a descendant content owns the caret.
+ RefPtr<nsCaret> caret = aBlockFrame->PresContext()->PresShell()->GetCaret();
+ if (caret && caret->IsVisible()) {
+ nsCOMPtr<nsISelection> domSelection = caret->GetSelection();
+ if (domSelection) {
+ nsCOMPtr<nsIDOMNode> node;
+ domSelection->GetFocusNode(getter_AddRefs(node));
+ nsCOMPtr<nsIContent> content = do_QueryInterface(node);
+ if (content && nsContentUtils::ContentIsDescendantOf(content,
+ aBlockFrame->GetContent())) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void
+TextOverflow::CreateMarkers(const nsLineBox* aLine,
+ bool aCreateIStart, bool aCreateIEnd,
+ const mozilla::LogicalRect& aInsideMarkersArea)
+{
+ if (aCreateIStart) {
+ DisplayListClipState::AutoSaveRestore clipState(mBuilder);
+
+ LogicalRect markerLogicalRect(
+ mBlockWM, aInsideMarkersArea.IStart(mBlockWM) - mIStart.mIntrinsicISize,
+ aLine->BStart(), mIStart.mIntrinsicISize, aLine->BSize());
+ nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
+ nsRect markerRect =
+ markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
+ ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
+ markerRect, clipState);
+ nsDisplayItem* marker = new (mBuilder)
+ nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
+ aLine->GetLogicalAscent(), mIStart.mStyle, 0);
+ mMarkerList.AppendNewToTop(marker);
+ }
+
+ if (aCreateIEnd) {
+ DisplayListClipState::AutoSaveRestore clipState(mBuilder);
+
+ LogicalRect markerLogicalRect(
+ mBlockWM, aInsideMarkersArea.IEnd(mBlockWM), aLine->BStart(),
+ mIEnd.mIntrinsicISize, aLine->BSize());
+ nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
+ nsRect markerRect =
+ markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
+ ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
+ markerRect, clipState);
+ nsDisplayItem* marker = new (mBuilder)
+ nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
+ aLine->GetLogicalAscent(), mIEnd.mStyle, 1);
+ mMarkerList.AppendNewToTop(marker);
+ }
+}
+
+void
+TextOverflow::Marker::SetupString(nsIFrame* aFrame)
+{
+ if (mInitialized) {
+ return;
+ }
+
+ if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
+ gfxTextRun* textRun = GetEllipsisTextRun(aFrame);
+ if (textRun) {
+ mISize = textRun->GetAdvanceWidth();
+ } else {
+ mISize = 0;
+ }
+ } else {
+ nsRenderingContext rc(
+ aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext());
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
+ mISize = nsLayoutUtils::AppUnitWidthOfStringBidi(mStyle->mString, aFrame,
+ *fm, rc);
+ }
+ mIntrinsicISize = mISize;
+ mInitialized = true;
+}
+
+} // namespace css
+} // namespace mozilla
diff --git a/layout/generic/TextOverflow.h b/layout/generic/TextOverflow.h
new file mode 100644
index 000000000..835b35be1
--- /dev/null
+++ b/layout/generic/TextOverflow.h
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TextOverflow_h_
+#define TextOverflow_h_
+
+#include "nsDisplayList.h"
+#include "nsTHashtable.h"
+#include "mozilla/Likely.h"
+#include "mozilla/WritingModes.h"
+#include <algorithm>
+
+class nsIScrollableFrame;
+class nsLineBox;
+
+namespace mozilla {
+namespace css {
+
+/**
+ * A class for rendering CSS3 text-overflow.
+ * Usage:
+ * 1. allocate an object using WillProcessLines
+ * 2. then call ProcessLine for each line you are building display lists for
+ */
+class TextOverflow {
+ public:
+ /**
+ * Allocate an object for text-overflow processing.
+ * @return nullptr if no processing is necessary. The caller owns the object.
+ */
+ static TextOverflow* WillProcessLines(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aBlockFrame);
+ /**
+ * Analyze the display lists for text overflow and what kind of item is at
+ * the content edges. Add display items for text-overflow markers as needed
+ * and remove or clip items that would overlap a marker.
+ */
+ void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine);
+
+ /**
+ * Get the resulting text-overflow markers (the list may be empty).
+ * @return a DisplayList containing any text-overflow markers.
+ */
+ nsDisplayList& GetMarkers() { return mMarkerList; }
+
+ /**
+ * @return true if aBlockFrmae has text-overflow:clip on both sides.
+ */
+ static bool HasClippedOverflow(nsIFrame* aBlockFrame);
+ /**
+ * @return true if aBlockFrame needs analysis for text overflow.
+ */
+ static bool CanHaveTextOverflow(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aBlockFrame);
+
+ typedef nsTHashtable<nsPtrHashKey<nsIFrame> > FrameHashtable;
+
+ protected:
+ TextOverflow(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aBlockFrame);
+
+ typedef mozilla::WritingMode WritingMode;
+ typedef mozilla::LogicalRect LogicalRect;
+
+ struct AlignmentEdges {
+ AlignmentEdges() : mAssigned(false) {}
+ void Accumulate(WritingMode aWM, const LogicalRect& aRect)
+ {
+ if (MOZ_LIKELY(mAssigned)) {
+ mIStart = std::min(mIStart, aRect.IStart(aWM));
+ mIEnd = std::max(mIEnd, aRect.IEnd(aWM));
+ } else {
+ mIStart = aRect.IStart(aWM);
+ mIEnd = aRect.IEnd(aWM);
+ mAssigned = true;
+ }
+ }
+ nscoord ISize() { return mIEnd - mIStart; }
+ nscoord mIStart;
+ nscoord mIEnd;
+ bool mAssigned;
+ };
+
+ struct InnerClipEdges {
+ InnerClipEdges() : mAssignedIStart(false), mAssignedIEnd(false) {}
+ void AccumulateIStart(WritingMode aWM, const LogicalRect& aRect)
+ {
+ if (MOZ_LIKELY(mAssignedIStart)) {
+ mIStart = std::max(mIStart, aRect.IStart(aWM));
+ } else {
+ mIStart = aRect.IStart(aWM);
+ mAssignedIStart = true;
+ }
+ }
+ void AccumulateIEnd(WritingMode aWM, const LogicalRect& aRect)
+ {
+ if (MOZ_LIKELY(mAssignedIEnd)) {
+ mIEnd = std::min(mIEnd, aRect.IEnd(aWM));
+ } else {
+ mIEnd = aRect.IEnd(aWM);
+ mAssignedIEnd = true;
+ }
+ }
+ nscoord mIStart;
+ nscoord mIEnd;
+ bool mAssignedIStart;
+ bool mAssignedIEnd;
+ };
+
+ LogicalRect
+ GetLogicalScrollableOverflowRectRelativeToBlock(nsIFrame* aFrame) const
+ {
+ return LogicalRect(mBlockWM,
+ aFrame->GetScrollableOverflowRect() +
+ aFrame->GetOffsetTo(mBlock),
+ mBlockSize);
+ }
+
+ /**
+ * Examines frames on the line to determine whether we should draw a left
+ * and/or right marker, and if so, which frames should be completely hidden
+ * and the bounds of what will be displayed between the markers.
+ * @param aLine the line we're processing
+ * @param aFramesToHide frames that should have their display items removed
+ * @param aAlignmentEdges the outermost edges of all text and atomic
+ * inline-level frames that are inside the area between the markers
+ */
+ void ExamineLineFrames(nsLineBox* aLine,
+ FrameHashtable* aFramesToHide,
+ AlignmentEdges* aAlignmentEdges);
+
+ /**
+ * LineHasOverflowingText calls this to analyze edges, both the block's
+ * content edges and the hypothetical marker edges aligned at the block edges.
+ * @param aFrame the descendant frame of mBlock that we're analyzing
+ * @param aContentArea the block's content area
+ * @param aInsideMarkersArea the rectangle between the markers
+ * @param aFramesToHide frames that should have their display items removed
+ * @param aAlignmentEdges the outermost edges of all text and atomic
+ * inline-level frames that are inside the area between the markers
+ * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
+ * inline-level frame is visible between the marker edges
+ * @param aClippedMarkerEdges the innermost edges of all text and atomic
+ * inline-level frames that are clipped by the current marker width
+ */
+ void ExamineFrameSubtree(nsIFrame* aFrame,
+ const LogicalRect& aContentArea,
+ const LogicalRect& aInsideMarkersArea,
+ FrameHashtable* aFramesToHide,
+ AlignmentEdges* aAlignmentEdges,
+ bool* aFoundVisibleTextOrAtomic,
+ InnerClipEdges* aClippedMarkerEdges);
+
+ /**
+ * ExamineFrameSubtree calls this to analyze a frame against the hypothetical
+ * marker edges (aInsideMarkersArea) for text frames and atomic inline-level
+ * elements. A text frame adds its extent inside aInsideMarkersArea where
+ * grapheme clusters are fully visible. An atomic adds its border box if
+ * it's fully inside aInsideMarkersArea, otherwise the frame is added to
+ * aFramesToHide.
+ * @param aFrame the descendant frame of mBlock that we're analyzing
+ * @param aFrameType aFrame's frame type
+ * @param aInsideMarkersArea the rectangle between the markers
+ * @param aFramesToHide frames that should have their display items removed
+ * @param aAlignmentEdges the outermost edges of all text and atomic
+ * inline-level frames that are inside the area between the markers
+ * inside aInsideMarkersArea
+ * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
+ * inline-level frame is visible between the marker edges
+ * @param aClippedMarkerEdges the innermost edges of all text and atomic
+ * inline-level frames that are clipped by the current marker width
+ */
+ void AnalyzeMarkerEdges(nsIFrame* aFrame,
+ const nsIAtom* aFrameType,
+ const LogicalRect& aInsideMarkersArea,
+ FrameHashtable* aFramesToHide,
+ AlignmentEdges* aAlignmentEdges,
+ bool* aFoundVisibleTextOrAtomic,
+ InnerClipEdges* aClippedMarkerEdges);
+
+ /**
+ * Clip or remove items given the final marker edges. ("clip" here just means
+ * assigning mVisIStartEdge/mVisIEndEdge for any nsCharClipDisplayItem that
+ * needs it; see nsDisplayList.h for a description of that item).
+ * @param aFramesToHide remove display items for these frames
+ * @param aInsideMarkersArea is the area inside the markers
+ */
+ void PruneDisplayListContents(nsDisplayList* aList,
+ const FrameHashtable& aFramesToHide,
+ const LogicalRect& aInsideMarkersArea);
+
+ /**
+ * ProcessLine calls this to create display items for the markers and insert
+ * them into mMarkerList.
+ * @param aLine the line we're processing
+ * @param aCreateIStart if true, create a marker on the inline start side
+ * @param aCreateIEnd if true, create a marker on the inline end side
+ * @param aInsideMarkersArea is the area inside the markers
+ */
+ void CreateMarkers(const nsLineBox* aLine,
+ bool aCreateIStart, bool aCreateIEnd,
+ const LogicalRect& aInsideMarkersArea);
+
+ LogicalRect mContentArea;
+ nsDisplayListBuilder* mBuilder;
+ nsIFrame* mBlock;
+ nsIScrollableFrame* mScrollableFrame;
+ nsDisplayList mMarkerList;
+ nsSize mBlockSize;
+ WritingMode mBlockWM;
+ bool mCanHaveInlineAxisScrollbar;
+ bool mAdjustForPixelSnapping;
+
+ class Marker {
+ public:
+ void Init(const nsStyleTextOverflowSide& aStyle) {
+ mInitialized = false;
+ mISize = 0;
+ mStyle = &aStyle;
+ }
+
+ /**
+ * Setup the marker string and calculate its size, if not done already.
+ */
+ void SetupString(nsIFrame* aFrame);
+
+ bool IsNeeded() const {
+ return mHasOverflow;
+ }
+ void Reset() {
+ mHasOverflow = false;
+ }
+
+ // The current width of the marker, the range is [0 .. mIntrinsicISize].
+ nscoord mISize;
+ // The intrinsic width of the marker.
+ nscoord mIntrinsicISize;
+ // The style for this side.
+ const nsStyleTextOverflowSide* mStyle;
+ // True if there is visible overflowing inline content on this side.
+ bool mHasOverflow;
+ // True if mMarkerString and mWidth have been setup from style.
+ bool mInitialized;
+ // True if the style is text-overflow:clip on this side and the marker
+ // won't cause the line to become empty.
+ bool mActive;
+ };
+
+ Marker mIStart; // the inline start marker
+ Marker mIEnd; // the inline end marker
+};
+
+} // namespace css
+} // namespace mozilla
+
+#endif /* !defined(TextOverflow_h_) */
diff --git a/layout/generic/Visibility.h b/layout/generic/Visibility.h
new file mode 100644
index 000000000..ddb893252
--- /dev/null
+++ b/layout/generic/Visibility.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Declares visibility-related types. @Visibility is an enumeration of the
+ * possible visibility states of a frame. @OnNonvisible is an enumeration that
+ * allows callers to request a specific action when a frame transitions from
+ * visible to nonvisible.
+ */
+
+#ifndef mozilla_layout_generic_Visibility_h
+#define mozilla_layout_generic_Visibility_h
+
+namespace mozilla {
+
+// Visibility states for frames.
+enum class Visibility : uint8_t
+{
+ // Indicates that we're not tracking visibility for this frame.
+ UNTRACKED,
+
+ // Indicates that the frame is probably nonvisible. Visible frames *may* be
+ // APPROXIMATELY_NONVISIBLE because approximate visibility is not updated
+ // synchronously. Some truly nonvisible frames may be marked
+ // APPROXIMATELY_VISIBLE instead if our heuristics lead us to think they may
+ // be visible soon.
+ APPROXIMATELY_NONVISIBLE,
+
+ // Indicates that the frame is either visible now or is likely to be visible
+ // soon according to our heuristics. As with APPROXIMATELY_NONVISIBLE, it's
+ // important to note that approximately visibility is not updated
+ // synchronously, so this information may be out of date.
+ APPROXIMATELY_VISIBLE
+};
+
+// Requested actions when frames transition to the nonvisible state.
+enum class OnNonvisible : uint8_t
+{
+ DISCARD_IMAGES // Discard images associated with the frame.
+};
+
+} // namespace mozilla
+
+#endif // mozilla_layout_generic_Visibility_h
diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h
new file mode 100644
index 000000000..4c4717337
--- /dev/null
+++ b/layout/generic/WritingModes.h
@@ -0,0 +1,2075 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WritingModes_h_
+#define WritingModes_h_
+
+#include "nsRect.h"
+#include "nsStyleContext.h"
+#include "nsBidiUtils.h"
+
+// It is the caller's responsibility to operate on logical-coordinate objects
+// with matched writing modes. Failure to do so will be a runtime bug; the
+// compiler can't catch it, but in debug mode, we'll throw an assertion.
+// NOTE that in non-debug builds, a writing mode mismatch error will NOT be
+// detected, yet the results will be nonsense (and may lead to further layout
+// failures). Therefore, it is important to test (and fuzz-test) writing-mode
+// support using debug builds.
+
+// Methods in logical-coordinate classes that take another logical-coordinate
+// object as a parameter should call CHECK_WRITING_MODE on it to verify that
+// the writing modes match.
+// (In some cases, there are internal (private) methods that don't do this;
+// such methods should only be used by other methods that have already checked
+// the writing modes.)
+// The check ignores the eSidewaysMask bit of writing mode, because this does
+// not affect the interpretation of logical coordinates.
+
+#define CHECK_WRITING_MODE(param) \
+ NS_ASSERTION(param.IgnoreSideways() == GetWritingMode().IgnoreSideways(), \
+ "writing-mode mismatch")
+
+namespace mozilla {
+
+namespace widget {
+struct IMENotification;
+} // namespace widget
+
+// Physical axis constants.
+enum PhysicalAxis {
+ eAxisVertical = 0x0,
+ eAxisHorizontal = 0x1
+};
+
+inline LogicalAxis GetOrthogonalAxis(LogicalAxis aAxis)
+{
+ return aAxis == eLogicalAxisBlock ? eLogicalAxisInline : eLogicalAxisBlock;
+}
+
+inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; }
+inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); }
+inline bool IsEnd(LogicalSide aSide) { return aSide & 0x1; }
+inline bool IsStart(LogicalSide aSide) { return !IsEnd(aSide); }
+
+inline LogicalAxis GetAxis(LogicalSide aSide)
+{
+ return IsInline(aSide) ? eLogicalAxisInline : eLogicalAxisBlock;
+}
+
+inline LogicalEdge GetEdge(LogicalSide aSide)
+{
+ return IsEnd(aSide) ? eLogicalEdgeEnd : eLogicalEdgeStart;
+}
+
+inline LogicalEdge GetOppositeEdge(LogicalEdge aEdge)
+{
+ // This relies on the only two LogicalEdge enum values being 0 and 1.
+ return LogicalEdge(1 - aEdge);
+}
+
+inline LogicalSide
+MakeLogicalSide(LogicalAxis aAxis, LogicalEdge aEdge)
+{
+ return LogicalSide((aAxis << 1) | aEdge);
+}
+
+inline LogicalSide GetOppositeSide(LogicalSide aSide)
+{
+ return MakeLogicalSide(GetAxis(aSide), GetOppositeEdge(GetEdge(aSide)));
+}
+
+enum LogicalSideBits {
+ eLogicalSideBitsNone = 0,
+ eLogicalSideBitsBStart = 1 << eLogicalSideBStart,
+ eLogicalSideBitsBEnd = 1 << eLogicalSideBEnd,
+ eLogicalSideBitsIEnd = 1 << eLogicalSideIEnd,
+ eLogicalSideBitsIStart = 1 << eLogicalSideIStart,
+ eLogicalSideBitsBBoth = eLogicalSideBitsBStart | eLogicalSideBitsBEnd,
+ eLogicalSideBitsIBoth = eLogicalSideBitsIStart | eLogicalSideBitsIEnd,
+ eLogicalSideBitsAll = eLogicalSideBitsBBoth | eLogicalSideBitsIBoth
+};
+
+enum LineRelativeDir {
+ eLineRelativeDirOver = eLogicalSideBStart,
+ eLineRelativeDirUnder = eLogicalSideBEnd,
+ eLineRelativeDirLeft = eLogicalSideIStart,
+ eLineRelativeDirRight = eLogicalSideIEnd
+};
+
+/**
+ * LogicalSides represents a set of logical sides.
+ */
+struct LogicalSides final {
+ LogicalSides() : mBits(0) {}
+ explicit LogicalSides(LogicalSideBits aSideBits)
+ {
+ MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
+ mBits = aSideBits;
+ }
+ bool IsEmpty() const { return mBits == 0; }
+ bool BStart() const { return mBits & eLogicalSideBitsBStart; }
+ bool BEnd() const { return mBits & eLogicalSideBitsBEnd; }
+ bool IStart() const { return mBits & eLogicalSideBitsIStart; }
+ bool IEnd() const { return mBits & eLogicalSideBitsIEnd; }
+ bool Contains(LogicalSideBits aSideBits) const
+ {
+ MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
+ return (mBits & aSideBits) == aSideBits;
+ }
+ LogicalSides operator|(LogicalSides aOther) const
+ {
+ return LogicalSides(LogicalSideBits(mBits | aOther.mBits));
+ }
+ LogicalSides operator|(LogicalSideBits aSideBits) const
+ {
+ return *this | LogicalSides(aSideBits);
+ }
+ LogicalSides& operator|=(LogicalSides aOther)
+ {
+ mBits |= aOther.mBits;
+ return *this;
+ }
+ LogicalSides& operator|=(LogicalSideBits aSideBits)
+ {
+ return *this |= LogicalSides(aSideBits);
+ }
+ bool operator==(LogicalSides aOther) const
+ {
+ return mBits == aOther.mBits;
+ }
+ bool operator!=(LogicalSides aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+private:
+ uint8_t mBits;
+};
+
+/**
+ * mozilla::WritingMode is an immutable class representing a
+ * writing mode.
+ *
+ * It efficiently stores the writing mode and can rapidly compute
+ * interesting things about it for use in layout.
+ *
+ * Writing modes are computed from the CSS 'direction',
+ * 'writing-mode', and 'text-orientation' properties.
+ * See CSS3 Writing Modes for more information
+ * http://www.w3.org/TR/css3-writing-modes/
+ */
+class WritingMode {
+public:
+ /**
+ * Absolute inline flow direction
+ */
+ enum InlineDir {
+ eInlineLTR = 0x00, // text flows horizontally left to right
+ eInlineRTL = 0x02, // text flows horizontally right to left
+ eInlineTTB = 0x01, // text flows vertically top to bottom
+ eInlineBTT = 0x03, // text flows vertically bottom to top
+ };
+
+ /**
+ * Absolute block flow direction
+ */
+ enum BlockDir {
+ eBlockTB = 0x00, // horizontal lines stack top to bottom
+ eBlockRL = 0x01, // vertical lines stack right to left
+ eBlockLR = 0x05, // vertical lines stack left to right
+ };
+
+ /**
+ * Line-relative (bidi-relative) inline flow direction
+ */
+ enum BidiDir {
+ eBidiLTR = 0x00, // inline flow matches bidi LTR text
+ eBidiRTL = 0x10, // inline flow matches bidi RTL text
+ };
+
+ /**
+ * Unknown writing mode (should never actually be stored or used anywhere).
+ */
+ enum {
+ eUnknownWritingMode = 0xff
+ };
+
+ /**
+ * Return the absolute inline flow direction as an InlineDir
+ */
+ InlineDir GetInlineDir() const { return InlineDir(mWritingMode & eInlineMask); }
+
+ /**
+ * Return the absolute block flow direction as a BlockDir
+ */
+ BlockDir GetBlockDir() const { return BlockDir(mWritingMode & eBlockMask); }
+
+ /**
+ * Return the line-relative inline flow direction as a BidiDir
+ */
+ BidiDir GetBidiDir() const { return BidiDir(mWritingMode & eBidiMask); }
+
+ /**
+ * Return true if the inline flow direction is against physical direction
+ * (i.e. right-to-left or bottom-to-top).
+ * This occurs when writing-mode is sideways-lr OR direction is rtl (but not
+ * if both of those are true).
+ */
+ bool IsInlineReversed() const { return !!(mWritingMode & eInlineFlowMask); }
+
+ /**
+ * Return true if bidi direction is LTR. (Convenience method)
+ */
+ bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
+
+ /**
+ * True if vertical-mode block direction is LR (convenience method).
+ */
+ bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
+
+ /**
+ * True if vertical-mode block direction is RL (convenience method).
+ */
+ bool IsVerticalRL() const { return eBlockRL == GetBlockDir(); }
+
+ /**
+ * True if vertical writing mode, i.e. when
+ * writing-mode: vertical-lr | vertical-rl.
+ */
+ bool IsVertical() const { return !!(mWritingMode & eOrientationMask); }
+
+ /**
+ * True if line-over/line-under are inverted from block-start/block-end.
+ * This is true only when writing-mode is vertical-lr.
+ */
+ bool IsLineInverted() const { return !!(mWritingMode & eLineOrientMask); }
+
+ /**
+ * Block-axis flow-relative to line-relative factor.
+ * May be used as a multiplication factor for block-axis coordinates
+ * to convert between flow- and line-relative coordinate systems (e.g.
+ * positioning an over- or under-line decoration).
+ */
+ int FlowRelativeToLineRelativeFactor() const
+ {
+ return IsLineInverted() ? -1 : 1;
+ }
+
+ /**
+ * True if the text-orientation will force all text to be rendered sideways
+ * in vertical lines, in which case we should prefer an alphabetic baseline;
+ * otherwise, the default is centered.
+ * Note that some glyph runs may be rendered sideways even if this is false,
+ * due to text-orientation:mixed resolution, but in that case the dominant
+ * baseline remains centered.
+ */
+ bool IsSideways() const { return !!(mWritingMode & eSidewaysMask); }
+
+#ifdef DEBUG // Used by CHECK_WRITING_MODE to compare modes without regard
+ // for the eSidewaysMask flag.
+ WritingMode IgnoreSideways() const {
+ return WritingMode(mWritingMode & ~eSidewaysMask);
+ }
+#endif
+
+ /**
+ * Return true if boxes with this writing mode should use central baselines.
+ */
+ bool IsCentralBaseline() const { return IsVertical() && !IsSideways(); }
+
+ /**
+ * Return true if boxes with this writing mode should use alphabetical
+ * baselines.
+ */
+ bool IsAlphabeticalBaseline() const { return !IsCentralBaseline(); }
+
+
+ static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis(
+ uint8_t aWritingModeValue,
+ LogicalAxis aAxis)
+ {
+ // This relies on bit 0 of a writing-value mode indicating vertical
+ // orientation and bit 0 of a LogicalAxis value indicating the inline axis,
+ // so that it can correctly form mozilla::PhysicalAxis values using bit
+ // manipulation.
+ static_assert(NS_STYLE_WRITING_MODE_HORIZONTAL_TB == 0 &&
+ NS_STYLE_WRITING_MODE_VERTICAL_RL == 1 &&
+ NS_STYLE_WRITING_MODE_VERTICAL_LR == 3 &&
+ eLogicalAxisBlock == 0 &&
+ eLogicalAxisInline == 1 &&
+ eAxisVertical == 0 &&
+ eAxisHorizontal == 1,
+ "unexpected writing-mode, logical axis or physical axis "
+ "constant values");
+ return mozilla::PhysicalAxis((aWritingModeValue ^ aAxis) & 0x1);
+ }
+
+ mozilla::PhysicalAxis PhysicalAxis(LogicalAxis aAxis) const
+ {
+ // This will set wm to either NS_STYLE_WRITING_MODE_HORIZONTAL_TB or
+ // NS_STYLE_WRITING_MODE_VERTICAL_RL, and not the other two (real
+ // and hypothetical) values. But this is fine; we only need to
+ // distinguish between vertical and horizontal in
+ // PhysicalAxisForLogicalAxis.
+ int wm = mWritingMode & eOrientationMask;
+ return PhysicalAxisForLogicalAxis(wm, aAxis);
+ }
+
+ static mozilla::Side PhysicalSideForBlockAxis(uint8_t aWritingModeValue,
+ LogicalEdge aEdge)
+ {
+ // indexes are NS_STYLE_WRITING_MODE_* values, which are the same as these
+ // two-bit values:
+ // bit 0 = the eOrientationMask value
+ // bit 1 = the eBlockFlowMask value
+ static const mozilla::css::Side kLogicalBlockSides[][2] = {
+ { NS_SIDE_TOP, NS_SIDE_BOTTOM }, // horizontal-tb
+ { NS_SIDE_RIGHT, NS_SIDE_LEFT }, // vertical-rl
+ { NS_SIDE_BOTTOM, NS_SIDE_TOP }, // (horizontal-bt)
+ { NS_SIDE_LEFT, NS_SIDE_RIGHT }, // vertical-lr
+ };
+
+ // Ignore the SIDEWAYS_MASK bit of the writing-mode value, as this has no
+ // effect on the side mappings.
+ aWritingModeValue &= ~NS_STYLE_WRITING_MODE_SIDEWAYS_MASK;
+
+ // What's left of the writing-mode should be in the range 0-3:
+ NS_ASSERTION(aWritingModeValue < 4, "invalid aWritingModeValue value");
+
+ return kLogicalBlockSides[aWritingModeValue][aEdge];
+ }
+
+ mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const
+ {
+ // indexes are four-bit values:
+ // bit 0 = the eOrientationMask value
+ // bit 1 = the eInlineFlowMask value
+ // bit 2 = the eBlockFlowMask value
+ // bit 3 = the eLineOrientMask value
+ // Not all of these combinations can actually be specified via CSS: there
+ // is no horizontal-bt writing-mode, and no text-orientation value that
+ // produces "inverted" text. (The former 'sideways-left' value, no longer
+ // in the spec, would have produced this in vertical-rl mode.)
+ static const mozilla::css::Side kLogicalInlineSides[][2] = {
+ { NS_SIDE_LEFT, NS_SIDE_RIGHT }, // horizontal-tb ltr
+ { NS_SIDE_TOP, NS_SIDE_BOTTOM }, // vertical-rl ltr
+ { NS_SIDE_RIGHT, NS_SIDE_LEFT }, // horizontal-tb rtl
+ { NS_SIDE_BOTTOM, NS_SIDE_TOP }, // vertical-rl rtl
+ { NS_SIDE_RIGHT, NS_SIDE_LEFT }, // (horizontal-bt) (inverted) ltr
+ { NS_SIDE_TOP, NS_SIDE_BOTTOM }, // sideways-lr rtl
+ { NS_SIDE_LEFT, NS_SIDE_RIGHT }, // (horizontal-bt) (inverted) rtl
+ { NS_SIDE_BOTTOM, NS_SIDE_TOP }, // sideways-lr ltr
+ { NS_SIDE_LEFT, NS_SIDE_RIGHT }, // horizontal-tb (inverted) rtl
+ { NS_SIDE_TOP, NS_SIDE_BOTTOM }, // vertical-rl (inverted) rtl
+ { NS_SIDE_RIGHT, NS_SIDE_LEFT }, // horizontal-tb (inverted) ltr
+ { NS_SIDE_BOTTOM, NS_SIDE_TOP }, // vertical-rl (inverted) ltr
+ { NS_SIDE_LEFT, NS_SIDE_RIGHT }, // (horizontal-bt) ltr
+ { NS_SIDE_TOP, NS_SIDE_BOTTOM }, // vertical-lr ltr
+ { NS_SIDE_RIGHT, NS_SIDE_LEFT }, // (horizontal-bt) rtl
+ { NS_SIDE_BOTTOM, NS_SIDE_TOP }, // vertical-lr rtl
+ };
+
+ // Inline axis sides depend on all three of writing-mode, text-orientation
+ // and direction, which are encoded in the eOrientationMask,
+ // eInlineFlowMask, eBlockFlowMask and eLineOrientMask bits. Use these four
+ // bits to index into kLogicalInlineSides.
+ static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
+ eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
+ "unexpected mask values");
+ int index = mWritingMode & 0x0F;
+ return kLogicalInlineSides[index][aEdge];
+ }
+
+ /**
+ * Returns the physical side corresponding to the specified logical side,
+ * given the current writing mode.
+ */
+ mozilla::Side PhysicalSide(LogicalSide aSide) const
+ {
+ if (IsBlock(aSide)) {
+ static_assert(eOrientationMask == 0x01 && eBlockFlowMask == 0x04,
+ "unexpected mask values");
+ int wm = ((mWritingMode & eBlockFlowMask) >> 1) |
+ (mWritingMode & eOrientationMask);
+ return PhysicalSideForBlockAxis(wm, GetEdge(aSide));
+ }
+
+ return PhysicalSideForInlineAxis(GetEdge(aSide));
+ }
+
+ /**
+ * Returns the logical side corresponding to the specified physical side,
+ * given the current writing mode.
+ * (This is the inverse of the PhysicalSide() method above.)
+ */
+ LogicalSide LogicalSideForPhysicalSide(mozilla::css::Side aSide) const
+ {
+ // indexes are four-bit values:
+ // bit 0 = the eOrientationMask value
+ // bit 1 = the eInlineFlowMask value
+ // bit 2 = the eBlockFlowMask value
+ // bit 3 = the eLineOrientMask value
+ static const LogicalSide kPhysicalToLogicalSides[][4] = {
+ // top right
+ // bottom left
+ { eLogicalSideBStart, eLogicalSideIEnd,
+ eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb ltr
+ { eLogicalSideIStart, eLogicalSideBStart,
+ eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl ltr
+ { eLogicalSideBStart, eLogicalSideIStart,
+ eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb rtl
+ { eLogicalSideIEnd, eLogicalSideBStart,
+ eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl rtl
+ { eLogicalSideBEnd, eLogicalSideIStart,
+ eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) (inv) ltr
+ { eLogicalSideIStart, eLogicalSideBEnd,
+ eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr sw-left rtl
+ { eLogicalSideBEnd, eLogicalSideIEnd,
+ eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) (inv) rtl
+ { eLogicalSideIEnd, eLogicalSideBEnd,
+ eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr sw-left ltr
+ { eLogicalSideBStart, eLogicalSideIEnd,
+ eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb (inv) rtl
+ { eLogicalSideIStart, eLogicalSideBStart,
+ eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl sw-left rtl
+ { eLogicalSideBStart, eLogicalSideIStart,
+ eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb (inv) ltr
+ { eLogicalSideIEnd, eLogicalSideBStart,
+ eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl sw-left ltr
+ { eLogicalSideBEnd, eLogicalSideIEnd,
+ eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) ltr
+ { eLogicalSideIStart, eLogicalSideBEnd,
+ eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr ltr
+ { eLogicalSideBEnd, eLogicalSideIStart,
+ eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) rtl
+ { eLogicalSideIEnd, eLogicalSideBEnd,
+ eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr rtl
+ };
+
+ static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
+ eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
+ "unexpected mask values");
+ int index = mWritingMode & 0x0F;
+ return kPhysicalToLogicalSides[index][aSide];
+ }
+
+ /**
+ * Returns the logical side corresponding to the specified
+ * line-relative direction, given the current writing mode.
+ */
+ LogicalSide LogicalSideForLineRelativeDir(LineRelativeDir aDir) const
+ {
+ auto side = static_cast<LogicalSide>(aDir);
+ if (IsInline(side)) {
+ return !IsInlineReversed() ? side : GetOppositeSide(side);
+ }
+ return !IsLineInverted() ? side : GetOppositeSide(side);
+ }
+
+ /**
+ * Default constructor gives us a horizontal, LTR writing mode.
+ * XXX We will probably eliminate this and require explicit initialization
+ * in all cases once transition is complete.
+ */
+ WritingMode()
+ : mWritingMode(0)
+ { }
+
+ /**
+ * Construct writing mode based on a style context
+ */
+ explicit WritingMode(nsStyleContext* aStyleContext)
+ {
+ NS_ASSERTION(aStyleContext, "we need an nsStyleContext here");
+ InitFromStyleVisibility(aStyleContext->StyleVisibility());
+ }
+
+ explicit WritingMode(const nsStyleVisibility* aStyleVisibility)
+ {
+ NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
+ InitFromStyleVisibility(aStyleVisibility);
+ }
+
+private:
+ void InitFromStyleVisibility(const nsStyleVisibility* aStyleVisibility)
+ {
+ switch (aStyleVisibility->mWritingMode) {
+ case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
+ mWritingMode = 0;
+ break;
+
+ case NS_STYLE_WRITING_MODE_VERTICAL_LR:
+ {
+ mWritingMode = eBlockFlowMask |
+ eLineOrientMask |
+ eOrientationMask;
+ uint8_t textOrientation = aStyleVisibility->mTextOrientation;
+ if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
+ mWritingMode |= eSidewaysMask;
+ }
+ break;
+ }
+
+ case NS_STYLE_WRITING_MODE_VERTICAL_RL:
+ {
+ mWritingMode = eOrientationMask;
+ uint8_t textOrientation = aStyleVisibility->mTextOrientation;
+ if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
+ mWritingMode |= eSidewaysMask;
+ }
+ break;
+ }
+
+ case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
+ mWritingMode = eBlockFlowMask |
+ eInlineFlowMask |
+ eOrientationMask |
+ eSidewaysMask;
+ break;
+
+ case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
+ mWritingMode = eOrientationMask |
+ eSidewaysMask;
+ break;
+
+ default:
+ NS_NOTREACHED("unknown writing mode!");
+ mWritingMode = 0;
+ break;
+ }
+
+ if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
+ mWritingMode ^= eInlineFlowMask | eBidiMask;
+ }
+ }
+public:
+
+ /**
+ * This function performs fixup for elements with 'unicode-bidi: plaintext',
+ * where inline directionality is derived from the Unicode bidi categories
+ * of the element's content, and not the CSS 'direction' property.
+ *
+ * The WritingMode constructor will have already incorporated the 'direction'
+ * property into our flag bits, so such elements need to use this method
+ * (after resolving the bidi level of their content) to update the direction
+ * bits as needed.
+ *
+ * If it turns out that our bidi direction already matches what plaintext
+ * resolution determined, there's nothing to do here. If it didn't (i.e. if
+ * the rtl-ness doesn't match), then we correct the direction by flipping the
+ * same bits that get flipped in the constructor's CSS 'direction'-based
+ * chunk.
+ *
+ * XXX change uint8_t to UBiDiLevel after bug 924851
+ */
+ void SetDirectionFromBidiLevel(uint8_t level)
+ {
+ if (IS_LEVEL_RTL(level) == IsBidiLTR()) {
+ mWritingMode ^= eBidiMask | eInlineFlowMask;
+ }
+ }
+
+ /**
+ * Compare two WritingModes for equality.
+ */
+ bool operator==(const WritingMode& aOther) const
+ {
+ return mWritingMode == aOther.mWritingMode;
+ }
+
+ bool operator!=(const WritingMode& aOther) const
+ {
+ return mWritingMode != aOther.mWritingMode;
+ }
+
+ /**
+ * Check whether two modes are orthogonal to each other.
+ */
+ bool IsOrthogonalTo(const WritingMode& aOther) const
+ {
+ return IsVertical() != aOther.IsVertical();
+ }
+
+ /**
+ * Returns true if this WritingMode's aLogicalAxis has the same physical
+ * start side as the parallel axis of WritingMode |aOther|.
+ *
+ * @param aLogicalAxis The axis to compare from this WritingMode.
+ * @param aOther The other WritingMode (from which we'll choose the axis
+ * that's parallel to this WritingMode's aLogicalAxis, for
+ * comparison).
+ */
+ bool ParallelAxisStartsOnSameSide(LogicalAxis aLogicalAxis,
+ const WritingMode& aOther) const
+ {
+ Side myStartSide =
+ this->PhysicalSide(MakeLogicalSide(aLogicalAxis,
+ eLogicalEdgeStart));
+
+ // Figure out which of aOther's axes is parallel to |this| WritingMode's
+ // aLogicalAxis, and get its physical start side as well.
+ LogicalAxis otherWMAxis = aOther.IsOrthogonalTo(*this) ?
+ GetOrthogonalAxis(aLogicalAxis) : aLogicalAxis;
+ Side otherWMStartSide =
+ aOther.PhysicalSide(MakeLogicalSide(otherWMAxis,
+ eLogicalEdgeStart));
+
+ NS_ASSERTION(myStartSide % 2 == otherWMStartSide % 2,
+ "Should end up with sides in the same physical axis");
+ return myStartSide == otherWMStartSide;
+ }
+
+ uint8_t GetBits() const { return mWritingMode; }
+
+ const char* DebugString() const {
+ return IsVertical()
+ ? IsVerticalLR()
+ ? IsBidiLTR()
+ ? IsSideways() ? "sw-lr-ltr" : "v-lr-ltr"
+ : IsSideways() ? "sw-lr-rtl" : "v-lr-rtl"
+ : IsBidiLTR()
+ ? IsSideways() ? "sw-rl-ltr" : "v-rl-ltr"
+ : IsSideways() ? "sw-rl-rtl" : "v-rl-rtl"
+ : IsBidiLTR() ? "h-ltr" : "h-rtl"
+ ;
+ }
+
+private:
+ friend class LogicalPoint;
+ friend class LogicalSize;
+ friend class LogicalMargin;
+ friend class LogicalRect;
+
+ friend struct IPC::ParamTraits<WritingMode>;
+ // IMENotification cannot store this class directly since this has some
+ // constructors. Therefore, it stores mWritingMode and recreate the
+ // instance from it.
+ friend struct widget::IMENotification;
+
+ /**
+ * Return a WritingMode representing an unknown value.
+ */
+ static inline WritingMode Unknown()
+ {
+ return WritingMode(eUnknownWritingMode);
+ }
+
+ /**
+ * Constructing a WritingMode with an arbitrary value is a private operation
+ * currently only used by the Unknown() static method.
+ */
+ explicit WritingMode(uint8_t aValue)
+ : mWritingMode(aValue)
+ { }
+
+ uint8_t mWritingMode;
+
+ enum Masks {
+ // Masks for our bits; true chosen as opposite of commonest case
+ eOrientationMask = 0x01, // true means vertical text
+ eInlineFlowMask = 0x02, // true means absolute RTL/BTT (against physical coords)
+ eBlockFlowMask = 0x04, // true means vertical-LR (or horizontal-BT if added)
+ eLineOrientMask = 0x08, // true means over != block-start
+ eBidiMask = 0x10, // true means line-relative RTL (bidi RTL)
+ // Note: We have one excess bit of info; WritingMode can pack into 4 bits.
+ // But since we have space, we're caching interesting things for fast access.
+
+ eSidewaysMask = 0x20, // true means text is being rendered vertically
+ // using rotated glyphs (i.e. writing-mode is
+ // sideways-*, or writing-mode is vertical-* AND
+ // text-orientation is sideways),
+ // which means we'll use alphabetic instead of
+ // centered default baseline for vertical text
+
+ // Masks for output enums
+ eInlineMask = 0x03,
+ eBlockMask = 0x05
+ };
+};
+
+
+/**
+ * Logical-coordinate classes:
+ *
+ * There are three sets of coordinate space:
+ * - physical (top, left, bottom, right)
+ * relative to graphics coord system
+ * - flow-relative (block-start, inline-start, block-end, inline-end)
+ * relative to block/inline flow directions
+ * - line-relative (line-over, line-left, line-under, line-right)
+ * relative to glyph orientation / inline bidi directions
+ * See CSS3 Writing Modes for more information
+ * http://www.w3.org/TR/css3-writing-modes/#abstract-box
+ *
+ * For shorthand, B represents the block-axis
+ * I represents the inline-axis
+ *
+ * The flow-relative geometric classes store coords in flow-relative space.
+ * They use a private ns{Point,Size,Rect,Margin} member to store the actual
+ * coordinate values, but reinterpret them as logical instead of physical.
+ * This allows us to easily perform calculations in logical space (provided
+ * writing modes of the operands match), by simply mapping to nsPoint (etc)
+ * methods.
+ *
+ * Physical-coordinate accessors/setters are responsible to translate these
+ * internal logical values as necessary.
+ *
+ * In DEBUG builds, the logical types store their WritingMode and check
+ * that the same WritingMode is passed whenever callers ask them to do a
+ * writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
+ * to avoid the overhead of storing WritingMode fields.
+ *
+ * Open question: do we need a different set optimized for line-relative
+ * math, for use in nsLineLayout and the like? Or is multiplying values
+ * by FlowRelativeToLineRelativeFactor() enough?
+ */
+
+/**
+ * Flow-relative point
+ */
+class LogicalPoint {
+public:
+ explicit LogicalPoint(WritingMode aWritingMode)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mPoint(0, 0)
+ { }
+
+ // Construct from a writing mode and individual coordinates (which MUST be
+ // values in that writing mode, NOT physical coordinates!)
+ LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mPoint(aI, aB)
+ { }
+
+ // Construct from a writing mode and a physical point, within a given
+ // containing rectangle's size (defining the conversion between LTR
+ // and RTL coordinates, and between TTB and BTT coordinates).
+ LogicalPoint(WritingMode aWritingMode,
+ const nsPoint& aPoint,
+ const nsSize& aContainerSize)
+#ifdef DEBUG
+ : mWritingMode(aWritingMode)
+#endif
+ {
+ if (aWritingMode.IsVertical()) {
+ I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
+ : aPoint.y;
+ B() = aWritingMode.IsVerticalLR() ? aPoint.x
+ : aContainerSize.width - aPoint.x;
+ } else {
+ I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
+ : aPoint.x;
+ B() = aPoint.y;
+ }
+ }
+
+ /**
+ * Read-only (const) access to the logical coordinates.
+ */
+ nscoord I(WritingMode aWritingMode) const // inline-axis
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mPoint.x;
+ }
+ nscoord B(WritingMode aWritingMode) const // block-axis
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mPoint.y;
+ }
+
+ /**
+ * These non-const accessors return a reference (lvalue) that can be
+ * assigned to by callers.
+ */
+ nscoord& I(WritingMode aWritingMode) // inline-axis
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mPoint.x;
+ }
+ nscoord& B(WritingMode aWritingMode) // block-axis
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mPoint.y;
+ }
+
+ /**
+ * Return a physical point corresponding to our logical coordinates,
+ * converted according to our writing mode.
+ */
+ nsPoint GetPhysicalPoint(WritingMode aWritingMode,
+ const nsSize& aContainerSize) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsVertical()) {
+ return nsPoint(aWritingMode.IsVerticalLR()
+ ? B() : aContainerSize.width - B(),
+ aWritingMode.IsInlineReversed()
+ ? aContainerSize.height - I() : I());
+ } else {
+ return nsPoint(aWritingMode.IsInlineReversed()
+ ? aContainerSize.width - I() : I(),
+ B());
+ }
+ }
+
+ /**
+ * Return the equivalent point in a different writing mode.
+ */
+ LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
+ const nsSize& aContainerSize) const
+ {
+ CHECK_WRITING_MODE(aFromMode);
+ return aToMode == aFromMode ?
+ *this : LogicalPoint(aToMode,
+ GetPhysicalPoint(aFromMode, aContainerSize),
+ aContainerSize);
+ }
+
+ bool operator==(const LogicalPoint& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return mPoint == aOther.mPoint;
+ }
+
+ bool operator!=(const LogicalPoint& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return mPoint != aOther.mPoint;
+ }
+
+ LogicalPoint operator+(const LogicalPoint& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ // In non-debug builds, LogicalPoint does not store the WritingMode,
+ // so the first parameter here (which will always be eUnknownWritingMode)
+ // is ignored.
+ return LogicalPoint(GetWritingMode(),
+ mPoint.x + aOther.mPoint.x,
+ mPoint.y + aOther.mPoint.y);
+ }
+
+ LogicalPoint& operator+=(const LogicalPoint& aOther)
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ I() += aOther.I();
+ B() += aOther.B();
+ return *this;
+ }
+
+ LogicalPoint operator-(const LogicalPoint& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ // In non-debug builds, LogicalPoint does not store the WritingMode,
+ // so the first parameter here (which will always be eUnknownWritingMode)
+ // is ignored.
+ return LogicalPoint(GetWritingMode(),
+ mPoint.x - aOther.mPoint.x,
+ mPoint.y - aOther.mPoint.y);
+ }
+
+ LogicalPoint& operator-=(const LogicalPoint& aOther)
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ I() -= aOther.I();
+ B() -= aOther.B();
+ return *this;
+ }
+
+private:
+ friend class LogicalRect;
+
+ /**
+ * NOTE that in non-DEBUG builds, GetWritingMode() always returns
+ * eUnknownWritingMode, as the current mode is not stored in the logical-
+ * geometry classes. Therefore, this method is private; it is used ONLY
+ * by the DEBUG-mode checking macros in this class and its friends;
+ * other code is not allowed to ask a logical point for its writing mode,
+ * as this info will simply not be available in non-DEBUG builds.
+ *
+ * Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
+ * WritingMode parameter to logical methods will generally be optimized
+ * away altogether.
+ */
+#ifdef DEBUG
+ WritingMode GetWritingMode() const { return mWritingMode; }
+#else
+ WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
+#endif
+
+ // We don't allow construction of a LogicalPoint with no writing mode.
+ LogicalPoint() = delete;
+
+ // Accessors that don't take or check a WritingMode value.
+ // These are for internal use only; they are called by methods that have
+ // themselves already checked the WritingMode passed by the caller.
+ nscoord I() const // inline-axis
+ {
+ return mPoint.x;
+ }
+ nscoord B() const // block-axis
+ {
+ return mPoint.y;
+ }
+
+ nscoord& I() // inline-axis
+ {
+ return mPoint.x;
+ }
+ nscoord& B() // block-axis
+ {
+ return mPoint.y;
+ }
+
+#ifdef DEBUG
+ WritingMode mWritingMode;
+#endif
+
+ // We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
+ // fields as the inline and block directions. Hence, this is not exposed
+ // directly, but only through accessors that will map them according to the
+ // writing mode.
+ nsPoint mPoint;
+};
+
+/**
+ * Flow-relative size
+ */
+class LogicalSize {
+public:
+ explicit LogicalSize(WritingMode aWritingMode)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mSize(0, 0)
+ { }
+
+ LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mSize(aISize, aBSize)
+ { }
+
+ LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
+#ifdef DEBUG
+ : mWritingMode(aWritingMode)
+#endif
+ {
+ if (aWritingMode.IsVertical()) {
+ ISize() = aPhysicalSize.height;
+ BSize() = aPhysicalSize.width;
+ } else {
+ ISize() = aPhysicalSize.width;
+ BSize() = aPhysicalSize.height;
+ }
+ }
+
+ void SizeTo(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ mSize.SizeTo(aISize, aBSize);
+ }
+
+ /**
+ * Dimensions in logical and physical terms
+ */
+ nscoord ISize(WritingMode aWritingMode) const // inline-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mSize.width;
+ }
+ nscoord BSize(WritingMode aWritingMode) const // block-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mSize.height;
+ }
+
+ nscoord Width(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ? BSize() : ISize();
+ }
+ nscoord Height(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ? ISize() : BSize();
+ }
+
+ /**
+ * Writable references to the logical dimensions
+ */
+ nscoord& ISize(WritingMode aWritingMode) // inline-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mSize.width;
+ }
+ nscoord& BSize(WritingMode aWritingMode) // block-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mSize.height;
+ }
+
+ /**
+ * Return an nsSize containing our physical dimensions
+ */
+ nsSize GetPhysicalSize(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ?
+ nsSize(BSize(), ISize()) : nsSize(ISize(), BSize());
+ }
+
+ /**
+ * Return a LogicalSize representing this size in a different writing mode
+ */
+ LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
+ {
+#ifdef DEBUG
+ // In DEBUG builds make sure to return a LogicalSize with the
+ // expected writing mode
+ CHECK_WRITING_MODE(aFromMode);
+ return aToMode == aFromMode ?
+ *this : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
+#else
+ // optimization for non-DEBUG builds where LogicalSize doesn't store
+ // the writing mode
+ return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
+ ? *this : LogicalSize(aToMode, BSize(), ISize());
+#endif
+ }
+
+ /**
+ * Test if a size is (0, 0).
+ */
+ bool IsAllZero() const
+ {
+ return ISize() == 0 && BSize() == 0;
+ }
+
+ /**
+ * Various binary operators on LogicalSize. These are valid ONLY for operands
+ * that share the same writing mode.
+ */
+ bool operator==(const LogicalSize& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return mSize == aOther.mSize;
+ }
+
+ bool operator!=(const LogicalSize& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return mSize != aOther.mSize;
+ }
+
+ LogicalSize operator+(const LogicalSize& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return LogicalSize(GetWritingMode(), ISize() + aOther.ISize(),
+ BSize() + aOther.BSize());
+ }
+ LogicalSize& operator+=(const LogicalSize& aOther)
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ ISize() += aOther.ISize();
+ BSize() += aOther.BSize();
+ return *this;
+ }
+
+ LogicalSize operator-(const LogicalSize& aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return LogicalSize(GetWritingMode(), ISize() - aOther.ISize(),
+ BSize() - aOther.BSize());
+ }
+ LogicalSize& operator-=(const LogicalSize& aOther)
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ ISize() -= aOther.ISize();
+ BSize() -= aOther.BSize();
+ return *this;
+ }
+
+private:
+ friend class LogicalRect;
+
+ LogicalSize() = delete;
+
+#ifdef DEBUG
+ WritingMode GetWritingMode() const { return mWritingMode; }
+#else
+ WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
+#endif
+
+ nscoord ISize() const // inline-size
+ {
+ return mSize.width;
+ }
+ nscoord BSize() const // block-size
+ {
+ return mSize.height;
+ }
+
+ nscoord& ISize() // inline-size
+ {
+ return mSize.width;
+ }
+ nscoord& BSize() // block-size
+ {
+ return mSize.height;
+ }
+
+#ifdef DEBUG
+ WritingMode mWritingMode;
+#endif
+ nsSize mSize;
+};
+
+/**
+ * Flow-relative margin
+ */
+class LogicalMargin {
+public:
+ explicit LogicalMargin(WritingMode aWritingMode)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mMargin(0, 0, 0, 0)
+ { }
+
+ LogicalMargin(WritingMode aWritingMode,
+ nscoord aBStart, nscoord aIEnd,
+ nscoord aBEnd, nscoord aIStart)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mMargin(aBStart, aIEnd, aBEnd, aIStart)
+ { }
+
+ LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
+#ifdef DEBUG
+ : mWritingMode(aWritingMode)
+#endif
+ {
+ if (aWritingMode.IsVertical()) {
+ if (aWritingMode.IsVerticalLR()) {
+ mMargin.top = aPhysicalMargin.left;
+ mMargin.bottom = aPhysicalMargin.right;
+ } else {
+ mMargin.top = aPhysicalMargin.right;
+ mMargin.bottom = aPhysicalMargin.left;
+ }
+ if (aWritingMode.IsInlineReversed()) {
+ mMargin.left = aPhysicalMargin.bottom;
+ mMargin.right = aPhysicalMargin.top;
+ } else {
+ mMargin.left = aPhysicalMargin.top;
+ mMargin.right = aPhysicalMargin.bottom;
+ }
+ } else {
+ mMargin.top = aPhysicalMargin.top;
+ mMargin.bottom = aPhysicalMargin.bottom;
+ if (aWritingMode.IsInlineReversed()) {
+ mMargin.left = aPhysicalMargin.right;
+ mMargin.right = aPhysicalMargin.left;
+ } else {
+ mMargin.left = aPhysicalMargin.left;
+ mMargin.right = aPhysicalMargin.right;
+ }
+ }
+ }
+
+ nscoord IStart(WritingMode aWritingMode) const // inline-start margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.left;
+ }
+ nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.right;
+ }
+ nscoord BStart(WritingMode aWritingMode) const // block-start margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.top;
+ }
+ nscoord BEnd(WritingMode aWritingMode) const // block-end margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.bottom;
+ }
+
+ nscoord& IStart(WritingMode aWritingMode) // inline-start margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.left;
+ }
+ nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.right;
+ }
+ nscoord& BStart(WritingMode aWritingMode) // block-start margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.top;
+ }
+ nscoord& BEnd(WritingMode aWritingMode) // block-end margin
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.bottom;
+ }
+
+ nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.LeftRight();
+ }
+ nscoord BStartEnd(WritingMode aWritingMode) const // block margins
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mMargin.TopBottom();
+ }
+
+ /*
+ * Return margin values for line-relative sides, as defined in
+ * http://www.w3.org/TR/css-writing-modes-3/#line-directions:
+ *
+ * line-left
+ * Nominally the side from which LTR text would start.
+ * line-right
+ * Nominally the side from which RTL text would start. (Opposite of
+ * line-left.)
+ */
+ nscoord LineLeft(WritingMode aWritingMode) const
+ {
+ // We don't need to CHECK_WRITING_MODE here because the IStart or IEnd
+ // accessor that we call will do it.
+ return aWritingMode.IsBidiLTR()
+ ? IStart(aWritingMode) : IEnd(aWritingMode);
+ }
+ nscoord LineRight(WritingMode aWritingMode) const
+ {
+ return aWritingMode.IsBidiLTR()
+ ? IEnd(aWritingMode) : IStart(aWritingMode);
+ }
+
+ /**
+ * Return a LogicalSize representing the total size of the inline-
+ * and block-dimension margins.
+ */
+ LogicalSize Size(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return LogicalSize(aWritingMode, IStartEnd(), BStartEnd());
+ }
+
+ /**
+ * Accessors for physical margins, using our writing mode to convert from
+ * logical values.
+ */
+ nscoord Top(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ?
+ (aWritingMode.IsInlineReversed() ? IEnd() : IStart()) : BStart();
+ }
+
+ nscoord Bottom(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ?
+ (aWritingMode.IsInlineReversed() ? IStart() : IEnd()) : BEnd();
+ }
+
+ nscoord Left(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ?
+ (aWritingMode.IsVerticalLR() ? BStart() : BEnd()) :
+ (aWritingMode.IsInlineReversed() ? IEnd() : IStart());
+ }
+
+ nscoord Right(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ?
+ (aWritingMode.IsVerticalLR() ? BEnd() : BStart()) :
+ (aWritingMode.IsInlineReversed() ? IStart() : IEnd());
+ }
+
+ nscoord LeftRight(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
+ }
+
+ nscoord TopBottom(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
+ }
+
+ void SizeTo(WritingMode aWritingMode,
+ nscoord aBStart, nscoord aIEnd, nscoord aBEnd, nscoord aIStart)
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
+ }
+
+ /**
+ * Return an nsMargin containing our physical coordinates
+ */
+ nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical()
+ ? (aWritingMode.IsVerticalLR()
+ ? (aWritingMode.IsInlineReversed()
+ ? nsMargin(IEnd(), BEnd(), IStart(), BStart())
+ : nsMargin(IStart(), BEnd(), IEnd(), BStart()))
+ : (aWritingMode.IsInlineReversed()
+ ? nsMargin(IEnd(), BStart(), IStart(), BEnd())
+ : nsMargin(IStart(), BStart(), IEnd(), BEnd())))
+ : (aWritingMode.IsInlineReversed()
+ ? nsMargin(BStart(), IStart(), BEnd(), IEnd())
+ : nsMargin(BStart(), IEnd(), BEnd(), IStart()));
+ }
+
+ /**
+ * Return a LogicalMargin representing this margin in a different
+ * writing mode
+ */
+ LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
+ {
+ CHECK_WRITING_MODE(aFromMode);
+ return aToMode == aFromMode ?
+ *this : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
+ }
+
+ void ApplySkipSides(LogicalSides aSkipSides)
+ {
+ if (aSkipSides.BStart()) {
+ BStart() = 0;
+ }
+ if (aSkipSides.BEnd()) {
+ BEnd() = 0;
+ }
+ if (aSkipSides.IStart()) {
+ IStart() = 0;
+ }
+ if (aSkipSides.IEnd()) {
+ IEnd() = 0;
+ }
+ }
+
+ bool IsAllZero() const
+ {
+ return (mMargin.left == 0 && mMargin.top == 0 &&
+ mMargin.right == 0 && mMargin.bottom == 0);
+ }
+
+ LogicalMargin operator+(const LogicalMargin& aMargin) const {
+ CHECK_WRITING_MODE(aMargin.GetWritingMode());
+ return LogicalMargin(GetWritingMode(),
+ BStart() + aMargin.BStart(),
+ IEnd() + aMargin.IEnd(),
+ BEnd() + aMargin.BEnd(),
+ IStart() + aMargin.IStart());
+ }
+
+ LogicalMargin operator+=(const LogicalMargin& aMargin)
+ {
+ CHECK_WRITING_MODE(aMargin.GetWritingMode());
+ mMargin += aMargin.mMargin;
+ return *this;
+ }
+
+ LogicalMargin operator-(const LogicalMargin& aMargin) const {
+ CHECK_WRITING_MODE(aMargin.GetWritingMode());
+ return LogicalMargin(GetWritingMode(),
+ BStart() - aMargin.BStart(),
+ IEnd() - aMargin.IEnd(),
+ BEnd() - aMargin.BEnd(),
+ IStart() - aMargin.IStart());
+ }
+
+private:
+ friend class LogicalRect;
+
+ LogicalMargin() = delete;
+
+#ifdef DEBUG
+ WritingMode GetWritingMode() const { return mWritingMode; }
+#else
+ WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
+#endif
+
+ nscoord IStart() const // inline-start margin
+ {
+ return mMargin.left;
+ }
+ nscoord IEnd() const // inline-end margin
+ {
+ return mMargin.right;
+ }
+ nscoord BStart() const // block-start margin
+ {
+ return mMargin.top;
+ }
+ nscoord BEnd() const // block-end margin
+ {
+ return mMargin.bottom;
+ }
+
+ nscoord& IStart() // inline-start margin
+ {
+ return mMargin.left;
+ }
+ nscoord& IEnd() // inline-end margin
+ {
+ return mMargin.right;
+ }
+ nscoord& BStart() // block-start margin
+ {
+ return mMargin.top;
+ }
+ nscoord& BEnd() // block-end margin
+ {
+ return mMargin.bottom;
+ }
+
+ nscoord IStartEnd() const // inline margins
+ {
+ return mMargin.LeftRight();
+ }
+ nscoord BStartEnd() const // block margins
+ {
+ return mMargin.TopBottom();
+ }
+
+#ifdef DEBUG
+ WritingMode mWritingMode;
+#endif
+ nsMargin mMargin;
+};
+
+/**
+ * Flow-relative rectangle
+ */
+class LogicalRect {
+public:
+ explicit LogicalRect(WritingMode aWritingMode)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mRect(0, 0, 0, 0)
+ { }
+
+ LogicalRect(WritingMode aWritingMode,
+ nscoord aIStart, nscoord aBStart,
+ nscoord aISize, nscoord aBSize)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mRect(aIStart, aBStart, aISize, aBSize)
+ { }
+
+ LogicalRect(WritingMode aWritingMode,
+ const LogicalPoint& aOrigin,
+ const LogicalSize& aSize)
+ :
+#ifdef DEBUG
+ mWritingMode(aWritingMode),
+#endif
+ mRect(aOrigin.mPoint, aSize.mSize)
+ {
+ CHECK_WRITING_MODE(aOrigin.GetWritingMode());
+ CHECK_WRITING_MODE(aSize.GetWritingMode());
+ }
+
+ LogicalRect(WritingMode aWritingMode,
+ const nsRect& aRect,
+ const nsSize& aContainerSize)
+#ifdef DEBUG
+ : mWritingMode(aWritingMode)
+#endif
+ {
+ if (aWritingMode.IsVertical()) {
+ mRect.y = aWritingMode.IsVerticalLR()
+ ? aRect.x : aContainerSize.width - aRect.XMost();
+ mRect.x = aWritingMode.IsInlineReversed()
+ ? aContainerSize.height - aRect.YMost() : aRect.y;
+ mRect.height = aRect.width;
+ mRect.width = aRect.height;
+ } else {
+ mRect.x = aWritingMode.IsInlineReversed()
+ ? aContainerSize.width - aRect.XMost() : aRect.x;
+ mRect.y = aRect.y;
+ mRect.width = aRect.width;
+ mRect.height = aRect.height;
+ }
+ }
+
+ /**
+ * Inline- and block-dimension geometry.
+ */
+ nscoord IStart(WritingMode aWritingMode) const // inline-start edge
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.X();
+ }
+ nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.XMost();
+ }
+ nscoord ISize(WritingMode aWritingMode) const // inline-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.Width();
+ }
+
+ nscoord BStart(WritingMode aWritingMode) const // block-start edge
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.Y();
+ }
+ nscoord BEnd(WritingMode aWritingMode) const // block-end edge
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.YMost();
+ }
+ nscoord BSize(WritingMode aWritingMode) const // block-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.Height();
+ }
+
+ /**
+ * Writable (reference) accessors are only available for the basic logical
+ * fields (Start and Size), not derivatives like End.
+ */
+ nscoord& IStart(WritingMode aWritingMode) // inline-start edge
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.x;
+ }
+ nscoord& ISize(WritingMode aWritingMode) // inline-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.width;
+ }
+ nscoord& BStart(WritingMode aWritingMode) // block-start edge
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.y;
+ }
+ nscoord& BSize(WritingMode aWritingMode) // block-size
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return mRect.height;
+ }
+
+ /**
+ * Accessors for line-relative coordinates
+ */
+ nscoord LineLeft(WritingMode aWritingMode,
+ const nsSize& aContainerSize) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsBidiLTR()) {
+ return IStart();
+ }
+ nscoord containerISize =
+ aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
+ return containerISize - IEnd();
+ }
+ nscoord LineRight(WritingMode aWritingMode,
+ const nsSize& aContainerSize) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsBidiLTR()) {
+ return IEnd();
+ }
+ nscoord containerISize =
+ aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
+ return containerISize - IStart();
+ }
+
+ /**
+ * Physical coordinates of the rect.
+ */
+ nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsVertical()) {
+ return aWritingMode.IsVerticalLR() ?
+ mRect.Y() : aContainerWidth - mRect.YMost();
+ } else {
+ return aWritingMode.IsInlineReversed() ?
+ aContainerWidth - mRect.XMost() : mRect.X();
+ }
+ }
+
+ nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsVertical()) {
+ return aWritingMode.IsInlineReversed() ? aContainerHeight - mRect.XMost()
+ : mRect.X();
+ } else {
+ return mRect.Y();
+ }
+ }
+
+ nscoord Width(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ? mRect.Height() : mRect.Width();
+ }
+
+ nscoord Height(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return aWritingMode.IsVertical() ? mRect.Width() : mRect.Height();
+ }
+
+ nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsVertical()) {
+ return aWritingMode.IsVerticalLR() ?
+ mRect.YMost() : aContainerWidth - mRect.Y();
+ } else {
+ return aWritingMode.IsInlineReversed() ?
+ aContainerWidth - mRect.X() : mRect.XMost();
+ }
+ }
+
+ nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsVertical()) {
+ return aWritingMode.IsInlineReversed() ? aContainerHeight - mRect.x
+ : mRect.XMost();
+ } else {
+ return mRect.YMost();
+ }
+ }
+
+ bool IsEmpty() const
+ {
+ return mRect.IsEmpty();
+ }
+
+ bool IsAllZero() const
+ {
+ return (mRect.x == 0 && mRect.y == 0 &&
+ mRect.width == 0 && mRect.height == 0);
+ }
+
+ bool IsZeroSize() const
+ {
+ return (mRect.width == 0 && mRect.height == 0);
+ }
+
+ void SetEmpty() { mRect.SetEmpty(); }
+
+ bool IsEqualEdges(const LogicalRect aOther) const
+ {
+ CHECK_WRITING_MODE(aOther.GetWritingMode());
+ return mRect.IsEqualEdges(aOther.mRect);
+ }
+
+ LogicalPoint Origin(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return LogicalPoint(aWritingMode, IStart(), BStart());
+ }
+ void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint)
+ {
+ IStart(aWritingMode) = aPoint.I(aWritingMode);
+ BStart(aWritingMode) = aPoint.B(aWritingMode);
+ }
+
+ LogicalSize Size(WritingMode aWritingMode) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ return LogicalSize(aWritingMode, ISize(), BSize());
+ }
+
+ LogicalRect operator+(const LogicalPoint& aPoint) const
+ {
+ CHECK_WRITING_MODE(aPoint.GetWritingMode());
+ return LogicalRect(GetWritingMode(),
+ IStart() + aPoint.I(), BStart() + aPoint.B(),
+ ISize(), BSize());
+ }
+
+ LogicalRect& operator+=(const LogicalPoint& aPoint)
+ {
+ CHECK_WRITING_MODE(aPoint.GetWritingMode());
+ mRect += aPoint.mPoint;
+ return *this;
+ }
+
+ LogicalRect operator-(const LogicalPoint& aPoint) const
+ {
+ CHECK_WRITING_MODE(aPoint.GetWritingMode());
+ return LogicalRect(GetWritingMode(),
+ IStart() - aPoint.I(), BStart() - aPoint.B(),
+ ISize(), BSize());
+ }
+
+ LogicalRect& operator-=(const LogicalPoint& aPoint)
+ {
+ CHECK_WRITING_MODE(aPoint.GetWritingMode());
+ mRect -= aPoint.mPoint;
+ return *this;
+ }
+
+ void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta)
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ CHECK_WRITING_MODE(aDelta.GetWritingMode());
+ IStart() += aDelta.I();
+ BStart() += aDelta.B();
+ }
+
+ void Inflate(nscoord aD) { mRect.Inflate(aD); }
+ void Inflate(nscoord aDI, nscoord aDB) { mRect.Inflate(aDI, aDB); }
+ void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ CHECK_WRITING_MODE(aMargin.GetWritingMode());
+ mRect.Inflate(aMargin.mMargin);
+ }
+
+ void Deflate(nscoord aD) { mRect.Deflate(aD); }
+ void Deflate(nscoord aDI, nscoord aDB) { mRect.Deflate(aDI, aDB); }
+ void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ CHECK_WRITING_MODE(aMargin.GetWritingMode());
+ mRect.Deflate(aMargin.mMargin);
+ }
+
+ /**
+ * Return an nsRect containing our physical coordinates within the given
+ * container size.
+ */
+ nsRect GetPhysicalRect(WritingMode aWritingMode,
+ const nsSize& aContainerSize) const
+ {
+ CHECK_WRITING_MODE(aWritingMode);
+ if (aWritingMode.IsVertical()) {
+ return nsRect(aWritingMode.IsVerticalLR()
+ ? BStart() : aContainerSize.width - BEnd(),
+ aWritingMode.IsInlineReversed()
+ ? aContainerSize.height - IEnd() : IStart(),
+ BSize(), ISize());
+ } else {
+ return nsRect(aWritingMode.IsInlineReversed()
+ ? aContainerSize.width - IEnd() : IStart(),
+ BStart(), ISize(), BSize());
+ }
+ }
+
+ /**
+ * Return a LogicalRect representing this rect in a different writing mode
+ */
+ LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
+ const nsSize& aContainerSize) const
+ {
+ CHECK_WRITING_MODE(aFromMode);
+ return aToMode == aFromMode ?
+ *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerSize),
+ aContainerSize);
+ }
+
+ /**
+ * Set *this to be the rectangle containing the intersection of aRect1
+ * and aRect2, return whether the intersection is non-empty.
+ */
+ bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2)
+ {
+ CHECK_WRITING_MODE(aRect1.mWritingMode);
+ CHECK_WRITING_MODE(aRect2.mWritingMode);
+ return mRect.IntersectRect(aRect1.mRect, aRect2.mRect);
+ }
+
+private:
+ LogicalRect() = delete;
+
+#ifdef DEBUG
+ WritingMode GetWritingMode() const { return mWritingMode; }
+#else
+ WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
+#endif
+
+ nscoord IStart() const // inline-start edge
+ {
+ return mRect.X();
+ }
+ nscoord IEnd() const // inline-end edge
+ {
+ return mRect.XMost();
+ }
+ nscoord ISize() const // inline-size
+ {
+ return mRect.Width();
+ }
+
+ nscoord BStart() const // block-start edge
+ {
+ return mRect.Y();
+ }
+ nscoord BEnd() const // block-end edge
+ {
+ return mRect.YMost();
+ }
+ nscoord BSize() const // block-size
+ {
+ return mRect.Height();
+ }
+
+ nscoord& IStart() // inline-start edge
+ {
+ return mRect.x;
+ }
+ nscoord& ISize() // inline-size
+ {
+ return mRect.width;
+ }
+ nscoord& BStart() // block-start edge
+ {
+ return mRect.y;
+ }
+ nscoord& BSize() // block-size
+ {
+ return mRect.height;
+ }
+
+#ifdef DEBUG
+ WritingMode mWritingMode;
+#endif
+ nsRect mRect;
+};
+
+} // namespace mozilla
+
+// Definitions of inline methods for nsStyleSides, declared in nsStyleCoord.h
+// but not defined there because they need WritingMode.
+inline nsStyleUnit nsStyleSides::GetUnit(mozilla::WritingMode aWM,
+ mozilla::LogicalSide aSide) const
+{
+ return GetUnit(aWM.PhysicalSide(aSide));
+}
+
+inline nsStyleUnit nsStyleSides::GetIStartUnit(mozilla::WritingMode aWM) const
+{
+ return GetUnit(aWM, mozilla::eLogicalSideIStart);
+}
+
+inline nsStyleUnit nsStyleSides::GetBStartUnit(mozilla::WritingMode aWM) const
+{
+ return GetUnit(aWM, mozilla::eLogicalSideBStart);
+}
+
+inline nsStyleUnit nsStyleSides::GetIEndUnit(mozilla::WritingMode aWM) const
+{
+ return GetUnit(aWM, mozilla::eLogicalSideIEnd);
+}
+
+inline nsStyleUnit nsStyleSides::GetBEndUnit(mozilla::WritingMode aWM) const
+{
+ return GetUnit(aWM, mozilla::eLogicalSideBEnd);
+}
+
+inline bool nsStyleSides::HasBlockAxisAuto(mozilla::WritingMode aWM) const
+{
+ return GetBStartUnit(aWM) == eStyleUnit_Auto ||
+ GetBEndUnit(aWM) == eStyleUnit_Auto;
+}
+
+inline bool nsStyleSides::HasInlineAxisAuto(mozilla::WritingMode aWM) const
+{
+ return GetIStartUnit(aWM) == eStyleUnit_Auto ||
+ GetIEndUnit(aWM) == eStyleUnit_Auto;
+}
+
+inline nsStyleCoord nsStyleSides::Get(mozilla::WritingMode aWM,
+ mozilla::LogicalSide aSide) const
+{
+ return Get(aWM.PhysicalSide(aSide));
+}
+
+inline nsStyleCoord nsStyleSides::GetIStart(mozilla::WritingMode aWM) const
+{
+ return Get(aWM, mozilla::eLogicalSideIStart);
+}
+
+inline nsStyleCoord nsStyleSides::GetBStart(mozilla::WritingMode aWM) const
+{
+ return Get(aWM, mozilla::eLogicalSideBStart);
+}
+
+inline nsStyleCoord nsStyleSides::GetIEnd(mozilla::WritingMode aWM) const
+{
+ return Get(aWM, mozilla::eLogicalSideIEnd);
+}
+
+inline nsStyleCoord nsStyleSides::GetBEnd(mozilla::WritingMode aWM) const
+{
+ return Get(aWM, mozilla::eLogicalSideBEnd);
+}
+
+// Definitions of inline methods for nsStylePosition, declared in
+// nsStyleStruct.h but not defined there because they need WritingMode.
+inline nsStyleCoord& nsStylePosition::ISize(mozilla::WritingMode aWM)
+{
+ return aWM.IsVertical() ? mHeight : mWidth;
+}
+inline nsStyleCoord& nsStylePosition::MinISize(mozilla::WritingMode aWM)
+{
+ return aWM.IsVertical() ? mMinHeight : mMinWidth;
+}
+inline nsStyleCoord& nsStylePosition::MaxISize(mozilla::WritingMode aWM)
+{
+ return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
+}
+inline nsStyleCoord& nsStylePosition::BSize(mozilla::WritingMode aWM)
+{
+ return aWM.IsVertical() ? mWidth : mHeight;
+}
+inline nsStyleCoord& nsStylePosition::MinBSize(mozilla::WritingMode aWM)
+{
+ return aWM.IsVertical() ? mMinWidth : mMinHeight;
+}
+inline nsStyleCoord& nsStylePosition::MaxBSize(mozilla::WritingMode aWM)
+{
+ return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
+}
+
+inline const nsStyleCoord&
+nsStylePosition::ISize(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? mHeight : mWidth;
+}
+inline const nsStyleCoord&
+nsStylePosition::MinISize(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? mMinHeight : mMinWidth;
+}
+inline const nsStyleCoord&
+nsStylePosition::MaxISize(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
+}
+inline const nsStyleCoord&
+nsStylePosition::BSize(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? mWidth : mHeight;
+}
+inline const nsStyleCoord&
+nsStylePosition::MinBSize(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? mMinWidth : mMinHeight;
+}
+inline const nsStyleCoord&
+nsStylePosition::MaxBSize(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
+}
+
+inline bool
+nsStylePosition::ISizeDependsOnContainer(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? HeightDependsOnContainer()
+ : WidthDependsOnContainer();
+}
+inline bool
+nsStylePosition::MinISizeDependsOnContainer(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? MinHeightDependsOnContainer()
+ : MinWidthDependsOnContainer();
+}
+inline bool
+nsStylePosition::MaxISizeDependsOnContainer(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? MaxHeightDependsOnContainer()
+ : MaxWidthDependsOnContainer();
+}
+inline bool
+nsStylePosition::BSizeDependsOnContainer(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? WidthDependsOnContainer()
+ : HeightDependsOnContainer();
+}
+inline bool
+nsStylePosition::MinBSizeDependsOnContainer(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? MinWidthDependsOnContainer()
+ : MinHeightDependsOnContainer();
+}
+inline bool
+nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const
+{
+ return aWM.IsVertical() ? MaxWidthDependsOnContainer()
+ : MaxHeightDependsOnContainer();
+}
+
+inline mozilla::StyleFloat
+nsStyleDisplay::PhysicalFloats(mozilla::WritingMode aWM) const
+{
+ using StyleFloat = mozilla::StyleFloat;
+ if (mFloat == StyleFloat::InlineStart) {
+ return aWM.IsBidiLTR() ? StyleFloat::Left : StyleFloat::Right;
+ }
+ if (mFloat == StyleFloat::InlineEnd) {
+ return aWM.IsBidiLTR() ? StyleFloat::Right : StyleFloat::Left;
+ }
+ return mFloat;
+}
+
+inline mozilla::StyleClear
+nsStyleDisplay::PhysicalBreakType(mozilla::WritingMode aWM) const
+{
+ using StyleClear = mozilla::StyleClear;
+ if (mBreakType == StyleClear::InlineStart) {
+ return aWM.IsBidiLTR() ? StyleClear::Left : StyleClear::Right;
+ }
+ if (mBreakType == StyleClear::InlineEnd) {
+ return aWM.IsBidiLTR() ? StyleClear::Right : StyleClear::Left;
+ }
+ return mBreakType;
+}
+
+inline bool
+nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const
+{
+ return mMargin.HasBlockAxisAuto(aWM);
+}
+inline bool
+nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const
+{
+ return mMargin.HasInlineAxisAuto(aWM);
+}
+
+#endif // WritingModes_h_
diff --git a/layout/generic/broken-image.png b/layout/generic/broken-image.png
new file mode 100644
index 000000000..2a1e0dc9e
--- /dev/null
+++ b/layout/generic/broken-image.png
Binary files differ
diff --git a/layout/generic/crashtests/1001233.html b/layout/generic/crashtests/1001233.html
new file mode 100644
index 000000000..ef5922122
--- /dev/null
+++ b/layout/generic/crashtests/1001233.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+li::-moz-list-bullet {
+ direction: rtl;
+ margin-right: 1em;
+}
+</style>
+</head>
+<body>
+Body
+<ul>
+<li>list item</li>
+</ul>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1001258-1.html b/layout/generic/crashtests/1001258-1.html
new file mode 100644
index 000000000..fdb852351
--- /dev/null
+++ b/layout/generic/crashtests/1001258-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+.r {
+ display: inline-block;
+ width: 40px;
+ height: 40px;
+}
+
+</style>
+</head>
+
+<body onload="document.getElementById('willFloatLeft').style.cssFloat = 'left';">
+ <div style="width: 100px; background: lightgray;">
+ <div class="r" style="background: red; width: 10px; float: left;"></div>
+ <div class="r" style="background: violet; width: 120px;" id="willFloatLeft" ></div>
+ <div style="clear: left;"></div>
+ <div class="r" style="background: blue;"></div>
+ <div class="r" style="background: yellow; float: right;"></div>
+ <div class="r" style="background: orange; position: relative;"></div>
+ </div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/1003441.xul b/layout/generic/crashtests/1003441.xul
new file mode 100644
index 000000000..998f5ca98
--- /dev/null
+++ b/layout/generic/crashtests/1003441.xul
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="setTimeout(boom,0,1)">
+
+<splitter style="all: inherit;">
+ <box/>
+ <box/>
+ <box/>
+ <box/>
+ <iframe id="a" src="aaa"/>
+</splitter>
+
+<script id="script" xmlns="http://www.w3.org/1999/xhtml">
+
+ <![CDATA[//<![CDATA[
+
+var doc = document;
+function boom(i) {
+ if (i>6)
+ return;
+ var x=doc.getElementsByTagName('*');
+ if (x[i] && x[i+1]) {
+ var temp = x[i+1].getAttribute('style');
+ x[i+1].setAttribute('style', x[i].getAttribute('style'));
+ x[i].setAttribute('style', temp);
+ } else {
+ return;
+ }
+ i++;
+ setTimeout(boom,50,i);
+}
+
+//]]>
+
+ </script>
+
+ </window>
diff --git a/layout/generic/crashtests/1015562.html b/layout/generic/crashtests/1015562.html
new file mode 100644
index 000000000..8975e64f2
--- /dev/null
+++ b/layout/generic/crashtests/1015562.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+/*
+user_pref("layout.css.grid.enabled", true);
+*/
+
+function boom()
+{
+ document.getElementById("r").appendChild(document.createTextNode("B"));
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id="r" style="display: grid;">A</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1015563-1.html b/layout/generic/crashtests/1015563-1.html
new file mode 100644
index 000000000..5acfb644d
--- /dev/null
+++ b/layout/generic/crashtests/1015563-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html style="display: inline-flex;">
+<body style="margin: -3642924795px; flex-grow: 1;"></body>
+</html>
diff --git a/layout/generic/crashtests/1015563-2.html b/layout/generic/crashtests/1015563-2.html
new file mode 100644
index 000000000..0245bb45c
--- /dev/null
+++ b/layout/generic/crashtests/1015563-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <div style="display: flex">
+ <div style="margin: -3642924795px; flex-grow: 1;"></div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1015844.html b/layout/generic/crashtests/1015844.html
new file mode 100644
index 000000000..b1a19afe6
--- /dev/null
+++ b/layout/generic/crashtests/1015844.html
@@ -0,0 +1,25 @@
+<style>
+.multicol {
+ width: 500px;
+ -moz-column-width: 100px;
+ height: 100px;
+ position: relative;
+}
+#clear {
+ position: absolute;
+}
+.float {
+ height: 300px;
+}
+</style>
+<div class="multicol">
+<div class="float">
+<div class="multicol">
+<div id="clear">
+<div class="float">
+<div class="multicol">
+<div class="float">&nbsp;
+</div>
+<div class="multicol">
+<div class="multicol">
+<div class="float">a
diff --git a/layout/generic/crashtests/1032450.html b/layout/generic/crashtests/1032450.html
new file mode 100644
index 000000000..b09637e52
--- /dev/null
+++ b/layout/generic/crashtests/1032450.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+</head>
+
+<body style="display: -moz-inline-grid;">
+<div style="display: table;position: relative; float: left;">
+<button style="display: table-column-group;">
+ <iframe style="position: absolute;"></iframe>
+</button>
+</div>
+
+</body></html>
diff --git a/layout/generic/crashtests/1032613-1.svg b/layout/generic/crashtests/1032613-1.svg
new file mode 100644
index 000000000..c1e863980
--- /dev/null
+++ b/layout/generic/crashtests/1032613-1.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" onload="tweak()">
+ <pattern>
+ <rect id="r" />
+ </pattern>
+ <script>
+ function tweak() {
+ document.getElementById("r").style.textDecoration = "underline";
+ }
+ </script>
+</svg>
diff --git a/layout/generic/crashtests/1032613-2.html b/layout/generic/crashtests/1032613-2.html
new file mode 100644
index 000000000..9ba11b314
--- /dev/null
+++ b/layout/generic/crashtests/1032613-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ function tweak() {
+ document.getElementById("c").style.textShadow = "3px 3px gray";
+ }
+ </script>
+ <body onload="tweak()">
+ <div id="c">hello
+ <svg height="0">
+ <clipPath>
+ <path d=""/>
+ </clipPath>
+ </svg>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/1037903.html b/layout/generic/crashtests/1037903.html
new file mode 100644
index 000000000..3905967ec
--- /dev/null
+++ b/layout/generic/crashtests/1037903.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="-moz-column-width: calc(15px);">
+<body>
+<video></video><audio style="box-decoration-break: clone; display: block; direction: rtl;"></audio>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1039454-1.html b/layout/generic/crashtests/1039454-1.html
new file mode 100644
index 000000000..b049cbfa3
--- /dev/null
+++ b/layout/generic/crashtests/1039454-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<body>
+<div id="outer" style="width:200px; height:200px; background:blue; transform:translateY(10px)">
+ <div id="inner" style="width:200px; height:100px; background:yellow; transform:translateX(100px)">
+ </div>
+</div>
+<script>
+console.log(outer.getBoundingClientRect());
+outer.style.width = "400px";
+console.log(outer.getBoundingClientRect());
+outer.style.transform = "translateY(100px)";
+</script>
diff --git a/layout/generic/crashtests/1042489.html b/layout/generic/crashtests/1042489.html
new file mode 100644
index 000000000..0a1cdfa30
--- /dev/null
+++ b/layout/generic/crashtests/1042489.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="-moz-column-width: 1px;">
+<body style="-moz-column-width: 1px; box-decoration-break: clone;">
+<div>A B</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1054010-1.html b/layout/generic/crashtests/1054010-1.html
new file mode 100644
index 000000000..52557340a
--- /dev/null
+++ b/layout/generic/crashtests/1054010-1.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style type="text/css">
+ .flexRow {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
+ .flexColumn {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ flex-basis: 100%;
+ }
+
+ .flexBlock {
+ flex: 0;
+ display: flex;
+ flex-direction: column;
+ padding: 5px;
+ border: 1px solid blue;
+ }
+
+ .flexColumn > .flexBlock:last-child {
+ flex: 1;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 1
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 2
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 3
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 4
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 5
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 6
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 7
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 8
+ <div class="flexRow">
+ <div class="flexColumn">
+ <div class="flexBlock">
+ Nested layout 9
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/1058954-1.html b/layout/generic/crashtests/1058954-1.html
new file mode 100644
index 000000000..bfd06016c
--- /dev/null
+++ b/layout/generic/crashtests/1058954-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+ <div style="display: -moz-grid-line;">
+ <div style="position: relative; direction: rtl;">
+ <div style="position: absolute;">
+ </div>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/1134531.html b/layout/generic/crashtests/1134531.html
new file mode 100644
index 000000000..35ddbb060
--- /dev/null
+++ b/layout/generic/crashtests/1134531.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body onload="x.style.textTransform = 'capitalize';"><div id="x">b</div></body>
+</html>
diff --git a/layout/generic/crashtests/1134667.html b/layout/generic/crashtests/1134667.html
new file mode 100644
index 000000000..e33eb99dd
--- /dev/null
+++ b/layout/generic/crashtests/1134667.html
@@ -0,0 +1,2 @@
+x
+<ruby><x>
diff --git a/layout/generic/crashtests/1137723-1.html b/layout/generic/crashtests/1137723-1.html
new file mode 100644
index 000000000..a507ead85
--- /dev/null
+++ b/layout/generic/crashtests/1137723-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+ <meta charset="utf-8">
+<title>Testcase bug 1137723</title>
+
+<style>
+ .grid-comment{position:fixed;top:100%;}
+ a[href]:after{content:" (" attr(href) ")";}
+ </style>
+
+<style>
+@font-face { font-family: "slick"; src: url("non-existent-file.eot"); }
+</style>
+</head>
+
+<body>
+ <div style="height:7in"></div>
+
+ <div class="grid-comment">Artikel teilen
+
+<div>
+<a href="https://www.facebook.com/sharer/sharer.php?u=http://www.stuttgarter-zeitung.de/inhalt.griechenland-hilfe-bundestag-stimmt-verlaengerung-zu.daa3baae-e17d-47e8-ad28-62eece0b4cfa.html">shares</a>
+<a href="https://twitter.com/share?text=Griechenland-Hilfe:Bundestag stimmt Verlängerung zu&amp;url=http://www.stuttgarter-zeitung.de/inhalt.griechenland-hilfe-bundestag-stimmt-verlaengerung-zu.daa3baae-e17d-47e8-ad28-62eece0b4cfa.html">tweets</a>
+</div>
+
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1137723-2.html b/layout/generic/crashtests/1137723-2.html
new file mode 100644
index 000000000..ed8761a81
--- /dev/null
+++ b/layout/generic/crashtests/1137723-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+ <meta charset="utf-8">
+<title>Testcase bug 1137723</title>
+
+<style>
+ .grid-comment{position:fixed;top:100%;}
+ a[href]:after{content:" (" attr(href) ")";}
+ </style>
+
+<style>
+@font-face { font-family: "slick"; src: url("non-existent-file.eot"); }
+</style>
+</head>
+
+<body>
+ <div style="height:3in"></div>
+
+ <div class="grid-comment">Artikel teilen
+
+<div>
+<a href="https://www.facebook.com/sharer/sharer.php?u=http://www.stuttgarter-zeitung.de/inhalt.griechenland-hilfe-bundestag-stimmt-verlaengerung-zu.daa3baae-e17d-47e8-ad28-62eece0b4cfa.html">shares</a>
+<a href="https://twitter.com/share?text=Griechenland-Hilfe:Bundestag stimmt Verlängerung zu&amp;url=http://www.stuttgarter-zeitung.de/inhalt.griechenland-hilfe-bundestag-stimmt-verlaengerung-zu.daa3baae-e17d-47e8-ad28-62eece0b4cfa.html">tweets</a>
+</div>
+
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1140268-1.html b/layout/generic/crashtests/1140268-1.html
new file mode 100644
index 000000000..5e5510ba7
--- /dev/null
+++ b/layout/generic/crashtests/1140268-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<script>
+function boom()
+{
+ var e = document.getElementsByTagName("mo")[0];
+ e.setAttribute("style", "position: absolute; top: 0px;");
+ document.documentElement.offsetHeight;
+ e.setAttribute("style", "position: absolute; top: 100px;");
+}
+</script>
+</head>
+<body onload="boom();">
+<math><mo>boom!</mo></math>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1145768.html b/layout/generic/crashtests/1145768.html
new file mode 100644
index 000000000..b8e6938db
--- /dev/null
+++ b/layout/generic/crashtests/1145768.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ var emptyTail = q.firstChild.splitText(2);
+ document.documentElement.offsetHeight;
+ emptyTail.splitText(0);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div style="width: 1px; height: 1px;"><div id="q" style="writing-mode: vertical-rl;">&#x07;R</div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/1146103.html b/layout/generic/crashtests/1146103.html
new file mode 100644
index 000000000..876e08bd6
--- /dev/null
+++ b/layout/generic/crashtests/1146103.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: ruby-text; margin: -47891343%"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1146107.html b/layout/generic/crashtests/1146107.html
new file mode 100644
index 000000000..661fa876c
--- /dev/null
+++ b/layout/generic/crashtests/1146107.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: ruby-base; margin: -47891343%"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1146114.html b/layout/generic/crashtests/1146114.html
new file mode 100644
index 000000000..5f148a167
--- /dev/null
+++ b/layout/generic/crashtests/1146114.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: ruby-text; margin: 288230376151711740%"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1153695.html b/layout/generic/crashtests/1153695.html
new file mode 100644
index 000000000..a7d7010d3
--- /dev/null
+++ b/layout/generic/crashtests/1153695.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta charset="UTF-8">
+ <script>
+ function boom()
+ {
+ document.documentElement.offsetHeight;
+ document.body.appendChild(document.getElementById("x"));
+ document.documentElement.offsetHeight;
+ }
+ </script>
+ </head>
+
+ <body style="-moz-column-count: 4;" onload="boom();">
+ <div style="float: left; height: 10px; width: 10px;">
+ <div id="x">
+ <div style="height: 80px;"></div>
+ <div style="float: left; height: 10px;"></div>
+ </div>
+ </div>
+ </body>
+
+</html>
diff --git a/layout/generic/crashtests/1156222.html b/layout/generic/crashtests/1156222.html
new file mode 100644
index 000000000..75dcc85d2
--- /dev/null
+++ b/layout/generic/crashtests/1156222.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div><span dir="rtl"></span><audio style="display: ruby-base;"></audio></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1156257.html b/layout/generic/crashtests/1156257.html
new file mode 100644
index 000000000..e58c1ba5d
--- /dev/null
+++ b/layout/generic/crashtests/1156257.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<!--
+user_pref("layout.css.grid.enabled", true);
+-->
+<script>
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.getElementById("g").firstChild.remove();
+}
+</script>
+</head>
+<body onload="boom();">
+<div style="display: grid;" id="g">a b<br></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1157011.html b/layout/generic/crashtests/1157011.html
new file mode 100644
index 000000000..c5fe133a8
--- /dev/null
+++ b/layout/generic/crashtests/1157011.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body style="display: inline-flex;"><div><div style="display: ruby-base;">f<br style="display: ruby-base-container;"></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/1169420-1.html b/layout/generic/crashtests/1169420-1.html
new file mode 100644
index 000000000..1ddbaea4f
--- /dev/null
+++ b/layout/generic/crashtests/1169420-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html style="writing-mode: vertical-lr;">
+ <body>
+ <div style="display: inline-flex;">
+ <div style="margin-inline-start: auto; margin-inline-end: 75px; direction: rtl;"></div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/1169420-2.html b/layout/generic/crashtests/1169420-2.html
new file mode 100644
index 000000000..e096ed7f9
--- /dev/null
+++ b/layout/generic/crashtests/1169420-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html style="writing-mode: vertical-lr;">
+ <body>
+ <div style="display: inline-flex;">
+ <div style="margin-bottom: auto; margin-top: 75px; direction: rtl;"></div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/1183431.html b/layout/generic/crashtests/1183431.html
new file mode 100644
index 000000000..e1d4c87c0
--- /dev/null
+++ b/layout/generic/crashtests/1183431.html
@@ -0,0 +1,6 @@
+<!DOCTYPE>
+<html>
+<body>
+<div style="writing-mode: vertical-lr;"><div style="position: fixed;"></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1221112-1.html b/layout/generic/crashtests/1221112-1.html
new file mode 100644
index 000000000..24e60a37b
--- /dev/null
+++ b/layout/generic/crashtests/1221112-1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ .flexContainer {
+ display: flex;
+
+ /* Just for easier visualization: */
+ width: 600px;
+ justify-content: space-around;
+ border: 1px solid black;
+ }
+
+ .flexContainer:before {
+ position:absolute;
+ content:'before';
+ }
+ .flexContainer:after {
+ position:absolute;
+ content:'after'
+ }
+
+ .ordered-item {
+ position:relative;
+ order:5;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="flexContainer">
+ <div class="ordered-item">ItemWithOrderSet
+ <!-- It's important that this remain unclosed, for some reason. -->
diff --git a/layout/generic/crashtests/1221112-2.html b/layout/generic/crashtests/1221112-2.html
new file mode 100644
index 000000000..2ae372a9d
--- /dev/null
+++ b/layout/generic/crashtests/1221112-2.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ .flexContainer {
+ display: flex;
+
+ /* Just for easier visualization: */
+ width: 600px;
+ justify-content: space-around;
+ border: 1px solid black;
+ }
+
+ .flexContainer:before {
+ position:absolute;
+ content:'before';
+ }
+ .flexContainer:after {
+ position:absolute;
+ content:'after'
+ }
+ </style>
+ </head>
+ <body>
+ <div class="flexContainer">
+ <div class="ordered-item">NormalFlexItem
+ <!-- It's important that this remain unclosed, for some reason. -->
diff --git a/layout/generic/crashtests/1221874-1.html b/layout/generic/crashtests/1221874-1.html
new file mode 100644
index 000000000..85d5bab3c
--- /dev/null
+++ b/layout/generic/crashtests/1221874-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.body.style.textOrientation = "sideways";
+}
+
+</script>
+</head>
+<body onload="boom();" style="writing-mode: tb-rl; display: table;">Hello</body>
+</html>
diff --git a/layout/generic/crashtests/1222783.xhtml b/layout/generic/crashtests/1222783.xhtml
new file mode 100644
index 000000000..29cba980f
--- /dev/null
+++ b/layout/generic/crashtests/1222783.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE html>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<title>Test, bug 1222783</title>
+<body>
+
+
+<div id="container" style="width: 400px">
+ <div id="float1" style="float: left; height: 50px; width: 50px"></div>
+ <div id="float2" style="float: left; clear: left; height: 50px; width: 200px"></div>
+
+ <frameset cols="50%,50%">
+ <frame></frame>
+ <frame></frame>
+ </frameset>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/1223568-1.html b/layout/generic/crashtests/1223568-1.html
new file mode 100644
index 000000000..8d09d878b
--- /dev/null
+++ b/layout/generic/crashtests/1223568-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="display:flex; justify-content: stretch end">a
diff --git a/layout/generic/crashtests/1223568-2.html b/layout/generic/crashtests/1223568-2.html
new file mode 100644
index 000000000..6b44e485c
--- /dev/null
+++ b/layout/generic/crashtests/1223568-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<span style="justify-content: stretch end true; display: inline-flex;"><span style="writing-mode: vertical-rl; display: -moz-inline-box;">f</span></span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1224230-1.html b/layout/generic/crashtests/1224230-1.html
new file mode 100644
index 000000000..2743b43ae
--- /dev/null
+++ b/layout/generic/crashtests/1224230-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script>
+function boom() {
+ var div = document.querySelector("div");
+ div.offsetHeight;
+ div.style.fontSize = "120%";
+}
+</script>
+<body onload="boom()">
+<div style="float:left; position:relative;">
+ <div style="display:inline;">
+ <img src="foo.jpg" style="float:left;">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam tortor nulla,
+eleifend eu eleifend eu, scelerisque sit amet sapien. Sed iaculis tellus ut quam
+pharetra consequat. Donec vitae nulla eu mi porta vulputate. In vestibulum, erat
+quis aliquam tempor, lectus augue viverra justo, vitae semper nibh neque tempor
+orci. Etiam luctus aliquet magna id pellentesque. Interdum et malesuada fames ac
+ante ipsum primis in faucibus. Suspendisse sit amet eros volutpat, convallis
+purus non, porta sapien. Duis fermentum at tortor nec ultricies. Morbi et lacus
+vitae risus elementum condimentum quis vitae justo. Cum sociis natoque penatibus
+et magnis dis parturient montes, nascetur ridiculus mus.<br><br>
diff --git a/layout/generic/crashtests/1225005.html b/layout/generic/crashtests/1225005.html
new file mode 100644
index 000000000..8c2164df2
--- /dev/null
+++ b/layout/generic/crashtests/1225005.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body style="padding: 810520769306363pt; -moz-column-count: 10; transform: translate(50%);"><div style="position: absolute;"></div></body>
+</html>
diff --git a/layout/generic/crashtests/1225118.html b/layout/generic/crashtests/1225118.html
new file mode 100644
index 000000000..833596243
--- /dev/null
+++ b/layout/generic/crashtests/1225118.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html style="width: -moz-fit-content; display: grid;">
+<body style="border: 1px solid green; padding-right: 56030668px;"></body>
+</html>
diff --git a/layout/generic/crashtests/1225376.html b/layout/generic/crashtests/1225376.html
new file mode 100644
index 000000000..f6e35be3e
--- /dev/null
+++ b/layout/generic/crashtests/1225376.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="display: inline-grid;">
+ <div style="display: flex; align-self: right safe;"></div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/1225592.html b/layout/generic/crashtests/1225592.html
new file mode 100644
index 000000000..e4b5e6e6d
--- /dev/null
+++ b/layout/generic/crashtests/1225592.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="grid-template-columns: repeat(16384, auto); display: grid;"></div>
+<div style="grid-template-columns: repeat(9000, auto) repeat(9000, auto); display: grid;"></div>
+
+<div style="grid-template-columns: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ; display: grid;"></div>
+
+
+<div style='grid-template-areas: ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "; display: grid;'></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/1229437-1.html b/layout/generic/crashtests/1229437-1.html
new file mode 100644
index 000000000..2f1e66c31
--- /dev/null
+++ b/layout/generic/crashtests/1229437-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+ rbc { display: ruby-base-container; }
+</style>
+<div style="-moz-column-width: 1px">
+ <ruby><rbc></rbc><rb><div style="float: right"></div></rb></ruby>X
+</div>
diff --git a/layout/generic/crashtests/1229437-2.html b/layout/generic/crashtests/1229437-2.html
new file mode 100644
index 000000000..5b6ce6329
--- /dev/null
+++ b/layout/generic/crashtests/1229437-2.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<div style="-moz-column-width: 1px">
+ <ruby><rb></rb><rb><div style="float: right"></div></rb></ruby>X
+</div>
diff --git a/layout/generic/crashtests/1233191.html b/layout/generic/crashtests/1233191.html
new file mode 100644
index 000000000..6a6a06edf
--- /dev/null
+++ b/layout/generic/crashtests/1233191.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="width: 1px;"><fieldset style="display: grid;">y<legend></legend>x</fieldset></div>
+<div style="width: 1px;"><fieldset style="display: grid; overflow:hidden">y<legend></legend>x</fieldset></div>
+<div style="width: 1px;"><fieldset style="display: flex;">y<legend></legend>x</fieldset></div>
+<div style="width: 1px;"><fieldset style="display: flex; overflow:hidden">y<legend></legend>x</fieldset></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1272983-1.html b/layout/generic/crashtests/1272983-1.html
new file mode 100644
index 000000000..32a5a302e
--- /dev/null
+++ b/layout/generic/crashtests/1272983-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div.foo {
+ margin-bottom: 50%;
+ display: -moz-box;
+ direction: rtl;
+ }
+ </style>
+</head>
+<body>
+ <div class="foo"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1272983-2.html b/layout/generic/crashtests/1272983-2.html
new file mode 100644
index 000000000..cff041891
--- /dev/null
+++ b/layout/generic/crashtests/1272983-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div.foo {
+ padding-bottom: 50%;
+ display: -moz-box;
+ direction: rtl;
+ }
+ </style>
+</head>
+<body>
+ <div class="foo"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/1275059.html b/layout/generic/crashtests/1275059.html
new file mode 100644
index 000000000..bfcbfa6be
--- /dev/null
+++ b/layout/generic/crashtests/1275059.html
@@ -0,0 +1,3 @@
+<div style="writing-mode: vertical-rl">
+<span style="text-combine-upright: all">
+<p>
diff --git a/layout/generic/crashtests/1278007.html b/layout/generic/crashtests/1278007.html
new file mode 100644
index 000000000..f4e9e40da
--- /dev/null
+++ b/layout/generic/crashtests/1278007.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title></title>
+ <style type="text/css">
+.grid {
+ display: grid;
+}
+
+.sfb { align-self:baseline; }
+.slb { align-self:last baseline; }
+
+.vl { writing-mode: vertical-lr; }
+
+</style>
+</head>
+<body>
+
+<div class="grid"><input class="slb"></div>
+<div class="grid"><input class="sfb"></div>
+
+<div class="grid"><input class="slb vl"></div>
+<div class="grid"><input class="sfb vl"></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/1278461-1.html b/layout/generic/crashtests/1278461-1.html
new file mode 100644
index 000000000..998f4d7d1
--- /dev/null
+++ b/layout/generic/crashtests/1278461-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252">
+<script>
+
+function boom()
+{
+ x.style.display = "contents";
+}
+
+</script>
+</head>
+<body onload="boom();">
+
+ <div style="-moz-column-width: 1px;">
+ <div style="display: grid;">
+ <div style="height: 200px;">v</div>
+ <div style="height: 200px;" id="x">x</div>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/1278461-2.html b/layout/generic/crashtests/1278461-2.html
new file mode 100644
index 000000000..291410cf7
--- /dev/null
+++ b/layout/generic/crashtests/1278461-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"></head><body>
+<style>
+html{-moz-columns:50px auto}
+body{display:grid}
+div{height:200px}
+</style>
+<script>
+window.onload=function(){
+var b = document.body, v;
+b.appendChild(document.createElementNS('http://www.w3.org/1999/xhtml', 'div'));
+b.appendChild(document.createElementNS('http://www.w3.org/1999/xhtml', 'div'));
+b.appendChild(v = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'));
+setTimeout(function(){
+ v.style.overflow = "-moz-scrollbars-none";
+ document.documentElement.className = '';
+ },100);
+};
+</script>
+
+<div></div><div></div><div style="overflow: hidden;"></div></body></html>
diff --git a/layout/generic/crashtests/1279814.html b/layout/generic/crashtests/1279814.html
new file mode 100644
index 000000000..71a9a6e3b
--- /dev/null
+++ b/layout/generic/crashtests/1279814.html
@@ -0,0 +1,35 @@
+<!-- 128 LRI / RLI -->
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+<!-- 64 PDI -->
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;&#x2069;
+<!-- 64 LRI / RLI -->
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
+&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;&#x2066;&#x2067;
diff --git a/layout/generic/crashtests/1297427-non-equal-centers.html b/layout/generic/crashtests/1297427-non-equal-centers.html
new file mode 100644
index 000000000..e51c8df32
--- /dev/null
+++ b/layout/generic/crashtests/1297427-non-equal-centers.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<meta charset="utf-8" />
+<style>
+#box {
+ border-radius: 335px;
+ width: 600px;
+ height: 401px;
+ border-style: dotted;
+ border-width: 41px 1px;
+}
+</style>
+<div id="box"></div>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/1304441.html b/layout/generic/crashtests/1304441.html
new file mode 100644
index 000000000..26d7dcdd6
--- /dev/null
+++ b/layout/generic/crashtests/1304441.html
@@ -0,0 +1,9 @@
+<details>
+<summary>
+<li>
+<style>
+summary{
+all:initial
+}
+:first-child::first-line
+{}
diff --git a/layout/generic/crashtests/1316649.html b/layout/generic/crashtests/1316649.html
new file mode 100644
index 000000000..cfef7d0f0
--- /dev/null
+++ b/layout/generic/crashtests/1316649.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<script>
+addEventListener('DOMContentLoaded', function(){
+ obj1.style.writingMode = "rl";
+ obj2.style.writingMode = "rl";
+ //obj3.style.writingMode = "rl";
+ //obj4.style.writingMode = "rl";
+ obj5.style.writingMode = "rl";
+ sobj1.style.writingMode = "rl";
+ sobj2.style.writingMode = "rl";
+ //sobj3.style.writingMode = "rl";
+ //sobj4.style.writingMode = "rl";
+ sobj5.style.writingMode = "rl";
+});
+</script>
+<body style="writing-mode:tb">
+<button style="display:grid">
+<div id=obj1></div>
+</button>
+<button style="display:inline-grid">
+<div id=obj2></div>
+</button>
+<!--
+<button style="display:flex">
+<div id=obj3></div>
+</button>
+<button style="display:inline-flex">
+<div id=obj4></div>
+</button>
+-->
+<button style="columns:2">
+<div id=obj5></div>
+</button>
+
+<button style="display:grid; overflow:hidden">
+<div id=sobj1></div>
+</button>
+<button style="display:inline-grid; overflow:hidden">
+<div id=sobj2></div>
+</button>
+<!--
+<button style="display:flex; overflow:hidden">
+<div id=sobj3></div>
+</button>
+<button style="display:inline-flex; overflow:hidden">
+<div id=sobj4></div>
+</button>
+-->
+<button style="columns:2; overflow:hidden">
+<div id=sobj5></div>
+</button>
+</body>
+</html>
diff --git a/layout/generic/crashtests/225868-1-inner.html b/layout/generic/crashtests/225868-1-inner.html
new file mode 100644
index 000000000..db8fbc73c
--- /dev/null
+++ b/layout/generic/crashtests/225868-1-inner.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<style type="text/css">
+
+html { overflow: hidden; }
+
+</style>
+</head>
+<body onload="setTimeout(function(){document.write('<body onload=&quot;parent.document.documentElement.removeAttribute(\'class\');&quot;>2</body>'); document.close();}, 0);">
+
+1
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/225868-1.html b/layout/generic/crashtests/225868-1.html
new file mode 100644
index 000000000..55f221a7c
--- /dev/null
+++ b/layout/generic/crashtests/225868-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head></head>
+<body>
+<iframe src="225868-1-inner.html"></iframe>
+</body>
+</html>
diff --git a/layout/generic/crashtests/255468.xhtml b/layout/generic/crashtests/255468.xhtml
new file mode 100644
index 000000000..62a859511
--- /dev/null
+++ b/layout/generic/crashtests/255468.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+<title>Iridescence</title>
+<style type="text/css" title="Crashme" disabled="disabled">
+ul.tree ul {
+ list-style-position: inside;
+}
+
+span.type:after {
+ content: ": ";
+}
+
+</style>
+</head>
+<body>
+<h1>Crash Test</h1>
+<table><tbody>
+<tr><td><ul class="struct tree"><li><span class="type">A</span> X<ul><li><span class="type">L</span> <var>Size</var></li><li><var>data</var></li></ul></li></ul></td></tr>
+</tbody></table>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/255982-1.html b/layout/generic/crashtests/255982-1.html
new file mode 100644
index 000000000..293886a2e
--- /dev/null
+++ b/layout/generic/crashtests/255982-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<body>
+<style type="text/css">
+@page { size:5in 3in; margin:0.5in; }
+</style>
+<div style="width: 60%; float: left; height: 1.6in; ">a float</div>
+<div style="width: 60%; float: left; height: 1.6in; border:1px solid black; ">overlaps page break</div>
+<br />
+<div style="width: 60%; float: left;">3rd float</div>
+<div style="width: 60%; float: left;">4th float</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/255982-2.html b/layout/generic/crashtests/255982-2.html
new file mode 100644
index 000000000..6e45016d8
--- /dev/null
+++ b/layout/generic/crashtests/255982-2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<body>
+<div style="width: 60%; float: left; height: 1.6in; ">a float</div>
+<div style="width: 60%; float: left; height: 1.6in; border:1px solid black; ">overlaps page break</div>
+Some text
+<div style="width: 60%; float: left;">3rd float</div>
+<div style="width: 60%; float: left;">4th float</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/255982-3.html b/layout/generic/crashtests/255982-3.html
new file mode 100644
index 000000000..2f62ee88e
--- /dev/null
+++ b/layout/generic/crashtests/255982-3.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<body>
+<div style="width: 60%; float: left; height: 1.6in; ">a float</div>
+<div style="width: 60%; float: left; height: 1.6in; border:1px solid black;">overlaps page break</div>
+Some text
+<div style="width: 60%; float: left;">3rd float</div>
+<div style="width: 60%; float: left;">4th float</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/255982-4.html b/layout/generic/crashtests/255982-4.html
new file mode 100644
index 000000000..7578e15b3
--- /dev/null
+++ b/layout/generic/crashtests/255982-4.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<body>
+<style type="text/css">
+@page { size:5in 3in; margin:0.5in; }
+</style>
+<div style="width: 60%; float: left; height: 1.6in; ">a float</div>
+<div style="width: 60%; float: left; height: 1.6in; border:1px solid black; ">overlaps page break</div>
+This is enough text to trigger a line break.
+<div style="width: 60%; float: left;">3rd float</div>
+<div style="width: 60%; float: left;">4th float</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/25888-1.html b/layout/generic/crashtests/25888-1.html
new file mode 100644
index 000000000..bfe0f79e5
--- /dev/null
+++ b/layout/generic/crashtests/25888-1.html
@@ -0,0 +1,6 @@
+<title>Hang while developing patch for bug 25888</title>
+
+<div style="width:500px">
+<div style="float:left;width:600px;height:30px"></div>
+Hello
+</div>
diff --git a/layout/generic/crashtests/25888-2.html b/layout/generic/crashtests/25888-2.html
new file mode 100644
index 000000000..065218f31
--- /dev/null
+++ b/layout/generic/crashtests/25888-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<title>Testcase for hang while developing bug 25888 (hit on www.flightaware.com)</title>
+
+<div style="border: solid 1em; width: 500px; height: 500px">
+ <div style="float:right; width: 300px; height: 3px;background:yellow;"></div>
+ <div style="float:left; width: 220px; height: 100px;background:aqua;"></div>
+ hi
+</div>
diff --git a/layout/generic/crashtests/264937-1.html b/layout/generic/crashtests/264937-1.html
new file mode 100644
index 000000000..fe0291481
--- /dev/null
+++ b/layout/generic/crashtests/264937-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug 264937</title>
+
+<style type="text/css">
+ p:first-letter {
+ background-color: lime;
+ }
+</style>
+
+</head>
+<body>
+
+ <p>"Test word</p>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/265867-1.html b/layout/generic/crashtests/265867-1.html
new file mode 100644
index 000000000..e9da8c7f6
--- /dev/null
+++ b/layout/generic/crashtests/265867-1.html
@@ -0,0 +1,11 @@
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<BODY STYLE="FLOAT:RIGHT;"></BODY>
+<MARQUEE STYLE="MARGIN:99999999999px;"></MARQUEE>
+<B STYLE="FLOAT:RIGHT; PADDING:99999999999px;"></B>
+</BODY>
+</HTML>
+
+
diff --git a/layout/generic/crashtests/265867-2.html b/layout/generic/crashtests/265867-2.html
new file mode 100644
index 000000000..c0ece7c27
--- /dev/null
+++ b/layout/generic/crashtests/265867-2.html
@@ -0,0 +1,3 @@
+<DIV STYLE="display:table-column-group;">
+ <DIV>Hello</DIV>
+</DIV>
diff --git a/layout/generic/crashtests/286491.html b/layout/generic/crashtests/286491.html
new file mode 100644
index 000000000..5b7e632f0
--- /dev/null
+++ b/layout/generic/crashtests/286491.html
@@ -0,0 +1,26 @@
+<html><head><title>Testcase bug 286491 - Crash with evil testcase with iframe and flash inside it</title>
+<style>a:hover{display:block;}</style>
+<script>
+function doe(){
+document.links[1].style.display='block';
+setTimeout(doe2,0);
+}
+function doe2(){
+document.links[1].style.display='';
+document.links[0].style.display='block';
+setTimeout(doe3,0);
+}
+function doe3(){
+document.links[0].style.display='';
+}
+</script>
+</head>
+<body onload="setInterval(doe,20)">
+<button onclick="doe()">doe()</button>
+<button onclick="setInterval(doe,20)">setInterval(doe,20)</button><br>
+<span><a href="#">link1</a><a href="#">link2</a></span>
+<br>
+<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%0A%3Cobject%20classid%3D%22clsid%3AD27CDB6E-AE6D-11cf-96B8-444553540000%22%20codebase%3D%22https%3A//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab%23version%3D3%2C0%2C0%2C0%22%3E%3Cembed%20src%3D%22data%3Aapplication/x-shockwave-flash%2CFWS%2505d%2500%2500%2500%2560%2500%253E%2580%2500%2519%2500%2500%250C%2501%2500C%2502%25FF%25CC%2500%253F%250C%250E%2500%2500%2500%2501%2500%2511%2500%2505Arial%2500%2500%2502%2500%257F%2509%2523%2500%2500%2500%2502%2500g%25EC%252Fg%25EC%250F0%250D0%2501%2500%2518%2501%2500%2500%2500%25FF%2502%2500%2500%2500%2500%2500%2500%28%2500mytext%2500%2589%2506%2506%2501%2500%2502%2500%2514%2583%2516%2520%2540%2500%2500%2500%22%20pluginspage%3D%22http%3A//www.macromedia.com/go/getflashplayer%22%20type%3D%22application/x-shockwave-flash%22%20height%3D%2240%22%20width%3D%22100%22%3E%3C/object%3E%0A%3C/body%3E%3C/html%3E"></iframe>
+
+
+</body></html>
diff --git a/layout/generic/crashtests/289864-1.html b/layout/generic/crashtests/289864-1.html
new file mode 100644
index 000000000..ed8299a9a
--- /dev/null
+++ b/layout/generic/crashtests/289864-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<img src="289864-1.jpg" height="9999999" width="9999999" />
+</body>
+</html>
diff --git a/layout/generic/crashtests/289864-1.jpg b/layout/generic/crashtests/289864-1.jpg
new file mode 100644
index 000000000..6337fc571
--- /dev/null
+++ b/layout/generic/crashtests/289864-1.jpg
Binary files differ
diff --git a/layout/generic/crashtests/295292-1.html b/layout/generic/crashtests/295292-1.html
new file mode 100644
index 000000000..1961db536
--- /dev/null
+++ b/layout/generic/crashtests/295292-1.html
@@ -0,0 +1,13 @@
+<html>
+ <head>
+ <style type="text/css">
+ div{overflow:hidden;}
+ div.menubar{position:fixed;width:100%;}
+ </style>
+ </head>
+ <body>
+ <div class="menubar">
+ </div>
+ if you can't see this, it crashed.
+ </body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/295292-2.html b/layout/generic/crashtests/295292-2.html
new file mode 100644
index 000000000..15d070f83
--- /dev/null
+++ b/layout/generic/crashtests/295292-2.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Test Page</title>
+<style>
+#problem {
+ left:0;
+ top:0;
+ width: 0;
+ overflow: hidden;
+ position: fixed;
+}
+</style>
+</head>
+<body>
+
+<div id="problem">
+this should be hidden
+</div>
+
+page loaded
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/302260-1.html b/layout/generic/crashtests/302260-1.html
new file mode 100644
index 000000000..ce4e0fb7a
--- /dev/null
+++ b/layout/generic/crashtests/302260-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+
+<style type="text/css">
+
+body {
+ text-indent:20px;
+}
+
+span {
+ float:right;
+}
+</style>
+</head>
+
+<body><span>What's Clicking Now</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/307979-1.html b/layout/generic/crashtests/307979-1.html
new file mode 100644
index 000000000..eb73d1215
--- /dev/null
+++ b/layout/generic/crashtests/307979-1.html
@@ -0,0 +1,27 @@
+<html class="reftest-wait">
+<head>
+
+<title>Crash testcase</title>
+
+<script>
+
+function foo()
+{
+ document.body.appendChild(document.createElement('frameset'));
+ setTimeout(bar, 30);
+}
+
+function bar()
+{
+ document.body.style.display = '-moz-groupbox';
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+</head>
+
+<body onload="setTimeout(foo, 30);">
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/309322-1.html b/layout/generic/crashtests/309322-1.html
new file mode 100644
index 000000000..438206174
--- /dev/null
+++ b/layout/generic/crashtests/309322-1.html
@@ -0,0 +1,56 @@
+<html><head>
+<title>Testcase1 bug 309322 - Evil testcase using multiple display:table-caption causes crash</title>
+<style>
+*[toggle_style],*[toggle_style1],*[toggle_style2],*[toggle_style3],*[toggle_style4]{
+display:table-caption;
+}
+</style>
+<script>
+function doe(i){
+var x=document.body.getElementsByTagName('*');
+var xl=x.length;i=i+1;
+x[i-1].removeAttribute('toggle_style');
+if ((i)<xl) x[i].setAttribute('toggle_style','toggle_style');
+if ((i+1)<xl) {x[i+1].setAttribute('toggle_style1','toggle_style');
+x[i].removeAttribute('toggle_style1');
+}
+if ((i+2)<xl) {x[i+2].setAttribute('toggle_style2','toggle_style');
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+3)<xl) {x[i+3].setAttribute('toggle_style3','toggle_style');
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+4)<xl) {x[i+4].setAttribute('toggle_style4','toggle_style');
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+4)==xl) {
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+3)==xl) {
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+2)==xl) {
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+1)==xl) {
+x[i].removeAttribute('toggle_style1');
+}
+setTimeout(doe,20,i);
+}
+
+function doe2(){
+var x=document.links[0].cloneNode(true);
+document.links[0].appendChild(x);
+}
+</script>
+</head>
+<body onload="doe(1)">
+<button onclick="doe(1)">Clicking on this button should not crash Mozilla</button>
+<table><tbody><tr><td>
+
+<span><br></span>
+<a href="#">Galloway<span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span></a>
+
+</td></tr></tbody></table>
+
+</body></html>
diff --git a/layout/generic/crashtests/309322-2.html b/layout/generic/crashtests/309322-2.html
new file mode 100644
index 000000000..f1450a047
--- /dev/null
+++ b/layout/generic/crashtests/309322-2.html
@@ -0,0 +1,56 @@
+<html><head>
+<title>Testcase2 bug 309322 - Evil testcase using multiple display:table-caption causes crash</title>
+<style>
+*[toggle_style],*[toggle_style1],*[toggle_style2],*[toggle_style3],*[toggle_style4]{
+display:table-caption;
+}
+</style>
+<script>
+function doe(i){
+var x=document.body.getElementsByTagName('*');
+var xl=x.length;i=i+1;
+x[i-1].removeAttribute('toggle_style');
+if ((i)<xl) x[i].setAttribute('toggle_style','toggle_style');
+if ((i+1)<xl) {x[i+1].setAttribute('toggle_style1','toggle_style');
+x[i].removeAttribute('toggle_style1');
+}
+if ((i+2)<xl) {x[i+2].setAttribute('toggle_style2','toggle_style');
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+3)<xl) {x[i+3].setAttribute('toggle_style3','toggle_style');
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+4)<xl) {x[i+4].setAttribute('toggle_style4','toggle_style');
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+4)==xl) {
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+3)==xl) {
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+2)==xl) {
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+1)==xl) {
+x[i].removeAttribute('toggle_style1');
+}
+setTimeout(doe,20,i);
+}
+
+function doe2(){
+var x=document.links[0].cloneNode(true);
+document.links[0].appendChild(x);
+}
+</script>
+</head>
+<body onload="doe(1)">
+<button onclick="doe(1)">Clicking on this button should not crash Mozilla</button>
+<table><tbody><tr><td>
+
+<span><br></span>
+<a href="#">Galloway<span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span><span>Galloway</span></a>
+
+</td></tr></tbody></table>
+
+</body></html>
diff --git a/layout/generic/crashtests/309322-3.html b/layout/generic/crashtests/309322-3.html
new file mode 100644
index 000000000..96148ffe4
--- /dev/null
+++ b/layout/generic/crashtests/309322-3.html
@@ -0,0 +1,48 @@
+<html><head>
+<title>Testcase3 bug 309322 - Evil testcase using multiple display:table-caption causes crash</title>
+<style>
+*[toggle_style],*[toggle_style1],*[toggle_style2],*[toggle_style3],*[toggle_style4]{
+display:table-caption;
+}
+</style>
+<script>
+function doe(i){
+var x=document.body.getElementsByTagName('*');
+var xl=x.length;i=i+1;
+x[i-1].removeAttribute('toggle_style');
+x[i].setAttribute('toggle_style','toggle_style');
+if ((i+1)<xl) {x[i+1].setAttribute('toggle_style1','toggle_style');
+x[i].removeAttribute('toggle_style1');
+}
+if ((i+2)<xl) {x[i+2].setAttribute('toggle_style2','toggle_style');
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+3)<xl) {x[i+3].setAttribute('toggle_style3','toggle_style');
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+4)<xl) {x[i+4].setAttribute('toggle_style4','toggle_style');
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+4)==xl) {
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+3)==xl) {
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+2)==xl) {
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+1)==xl) {
+x[i].removeAttribute('toggle_style1');
+}
+setTimeout(doe,20,i);
+}
+
+
+</script></head><body onload="doe(3)">
+<button onclick="doe(3)">Clicking on this button should not create extra "Galloway" text</button>
+<table><tbody><tr><td>
+<span><br></span>
+<a href="#">Galloway</a>
+</td></tr></tbody></table>
+</body></html>
diff --git a/layout/generic/crashtests/309322-4.html b/layout/generic/crashtests/309322-4.html
new file mode 100644
index 000000000..9b9b6a5bb
--- /dev/null
+++ b/layout/generic/crashtests/309322-4.html
@@ -0,0 +1,48 @@
+<html><head>
+<title>Testcase4 bug 309322 - Evil testcase using multiple display:table-caption causes crash</title>
+<style>
+*[toggle_style],*[toggle_style1],*[toggle_style2],*[toggle_style3],*[toggle_style4]{
+display:table-caption;
+}
+</style>
+<script>
+function doe(i){
+var x=document.body.getElementsByTagName('*');
+var xl=x.length;i=i+1;
+x[i-1].removeAttribute('toggle_style');
+x[i].setAttribute('toggle_style','toggle_style');
+if ((i+1)<xl) {x[i+1].setAttribute('toggle_style1','toggle_style');
+x[i].removeAttribute('toggle_style1');
+}
+if ((i+2)<xl) {x[i+2].setAttribute('toggle_style2','toggle_style');
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+3)<xl) {x[i+3].setAttribute('toggle_style3','toggle_style');
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+4)<xl) {x[i+4].setAttribute('toggle_style4','toggle_style');
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+4)==xl) {
+x[i+3].removeAttribute('toggle_style4');
+}
+if ((i+3)==xl) {
+x[i+2].removeAttribute('toggle_style3');
+}
+if ((i+2)==xl) {
+x[i+1].removeAttribute('toggle_style2');
+}
+if ((i+1)==xl) {
+x[i].removeAttribute('toggle_style1');
+}
+setTimeout(doe,20,i);
+}
+
+
+</script></head><body onload="doe(3)">
+<button onclick="doe(3)">Clicking on this button and then closing this tab/window should not crash Mozilla</button>
+<table><tbody><tr><td>
+<span><br></span>
+<a href="#"><img src="data:image/gif;base64,R0lGODlhAQABAIABAAD/AP///ywAAAAAAQABAAACAkQBADs="></a>
+</td></tr></tbody></table>
+</body></html>
diff --git a/layout/generic/crashtests/310556-1.xhtml b/layout/generic/crashtests/310556-1.xhtml
new file mode 100644
index 000000000..260960008
--- /dev/null
+++ b/layout/generic/crashtests/310556-1.xhtml
@@ -0,0 +1,21 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<style type="text/css">
+
+span:before {
+ content: "";
+}
+
+</style>
+
+</head>
+
+<body>
+
+X<span/>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/321224.xul b/layout/generic/crashtests/321224.xul
new file mode 100644
index 000000000..a21c4b7b0
--- /dev/null
+++ b/layout/generic/crashtests/321224.xul
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <tabpanels>
+ <nativescrollbar/>
+ </tabpanels>
+</window> \ No newline at end of file
diff --git a/layout/generic/crashtests/322780-1.xul b/layout/generic/crashtests/322780-1.xul
new file mode 100644
index 000000000..71eb058ce
--- /dev/null
+++ b/layout/generic/crashtests/322780-1.xul
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <label>
+ <foopy style="float:left;" />
+ </label>
+</window>
diff --git a/layout/generic/crashtests/323381-1.html b/layout/generic/crashtests/323381-1.html
new file mode 100644
index 000000000..3f2c17f9c
--- /dev/null
+++ b/layout/generic/crashtests/323381-1.html
@@ -0,0 +1 @@
+<iframe src="data:text/html,foo" width="808080"></iframe>
diff --git a/layout/generic/crashtests/323381-2.html b/layout/generic/crashtests/323381-2.html
new file mode 100644
index 000000000..ab28e6bb5
--- /dev/null
+++ b/layout/generic/crashtests/323381-2.html
@@ -0,0 +1 @@
+<div style="width: 808080px;">foo</div>
diff --git a/layout/generic/crashtests/323386-1.html b/layout/generic/crashtests/323386-1.html
new file mode 100644
index 000000000..437b9a16b
--- /dev/null
+++ b/layout/generic/crashtests/323386-1.html
@@ -0,0 +1 @@
+<TEXTAREA COLS="381762666"> \ No newline at end of file
diff --git a/layout/generic/crashtests/323389-1.html b/layout/generic/crashtests/323389-1.html
new file mode 100644
index 000000000..591ebb539
--- /dev/null
+++ b/layout/generic/crashtests/323389-1.html
@@ -0,0 +1,7 @@
+<HTML>
+<HEAD>
+<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
+
+<TEXTAREA>
+ ˆˆˆˆˆˆ
+
diff --git a/layout/generic/crashtests/323389-2.html b/layout/generic/crashtests/323389-2.html
new file mode 100644
index 000000000..74fea379a
--- /dev/null
+++ b/layout/generic/crashtests/323389-2.html
@@ -0,0 +1,8 @@
+<html><head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1"></head>
+
+<body>
+
+blocking&#8209;aviary1.0.8
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/323493-1.html b/layout/generic/crashtests/323493-1.html
new file mode 100644
index 000000000..12e14f6a8
--- /dev/null
+++ b/layout/generic/crashtests/323493-1.html
@@ -0,0 +1,16 @@
+<html><head><style>
+* {
+ display: table;
+ position: absolute;
+}
+</style>
+
+</head>
+
+<body>
+<table><tbody><tr><td>One</td><td>Two</td></tr><tr><td>Three</td><td>Four</td></tr></tbody></table>
+<ul><li>One</li><li>Two</li><li>Three</li></ul>
+
+<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
+
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/323495-1.html b/layout/generic/crashtests/323495-1.html
new file mode 100644
index 000000000..fcffe0166
--- /dev/null
+++ b/layout/generic/crashtests/323495-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+
+<style>
+select { width: 1000000px; }
+</style>
+
+</head>
+
+<body>
+
+<select><option>a</option></select>
+
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/324318-1.html b/layout/generic/crashtests/324318-1.html
new file mode 100644
index 000000000..2c1d0cdbb
--- /dev/null
+++ b/layout/generic/crashtests/324318-1.html
@@ -0,0 +1,29 @@
+<script>
+
+var tr;
+
+function init() {
+
+ tr = one.document.getElementsByTagName("tr")[0];
+ tr = document.adoptNode(tr);
+ document.getElementsByTagName("html")[0].appendChild(tr);
+
+ setTimeout(
+ function(){
+ var frameset = document.getElementsByTagName("frameset")[0];
+ document.getElementsByTagName("td")[0].appendChild(frameset);
+ document.documentElement.removeAttribute("class");
+ },
+ 100);
+
+}
+
+window.addEventListener("load", init, false);
+document.documentElement.setAttribute("class", "reftest-wait");
+
+</script>
+
+<frameset resizable="yes" rows="50%,*">
+ <frame name="one" src="data:text/html,<table border='1'><tr><td>tdc</td></tr></table>">
+ <frame name="two" src="data:text/html,">
+</frameset>
diff --git a/layout/generic/crashtests/328946-1.html b/layout/generic/crashtests/328946-1.html
new file mode 100644
index 000000000..75345ce5d
--- /dev/null
+++ b/layout/generic/crashtests/328946-1.html
@@ -0,0 +1 @@
+<table> \ No newline at end of file
diff --git a/layout/generic/crashtests/331284-1.xhtml b/layout/generic/crashtests/331284-1.xhtml
new file mode 100644
index 000000000..fc1bf1847
--- /dev/null
+++ b/layout/generic/crashtests/331284-1.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+ <hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <ttt style="float: right">
+ <img src="../../../testing/crashtest/images/animfish.gif" xmlns="http://www.w3.org/1999/xhtml" />
+ </ttt>
+ </hbox>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/331292.html b/layout/generic/crashtests/331292.html
new file mode 100644
index 000000000..dfc4cf60b
--- /dev/null
+++ b/layout/generic/crashtests/331292.html
@@ -0,0 +1,258 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head>
+<title>Testcase bug 331292 - Loading page causes freeze, no Talkback, both WindowsXP & Linux</title>
+<style>
+.floatA{
+ width:110px;
+ float: left;
+ border:1px solid red;
+}
+.floatB{
+ width:430px;
+ float: left;
+ border:1px solid green;
+}
+.floatC{
+ width:430px;
+ float: left;
+ border:1px solid blue;
+}
+.floatclear{
+clear: both;
+}
+</style>
+</head>
+<body>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatB"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatC"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear">
+<div class="floatA"></div>
+<div class="floatclear"></div>
+</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
+</body></html>
diff --git a/layout/generic/crashtests/334105-1.xhtml b/layout/generic/crashtests/334105-1.xhtml
new file mode 100644
index 000000000..6b5c135f8
--- /dev/null
+++ b/layout/generic/crashtests/334105-1.xhtml
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+
+<style>
+html { width: 2em; border: 1px solid red; }
+body { display: inline; }
+.padded { padding: 1em; border: 1px solid black; }
+#float1 { background: lightgreen; } /* float is added to this one dynamically */
+#float2 { float: right; background: lightblue; }
+</style>
+
+<script>
+
+function foo()
+{
+ document.getElementById("float1").style.cssFloat = "right";
+ document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("load", function() { setTimeout(foo, 30); }, false);
+
+</script>
+
+</head>
+
+<body>
+
+ X
+ <span id="float1">FFFFFF</span>
+ <span id="float2">GGGGGG</span>
+ <span class="padded"/>
+
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/334107-1.xhtml b/layout/generic/crashtests/334107-1.xhtml
new file mode 100644
index 000000000..3e6b25a5c
--- /dev/null
+++ b/layout/generic/crashtests/334107-1.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body>
+
+<p style="letter-spacing: -2em;">Foo</p>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/334147-1.xhtml b/layout/generic/crashtests/334147-1.xhtml
new file mode 100644
index 000000000..549015722
--- /dev/null
+++ b/layout/generic/crashtests/334147-1.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<style>
+.x:first-letter { }
+.x { direction: rtl; }
+</style>
+
+</head>
+
+<body>
+
+<p class="x">2 3</p>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/334148-1.xhtml b/layout/generic/crashtests/334148-1.xhtml
new file mode 100644
index 000000000..dab508324
--- /dev/null
+++ b/layout/generic/crashtests/334148-1.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>
+* { float: right; }
+* { display: list-item; }
+</style>
+</head>
+
+<body>
+
+<menupopup xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" />
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/334602-1.html b/layout/generic/crashtests/334602-1.html
new file mode 100644
index 000000000..2bb820ebc
--- /dev/null
+++ b/layout/generic/crashtests/334602-1.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<title>Testcase bug 334602 - ASSERTION: Reparenting something that has no usable parent? Shouldn't happen!: 'Not Reached'</title>
+<style>
+html::first-line { }
+html::before { content:"This should not give an assertion in Mozilla";}
+</style>
+</head>
+<body>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/337412-1.html b/layout/generic/crashtests/337412-1.html
new file mode 100644
index 000000000..25a7cce61
--- /dev/null
+++ b/layout/generic/crashtests/337412-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<style>
+
+.columns {
+ -moz-column-width: 20em;
+ -moz-column-gap: 3em;
+}
+
+
+</style>
+
+</head>
+
+<body>
+
+<div class="columns">
+
+<ul>
+<li>XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX!</li>
+</ul>
+
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/337883-1.html b/layout/generic/crashtests/337883-1.html
new file mode 100644
index 000000000..d89466a51
--- /dev/null
+++ b/layout/generic/crashtests/337883-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html class="reftest-wait">
+<head>
+<script>
+
+function foop()
+{
+ document.getElementById("xxx").style.height = "10%";
+ document.getElementById("yyy").style.position = "relative";
+
+ document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("load", function(){setTimeout(foop, 30)}, false);
+
+</script>
+</head>
+
+<body>--<a id="yyy"><IMG align="right"></a><br id="xxx">==</body></html>
+
diff --git a/layout/generic/crashtests/337883-2.html b/layout/generic/crashtests/337883-2.html
new file mode 100644
index 000000000..1334bf46b
--- /dev/null
+++ b/layout/generic/crashtests/337883-2.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html class="reftest-wait">
+<head>
+<style id="s"></style>
+<script>
+
+function foop()
+{
+ var stylesheet = document.getElementById("s");
+ stylesheet.textContent = "#xxx { height: 10%; } #yyy { position: relative; }";
+
+ document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("load", function(){setTimeout(foop, 30)}, false);
+
+</script>
+</head>
+
+<body>--<a id="yyy"><IMG align="right"></a><br id="xxx">==</body></html>
+
diff --git a/layout/generic/crashtests/339769-1.html b/layout/generic/crashtests/339769-1.html
new file mode 100644
index 000000000..a9772e34a
--- /dev/null
+++ b/layout/generic/crashtests/339769-1.html
@@ -0,0 +1,22 @@
+<html class="reftest-wait">
+<head>
+<script>
+
+function foo()
+{
+ var div = document.getElementById("div");
+ div.parentNode.removeChild(div);
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(foo, 0);">
+
+ <div id="div" style="display: inline;"><p>x</p></div>
+
+ <iframe src='data:text/html,<html><body><input value="q">'></iframe>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/342322-1.html b/layout/generic/crashtests/342322-1.html
new file mode 100644
index 000000000..9d0d08261
--- /dev/null
+++ b/layout/generic/crashtests/342322-1.html
@@ -0,0 +1,28 @@
+<html class="reftest-wait">
+<head>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var q1 = document.getElementById("q1");
+ var q2 = document.getElementById("q2");
+
+ q1.style.cssFloat = "right"
+ q2.style.cssFloat = "right"
+
+ setTimeout(function(){
+ q1.style.cssFloat = "none";
+ document.documentElement.removeAttribute("class");
+ }, 30);
+}
+
+</script>
+
+</head>
+
+<body onload="setTimeout(boom, 30);">
+ <b id="q1">AAA</b>
+ <b id="q2">BBB</b>
+</body>
+</html>
diff --git a/layout/generic/crashtests/343206-1.xhtml b/layout/generic/crashtests/343206-1.xhtml
new file mode 100644
index 000000000..5be6fc2a6
--- /dev/null
+++ b/layout/generic/crashtests/343206-1.xhtml
@@ -0,0 +1,21 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+
+<style id="sty">
+html::before {
+ content: "before text";
+ float: right;
+}
+</style>
+
+<script>
+function boom()
+{
+ var sty = document.getElementById("sty");
+ sty.appendChild(document.createTextNode(" "));
+ document.documentElement.removeAttribute("class");
+}
+
+setTimeout(boom, 30);
+</script>
+
+</html>
diff --git a/layout/generic/crashtests/344557-1.html b/layout/generic/crashtests/344557-1.html
new file mode 100644
index 000000000..0ed806257
--- /dev/null
+++ b/layout/generic/crashtests/344557-1.html
@@ -0,0 +1,32 @@
+<html class="reftest-wait"><head>
+<title>Testcase bug 344557 - [columns] Crash [@ nsLineBox::DeleteLineList] with moz-column-count and generated content</title>
+<script>
+function removestyles(i){
+document.getElementsByTagName('div')[0].removeAttribute('style');
+document.documentElement.removeAttribute("class");
+}
+window.resizeTo(1440, 600);
+setTimeout(removestyles,200);
+</script>
+
+<style>
+ div::first-line, ul::first-line { text-transform: uppercase; font-size:110%;}
+sub::after, div::after { content:"anonymous text"; border:3px solid black;text-transform: uppercase;}
+sub::before, div::before { content:"before text"; font-size: 10px;}
+</style>
+</head>
+<body>
+
+<ul>
+ <sub>t</sub>
+ <div style="-moz-column-count: 3;">
+ <sub>t-column-councode&gt;text</sub>
+ <div style="-moz-column-count: 3;"><sub>text styletttmoztcolumntcountt 2;ttext</sub>
+ <div>
+ <sub>text</sub>
+ </div>
+ </div>
+ </div>
+</ul>
+
+</body></html>
diff --git a/layout/generic/crashtests/345139-1.xhtml b/layout/generic/crashtests/345139-1.xhtml
new file mode 100644
index 000000000..1dfbe8f23
--- /dev/null
+++ b/layout/generic/crashtests/345139-1.xhtml
@@ -0,0 +1,53 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head style="display: none ! important;">
+
+<style>
+#a { direction: rtl; }
+#a { position: absolute; }
+#a { right: 5px; }
+#a { left: -10em; }
+#b { direction: ltr; }
+#b { width: 10%; }
+#b { display: -moz-inline-block; }
+#c { display: -moz-inline-block; }
+#e { display: -moz-inline-block; }
+#d { display: -moz-inline-block; }
+#d { padding: 5px 10px; }
+#d dl, #d dt, #d dd { display: inline; }
+#d dt { margin-left: 5px; }
+#d dd { margin-left: 2px; }
+#d dd:before { content: "="; }
+</style>
+
+<style id="newstyle"></style>
+<script>
+function change()
+{
+ document.getElementById("newstyle").appendChild(document.createTextNode("#b { white-space: pre; }"));
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</head>
+
+<body onload="setTimeout(change, 1);">
+
+<div id="a"><div id="b">
+
+<div id="c"></div>
+
+<div id="d">
+<dl>
+<dt>0</dt><dd><span id="e">Accessibility Statement</span></dd>
+<dt>1</dt><dd>Main Page</dd>
+<dt>2</dt><dd>Skip to Content</dd>
+<dt>3</dt><dd>List of Posts</dd>
+<dt>4</dt><dd>Search</dd>
+<dt>p</dt><dd>Previous (individual/monthly archive page)</dd>
+</dl>
+</div>
+
+</div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/345617-1.html b/layout/generic/crashtests/345617-1.html
new file mode 100644
index 000000000..03de63958
--- /dev/null
+++ b/layout/generic/crashtests/345617-1.html
@@ -0,0 +1,8 @@
+<html>
+<body style="line-height: 30760827em;">
+
+x
+<p>y
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/348510-1.html b/layout/generic/crashtests/348510-1.html
new file mode 100644
index 000000000..6e00e71f1
--- /dev/null
+++ b/layout/generic/crashtests/348510-1.html
@@ -0,0 +1,7 @@
+<marquee>
+<a>
+<object>
+<dd>
+<form>
+</object>
+aaaaaaa
diff --git a/layout/generic/crashtests/348510-2.html b/layout/generic/crashtests/348510-2.html
new file mode 100644
index 000000000..8f8c998cf
--- /dev/null
+++ b/layout/generic/crashtests/348510-2.html
@@ -0,0 +1,7 @@
+<listing>
+<marquee>
+<aa>
+<object>
+<fieldset>
+</object>
+a \ No newline at end of file
diff --git a/layout/generic/crashtests/348887-1-inner.html b/layout/generic/crashtests/348887-1-inner.html
new file mode 100644
index 000000000..f78cd351f
--- /dev/null
+++ b/layout/generic/crashtests/348887-1-inner.html
@@ -0,0 +1,21 @@
+<html><head>
+<style>
+blockquote::first-letter {float: right;}
+</style>
+<title>
+Testcase bug - Crash [@ nsFrameList::DestroyFrames] on reload with -moz-column-count, -moz-inline-block and blockquote::first-letter
+</title>
+</head>
+<body>
+This page should not crash on reload
+<div style=" -moz-column-count: 2;">
+<span style="display: table;"></span>
+<blockquote style="display: -moz-inline-block;">anonymous text</blockquote>
+</div>
+
+<script>
+document.location.reload();
+</script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/348887-1.html b/layout/generic/crashtests/348887-1.html
new file mode 100644
index 000000000..b865b4beb
--- /dev/null
+++ b/layout/generic/crashtests/348887-1.html
@@ -0,0 +1,9 @@
+<html class="reftest-wait">
+<head>
+<script>
+setTimeout('document.documentElement.className = ""', 1000);
+</script>
+<body>
+<iframe src="348887-1-inner.html"></iframe>
+</body>
+</html>
diff --git a/layout/generic/crashtests/348991-1.xhtml b/layout/generic/crashtests/348991-1.xhtml
new file mode 100644
index 000000000..a4e312d7a
--- /dev/null
+++ b/layout/generic/crashtests/348991-1.xhtml
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+ <div style="width: 10em; display: -moz-grid;">
+ <select><option>A</option></select>
+ <select><option>B</option></select>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/350370.html b/layout/generic/crashtests/350370.html
new file mode 100644
index 000000000..61a230f86
--- /dev/null
+++ b/layout/generic/crashtests/350370.html
@@ -0,0 +1,42 @@
+<html><head>
+<title>Testcase bug 350370 - Crash [@ nsStyleContext::FindChildWithRules] with ::first-line, appending rows and table-cells, etc</title>
+<style>
+#b td::first-line { font-size:110%;}
+nobr::first-line { font-size:110%;}
+
+#b td::after { content:"anonymous text"; }
+nobr::after{ content:"anonymous text"; }
+
+#b::before { content:"before text";}
+#b td::before { content:"before text";}
+</style>
+</head>
+<body>
+<table style="display: table-row;"></table><nobr style="display: list-item; -moz-column-count: 2;">
+<table id="b" style="display: inline;"></table>
+</nobr>
+<br>
+This page should not crash Mozilla
+<script>
+function doe(){
+ var td = document.createElement('td');;
+ td.setAttribute('height', '50%');
+ var tr = document.createElement('tr');;
+ tr.setAttribute('height', '50%');
+ tr.appendChild(td);
+ document.getElementsByTagName('table')[1].appendChild(tr);
+ document.body.offsetHeight;
+
+ var td = document.createElement('td');;
+ td.setAttribute('height', '50%');
+ document.getElementsByTagName('tr')[0].appendChild(td);
+ document.body.offsetHeight;
+
+ var td = document.createElement('td');;
+ td.setAttribute('height', '50%');
+ document.getElementsByTagName('tr')[0].appendChild(td);
+}
+setTimeout(doe, 60);
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/354458-1.html b/layout/generic/crashtests/354458-1.html
new file mode 100644
index 000000000..2f361ffaf
--- /dev/null
+++ b/layout/generic/crashtests/354458-1.html
@@ -0,0 +1,10 @@
+<html>
+
+<body>
+ <div style="height: 5px; -moz-column-count: 3;">
+ Text!
+ <span style="float: left">Float!</span>
+ </div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/354458-2.html b/layout/generic/crashtests/354458-2.html
new file mode 100644
index 000000000..27a80c1c6
--- /dev/null
+++ b/layout/generic/crashtests/354458-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta http-equiv="Content-Style-Type" content="text/css">
+ <style type="text/css">
+
+ p { margin: 0; }
+
+ </style>
+</head>
+<body>
+
+<div style="-moz-column-width: 10em; height: 2.5em">
+ text
+ <img src="../../../testing/crashtest/images/tree.gif" width="197" height="200" style="float:left">
+ <p>text</p>
+ <p>text</p>
+ <p>text</p>
+ <p>text</p>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/355426-1.html b/layout/generic/crashtests/355426-1.html
new file mode 100644
index 000000000..a651c48d2
--- /dev/null
+++ b/layout/generic/crashtests/355426-1.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+
+<script>
+
+function foo()
+{
+ document.getElementById("navish").style.display = "inline-block";
+}
+</script>
+
+</head>
+
+
+<body onload="foo()">
+
+<div style="-moz-column-count: 2;">
+ <ol id="ol1" style="height: 30px;">
+ <li>A</li>
+ <li><p id="p3" style="width: 1em;">Foo Foo Foo Foo Foo Foo Foo Foo Foo Foo Foo</p></li>
+ </ol>
+</div>
+
+<p id="navish">E</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/359371-1.html b/layout/generic/crashtests/359371-1.html
new file mode 100644
index 000000000..d9c59633d
--- /dev/null
+++ b/layout/generic/crashtests/359371-1.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+body {
+ width: 500px;
+}
+
+p {
+ font-family: monospace;
+ font-size: 12px;
+}
+.columns {
+ -moz-column-count: 3;
+}
+
+.toppadded
+{
+ padding-top: 20px;
+}
+
+.floatbox {
+ float: left;
+ margin: 3px 10px 0 0;
+ padding: 8px 3px;
+ width: 55px;
+}
+
+</style>
+</head>
+
+<body onload="document.getElementById('foo').appendChild(document.createTextNode('aaaaaaa'));">
+
+<div class="columns">
+
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+
+ <p id="foo">xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx </p>
+
+ <div class="toppadded">
+ T
+ <p class="floatbox">W W W W W W</p>
+ </div>
+
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/359371-2.html b/layout/generic/crashtests/359371-2.html
new file mode 100644
index 000000000..61e42aa7b
--- /dev/null
+++ b/layout/generic/crashtests/359371-2.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+body {
+ width: 500px;
+}
+
+p {
+ font-family: monospace;
+ font-size: 12px;
+}
+.columns {
+ -moz-column-count: 3;
+}
+
+.toppadded
+{
+ padding-top: 18px;
+}
+
+.floatbox {
+ float: left;
+ margin: 20px 0px 0 0;
+ width: 90px;
+}
+
+</style>
+</head>
+
+<!-- <body onload="document.getElementById('foo').appendChild(document.createTextNode('aaaaaaa'));"> -->
+<div class="columns">
+
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+ <p>C
+
+ <p id="foo">xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx </p>
+
+ <div class="toppadded">
+ T
+ <p class="floatbox">W W W W W W W W Waaaaaaa</p>
+ </div>
+
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/360599.html b/layout/generic/crashtests/360599.html
new file mode 100644
index 000000000..210add23d
--- /dev/null
+++ b/layout/generic/crashtests/360599.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+<style>
+#b::first-letter { }
+#c::first-line { }
+</style>
+<title>Testcase bug 360599 - Crash [@ nsFrameList::DestroyFrames] with first-letter/first-line css and position: fixed</title>
+</head>
+<body>
+This page should not crash Mozilla
+<div id="c">
+ <table>
+ <div id="b" style="display:table-header-group;">
+ <q>
+ text
+ <div style="position:fixed;">
+ <q>y</q>
+ </div>
+ </q>
+ </div>
+ <span style="display: table;"></span>
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/361109.html b/layout/generic/crashtests/361109.html
new file mode 100644
index 000000000..fef9c6be8
--- /dev/null
+++ b/layout/generic/crashtests/361109.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Testcase bug 361109 - Crash [@ nsBlockBandData::Init] with position:fixed on select</title>
+</head>
+<body>
+This page should not crash Mozilla
+<select style="position: fixed;"><select>
+</body>
+</html>
diff --git a/layout/generic/crashtests/363448.html b/layout/generic/crashtests/363448.html
new file mode 100644
index 000000000..960aa5445
--- /dev/null
+++ b/layout/generic/crashtests/363448.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Testcase bug 363448 - Crash [@ nsCachedStyleData::GetStyleData] with testcase, using floating and absolutely positioned iframes</title>
+</head>
+<body>
+This page should not crash Mozilla
+<div style="width:300px;">
+ <span id="b">
+ <iframe style="float: left;" id="a"></iframe>
+ <wbr>text
+ <iframe id="c" style="position: absolute;"></iframe>
+ </span>
+</div>
+
+<script>
+function stirdom(){
+ document.body.appendChild(document.getElementById('a'));
+ document.getElementById('b').appendChild(document.getElementById('c'));
+}
+setTimeout(stirdom,200);
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/363722-1.html b/layout/generic/crashtests/363722-1.html
new file mode 100644
index 000000000..f83671c5a
--- /dev/null
+++ b/layout/generic/crashtests/363722-1.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+ <marquee style="background: yellow;">
+ <marquee style="background: lightgreen;">
+ I am a double-marquee.
+ </marquee>
+ </marquee>
+</body>
+</html>
diff --git a/layout/generic/crashtests/363722-2.html b/layout/generic/crashtests/363722-2.html
new file mode 100644
index 000000000..1a12a227e
--- /dev/null
+++ b/layout/generic/crashtests/363722-2.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+ <marquee style="background: yellow;">
+ [inside OUTER marquee]
+ <marquee style="background: lightgreen;">
+ [inside INNER marquee]
+ </marquee>
+ </marquee>
+</body>
+</html>
diff --git a/layout/generic/crashtests/363848-1.xhtml b/layout/generic/crashtests/363848-1.xhtml
new file mode 100644
index 000000000..0af2085ef
--- /dev/null
+++ b/layout/generic/crashtests/363848-1.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<body>
+
+<xul:listbox />
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/364220.html b/layout/generic/crashtests/364220.html
new file mode 100644
index 000000000..12299ecec
--- /dev/null
+++ b/layout/generic/crashtests/364220.html
@@ -0,0 +1,17 @@
+<html><head>
+<title>Testcase bug 364220 - [reflow branch][columns] Crash [@ nsLineLayout::ReflowFrame] using moz-column-count, floats, generated content and first-line</title>
+<style>
+body > span::first-line { }
+span::before { content:"before text"; border:3px solid black;}
+</style>
+</head>
+<body>
+
+<span style=" float: left; -moz-column-count: 2;">
+ <span style="float: right;">
+ <span style=" float: right;-moz-column-count: 2;"></span>
+ </span>
+</span>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/364407-1.html b/layout/generic/crashtests/364407-1.html
new file mode 100644
index 000000000..2ae181886
--- /dev/null
+++ b/layout/generic/crashtests/364407-1.html
@@ -0,0 +1,44 @@
+<html>
+<body>
+
+<div style="overflow: scroll">
+ <table border="1">
+ <tr>
+ <td>
+ <select style="height: 200%">
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ <option>Option</option>
+ </select>
+ </td>
+ </tr>
+ </table>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/364686-1.xhtml b/layout/generic/crashtests/364686-1.xhtml
new file mode 100644
index 000000000..93a1eeaa4
--- /dev/null
+++ b/layout/generic/crashtests/364686-1.xhtml
@@ -0,0 +1,12 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:math="http://www.w3.org/1998/Math/MathML">
+
+<body>
+
+<math:merror>
+ <img/>
+</math:merror>
+
+</body>
+</html>
+
diff --git a/layout/generic/crashtests/366021-1.xhtml b/layout/generic/crashtests/366021-1.xhtml
new file mode 100644
index 000000000..5bbc7fe18
--- /dev/null
+++ b/layout/generic/crashtests/366021-1.xhtml
@@ -0,0 +1,24 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+
+<head>
+<script>
+
+function boom()
+{
+ document.getElementById("xulwin").appendChild(document.getElementById("sss"));
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+
+<body onload="setTimeout(boom, 10)">
+
+<span id="sss"><tr><td style="position: absolute;">td</td></tr></span>
+
+<window id="xulwin" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"></window>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/366667-1.html b/layout/generic/crashtests/366667-1.html
new file mode 100644
index 000000000..a50ce84e4
--- /dev/null
+++ b/layout/generic/crashtests/366667-1.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ <span><span style="display: -moz-inline-box"><img></span></span>
+ </body>
+</html>
+
diff --git a/layout/generic/crashtests/366952-1.html b/layout/generic/crashtests/366952-1.html
new file mode 100644
index 000000000..96007b8eb
--- /dev/null
+++ b/layout/generic/crashtests/366952-1.html
@@ -0,0 +1,17 @@
+<html>
+
+<head>
+</head>
+
+<body>
+
+<div style="display: -moz-inline-grid;">
+ <div style="padding-right: 3%;">
+ <div style="position: fixed;">
+ Foo
+ </div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/367246-1.html b/layout/generic/crashtests/367246-1.html
new file mode 100644
index 000000000..5a5a84565
--- /dev/null
+++ b/layout/generic/crashtests/367246-1.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+
+<div style="direction: rtl">
+ <div style="padding: 10px; width: 1px; overflow: scroll;">Foopy</div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/367360.html b/layout/generic/crashtests/367360.html
new file mode 100644
index 000000000..3381ad34e
--- /dev/null
+++ b/layout/generic/crashtests/367360.html
@@ -0,0 +1,30 @@
+<html><head>
+<title>Testcase bug - Crash [@ nsHTMLButtonControlFrame::GetContentInsertionFrame] with -moz-column-count and display: list-item</title>
+</head>
+<body>
+<div style="display: table;">
+text
+<listing style="-moz-column-count: 2;">
+<dl>
+
+
+
+
+
+
+
+
+tesxt
+
+
+<menu>
+<span style="display: list-item; -moz-column-count: 1;">
+<span style="display: list-item; -moz-column-count: 1;">
+text
+</span></span>
+</menu>
+<div>text</div>
+</dl>
+</listing>
+</div>
+</body></html>
diff --git a/layout/generic/crashtests/368330-1.html b/layout/generic/crashtests/368330-1.html
new file mode 100644
index 000000000..1a4c93e39
--- /dev/null
+++ b/layout/generic/crashtests/368330-1.html
@@ -0,0 +1,15 @@
+<html>
+
+<body>
+
+<div style="width: 1px">
+ <h3 style="float: right;">Foo bar</h3>
+ <ul>
+ <li style="float: right;">E</li>
+ <li style="float: right;">PPP XXXXXX</li>
+ </ul>
+</div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/368461-1.xhtml b/layout/generic/crashtests/368461-1.xhtml
new file mode 100644
index 000000000..d5baccf52
--- /dev/null
+++ b/layout/generic/crashtests/368461-1.xhtml
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+
+<head>
+</head>
+
+<body>
+
+<p><math:msubsup><span>Foo bar baz<td></td></span></math:msubsup></p>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/368568.html b/layout/generic/crashtests/368568.html
new file mode 100644
index 000000000..85330fad5
--- /dev/null
+++ b/layout/generic/crashtests/368568.html
@@ -0,0 +1,14 @@
+<html><head>
+<style>
+*::first-line { }
+*::after { content:"anonymous text"; }
+*::before { content:"before text"; }
+</style>
+</head>
+<body>
+
+<ol style="overflow: hidden; float: right; -moz-column-count: 3;">
+<span style="overflow: auto; float: left;"></span>
+</ol>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/368752.html b/layout/generic/crashtests/368752.html
new file mode 100644
index 000000000..07d5c7002
--- /dev/null
+++ b/layout/generic/crashtests/368752.html
@@ -0,0 +1,20 @@
+<html class="reftest-wait"><head>
+<title>Testcase bug - Crash [@ nsIFrame::Invalidate] with display: inherit, large margin and padding and generated content</title>
+<script>
+function addstyle(){
+var x=document.createElementNS('http://www.w3.org/1999/xhtml','style');
+x.innerHTML='body::before {content: "text"; }';
+document.documentElement.appendChild(x);
+document.documentElement.offsetHeight;
+document.documentElement.removeAttribute("class");
+}
+</script>
+
+</head>
+<body onload="setTimeout(addstyle,0);">
+<div style="display: table;">
+<div style="display: inherit;float: left;margin-bottom: -9999999px;padding-top: 9999999999px;">
+<span style="position: fixed;">t</span>
+</div>
+</div>
+</body></html>
diff --git a/layout/generic/crashtests/368860-1.html b/layout/generic/crashtests/368860-1.html
new file mode 100644
index 000000000..f38b5b3c4
--- /dev/null
+++ b/layout/generic/crashtests/368860-1.html
@@ -0,0 +1,12 @@
+<html>
+
+<head></head>
+
+<body>
+
+<table border="1"><tr><td>
+ <span>&rlm;X</span>
+</td></tr></table>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/368863-1.html b/layout/generic/crashtests/368863-1.html
new file mode 100644
index 000000000..ea281da70
--- /dev/null
+++ b/layout/generic/crashtests/368863-1.html
@@ -0,0 +1,5 @@
+<style>
+*::first-line { }
+*::before { content:"before text";}
+</style>
+<object style="position: fixed;-moz-column-count: 100;"><ol style="float: right;"> \ No newline at end of file
diff --git a/layout/generic/crashtests/369038-1.xhtml b/layout/generic/crashtests/369038-1.xhtml
new file mode 100644
index 000000000..dea9972e6
--- /dev/null
+++ b/layout/generic/crashtests/369038-1.xhtml
@@ -0,0 +1,29 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ class="reftest-wait">
+
+<head>
+<script>
+
+var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function boom()
+{
+ var hbox = document.getElementById("hbox");
+ var checkbox = document.createElementNS(XUL_NS, "checkbox");
+
+ hbox.appendChild(checkbox);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(boom, 30)">
+
+<xul:scrollbar><span><xul:hbox id="hbox"/></span></xul:scrollbar>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/369150-1.html b/layout/generic/crashtests/369150-1.html
new file mode 100644
index 000000000..b492c0a06
--- /dev/null
+++ b/layout/generic/crashtests/369150-1.html
@@ -0,0 +1,22 @@
+<html class="reftest-wait">
+<head>
+
+<script>
+
+function boom()
+{
+ document.getElementById("frameset").appendChild(document.createTextNode(""));
+ document.getElementById("frameset").setAttribute("rows", "100%, *");
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+</head>
+
+ <frameset id="frameset" cols="130, *" onload="setTimeout(boom, 30);">
+ <frame src="data:text/html,foo">
+ <frame src="data:text/html,bar">
+ </frameset>
+
+</html>
diff --git a/layout/generic/crashtests/369150-2.html b/layout/generic/crashtests/369150-2.html
new file mode 100644
index 000000000..59215b14a
--- /dev/null
+++ b/layout/generic/crashtests/369150-2.html
@@ -0,0 +1,22 @@
+<html class="reftest-wait">
+<head>
+<title>Bug 369150 - Crash [@ nsHTMLFramesetFrame::GetNoResize] with dynamic changes</title>
+<script>
+
+function boom()
+{
+ document.getElementById("frameset").appendChild(document.createElement("frame"));
+ document.getElementById("frameset").setAttribute("rows", "100%, *");
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+</head>
+
+ <frameset id="frameset" cols="130, *" onload="setTimeout(boom, 30);">
+ <frame src="data:text/html,foo">
+ <frame src="data:text/html,bar">
+ </frameset>
+
+</html>
diff --git a/layout/generic/crashtests/369227-1.xhtml b/layout/generic/crashtests/369227-1.xhtml
new file mode 100644
index 000000000..7a61e4c20
--- /dev/null
+++ b/layout/generic/crashtests/369227-1.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>document.documentElement.offsetHeight</script>
+</head>
+<body>
+
+<div style="height: 3000px; background: lightgreen;"/>
+
+<div style="width: 4px; float: left; background: yellow;">
+ A
+ <span style="padding: 5px">
+ <span style="float: left;" />
+ </span>
+ Z
+</div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/369542-1.html b/layout/generic/crashtests/369542-1.html
new file mode 100644
index 000000000..ff62a3d05
--- /dev/null
+++ b/layout/generic/crashtests/369542-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<style>
+body::first-letter { float: right; }
+body::before { content:"before text"; }
+</style>
+</head><body></body></html>
diff --git a/layout/generic/crashtests/369542-2.html b/layout/generic/crashtests/369542-2.html
new file mode 100644
index 000000000..b2c208591
--- /dev/null
+++ b/layout/generic/crashtests/369542-2.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<style>
+html::first-letter { float: right; }
+html::before { content:"before text"; float:right; }
+span::before { content:"before text"; float:right; }
+</style>
+</head>
+<body>
+<span>
+ <div></div>
+</span>
+<button onclick="document.body.style.width='100px'">Click</button>
+</body>
+</html>
diff --git a/layout/generic/crashtests/369547-1.html b/layout/generic/crashtests/369547-1.html
new file mode 100644
index 000000000..2f7516d35
--- /dev/null
+++ b/layout/generic/crashtests/369547-1.html
@@ -0,0 +1,16 @@
+<html class="reftest-wait"><head><script>
+function doe2() {
+document.getElementById('a').setAttribute('style', 'display: inline-block;');
+document.body.offsetHeight;
+document.getElementById('b').removeAttribute('style');
+document.body.offsetHeight;
+document.documentElement.className = '';
+}
+setTimeout(doe2,200,0);
+</script>
+</head>
+<body style="display: -moz-inline-box;"><span style="display: inline-block;"><span style="display: inline-block;"></span></span><span id="a">
+<iframe></iframe>
+<div id="b" style="display: table-footer-group;"></div>
+</span></body>
+</html>
diff --git a/layout/generic/crashtests/370174-1.html b/layout/generic/crashtests/370174-1.html
new file mode 100644
index 000000000..57493a7d3
--- /dev/null
+++ b/layout/generic/crashtests/370174-1.html
@@ -0,0 +1,566 @@
+<html>
+<head>
+<style style="display: none; direction: ltr;">
+<label style="display: table-column-group;direction: rtl;">
+</big>
+</textarea>
+</q>
+<q style="display: table-row;direction: ltr;">
+</span>
+</ol>
+</dir>
+<html style="display: table-row;direction: rtl;">
+</strong>
+<basefont style="display: inline-table;direction: auto;">
+<map style="display: table-footer-group;direction: ltr;">
+<dir="rtl" style="display: inline-block;direction: ltr;">
+<center style="display: none;direction: ltr;">
+<style style="display: block;direction: auto;">
+<samp style="display: -moz-inline-box;direction: rtl;">
+<noframes style="display: inline;direction: rtl;">
+<code style="display: table-header-group;direction: ltr;">
+</li>
+</body>
+</div>
+<embed style="display: table-header-group;direction: auto;">
+</table>
+</fieldset>
+<legend style="display: table-row;direction: auto;">
+</ul>
+<canvas style="display: table;direction: ltr;">
+<font style="display: inline-table;direction: auto;">
+</option>
+</code>
+</menu>
+<thead style="display: inline-table;direction: rtl;">
+</button>
+</div>
+</ol>
+</isindex>
+<code style="display: table-row-group;direction: rtl;">
+<dl style="display: table-row;direction: ltr;">
+<style style="display: table-footer-group;direction: ltr;">
+<marquee style="display: table-row;direction: auto;">
+</object>
+</hx>
+<nobr style="display: table-cell;direction: rtl;">
+<a style="display: table-header-group;direction: ltr;">
+</thead>
+<tr style="display: table-caption;direction: ltr;">
+<tr style="display: table-row;direction: rtl;">
+<base style="display: inline;direction: auto;">
+</input>
+<hx style="display: inline-block;direction: auto;">
+<link style="display: table-column;direction: rtl;">
+<code style="display: inline;direction: ltr;">
+<li style="display: table-column-group;direction: ltr;">
+</dd>
+</spacer>
+<address style="display: table-header-group;direction: auto;">
+</object>
+</hx>
+</th>
+</sup>
+<u style="display: -moz-inline-box;direction: auto;">
+</del>
+<bdo style="display: table-column-group;direction: ltr;">
+<param style="display: block;direction: auto;">
+<td rowspan="5" style="display: inline-table;direction: rtl;">
+<head style="display: none;direction: ltr;">
+</abbr>
+<address style="display: table-row;direction: rtl;">
+</input type="password">
+</li>
+<area style="display: -moz-inline-box;direction: ltr;">
+</noframes>
+</b>
+</input type="image">
+<title style="display: -moz-inline-box;direction: ltr;">
+</font>
+<blockquote style="display: table-column-group;direction: auto;">
+<area style="display: none;direction: auto;">
+</script>
+<colgroup style="display: -moz-inline-box;direction: auto;">
+<font style="display: list-item;direction: auto;">
+<isindex style="display: table-cell;direction: ltr;">
+<sup style="display: table-footer-group;direction: ltr;">
+<abbr style="display: block;direction: auto;">
+<address style="display: table-caption;direction: rtl;">
+</span>
+</code>
+</strong>
+<select style="display: table-cell;direction: auto;">
+<dl style="display: inline-table;direction: rtl;">
+</small>
+</listing>
+<colgroup style="display: table-header-group;direction: rtl;">
+<title style="display: none;direction: auto;">
+</input type="submit">
+<tbody style="display: table-column-group;direction: rtl;">
+</blink>
+<i style="display: table;direction: ltr;">
+</td>
+</dir>
+</tt>
+</a<bgsound style="display: inline;direction: auto;">
+<multicol style="display: -moz-inline-box;direction: auto;">
+</th>
+</font>
+</base>
+<textarea style="display: inline-block;direction: ltr;">
+<frameset style="display: list-item;direction: rtl;">
+</span>
+<blockquote style="display: table-caption;direction: auto;">
+</code>
+<dir style="display: inline-table;direction: rtl;">
+<fieldset style="display: table-column;direction: ltr;">
+</kbd>
+<noframes style="display: none;direction: rtl;">
+</spacer type="block">
+</i>
+<area style="display: table-column-group;direction: ltr;">
+<form style="display: none;direction: rtl;">
+<tt style="display: table-row;direction: auto;">
+</bdo>
+</th>
+<cite style="display: none;direction: auto;">
+</td colspan="5">
+</dir>
+</noscript>
+<spacer style="display: table-caption;direction: rtl;">
+</legend>
+<ins style="display: table-row-group;direction: rtl;">
+</multicol>
+</base>
+</img>
+<nobr style="display: table-header-group;direction: auto;">
+</map>
+<br style="display: none;direction: rtl;">
+</spacer type="block">
+<table style="display: table-row-group;direction: ltr;">
+</td rowspan="5">
+</caption>
+<embed style="display: inline-table;direction: auto;">
+</script>
+<style style="display: list-item;direction: rtl;">
+</nobr>
+<button style="display: table-column;direction: auto;">
+</select>
+</strike>
+<hx style="display: table-header-group;direction: ltr;">
+</bdo>
+<td rowspan="5" style="display: none;direction: ltr;">
+</code>
+<input type="hidden" style="display: table;direction: auto;">
+<spacer type="block" style="display: inline-block;direction: ltr;">
+</marquee>
+</dir="rtl">
+</html>
+<le</i>gend style="display: table-caption;direction: ltr;">
+<sup style="display: table-column-group;direction: ltr;">
+</address>
+<title style="display: none;direction: rtl;">
+<font style="display: table-row;direction: ltr;">
+<ol style="display: inline-table;direction: rtl;">
+</th>
+</link>
+</isindex>
+</iframe>
+<th style="display: table;direction: rtl;">
+</hx>
+<object style="display: table-caption;direction: auto;">
+</embed>
+<small style="display: block;direction: auto;">
+</abbr>
+<object style="display: inline-table;direction: ltr;">
+<div style="display: table-column-group;direction: auto;">
+</i>
+</u>
+</wbr>
+<abbr style="display: inline;direction: ltr;">
+</sub>
+<dir style="display: block;direction: auto;">
+</small>
+</code>
+<style style="display: table-column;direction: ltr;">
+<fieldset style="display: table-cell;direction: rtl;">
+</b>
+<blockquote style="display: table-header-group;direction: rtl;">
+<q style="display: inline;direction: auto;">
+<iframe style="display: block;direction: rtl;">
+<button style="display: table-footer-group;direction: auto;">
+</s>
+</caption>
+<input type="submit" style="display: inline-block;direction: auto;">
+</wbr>
+</link>
+</sub>
+<dt style="display: table-column-group;direction: auto;">
+<noscript style="display: table-cell;direction: ltr;">
+<spacer type="block" style="display: inline-table;direction: rtl;">
+<dl style="display: inline;direction: auto;">
+<thead style="display: table-row;direction: auto;">
+</td>
+</strike>
+<body style="display: inline;direction: auto;">
+<img style="display: table-column;direction: rtl;">
+</input type="hidden">
+</label>
+<img src="fish.gif" style="display: inline-table;direction: auto;">
+</ins>
+<img src="fish.gif" style="display: block;direction: rtl;">
+</caption>
+<abbr style="display: table-header-group;direction: auto;">
+</meta>
+<area style="display: list-item;direction: auto;">
+<ul style="display: table-row-group;direction: rtl;">
+<img style="display: table-cell;direction: rtl;">
+</nobr>
+<blockquote style="display: table;direction: ltr;">
+<a style="display: table-header-group;direction: auto;">
+</basefont>
+</tbody>
+<dt style="display: table-footer-group;direction: auto;">
+</address>
+<strong style="display: table-header-group;direction: auto;">
+<strong style="display: table-row;direction: rtl;">
+<u style="display: table;direction: rtl;">
+</font>
+</dir>gend style="display: ta<samp style="display: table-row-group;direction: rt<kbd style="display: table-column;direction: auto;">
+<acronym style="display: table-footer-group;direction: auto;">
+</html>
+</cite>
+</keygen>
+<kbd style="display: none;direction: ltr;">
+</spacer>
+<div style="display: inline;direction: rtl;">
+</marquee>
+</code>
+<select style="display: table-cell;direction: rtl;">
+<i style="display: none;direction: rtl;">
+<bgsound style="display: table-header-group;direction: ltr;">
+</isindex>
+<table style="display: none;direction: rtl;">
+</td>
+<bgsound style="display: inline-table;direction: rtl;">
+<canvas style="display: inline-table;direction: ltr;">
+<textarea style="display: table-header-group;direction: auto;">
+</tbody>
+</li>
+</th>
+</body>
+<th style="display: inline;direction: ltr;">
+</sub>
+</sup>
+<li style="display: table;direction: ltr;">
+<l<sup style="display: none;direction: ltr;">
+</bgsound>
+<select style="display: table-row;direction: auto;">
+</del>
+</cite>
+</img>
+<abbr style="display: table-row;direction: ltr;">
+<multicol style="display: table;direction: ltr;">
+<style style="display: inline-table;direction: ltr;">
+<code style="display: table-caption;direction: ltr;">
+</tbody>
+<button style="display: -moz-inline-box;direction: rtl;">
+<basefont style="display: table-column-group;direction: auto;">
+<img style="display: inline;direction: auto;">
+<wbr style="display: table-row;direction: rtl;">
+</isindex>
+<html style="display: table-footer-group;direction: rtl;">
+</nobr>
+<col style="display: table-column;direction: ltr;">
+</li>
+<fieldset style="display: table-row-group;direction: auto;">
+<a style="display: inline;direction: rtl;">
+</textarea>
+<input type="hidden" style="display: table;direction: auto;">
+</td rowspan="5">
+</wbr>
+</script>
+</optgroup>
+</noframes>
+<le</i>
+</label>
+</li>
+<frame style="display: table-footer-group;direction: ltr;">
+<noscript style="display: table-column;direction: ltr;">
+<thead style="display: none;direction: rtl;">
+</blink>
+<style style="display: -moz-inline-box;direction: ltr;">
+</strike>
+<noframes style="display: -moz-inline-box;direction: auto;">
+</u>
+</frame>
+</big>
+</strike>
+<label style="display: table-cell;direction: rtl;">
+</p>
+</optgroup>
+<kbd style="display: table-row-group;direction: rtl;">
+<hx style="display: block;direction: ltr;">
+</spacer type="block">
+<strike style="display: -moz-inline-box;direction: rtl;">
+</td>
+<input type="submit" style="display: inline-block;direction: auto;">
+</fieldset>
+</dir="rtl">
+<label style="display: list-item;direction: rtl;">
+<base style="display: -moz-inline-box;direction: ltr;">
+<iframe style="display: table-footer-group;direction: ltr;">
+</link>
+</s>
+<sup style="display: table-row;direction: rtl;">
+</colgroup>
+<map style="display: inline;direction: rtl;">
+<script style="display: -moz-inline-box;direction: auto;">
+<spacer style="display: list-item;direction: auto;">
+</acronym>
+<spacer style="display: none;direction: rtl;">
+</spacer>
+</menu>
+</blockquote>
+<a style="display: table;direction: ltr;">
+</big>
+<script style="display: table-column;direction: auto;">
+</dd>
+<div style="display: table-caption;direction: rtl;">
+</object>
+<label style="display: block;direction: ltr;">
+<colgroup style="display: table;direction: rtl;">
+</cite>
+<ol style="display: inline-table;direction: auto;">
+</cite>
+</small>
+</big>
+<input type="hidden" style="display: inline-table;direction: auto;">
+<big style="display: -moz-inline-box;direction: rtl;">
+<strong style="display: table-row-group;direction: rtl;">
+<dd style="display: block;direction: ltr;">
+</font>
+<dt style="display: list-item;direction: rtl;">
+<cite style="display: table-header-group;direction: rtl;">
+<em style="display: table-row-group;direction: rtl;">
+<basefont style="display: table-column;direction: rtl;">
+<legend style="display: -moz-inline-box;direction: auto;">
+</img>
+<tt style="display: table-row-group;direction: ltr;">
+<link style="display: table-caption;direction: ltr;">
+<q style="display: block;direction: ltr;">
+</title>
+</bdo>
+</meta>
+</iframe>
+<b style="display: table-header-group;direction: rtl;">
+<script style="display: block;direction: auto;">
+</link>
+</embed>
+<code style="display: -moz-inline-box;direction: rtl;">
+</select>
+</label>
+</hx>
+</td>
+</textarea>
+<q style="display: none;direction: rtl;">
+<del style="display: table-cell;direction: auto;">
+<input type="submit" style="display: -moz-inline-box;direction: rtl;">
+<tfoot style="display: inline;direction: rtl;">
+</abbr>
+</tr>
+<strike style="display: inline;direction: rtl;">
+</big>
+<input type="hidden" style="display: inline-block;direction: rtl;">
+</strong>
+<q style="display: table-header-group;direction: rtl;">
+</big>
+<tr style="display: list-item;direction: auto;">
+</object>
+</input type="hidden">
+</bdo>
+</thead>
+</span>
+</tt>
+</listing>
+</blink>
+</dfn>
+</b>
+</a>
+</tt>
+</dir>
+</a<nobr</keygen> styl<sup style="display: -moz-inline-box;direction: ltr<ol style="display: table;direction: auto;">
+</span>
+</ins>
+<div style="display: table-row-group;direction: auto;">
+<pre style="display: -moz-inline-box;direction: rtl;">
+</tfoot>
+<frameset style="display: table-footer-group;direction: auto;">
+</img>
+</keygen>
+<span style="display: table-caption;direction: auto;">
+</samp>
+</object>
+<code style="display: table-row-group;direction: ltr;">
+<sup style="display: table-column-group;direction: ltr;">
+<frame style="display: table-cell;direction: ltr;">
+</spacer type="block">
+<button style="display: table-caption;direction: rtl;">
+<td style="display: table;direction: ltr;">
+<td colspan="5" style="display: inline-table;direction: ltr;">
+<pre style="display: inline-table;direction: ltr;">
+<object style="display: table-row-group;direction: auto;">
+</a>
+</tt>
+</optgroup>
+</span>
+<style style="display: table-footer-group;direction: auto;">
+<hx style="display: block;direction: rtl;">
+<spacer style="display: list-item;direction: auto;">
+</title>
+<ins style="display: table-column;direction: auto;">
+</object>
+</br>
+<frameset style="display: inline-block;direction: rtl;">
+<span style="display: block;direction: auto;">
+<kbd style="display: list-item;direction: auto;">
+<dir style="display: table;direction: rtl;">
+</address>
+</cite>
+</big>
+<optgroup style="display: inline-block;direction: ltr;">
+</fieldset>
+</base>
+<input type="image" style="display: inline-block;direction: ltr;">
+</object>
+<col style="display: table-row-group;direction: auto;">
+<noscript style="display: none;direction: auto;">
+</blink>
+</script>
+<input type="hidden" style="display: -moz-inline-box;direction: rtl;">
+<ul style="display: -moz-inline-box;direction: rtl;">
+</menu>
+<sup style="display: table-row-group;direction: ltr;">
+<tr style="display: table-column;direction: auto;">
+<ul style="display: table-row;direction: ltr;">
+<noscript style="display: table;direction: ltr;">
+</i>
+</dir="rtl">
+</input type="submit">
+</cite>
+</code>
+<title style="display: table-footer-group;direction: rtl;">
+<sub style="display: block;direction: rtl;">
+</code>
+</acronym>
+</caption>
+</code>
+</font>
+</em>
+</optgroup>
+<multicol style="display: table-row-group;direction: auto;">
+<bdo style="display: list-item;direction: auto;">
+<canvas style="display: table-header-group;direction: rtl;">
+</form>
+<tt style="display: table-footer-group;direction: ltr;">
+<frame style="display: -moz-inline-box;direction: rtl;">
+</multicol>
+</acronym>
+<em style="display: -moz-inline-box;direction: ltr;">
+</wbr>
+<legend style="display: inline-block;direction: auto;">
+</samp>
+</var>
+<dir style="display: table-footer-group;direction: auto;">
+</title>
+</input type="submit">
+<spacer style="display: inline-block;direction: auto;">
+<ol style="display: table-header-group;direction: ltr;">
+</option>
+<i style="display: list-item;direction: rtl;">
+</caption>
+</spacer>
+</noframes>
+</sub>
+</tfoot>
+</legend>
+</select>
+<marquee style="display: table-caption;direction: ltr;">
+<script style="display: table-footer-group;direction: rtl;">
+</q>
+</strong>
+</code>
+</div>
+</code>
+</td>
+</optgroup>
+<map style="display: table-row-group;direction: ltr;">
+</marquee>
+<optgroup style="display: none;direction: ltr;">
+<hr style="display: table-row-group;direction: rtl;">
+</br>
+<param style="display: table-row;direction: ltr;">
+</dfn>
+</keygen>
+<param style="display: none;direction: ltr;">
+<legend style="display: list-item;direction: ltr;">
+<b style="display: list-item;direction: ltr;">
+</bdo>
+</dfn>
+</option>
+</input>
+</u>
+</code>
+<ul style="display: table-header-group;direction: ltr;">
+</thead>
+</span>
+</link>
+<img src="fish.gif" style="display: inline-table;direction: auto;">
+</menu>
+<u style="display: table-row;direction: rtl;">
+</cite>
+<listing style="display: inline-block;direction: auto;">
+</style>
+</head>
+<body>
+<code style="display: table-row-group; direction: ltr;">
+<button style="display: list-item; direction: ltr;">
+<kbd style="display: table-row; direction: rtl;">
+<input style="display: list-item; direction: rtl;" type="hidden">
+<iframe style="display: table-column-group;">
+</iframe>gend style="display: ta<samp style="display: table-row-group;" display:="" table-column;direction:="" auto;="">
+</samp>
+</kbd>
+</button>
+</code>
+<script>
+//<![CDATA[
+/*template*/
+var doc = document;
+if (document.getElementById('content'))
+ doc = document.getElementById('content').contentDocument;var tt;
+var l=0;
+function replacestyles(i){
+l++;
+window.status=l+':'+i;
+if (l>70)
+ return;
+var x=doc.getElementsByTagName('*');
+
+if (x[i] && x[i+1])
+ {
+var temp = x[i+1].getAttribute('style');
+x[i+1].setAttribute('style', x[i].getAttribute('style'));
+x[i].setAttribute('style', temp);
+}
+else { i = 0;}
+ i++;
+tt=setTimeout(replacestyles,10,i);
+}
+//window.onmousedown=function(){clearTimeout(tt);}
+tt=setTimeout(replacestyles,100,0);
+/*template*/
+//]]>
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/370174-2.html b/layout/generic/crashtests/370174-2.html
new file mode 100644
index 000000000..fd9649636
--- /dev/null
+++ b/layout/generic/crashtests/370174-2.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<title>Bug 370174 – Crash [@ FindBlockFrameOrBR] with unminimised testcase triple-clicking at the bottom of the page</title>
+</head>
+<body>
+<script>
+function doe() {
+document.documentElement.innerHTML = '';
+}
+setTimeout(doe, 300);
+</script>
+</body>
+</html>
diff --git a/layout/generic/crashtests/370174-3.html b/layout/generic/crashtests/370174-3.html
new file mode 100644
index 000000000..bfbcd25ad
--- /dev/null
+++ b/layout/generic/crashtests/370174-3.html
@@ -0,0 +1,26 @@
+<HTML><HEAD style="display: table-row;"></HEAD>
+<BODY style="display: table-row;">
+<CODE>
+<span>
+<IFRAME style="display:table-column-group;"></IFRAME>
+</span>
+</CODE>
+<span style="display:table-column-group;"></span>
+<SCRIPT>
+function doe2() {
+document.body.setAttribute('style', '');
+document.getElementsByTagName('head')[0].setAttribute('style', '');
+document.body.offsetHeight;
+document.getElementsByTagName('span')[0].setAttribute('style', 'display: table-row;');
+}
+setTimeout(doe2,100);
+
+function tripleclick(){
+var wu = SpecialPowers.DOMWindowUtils;
+wu.sendMouseEvent('mousedown', 500, 500, 0, 3, 0);
+setTimeout(tripleclick,20);
+setTimeout(function(){window.location.reload()}, 200);
+}
+setTimeout(tripleclick,200);
+</SCRIPT>
+</BODY></HTML> \ No newline at end of file
diff --git a/layout/generic/crashtests/370174-4.html b/layout/generic/crashtests/370174-4.html
new file mode 100644
index 000000000..a5a562dbd
--- /dev/null
+++ b/layout/generic/crashtests/370174-4.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+<title>Untitled</title>
+
+<style>
+
+
+</style>
+</head>
+<body>
+
+
+<iframe id="content" src="data:text/html;charset=utf-8,%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%0A%3Cscript%3E%0Avar%20wu%20%3D%20SpecialPowers.DOMWindowUtils%3B%0Avar%20i%20%3D%20Math.floor%28Math.random%28%29*20%29%3B%0Awu.sendMouseEvent%28%27mousedown%27%2C%20%2020*i%2C%2020*i%2C%200%2C%203%2C%200%29%3B%0Awu.sendMouseEvent%28%27mouseup%27%2C%20%2020*i%2C%2020*i%2C%200%2C%203%2C%200%29%3B%0A%3C/script%3E%0A%0A%3Cembed%20type%3D%22bbb%22%20style%3D%22display%3A%20-moz-inline-stack%3B%22%3E%0A%0A%3C/body%3E%3C/html%3E%22%20style%3D%22width%3A1000px%3Bheight%3A%20700px%3B"></iframe>
+<script>
+function doe2() {
+document.getElementById('content').src = document.getElementById('content').src + '?1';
+
+}
+setInterval(doe2, 1000);
+</script>
+</body>
+</html>
diff --git a/layout/generic/crashtests/370699-1.html b/layout/generic/crashtests/370699-1.html
new file mode 100644
index 000000000..d977c937a
--- /dev/null
+++ b/layout/generic/crashtests/370699-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+</head>
+
+<body style="width: 100px; border: 2px solid yellow;">
+
+<span style="position: relative;">
+ <img width="200" height="100" align="left">
+ <span style="position:absolute; float:left;"></span>
+ Foo
+</span>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/370794-1.html b/layout/generic/crashtests/370794-1.html
new file mode 100644
index 000000000..d4f489d14
--- /dev/null
+++ b/layout/generic/crashtests/370794-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+
+<div style="overflow: hidden; padding-right: 3%;">
+ <h2 style="position: fixed;">H2</h2>
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/370866-1.xhtml b/layout/generic/crashtests/370866-1.xhtml
new file mode 100644
index 000000000..dbc673cc4
--- /dev/null
+++ b/layout/generic/crashtests/370866-1.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+</head>
+
+<body>
+
+<table style="display: -moz-deck;"><tbody><tr><td>
+ <span style="position: relative;"><marquee><marquee><span style="position: absolute;">X</span></marquee></marquee></span>
+</td></tr></tbody></table>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/370884-1.xhtml b/layout/generic/crashtests/370884-1.xhtml
new file mode 100644
index 000000000..3959d4b17
--- /dev/null
+++ b/layout/generic/crashtests/370884-1.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+<head>
+</head>
+
+<body>
+
+<math:mroot>
+ <div>
+ <div style="position: fixed;">Y</div>
+ </div>
+</math:mroot>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/371348-1.xhtml b/layout/generic/crashtests/371348-1.xhtml
new file mode 100644
index 000000000..1b2252f11
--- /dev/null
+++ b/layout/generic/crashtests/371348-1.xhtml
@@ -0,0 +1,41 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ class="reftest-wait">
+
+<head>
+<script>
+
+function boom()
+{
+ var span = document.getElementById("span");
+ var table = document.getElementById("table");
+
+ table.appendChild(span);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+
+<body onload="setTimeout(boom, 20)">
+
+<span id="span"></span>
+
+<table id="table">
+ <tbody>
+ <tr>
+ <td style="float: left">
+ Table
+ <xul:box>
+ <input type="checkbox"/>
+ </xul:box>
+ cell
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/371561-1.html b/layout/generic/crashtests/371561-1.html
new file mode 100644
index 000000000..bce6075c4
--- /dev/null
+++ b/layout/generic/crashtests/371561-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <div style="position: relative; display: inline;">
+ <table style="position: absolute;"><tr><td></td></tr></table>
+ </div>
+ <body>
+</html>
diff --git a/layout/generic/crashtests/371566-1.xhtml b/layout/generic/crashtests/371566-1.xhtml
new file mode 100644
index 000000000..2797f1567
--- /dev/null
+++ b/layout/generic/crashtests/371566-1.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+ <div style="display: -moz-inline-stack;">
+ <div style="float: left; padding-right: 3%">
+ <div style="position: absolute;"></div>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/372376-1.xhtml b/layout/generic/crashtests/372376-1.xhtml
new file mode 100644
index 000000000..ddb7d2d17
--- /dev/null
+++ b/layout/generic/crashtests/372376-1.xhtml
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<script>
+<![CDATA[
+
+var HTML_NS = "http://www.w3.org/1999/xhtml";
+
+function boom()
+{
+ var $table = document.getElementById('table');
+ var $oldtd = document.getElementById('oldtd');
+
+ $table.style.outline = "1px solid green";
+
+ var $tr = document.createElementNS(HTML_NS, 'tr');
+ $tr.style.display = "inherit";
+ $table.insertBefore($tr, $oldtd);
+
+ var $td = document.createElementNS(HTML_NS, 'td');
+ $tr.appendChild($td);
+
+ var $anothertr = document.createElementNS(HTML_NS, 'tr');
+ $table.insertBefore($anothertr, $tr);
+
+ document.documentElement.removeAttribute("class");
+}
+
+]]>
+</script>
+</head>
+
+<body onload="setTimeout(boom, 30)">
+
+<table border="1" id="table">
+<td id="oldtd"></td>
+</table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/373859-1.html b/layout/generic/crashtests/373859-1.html
new file mode 100644
index 000000000..9fbb147cb
--- /dev/null
+++ b/layout/generic/crashtests/373859-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function boom()
+{
+ document.getElementById("k").style.display = "-moz-inline-stack";
+}
+</script>
+
+<body onload="boom();">
+
+<div style="display: table;"><div id="k" style="height: 18000000px;">x</div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/373868-1.xhtml b/layout/generic/crashtests/373868-1.xhtml
new file mode 100644
index 000000000..712569386
--- /dev/null
+++ b/layout/generic/crashtests/373868-1.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+<span style="float: left; height: 18000000px"/>
+<p style="clear: left;"/>
+<div>
+ <span style="float: right; height: 100px;">a</span>
+ b
+ <span style="float: right; height: 18000000px;"/>
+ <p/>
+ <span style="float: right; height: 18000000px;">c</span>
+ <table style="height: 20000000px;"/>
+ <p style="float: right;"/>
+</div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/374090.html b/layout/generic/crashtests/374090.html
new file mode 100644
index 000000000..250f8c11d
--- /dev/null
+++ b/layout/generic/crashtests/374090.html
@@ -0,0 +1,11 @@
+<html><head><script>
+function doe() {
+window.location.reload();
+}
+setTimeout(doe, 500);
+</script></head><noframes style="overflow: scroll; display: block;"></noframes><body><li></li>
+
+<script>
+document.documentElement.setAttribute('style','-moz-binding: url(binding.xml#randomxbl);');
+</script>
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/374420.xhtml b/layout/generic/crashtests/374420.xhtml
new file mode 100644
index 000000000..e1cfa13dd
--- /dev/null
+++ b/layout/generic/crashtests/374420.xhtml
@@ -0,0 +1,34 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML">
+<title>Testcase bug - Crash [@ nsHTMLReflowState::GetNearestContainingBlock] with testcase using generated content, xbl and mathml</title>
+<bindings xmlns="http://www.mozilla.org/xbl"><binding id="a">
+<content><children>
+<div xmlns="http://www.w3.org/1999/xhtml" style="position: absolute;">
+<div style="position: absolute;"/>
+</div>
+</children></content>
+</binding></bindings>
+<style>
+ mtable::after { content:"anonymous text"; }
+ mtable::before { content:"before text"; }
+</style>
+
+<thead style="overflow: scroll;">
+ <mathml:mrow>
+ <mathml:mtable>
+ <tfoot style="-moz-binding: url(#a);" id="a_1">
+ <td style="-moz-binding: url(#a);"/>
+ </tfoot>
+ </mathml:mtable>
+ <td id="a_2"/>
+ </mathml:mrow>
+</thead>
+
+<script xmlns="http://www.w3.org/1999/xhtml">
+ function doe() {
+ document.getElementById('a_1').style.borderCollapse = "";
+ document.documentElement.offsetHeight;
+ document.getElementById('a_2').parentNode.removeChild(document.getElementById('a_2'));
+ }
+ setTimeout(doe, 400);
+</script>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/375462-1.html b/layout/generic/crashtests/375462-1.html
new file mode 100644
index 000000000..a0d9dca75
--- /dev/null
+++ b/layout/generic/crashtests/375462-1.html
@@ -0,0 +1,781 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+ lang="en">
+
+ <head>
+ <meta http-equiv="Content-Type"
+ content="text/html;charset=utf-8" />
+
+ <title>Testcase #1 for bug 375462</title>
+
+<!-- Reduced from http://www.bethelmaine.com/events/index_events_bugy -->
+
+
+ </head>
+
+ <body class="section-events" dir="ltr">
+
+
+
+ <div>
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">"Remembering Sis Post"</h3>
+ <h3 style="float: right;">
+ <span>07/01</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">BHS members and friends will gather to share stories and memories in celebration of the life of the late Persis G. Post, who was an outstanding volunteer at the Society for more than 20 years.</div>
+ <br />
+ <div style="position: relative;" class="stx">1:00 PM, Mason House Exhibit Hall</div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Dr. Moses Mason House</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Bethel Historical Society</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:info@bethelhistorical.org"><span>info@bethelhistorical.org</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207-824-2908</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">"An Androscoggin Sampler"</h3>
+ <h3 style="float: right;">
+ <span>07/01</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">In conjunction with the Society's newest exhibit, "A River's Journey: The Story of the Androscoggin," a series of slides of old photos taken in various locations throughout the Androscoggin valley will be shown. Free and open to the public.</div>
+ <br />
+ <div style="position: relative;" class="stx">2:00 PM, Mason House Exhibit Hall (the exhibit will be open at the Robinson House following the program)</div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Bethel Historical Society</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Bethel Historical Society</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:info@bethelhistorical.org"><span>info@bethelhistorical.org</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207-824-2908</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">Summer Season Mason House Period Room Tours</h3>
+ <h3 style="float: right;">
+ <span>07/02</span>
+ <span>- 08/01</span>
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">Summer season guided tours of the Mason House period rooms begin (Tuesday through Sunday, 1:00 to 4:00 PM, until Labor Day); tours may be arranged during the remainder of the year by calling 207-824-2908.</div>
+ <br />
+ <div style="position: relative;" class="stx"></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Dr. Moses Mason House</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Bethel Historical Society</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:info@bethelhistorical.org"><span>info@bethelhistorical.org</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207-824-2908</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">4th of July Community Picnic</h3>
+ <h3 style="float: right;">
+ <span>07/04</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;"></div>
+ <br />
+ <div style="position: relative;" class="stx"><p>In the 1850s, Dr. Moses Mason began hosting a Fourth of July community picnic in "the grove" behind his residence. Today, the Bethel Historical Society proudly carries on this tradition. </p><p>This year's event will start at noon on the lawn beside the Mason house, and after the presentation of colors and the National Anthem, a two-hour concert will be presented by the Portland Brass Quintet. </p><p>In case of rain, the picnic and concert will be held in the historic Middle Intervale Meetinghouse (1816) on Intervale Road, approximately four miles down river from Bethel Hill village. Bring your lunch and a lawn chair or blanket. Free and open to the public (donations accepted).</p></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>14 Broad Street</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Bethel Historical Society</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:info@bethelhistorical.org"><span>info@bethelhistorical.org</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207-824-2908</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">Mama's Night Out</h3>
+ <h3 style="float: right;">
+ <span>07/05</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">You don't have to be a mother to enjoy the hilarious comedy of these three finalists in Nick at Nite's “Search for the Funniest Mom in America.†The uproarious views on life and laughter of these ladies’ – a southerner living in Maine, a sassy Brit, and a New Yorker - are for everyone. Okay, perhaps they are not really for children, but that's why it's called a night out. For further information and reservations please call the Deertrees Box Office at 207 583 6747 or visit www.deertreestheatre.org</div>
+ <br />
+ <div style="position: relative;" class="stx"><p>8pm $18 <br /> <br />The Box Office is open from 10 AM until 5 PM Tuesday through Saturday and one hour before performances. <br /> <br />Tickets are also available at: <br />Books-N-Things, Bethel <br />The Cool Moose, Bridgton <br />Center Lovell Market, Lovell <br />The Country Sleigh, Naples <br />Fare Share, Norway <br /> <br />Deertrees Theatre is handicap accessible. <br />Free parking on the grounds.</p></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Deertrees Theatre</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>C.Randolph Parker (house manager)</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:deertrees@usa.net"><span>deertrees@usa.net</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207 583 6747</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">The Casco Bay Tummlers</h3>
+ <h3 style="float: right;">
+ <span>07/06</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">Klezmer music is music that speaks, it's Balkans and blues, ancient Jewish culture and prayer and history, spirit and jazz all mixed together. Emotionally charged and played with abandon by musicians who continue to change, expand, and morph musical and cultural boundaries. For further information and reservations please call the Deertrees Box Office at 207 583 6747 or visit www.deertreestheatre.org
+ </div>
+ <br />
+ <div style="position: relative;" class="stx"><p>8pm $18 <br /> <br />Box Office open, 10 AM until 5 PM, Tuesday through Saturday, and one hour before performances. <br /> <br />Tickets are also available at: <br />Books-N-Things, Bethel <br />The Cool Moose, Bridgton <br />Center Lovell Market, Lovell <br />The Country Sleigh, Naples <br />Fare Share, Norway <br /> <br />Deertrees Theatre is handicapped accessible. <br />Free parking on the grounds.</p></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Deertrees Theatre</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>C.Randolph Parker (house manager)</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:deertrees@usa.net"><span>deertrees@usa.net</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207 583 6747</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">18th Annual Bethel Art Fair</h3>
+ <h3 style="float: right;">
+ <span>07/07</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">Fifty artists &amp; fine artisans display and sell their creations -- </div>
+ <br />
+ <div style="position: relative;" class="stx"><div><div>Scroll down for complete schedule...<br /></div></div></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Bethel Town Common</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Bethel Area Chamber of Commerce</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:info@bethelmaine.com"><span>info@bethelmaine.com</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207.824.2282</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">The Hunger Mountain Boys with The Wiyos</h3>
+ <h3 style="float: right;">
+ <span>07/07</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">This double dose of traditional music may be resolutely old-school but it is also resolutely eclectic. With their ears tuned to music from the 30’s, 40’s and 50’s, the Massachusetts bluegrass band, The Hunger Mountain Boys, serve up a hard-hittin’, high energy sound that pays homage to the soulfulness and sincerity of old-time country while subtly incorporating rock, jazz and ska influences. The opening set by Brooklyn based “the WIYOS†can only be called Vaudevillian Ragtime-Blues and HillBilly Swing. For further information and reservations please call the Deertrees Box Office at 207 583 6747 or visit www.deertreestheatre.org
+
+</div>
+ <br />
+ <div style="position: relative;" class="stx"><p>8pm $16 <br /> <br />The Box Office is open from 10 AM until 5 PM Tuesday through Saturday and one hour before performances. <br /> <br />Tickets are also available at: <br />Books-N-Things, Bethel <br />The Cool Moose, Bridgton <br />Center Lovell Market, Lovell <br />The Country Sleigh, Naples <br />Fare Share, Norway <br /> <br />Deertrees Theatre is handicap accessible. <br />Free parking on the grounds.</p></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Deertrees Theatre</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>C.Randolph Parker (house manager)</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:deertrees@usa.net"><span>deertrees@usa.net</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207 583 6747</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">Summer Piano Festival at Gould Academy</h3>
+ <h3 style="float: right;">
+ <span>07/07</span>
+
+ </h3>
+ <div style="clear: both;" />
+
+ <div style="position: relative;">Free piano recitals, July 1-20, 2007 at Gould Academy, an all-Steinway school!
+</div>
+ <br />
+ <div style="position: relative;" class="stx"><p>
+
+</p><p>Saturday, July 7, 7 p.m.,
+Bingham Hall</p><p>Distinguished Russian pianist and Master Teacher, Tamara
+Poddubnaya, and her students present free public recitals.</p>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<p>FULL SCHEDULE:<br />Saturday, July 7, 7 p.m.,
+Bingham Hall<br />Wednesday, July 11, 7 p.m.,
+Bingham Hall<br />Friday, July 13, 7 p.m.,
+Bingham Hall<br />Wednesday, July 18, 7 p.m.,
+Bingham Hall<br />Friday, July 20, 7 p.m.,
+<strong>McLaughlin Science Center, Trustees' Auditorium</strong></p><p><br /></p><p><br /></p><br /><p><br />
+</p>
+
+</div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Bingham Hall, Gould Academy, Bethel, Maine</span>
+ </td>
+ </tr>
+
+
+
+ </table>
+
+ </div>
+ <hr />
+
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">5th Annual Skunk Run</h3>
+ <h3 style="float: right;">
+ <span>07/28</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">SATURDAY, July 28,2007 -- Benefiting the Sandon A. Morgan Memorial Scholarship Fund awarded exclusively at Telstar Regional High School, Bethel, Maine. The scholarship is awarded each year to a graduating senior at Telstar to attend CMCC in Auburn for 2 full years, all academics are covered. </div>
+ <br />
+ <div style="position: relative;" class="stx"><p>
+
+</p>
+
+<p>MOTORCYCLE RUN</p>
+
+
+
+
+
+
+
+
+
+<p>$10.00 includes run, bottled water, sandwich, chips &amp;
+FUN!<br />Registration 8:30am-9:45am<br />Vertical Outlaws Stunt Team to perform in the morning.<br />Run leaves at 10:00 am </p>
+
+<p>Ride through Bethel to Grafton Notch State Park, into New
+Hampshire and back to Greenwood (Locke Mills). Designated bathroom/rest &amp;
+gas stops. Great scenery and roads! All of our traffic is taken care of by our
+traffic control volunteers, so the run stays together for safety reasons. We
+also have a red Hummer leading the pack of bikes, as well as a tail vehicle.</p>
+
+
+
+<p> </p>
+
+
+
+
+
+
+
+
+
+
+
+<p>PIG ROAST/TURKEY FRY<br />3:00-5:00 PM<br />$10:00<br />Includes pig, turkey, summer salads, bean hole beans, rolls,
+watermelon, homemade desserts, and assorted beverages. BYOB.<br />Music, vendor showcase, Skunk Run T's and assorted items for
+sale. </p>
+
+
+
+<p>Directions:<br />From the south - go north from Gray on Route 26 to Bryant
+Pond, after sharp "S" turn with light, follow Rt. 26 for a few more
+miles. You will see North Pond on your right, take the 2nd right onto Gore
+Road.</p>
+
+
+
+<p>From the north - go west on Route 2 through Rumford to Route
+232 south. Turn right and travel north on Route 26 to Bryant Pond, after sharp
+"S" turn with light, follow Rt. 26 for a few more miles. You will see
+North Pond on your right, take the 2nd right onto Gore Road.</p>
+
+
+
+<p> From the west - take Route 2 east to Route 26 south to
+Greenwood (Locke Mills). Past Mt. Mica Rarities (purple building) take Gore
+Road on left.</p>
+
+
+
+<p>From the east - from Augusta, take Route 202 west to
+Route 133 to Route 219 to Route 26 North to Greenwood (Locke Mills). Past Mt.
+Mica Rarities (purple building) take Gore Road on left.</p><p><br /></p></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Gore Road, Greenwood, ME, just off Rt. 26</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Mark</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:mdmorgandesigns@yahoo.com"><span>mdmorgandesigns@yahoo.com</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207.743.8254 or 207.890.6767</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">The Bill &amp; Bo Winiker Quartet</h3>
+ <h3 style="float: right;">
+ <span>07/28</span>
+
+ </h3>
+ <div style="clear: both;" />
+ <div style="float: right; padding: 1px; margin-left: 1em; margin-bottom: 1em; border: 1px solid black;">
+ </div>
+ <div style="position: relative;">Featuring jazz savant pianist Tony Deblois, this Boston based quartet embarks on an eclectic journey through the many styles of jazz including traditional, swing, bebop, jazzy takes on Broadway classics, world music and even some original compositions. Making music an uplifting and joyous experience. Sponsored by Gordon Dexter and Barbara Grandolfo. For further information and reservations please call the Deertrees Box Office at 207 583 6747 or visit www.deertreestheatre.org </div>
+ <br />
+ <div style="position: relative;" class="stx"><p>Saturday, July 28 at 8:00 pm <br /> <br />$20 <br /> <br />The Box Office is open from 10 AM until 5 PM Tuesday through Saturday and one hour before performances. <br /> <br />Tickets are also available at: <br />Books-N-Things, Bethel <br />The Cool Moose, Bridgton <br />Center Lovell Market, Lovell <br />The Country Sleigh, Naples <br />Fare Share, Norway <br /> <br />Deertrees Theatre is handicap accessible. <br />Free parking on the grounds.</p></div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Location:
+ </td>
+ <td>
+ <span>Deertrees Theatre</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>C.Randolph Parker (house manager)</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:deertrees@usa.net"><span>deertrees@usa.net</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207 583 6747</span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ <hr />
+
+
+ <div>
+ <div style="clear: both;" />
+ <h3 style="float: left;">Tenth Annual Canoe and Kayak Outdoor Adventure Day Camp </h3>
+ <h3 style="float: right;">
+ <span>07/30</span>
+ <span>- 08/03</span>
+ </h3>
+ <div style="clear: both;" />
+
+ <div style="position: relative;">Designed with young people in mind, all aspects of canoeing and kayaking will be taught, as well as some other adventure activities. This is a hands on active program! June 23rd - July 27th from 9am-3pm for ages 8-13. $175.00 for the week session.</div>
+ <br />
+ <div style="position: relative;" class="stx">Included in the session will be basic canoe and kayak strokes, rescue techniques, expeditioning by canoe, simple camping skills, simple white water techniques and other adventure activities. Participants will learn how to skillfully maneuver canoes and kayaks while exploring the many channels and islands on the river, and develop proficiency and confidence in all types of outdoor skills. Meet at Bethel Outdoor Adventure every morning and travel to local lakes , ponds, islands, and the Androscoggin River. To sign up call 207.824.4224 or info@betheloutdooradventure.com
+Event Starts: July 30, 2007
+Event Ends: August 3, 2007
+
+</div>
+ <table style="border: 1px solid grey; margin-top: 1em; margin-bottom: 1em;" cellspacing="0" cellpadding="2px">
+
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Name:
+ </td>
+ <td>
+ <span>Jeff and Pattie Parsons</span>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact E-mail:
+ </td>
+ <td>
+ <a href="mailto:info@betheloutdooradventure.com"><span>info@betheloutdooradventure.com</span></a>
+ </td>
+ </tr>
+ <tr>
+ <td style="border-right: 1px solid grey;">
+ Contact Phone:
+ </td>
+ <td>
+ <span>207.824.4224 </span>
+ </td>
+ </tr>
+ </table>
+
+ </div>
+
+
+ </div>
+
+ <hr />
+
+ </div>
+
+
+ <div class="discussion">
+
+</div>
+
+
+ </div>
+
+ </div>
+
+
+ </td>
+
+
+
+
+
+ </tr>
+ </tbody>
+ </table>
+
+
+ <div class="visualClear"><!-- --></div>
+
+
+ <hr class="netscape4" />
+
+
+
+ <div id="portal-footer">
+
+<p>
+</p>
+
+</div>
+
+ <div id="portal-colophon">
+ <p>
+ Bethel Area Chamber of Commerce<br />
+ 8 Station Place<br />
+ PO Box 1247<br />
+ Bethel, ME 04217<br />
+ Tel: 207.824.2282 or 800.442.5826 Fax: 207.824.7123<br />
+ Email: info@bethelmaine.com
+ </p>
+
+
+ </div>
+
+
+ </div>
+
+</body>
+</html>
+
+
diff --git a/layout/generic/crashtests/375831.html b/layout/generic/crashtests/375831.html
new file mode 100644
index 000000000..79c334738
--- /dev/null
+++ b/layout/generic/crashtests/375831.html
@@ -0,0 +1,11 @@
+<html>
+<head style="overflow: scroll; display: block; float: right; ">
+</head>
+<body>
+
+<script id="script">
+document.documentElement.setAttribute('style', 'overflow: scroll; ');
+document.getElementsByTagName('head')[0].removeAttribute('style');
+</script>
+</body>
+</html>
diff --git a/layout/generic/crashtests/376419.html b/layout/generic/crashtests/376419.html
new file mode 100644
index 000000000..fd6267c94
--- /dev/null
+++ b/layout/generic/crashtests/376419.html
@@ -0,0 +1,28 @@
+<html><head>
+<style>
+*::first-line { }
+*::after { content:"anonymous text"; border:3px solid black;}
+*::before { content:"before text"; border:3px solid black;font-size: 10px;}
+</style>
+</head>
+<body>
+<div style="-moz-column-count: 2; width: 1400px;">
+ &#1593; tesxt
+ <span>
+ &#1593; tesxt &#1593; tesxt &#1593; tesxt
+ <span>
+ &#1593; tesxt &#1593; tesxt &#1593; tesxt &#1593; tesxt
+ </span>
+ </span>
+<div style="-moz-column-count: 2;">
+<div style="-moz-column-count: 2; white-space: nowrap;">
+<div style="-moz-column-count: 2;">
+<span>
+&#1593; tesxt &#1593; tesxt &#1593; tesxt
+</span>
+</div>
+</div>
+</div>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/377522.html b/layout/generic/crashtests/377522.html
new file mode 100644
index 000000000..418fb8552
--- /dev/null
+++ b/layout/generic/crashtests/377522.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<script>
+
+function boom() {
+ var div = document.getElementById('div');
+ div.appendChild(document.createTextNode(String.fromCharCode(8238)));
+ div.appendChild(document.createTextNode(String.fromCharCode(50377)));
+ div.appendChild(document.createTextNode(String.fromCharCode(50)));
+ div.textContent = div.textContent.slice(1);
+}
+
+</script>
+</head>
+<body onload="boom()">
+<div id="div" style="word-spacing:2px; text-align:right">x</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/37757-1.html b/layout/generic/crashtests/37757-1.html
new file mode 100644
index 000000000..9a7ddab5d
--- /dev/null
+++ b/layout/generic/crashtests/37757-1.html
@@ -0,0 +1 @@
+<p style="font-size: 1px">Text</p>
diff --git a/layout/generic/crashtests/379217-1.xhtml b/layout/generic/crashtests/379217-1.xhtml
new file mode 100644
index 000000000..0614b9a54
--- /dev/null
+++ b/layout/generic/crashtests/379217-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+
+<button style="width: 3em;"><b>aaa<u>bbb<p style="float: left"/></u></b></button>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/379217-2.xhtml b/layout/generic/crashtests/379217-2.xhtml
new file mode 100644
index 000000000..8a8b32693
--- /dev/null
+++ b/layout/generic/crashtests/379217-2.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body style="width: 1px;">
+
+<span>x
+<span style="border: 1px dotted red;"><span style="float: left;"></span></span></span>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/379917-1.xhtml b/layout/generic/crashtests/379917-1.xhtml
new file mode 100644
index 000000000..a99bd7f4a
--- /dev/null
+++ b/layout/generic/crashtests/379917-1.xhtml
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:math="http://www.w3.org/1998/Math/MathML"
+ class="reftest-wait">
+<head>
+<script>
+
+// This testcase uses long timeouts to make sure the marquee has a chance to animate.
+
+function boom()
+{
+ var div1 = document.getElementById("div1");
+ var marquee = document.getElementById("marquee");
+
+ div1.parentNode.removeChild(div1);
+ marquee.width = 4;
+
+ setTimeout(done, 100);
+}
+
+function done()
+{
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(boom, 100);">
+
+<math:math><div id="div1"/>x&#1506;</math:math>
+<marquee id="marquee">m</marquee>
+<div/>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/380012-1.html b/layout/generic/crashtests/380012-1.html
new file mode 100644
index 000000000..6cd7e1018
--- /dev/null
+++ b/layout/generic/crashtests/380012-1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+
+<style>
+ .fl:first-line { }
+ .inh { position: inherit; }
+ .abs { position: absolute; }
+</style>
+
+<script>
+
+var x, y;
+
+function boom()
+{
+ x = document.getElementById("x");
+ y = document.getElementById("y");
+
+ x.setAttribute('class', "fl abs");
+ y.setAttribute('class', "inh");
+ setTimeout(boom2, 5);
+}
+
+function boom2()
+{
+ y.setAttribute('class', "abs");
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+</head>
+
+<body onload="setTimeout(boom, 5);">
+<div id="x">
+ <p id="y">foo</p>
+</div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/381152-1.html b/layout/generic/crashtests/381152-1.html
new file mode 100644
index 000000000..23ea97e6c
--- /dev/null
+++ b/layout/generic/crashtests/381152-1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title>Bug 381152 - Hang with float, large padding and margin</title>
+</head>
+<body>
+<div style="float: left;padding-top: 9999999999px;">
+<div style="float: left;margin-top: 9999999999px;margin-bottom: -9999999px;">
+</div>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/381786-1.html b/layout/generic/crashtests/381786-1.html
new file mode 100644
index 000000000..fd2bc7dc9
--- /dev/null
+++ b/layout/generic/crashtests/381786-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="display:-moz-deck">
+ <div>
+ <span style="float: right; width: 0;">x</span>
+ </div>
+ <div style="position: relative;">
+ <span style="float: right;">
+ <span style="position: absolute;">y</span>
+ </span>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/382129-1.xhtml b/layout/generic/crashtests/382129-1.xhtml
new file mode 100644
index 000000000..0d52309f3
--- /dev/null
+++ b/layout/generic/crashtests/382129-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+
+<p style="display: -moz-box;"><img style="-moz-appearance: checkbox" src="../../../testing/crashtest/images/tree.gif"/></p>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/382131-1.html b/layout/generic/crashtests/382131-1.html
new file mode 100644
index 000000000..82941f2cb
--- /dev/null
+++ b/layout/generic/crashtests/382131-1.html
@@ -0,0 +1,25 @@
+<html class="reftest-wait">
+<head>
+<script>
+
+function crash()
+{
+ try {
+ window.getSelection().containsNode([], false);
+ } catch(e) { }
+
+ try {
+ window.getSelection().containsNode(null, false);
+ } catch(e) { }
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(crash, 10);">
+Foo
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/382199-1.html b/layout/generic/crashtests/382199-1.html
new file mode 100644
index 000000000..07aab1381
--- /dev/null
+++ b/layout/generic/crashtests/382199-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+
+<html style="display: table;">
+ <body>
+ <div style="float: left; border: 1px solid blue;">float</div>
+ <div style="position: absolute; border: 1px solid red;">absolute</div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/382208-1.xhtml b/layout/generic/crashtests/382208-1.xhtml
new file mode 100644
index 000000000..5264b8845
--- /dev/null
+++ b/layout/generic/crashtests/382208-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+<body>
+
+<div><math:mfrac><math:mmultiscripts/><math:mi/></math:mfrac></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/382262-1.html b/layout/generic/crashtests/382262-1.html
new file mode 100644
index 000000000..452e68a92
--- /dev/null
+++ b/layout/generic/crashtests/382262-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="-moz-column-count: 2;">
+a<span style="float: right">e<select></select></span> r
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/382396-1.xhtml b/layout/generic/crashtests/382396-1.xhtml
new file mode 100644
index 000000000..f334bbfdf
--- /dev/null
+++ b/layout/generic/crashtests/382396-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+<body>
+
+<p style="text-indent: 0%">a<math:ms/></p>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/382745-1-binding.xml b/layout/generic/crashtests/382745-1-binding.xml
new file mode 100644
index 000000000..de8047e0c
--- /dev/null
+++ b/layout/generic/crashtests/382745-1-binding.xml
@@ -0,0 +1,3 @@
+<bindings xmlns="http://www.mozilla.org/xbl">
+<binding id="randomxbl" inheritstyle="false">
+<content><children><style xmlns="http://www.w3.org/1999/xhtml">*::before { content:"before text"; }\</style></children></content></binding></bindings> \ No newline at end of file
diff --git a/layout/generic/crashtests/382745-1.xhtml b/layout/generic/crashtests/382745-1.xhtml
new file mode 100644
index 000000000..63c0c1704
--- /dev/null
+++ b/layout/generic/crashtests/382745-1.xhtml
@@ -0,0 +1,10 @@
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<tree/>
+<tree style="-moz-binding:url(382745-1-binding.xml#randomxbl); position: fixed;">
+ <treecols>
+ <treecol style="-moz-binding:url(382745-1-binding.xml#randomxbl);"/>
+ </treecols>
+</tree>
+
+</window> \ No newline at end of file
diff --git a/layout/generic/crashtests/383089-1.html b/layout/generic/crashtests/383089-1.html
new file mode 100644
index 000000000..c6497719b
--- /dev/null
+++ b/layout/generic/crashtests/383089-1.html
@@ -0,0 +1,86 @@
+<html style="width: 1px;" class="reftest-wait"><head style="float: left; position: fixed; display: initial;" id="head">
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+
+
+
+<script style="display: none;" type="text/javascript;version=1.7">
+
+var iter;
+var interv;
+
+function olo()
+{
+ iter = foo();
+ interv = setInterval(neext, 30);
+}
+
+function neext()
+{
+ try {
+ iter.next();
+ } catch (e if e instanceof StopIteration) {
+ clearInterval(interv);
+ }
+}
+
+function foo()
+{
+ var docElem = document.documentElement;
+ var head = document.getElementById("head");
+ var br1 = document.getElementById("br1");
+ var br2 = document.getElementById("br2");
+ var br3 = document.getElementById("br3");
+ var br4 = document.getElementById("br4");
+ var br5 = document.getElementById("br5");
+ var br6 = document.getElementById("br6");
+ var br7 = document.getElementById("br7");
+ var tableRow = document.getElementById("tableRow");
+
+ br6.style.width = "1px";
+ br1.style.overflow = "visible";
+ head.style.cssFloat = "left";
+ br4.style.position = "static";
+ br7.style.color = "green";
+ br3.style.height = "auto";
+ yield;
+
+ br7.style.width = "2px";
+ yield;
+
+ br5.style.background = "yellow";
+ br7.style.color = "black";
+ yield;
+
+ br7.style.display = "table-cell";
+ head.style.position = "fixed";
+ br5.style.display = "inline";
+ br3.style.clear = "both";
+ br6.style.visibility = "visible";
+ yield;
+
+ tableRow.style.display = "list-item";
+ br3.style.width = "auto";
+ br2.style.clear = "none";
+ head.style.display = "initial"; // doesn't seem to crash when this is "block"!
+ docElem.style.width = "1px";
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+<style>
+</style>
+
+</head><body onload="setTimeout(olo, 30);">
+
+<br style="overflow: visible;" id="br1">
+<br style="clear: none;" id="br2">
+<br style="height: auto; clear: both; width: auto;" id="br3">
+<br style="position: static;" id="br4">
+<br style="background: yellow none repeat scroll 0% 0%; background-clip: initial; background-origin: initial; display: inline;" id="br5">
+<br style="width: 1px; visibility: visible;" id="br6">
+<br style="color: black; width: 2px; display: table-cell;" id="br7">
+<table border="1"><tbody><tr style="display: list-item;" id="tableRow"><td>x</td></tr></tbody></table>
+
+</body></html>
diff --git a/layout/generic/crashtests/385265-1.xhtml b/layout/generic/crashtests/385265-1.xhtml
new file mode 100644
index 000000000..7994653ff
--- /dev/null
+++ b/layout/generic/crashtests/385265-1.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+
+<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
+ <mtable>
+ <mtr>
+ <mtd><mi>x</mi></mtd>
+ </mtr>
+ </mtable>
+</math>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/385295-1.xhtml b/layout/generic/crashtests/385295-1.xhtml
new file mode 100644
index 000000000..734270d75
--- /dev/null
+++ b/layout/generic/crashtests/385295-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="letter-spacing: 1px; -moz-appearance: checkbox;">
+<body onload="document.getElementById('s').style.cssFloat = 'left';">
+<p style="float: left; direction: rtl">zzz yyy <span id="s">foo</span></p>
+</body>
+</html>
diff --git a/layout/generic/crashtests/385344-1.html b/layout/generic/crashtests/385344-1.html
new file mode 100644
index 000000000..7fbb7a73d
--- /dev/null
+++ b/layout/generic/crashtests/385344-1.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<style id="script">
+ body::first-letter {float: right; }
+</style>
+</head>
+<body>
+<dd style="white-space: -moz-pre-wrap;">m
+ <dt>m</dt>
+</dd>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/385344-2.html b/layout/generic/crashtests/385344-2.html
new file mode 100644
index 000000000..f22336d1b
--- /dev/null
+++ b/layout/generic/crashtests/385344-2.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<style>body::first-letter {float: right; }
+</style>
+</head>
+<body>
+<span style="direction: rtl; unicode-bidi: embed;">*::first
+m</span>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/385414-1.html b/layout/generic/crashtests/385414-1.html
new file mode 100644
index 000000000..1b86f7bdc
--- /dev/null
+++ b/layout/generic/crashtests/385414-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<div style="font-variant: small-caps">&shy;</div>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/385414-2.html b/layout/generic/crashtests/385414-2.html
new file mode 100644
index 000000000..d2d8c5e28
--- /dev/null
+++ b/layout/generic/crashtests/385414-2.html
@@ -0,0 +1,5 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+</head><body>
+<div style="font-variant: small-caps;">&shy; </div>
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/385426-1.html b/layout/generic/crashtests/385426-1.html
new file mode 100644
index 000000000..534e1cef2
--- /dev/null
+++ b/layout/generic/crashtests/385426-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<div><span>&#8238;</span><span>&shy;</span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/385526.html b/layout/generic/crashtests/385526.html
new file mode 100644
index 000000000..629f7cfe3
--- /dev/null
+++ b/layout/generic/crashtests/385526.html
@@ -0,0 +1,116 @@
+<html class="reftest-wait"><head>
+<style>
+*::first-line { font-size:310%; }
+</style>
+</head>
+<body style="display: inline; white-space: nowrap;">
+<span>mmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmm•mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmÈmmmmmmmmmmmmÞmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm!mmmmmmm
+
+mmmmmmmm mmmmmmmmmmmmmmmm
+m
+ mm mmmmmmm m mmmmmmmm mmmmmm mmmm mmmmmm mmmmmmm m mmmmmmmmmmm mmm m mmmm mmmmmmmmmmm mmmm mmmmmmmmmmmmm mmm mmmm m mmmm m mmmmmmmm
+ mm mmmmmmm mm mm mmmmmm mm mmmm mmmmmm m mmmmmmmmmmmm mmmm
+ mmmmmmmm mmmmmmmmm m
+ mmmm m mmmmmmmmmm m mmmmmm m mmmmmmmmm
+ mmmmmm mmmmmmmmmmmmmmmm
+ mm
+
+ mm mmmmmmm m mmmmmmmm mmmmmm mmmmmmm mm mmm mmmmm mmm mm mm mmmm mmmmmmmmm
+ mmmmmmmm mmmmmmmmmmmmmm m
+ mmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ mm
+
+ mmmmmm mmmmmmm
+m
+
+
+mmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmm mmm m mmmmmmmmmmmmmmmmmmmmmmm
+
+mmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmømmmmmmmmmmmmÍmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmm§mmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmÌmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmŸmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmÃmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmm mmmmmmmmmmm ?mmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmm m mmmmmmmmmmmmmmm mmmmmm mmmmmmmmmmmmmmmmmmmmmm mmmmm mmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmm\mmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmÍmmmmmmmmmmmmmmmmmùmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmÂmmmmmmmmmmmmÖmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmÞmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmm mmmmmmmmmmm mmmmmm!mmmmmmm
+
+mmmmmmmm mmmmmmmmmmmmmmmm
+m
+ mm mmmmmmm m mmmmmmmm mmmmmm mmmm mmmmmm mmmmmmm m mmmmmmmmmmm mmm m mmmm mmmmmmmmmmm mmmm mmmmmmmmmmmmm mmm mmmm m mmmm m mmmmmmmm
+ mm mmmmmmm mm mm mmmmmm mm mmmm mmmmmm m mmmmmmmmmmmm mmmm
+ mmmmmmmm mmmmmmmmm m
+ mmmm m mmmmmmmmmm m mmmmmm m mmmmmmmmm
+ mmmmmm mmmmmmmmmmmmmmmm
+ mm
+
+ mm mmmmmmm m mmmmmmmm mmmmmm mmmmmmm mm mmm mmmmm mmm mm mm mmmm mmmmmmmmm
+ mmmmmmmm mmmmmmmmmmmmmm m
+ mmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ mm
+
+ mmmmmm mmmmmmm
+m
+
+
+mmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmm mmm m mmmmmmmmmmmmmmmmmmmmmmm
+
+mmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmÔmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmœmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmËmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmÁmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmm m mmmmmmmmmmmmmmm mmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm»mmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmÀmmmmmmmmmm½mmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmm mmmmmmmmmmmmm^mmmmmmmmmmmmmmmmmmmmmmmmm mmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm!mmmmmmm
+
+mmmmmmmm mmmmmmmmmmmmmmmm
+m
+ mm mmmmmmm m mmmmmmmm mmmmmm mmmm mmmmmm mmmmmmm m mmmmmmmmmmm mmm m mmmm mmmmmmmmmmm mmmm mmmmmmmmmmmmm mmm mmmm m mmmm m mmmmmmmm
+ mm mmmmmmm mm mm mmmmmm mm mmmm mmmmmm m mmmmmmmmmmmm mmmm
+ mmmmmmmm mmmmmmmmm m
+ mmmm m mmmmmmmmmm m mmmmmm m mmmmmmmmm
+ mmmmmm mmmmmmmmmmmmmmmm
+ mm
+
+ mm mmmmmmm m mmmmmmmm mmmmmm mmmmmmm mm mmm mmmmm mmm mm mm mmmm mmmmmmmmm
+ mmmmmmmm mmmmmmmmmmmmmm m
+ mmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ mm
+
+ mmmmmm mmmmmmm
+m
+
+
+mmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmm mmm m mmmmmmmmmmmmmmmmmmmmmmm
+
+mmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mm
+mmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmm mmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmmmm mmmmmmmmmm mmmm mmmmmmmmmmmmm mmmmmmmm mmmmmmmmmmmm mmmmmmmmmmmmmÒ°mÞmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmm mmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmm mmmmmmmmmm mmmm mmmmmmmmmmmmm mmmmmmmmmmmmmmmmm mmmmmmmmmmmm mmmmmmm mmmmmmmmm mmmmmmmmmmmm˜mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmmmmmmmmmmmmmmmmmmmmmmmmm
+</span>
+<script>
+function finish() {
+ document.documentElement.removeAttribute('class');
+}
+function doe(){
+ document.body.removeAttribute('style');
+ setTimeout(finish, 100);
+}
+setTimeout(doe,300,0);
+</script>
+</body></html>
diff --git a/layout/generic/crashtests/385681.html b/layout/generic/crashtests/385681.html
new file mode 100644
index 000000000..43707f65c
--- /dev/null
+++ b/layout/generic/crashtests/385681.html
@@ -0,0 +1,34 @@
+<html><head>
+<title></title>
+<style>
+a {
+ font-variant: small-caps;
+}
+a:hover {
+ color: red;
+}
+</style>
+</head>
+<body>
+<a href="#">home</a><br>
+<a href="#">login</a><br>
+<a href="#">signup</a><br>
+
+<script>
+function doe(i) {
+if (!i) {
+ document.links[1].style.color='red';
+ document.links[0].offsetHeight;
+ document.links[0].style.color = 'red';
+ }
+else {
+ document.links[1].style.color='blue';
+ document.links[0].style.color = 'blue';
+ }
+setTimeout(doe, 100, !i);
+}
+setTimeout(doe, 500, true);
+</script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/385885-1.xul b/layout/generic/crashtests/385885-1.xul
new file mode 100644
index 000000000..e36a41034
--- /dev/null
+++ b/layout/generic/crashtests/385885-1.xul
@@ -0,0 +1,19 @@
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="boom()">
+
+<script>
+<![CDATA[
+
+function boom()
+{
+ var de = document.documentElement;
+
+ de.style.MozBinding = "url('data:text/xml,<bindings xmlns=%22http://www.mozilla.org/xbl%22><binding id=%22foo%22><content></content></binding></bindings>')";
+
+ document.removeChild(de);
+ document.appendChild(de);
+}
+
+]]>
+</script>
+
+</window>
diff --git a/layout/generic/crashtests/386799-1.html b/layout/generic/crashtests/386799-1.html
new file mode 100644
index 000000000..2fd2eb130
--- /dev/null
+++ b/layout/generic/crashtests/386799-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <canvas style="padding: 6px; width: 0px;"></canvas>
+ <div style="overflow: scroll;"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/386807-1.html b/layout/generic/crashtests/386807-1.html
new file mode 100644
index 000000000..af8169d8a
--- /dev/null
+++ b/layout/generic/crashtests/386807-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ s = document.getElementById("s");
+ document.body.insertBefore(document.createTextNode("\n"), s);
+}
+
+</script>
+</head>
+
+<body onload="boom()">
+<span id="s" style="text-transform: uppercase;">
+</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/386812-1.html b/layout/generic/crashtests/386812-1.html
new file mode 100644
index 000000000..52a4526fe
--- /dev/null
+++ b/layout/generic/crashtests/386812-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function boom() {
+ document.body.style.textIndent = "0%";
+}
+</script>
+
+<style>
+body { float: left; }
+.c { padding-left: 60%; padding-right: 60%; white-space: pre; }
+.f { float: left; height: 2em; }
+</style>
+</head>
+
+<body onload="boom()">
+
+<div class="c"> <div class="f">a</div>
+ <div class="f">b</div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/386827-1.html b/layout/generic/crashtests/386827-1.html
new file mode 100644
index 000000000..fa1ed96b2
--- /dev/null
+++ b/layout/generic/crashtests/386827-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function boom()
+{
+ document.getElementById("d1").appendChild(document.createElement("span"));
+ document.getElementById("d2").style.direction = "rtl";
+}
+</script>
+</head>
+<body onload="boom()">
+<div id="d1" style="-moz-column-width: 1em;">a b c d</div>
+<div id="d2"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/387058-1.html b/layout/generic/crashtests/387058-1.html
new file mode 100644
index 000000000..5a3eebcaa
--- /dev/null
+++ b/layout/generic/crashtests/387058-1.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ document.getElementById("div").style.letterSpacing = "";
+}
+</script>
+</head>
+<body onload="boom()" style="font-family: monospace; width: 20em;">
+<div id="div" style="white-space: pre-wrap; letter-spacing: 3em;">
+ x x x x x x
+ x x x x x x
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/387058-2.html b/layout/generic/crashtests/387058-2.html
new file mode 100644
index 000000000..eb5087aed
--- /dev/null
+++ b/layout/generic/crashtests/387058-2.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ document.getElementById("div").style.letterSpacing = "";
+}
+</script>
+</head>
+
+<body onload="boom()" style="font-family: monospace; width: 3em;">
+<div id="div" style="white-space: pre-wrap; letter-spacing: 1em;">
+ x
+
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/387088-1.html b/layout/generic/crashtests/387088-1.html
new file mode 100644
index 000000000..a92708188
--- /dev/null
+++ b/layout/generic/crashtests/387088-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<div style="line-height: 30760827em;">x</div>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/387209-1.html b/layout/generic/crashtests/387209-1.html
new file mode 100644
index 000000000..c6ac101d9
--- /dev/null
+++ b/layout/generic/crashtests/387209-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: table-header-group; overflow: scroll; top: 10%;"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/387213-1.html b/layout/generic/crashtests/387213-1.html
new file mode 100644
index 000000000..1b3fd1041
--- /dev/null
+++ b/layout/generic/crashtests/387213-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+ <div style="position: fixed; overflow: scroll;">
+ <div style="position: fixed;"></div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/387215-1.xhtml b/layout/generic/crashtests/387215-1.xhtml
new file mode 100644
index 000000000..ba14e6766
--- /dev/null
+++ b/layout/generic/crashtests/387215-1.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+<div style="float: left; height: 18000000px"></div>
+<p style="clear: left;"/>
+<div>
+ <div style="float: right; height: 100px;">a</div>
+ <div style="float: right; height: 18000000px;"></div>
+ <p/>
+</div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/387219-1.xhtml b/layout/generic/crashtests/387219-1.xhtml
new file mode 100644
index 000000000..043932407
--- /dev/null
+++ b/layout/generic/crashtests/387219-1.xhtml
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<head>
+<style id="s"></style>
+</head>
+<body onload="document.getElementById('s').appendChild(document.createTextNode('#sb { display: list-item; }'));">
+<xul:scrollbox id="sb"/>
+</body>
+</html>
diff --git a/layout/generic/crashtests/387233-1.html b/layout/generic/crashtests/387233-1.html
new file mode 100644
index 000000000..3ea2c0544
--- /dev/null
+++ b/layout/generic/crashtests/387233-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+
+<div style="display: -moz-inline-box;">
+ <table style="height: 200%;">
+ <tr>
+ <td></td>
+ </tr>
+ </table>
+ <div>
+ c
+ <div style="height: 200%;">
+ </div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/387233-2.html b/layout/generic/crashtests/387233-2.html
new file mode 100644
index 000000000..fdad7e872
--- /dev/null
+++ b/layout/generic/crashtests/387233-2.html
@@ -0,0 +1,18 @@
+<html>
+<body>
+
+<div style="display: -moz-inline-box; background: yellow;">
+ <table style="height: 101%; background: lightgreen;">
+ <tr>
+ <td></td>
+ </tr>
+ </table>
+ <div>
+ c
+ <div style="height: 100%; background: lightblue;">
+ </div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/387282-1.html b/layout/generic/crashtests/387282-1.html
new file mode 100644
index 000000000..a82983d09
--- /dev/null
+++ b/layout/generic/crashtests/387282-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+<div id="b" style="float: left; height: 18000000px; overflow: scroll;"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/388175-1.html b/layout/generic/crashtests/388175-1.html
new file mode 100644
index 000000000..9e9a2879f
--- /dev/null
+++ b/layout/generic/crashtests/388175-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ document.getElementById("tr").focus();
+ document.getElementById("b").focus();
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="setTimeout(boom, 30);">
+
+<table border="1">
+ <tbody dir="rtl">
+ <tr id="tr" contenteditable="true"><td>a</td><td contenteditable="false">
+ <div id="b" contenteditable="true">b</div>
+ </td></tr>
+ </tbody>
+</table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/388367-1.html b/layout/generic/crashtests/388367-1.html
new file mode 100644
index 000000000..6c368fcc4
--- /dev/null
+++ b/layout/generic/crashtests/388367-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+<pre style="direction: rtl;">&#12;</pre>
+</body>
+</html>
diff --git a/layout/generic/crashtests/388709-1.html b/layout/generic/crashtests/388709-1.html
new file mode 100644
index 000000000..29d01955b
--- /dev/null
+++ b/layout/generic/crashtests/388709-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<style>
+#d:after { content: 'a'; }
+#d:first-letter { float: right; }
+</style>
+</head>
+
+<body>
+<div id="d"></div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/389635-1.html b/layout/generic/crashtests/389635-1.html
new file mode 100644
index 000000000..dffd61a4a
--- /dev/null
+++ b/layout/generic/crashtests/389635-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+
+<div style="position: fixed; overflow-x: scroll;">
+ <div style="padding: 0%;">
+ <div style="position: fixed;"></div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/390050-1.html b/layout/generic/crashtests/390050-1.html
new file mode 100644
index 000000000..625f7cbb1
--- /dev/null
+++ b/layout/generic/crashtests/390050-1.html
@@ -0,0 +1,48 @@
+<html>
+<head>
+<style type="text/css">
+/*<![CDATA[*/
+
+body {
+ font-size: 62.5%; /* Resets 1em to 10px */
+}
+
+#content { font-size: 1.2em; }
+
+.entry p { font-size: 1.05em; }
+
+.entry {
+ -moz-column-width: 25em;
+ -moz-column-gap: 3em;
+}
+
+.entry img {
+ float: left;
+ margin: 3px 10px;
+}
+
+.alt {
+ padding: 10px;
+}
+
+#content {
+ margin-left: 20px;
+ margin-right: 260px;
+ padding: 0 17px 20px 17px;
+}
+
+
+/*]]>*/
+</style>
+</head>
+<body>
+<div id="content" class="entry">
+
+<img width="222" height="164">
+
+ <p class="alt">
+, .You can follow any responses the
+ </p>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/390050-2.html b/layout/generic/crashtests/390050-2.html
new file mode 100644
index 000000000..66dbde2dd
--- /dev/null
+++ b/layout/generic/crashtests/390050-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+<style type="text/css">
+.entry {
+ -moz-column-width: 25em;
+ -moz-column-gap: 3em;
+}
+</style>
+
+</script>
+
+</head>
+<body><div class="entry">
+<p>Edit: Wow, quick response there! Thanks for the pointers guys, looks like the dbginfo
+packages are there, just take a bit to get to. I had installed the dbg packages (or at
+least some of them, the ones that I saw with apt-cache search dbg that were relevant), but
+there wasn&#8217;t coverage for some packages &#8212; in particular the x server. I&#8217;m
+going to try Travis&#8217;s suggestion, thanks!</p>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/390050-3.html b/layout/generic/crashtests/390050-3.html
new file mode 100644
index 000000000..a5cb5da4d
--- /dev/null
+++ b/layout/generic/crashtests/390050-3.html
@@ -0,0 +1,4 @@
+<div class="entry" style="margin-right: 260px; padding: 0 17px; -moz-column-width: 25em; -moz-column-gap: 3em">
+<img width="222" style="float: left; margin: 3px 10px;">
+<p style="padding: 10px; font-size: 1.05em;">ABCDEF GHI JKLMNOPQR</p>
+</div>
diff --git a/layout/generic/crashtests/390762-1.html b/layout/generic/crashtests/390762-1.html
new file mode 100644
index 000000000..4ef39654e
--- /dev/null
+++ b/layout/generic/crashtests/390762-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<script>
+function boom()
+{
+ var s = document.getElementById("s");
+ var t = document.getElementById("t");
+
+ s.parentNode.removeChild(s);
+ t.style.display = "none";
+}
+</script>
+
+<style id="s">
+.float { float: right; height: 1em; }
+</style>
+
+<style>
+#t { border: 1px solid green; }
+</style>
+
+</head>
+
+<body onload="boom();">
+<div style="-moz-column-count: 2;"><div id="t" class="float"></div></div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/391053-1.xhtml b/layout/generic/crashtests/391053-1.xhtml
new file mode 100644
index 000000000..7a971bcd1
--- /dev/null
+++ b/layout/generic/crashtests/391053-1.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body>
+
+<div>
+ <select>
+ <option style="margin: 100%;">A</option>
+ <optgroup label="outgroup" style="padding: 10%;">
+ <option style="padding: 10%;">M</option>
+ </optgroup>
+ </select>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/391894-1.html b/layout/generic/crashtests/391894-1.html
new file mode 100644
index 000000000..2753e8511
--- /dev/null
+++ b/layout/generic/crashtests/391894-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<style>
+
+#q:first-line { }
+
+</style>
+
+</head>
+<body style="-moz-column-width: 0em">
+
+<div id="q">a b c<span style="float: right;"></span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/392698-1.html b/layout/generic/crashtests/392698-1.html
new file mode 100644
index 000000000..9a2d4f237
--- /dev/null
+++ b/layout/generic/crashtests/392698-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script type="application/javascript">
+window.onload = function()
+{
+ document.getElementById("i")
+ .setAttribute("width", 100);
+};
+ </script>
+</head>
+<body>
+<iframe src="data:text/html,&lt;div style='position:fixed'&gt;foo&lt;/div&gt;"
+ id="i" width="75" height="150"></iframe>
+</body>
+</html>
diff --git a/layout/generic/crashtests/393758-1.xhtml b/layout/generic/crashtests/393758-1.xhtml
new file mode 100644
index 000000000..4c90a55f2
--- /dev/null
+++ b/layout/generic/crashtests/393758-1.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>
+span { letter-spacing: 1em; unicode-bidi: bidi-override; }
+</style>
+</head>
+<body>
+<table><tbody><tr><td><span>×”G</span></td></tr></tbody></table>
+</body>
+</html>
diff --git a/layout/generic/crashtests/393906-1.html b/layout/generic/crashtests/393906-1.html
new file mode 100644
index 000000000..22fcfa387
--- /dev/null
+++ b/layout/generic/crashtests/393906-1.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ document.getElementById("div").setAttribute("dir", "rtl");
+}
+</script>
+<body onload="boom()">
+<div id="div" style="width: 0;"> Foo a bar baz</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/393923-1.html b/layout/generic/crashtests/393923-1.html
new file mode 100644
index 000000000..44c886d7d
--- /dev/null
+++ b/layout/generic/crashtests/393923-1.html
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>
+ div::first-letter { color: magenta; }
+</style>
+</head>
+
+<body style="direction: rtl;">
+
+<div>
+ AB</div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/393956-1.html b/layout/generic/crashtests/393956-1.html
new file mode 100644
index 000000000..77f00ae56
--- /dev/null
+++ b/layout/generic/crashtests/393956-1.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+<style>
+body {
+ -moz-column-count: 2;
+}
+#x {
+ height: 20px;
+}
+#y {
+ height: 80px;
+}
+</style>
+
+<script>
+function boom()
+{
+ document.getElementById("x").style.content = "'0'";
+}
+</script>
+</head>
+
+<body onload="boom();"><div id="x"><div id="y"></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/393956-2.html b/layout/generic/crashtests/393956-2.html
new file mode 100644
index 000000000..3260178e8
--- /dev/null
+++ b/layout/generic/crashtests/393956-2.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+<style>
+body {
+ height: 40px;
+ -moz-column-count: 2;
+}
+#x {
+ height: 20px;
+}
+#y {
+ height: 80px;
+}
+</style>
+
+<script>
+function boom()
+{
+ document.getElementById("x").style.content = "'0'";
+}
+</script>
+</head>
+
+<body onload="boom();"><div id="x"><div id="y"></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/393956-3.html b/layout/generic/crashtests/393956-3.html
new file mode 100644
index 000000000..afa5ab683
--- /dev/null
+++ b/layout/generic/crashtests/393956-3.html
@@ -0,0 +1,11 @@
+<html>
+<head><style>
+body { -moz-column-count: 1; }
+#x { height: 1px; }
+#y { height: 2px; }
+
+</style></head>
+
+<body><div id="x"><div id="y"></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/393956-4.html b/layout/generic/crashtests/393956-4.html
new file mode 100644
index 000000000..fe16ea146
--- /dev/null
+++ b/layout/generic/crashtests/393956-4.html
@@ -0,0 +1,11 @@
+<html>
+<head><style>
+body { -moz-column-count: 2; }
+#x { height: 1px; }
+#y { height: 2px; }
+
+</style></head>
+
+<body><div id="x"><div id="y"></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/394237-1.html b/layout/generic/crashtests/394237-1.html
new file mode 100644
index 000000000..53bbc16f6
--- /dev/null
+++ b/layout/generic/crashtests/394237-1.html
@@ -0,0 +1,38 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>
+
+body {
+ height: 50px;
+ -moz-column-count: 2;
+}
+.container {
+ height: 10px;
+}
+.overflow {
+ height: 100px;
+}
+.bb {
+ border-bottom: solid 4px magenta;
+}
+
+</style>
+
+<script>
+
+function boom()
+{
+ document.getElementById("x").setAttribute("class", "");
+ document.getElementById("y").setAttribute("class", "bb");
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+ <div class="container"><div class="overflow" id="x"></div></div>
+ <div id="y"></div>
+ <div class="container"><div class="overflow"></div></div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/394818-1.html b/layout/generic/crashtests/394818-1.html
new file mode 100644
index 000000000..a6a6fdba8
--- /dev/null
+++ b/layout/generic/crashtests/394818-1.html
@@ -0,0 +1,13 @@
+<html class="reftest-wait"><head>
+</head><body>
+<iframe></iframe><iframe></iframe>
+<script>
+window.frames[0].focus();
+function doe() {
+document.body.setAttribute('onbeforecopy','document.removeChild(document.documentElement)');
+document.body.setAttribute('style','display:none');
+document.documentElement.removeAttribute("class");
+}
+setTimeout(doe, 100);
+</script>
+</body></html>
diff --git a/layout/generic/crashtests/394818-2.html b/layout/generic/crashtests/394818-2.html
new file mode 100644
index 000000000..6aa00e275
--- /dev/null
+++ b/layout/generic/crashtests/394818-2.html
@@ -0,0 +1,16 @@
+<html class="reftest-wait"><head><script>
+ function load() {
+ window.frames[0].focus();
+ setTimeout("load2();", 100);
+ }
+ function load2() {
+ document.body.setAttribute('style','display: inline');
+ document.documentElement.removeAttribute("class");
+ }
+ function beforeCopy() {
+ document.removeChild(document.documentElement);
+ }
+</script></head>
+<body onload="load();"
+ onbeforecopy="beforeCopy();"><iframe></iframe></body>
+</html>
diff --git a/layout/generic/crashtests/394820-1.html b/layout/generic/crashtests/394820-1.html
new file mode 100644
index 000000000..0c012877c
--- /dev/null
+++ b/layout/generic/crashtests/394820-1.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var d = document.getElementById("d");
+ d.style.direction = "rtl";
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id=d style="white-space: pre;">
+e
+
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/395316-1.html b/layout/generic/crashtests/395316-1.html
new file mode 100644
index 000000000..60c291075
--- /dev/null
+++ b/layout/generic/crashtests/395316-1.html
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ document.getElementById("float").style.cssFloat = "";
+}
+</script>
+</head>
+<body onload="boom();">
+<div style="-moz-column-count: 2; width: 3ch;"><span><span id="float" style="float: right;">za za za za za za za za</span></span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/395450-1.xhtml b/layout/generic/crashtests/395450-1.xhtml
new file mode 100644
index 000000000..79510267b
--- /dev/null
+++ b/layout/generic/crashtests/395450-1.xhtml
@@ -0,0 +1,28 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+
+<head>
+<style>
+
+/*
+ This testcase uses [class~="foo"] instead of .foo to work around bug 276267
+ (see bug 379178 comment 78)
+*/
+
+[class~="abs"] { position: absolute; }
+[class~="marg"] { margin: 1em; }
+[class="noheight"] { height: 0; }
+
+</style>
+</head>
+
+<body>
+
+<math:mrow class="noheight">
+ <span class="abs">
+ <math:mroot class="abs marg" />
+ <span class="abs" />
+ </span>
+</math:mrow>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/397007-1.html b/layout/generic/crashtests/397007-1.html
new file mode 100644
index 000000000..628733ba6
--- /dev/null
+++ b/layout/generic/crashtests/397007-1.html
@@ -0,0 +1,37 @@
+<html>
+<head>
+<script type="text/javascript">
+function boom()
+{
+ var img = document.createElement("img");
+ document.getElementById("g").appendChild(img);
+ img.width = 1;
+}
+
+</script>
+
+<style type="text/css">
+
+.margin {
+ margin: 1em 0;
+}
+
+.dd:before {
+ white-space: pre;
+ line-height: 0;
+ content: "b";
+}
+
+</style>
+
+</head>
+
+<body onload="boom();">
+<div style="-moz-column-count: 2;">
+ X
+ <div class="margin">y</div>
+ <span><span id="g"></span>&shy;<div class="margin"></div><span class="dd"><div></div></span></span>
+</div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/397187-1.html b/layout/generic/crashtests/397187-1.html
new file mode 100644
index 000000000..7227a4823
--- /dev/null
+++ b/layout/generic/crashtests/397187-1.html
@@ -0,0 +1,32 @@
+<html class="reftest-wait">
+<head>
+<style type="text/css">
+
+.a:first-letter { float: right; }
+
+</style>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.style.overflow = "auto";
+ document.body.className = "a";
+
+ setTimeout(boom2, 30);
+}
+
+function boom2()
+{
+ var span = document.createElement("span");
+ document.body.appendChild(span);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="boom();">&#65207;
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/397844-1.xhtml b/layout/generic/crashtests/397844-1.xhtml
new file mode 100644
index 000000000..3ad9b8464
--- /dev/null
+++ b/layout/generic/crashtests/397844-1.xhtml
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+
+<style type="text/css">
+.pad { padding: 3ch; }
+.fl:first-letter { }
+</style>
+
+
+<style id="s" type="text/css"></style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("td").nextSibling.splitText(11);
+ document.getElementById("td").style.padding = "11px";
+
+ setTimeout(boom2, 10);
+}
+
+function boom2()
+{
+ document.body.style.counterReset = "chicken";
+
+ setTimeout(boom3, 10);
+}
+
+function boom3()
+{
+ document.getElementById("s").textContent = "#xxx { }";
+
+ setTimeout(boom4, 10);
+}
+
+function boom4()
+{
+ document.getElementById("td").style.padding = "";
+ setTimeout(boom5, 10);
+}
+
+function boom5()
+{
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body style="width: 10em;" dir="rtl" onload="boom();">
+
+<div class="pad fl"><span><td class="pad fl" id="td"><span>abcd</span></td>ghi jklmnop 2 qrs tuvwxy</span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/397844-2.xhtml b/layout/generic/crashtests/397844-2.xhtml
new file mode 100644
index 000000000..d97600812
--- /dev/null
+++ b/layout/generic/crashtests/397844-2.xhtml
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+
+<style type="text/css">
+.pad { padding: 3ch; }
+.fl:first-letter { }
+td { border: 1px solid green }
+</style>
+
+<style id="s" type="text/css"></style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("td").nextSibling.splitText(6);
+ document.getElementById("td").style.padding = "2px";
+
+ setTimeout(boom2, 10);
+}
+
+function boom2()
+{
+ document.body.style.counterReset = "chicken";
+
+ setTimeout(boom3, 10);
+}
+
+function boom3()
+{
+ document.getElementById("s").textContent = "#xxx { }";
+
+ setTimeout(boom4, 10);
+}
+
+function boom4()
+{
+ document.getElementById("td").style.padding = "";
+ setTimeout(boom5, 10);
+}
+
+function boom5()
+{
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body style="font-family: monospace; width: 14ch; border: 1px solid orange;" dir="rtl" onload="boom();">
+
+<div class="pad fl"><span><td class="pad fl" id="td"><span>abcd</span></td>ghi jk 2</span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/397852-1.xhtml b/layout/generic/crashtests/397852-1.xhtml
new file mode 100644
index 000000000..cafb82d9c
--- /dev/null
+++ b/layout/generic/crashtests/397852-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.getElementById('o').style.overflow = 'auto';">
+
+<td></td><div id="o"><table style="margin: 100%;"></table></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/398181-1.html b/layout/generic/crashtests/398181-1.html
new file mode 100644
index 000000000..bc6445f5a
--- /dev/null
+++ b/layout/generic/crashtests/398181-1.html
@@ -0,0 +1,10 @@
+<html style="min-width: -moz-max-content;">
+ <head>
+ </head>
+ <body>
+ <div>
+ <span style="margin: 0 100%; display: inline-block;"></span>
+ t
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/398181-2.html b/layout/generic/crashtests/398181-2.html
new file mode 100644
index 000000000..57402aeac
--- /dev/null
+++ b/layout/generic/crashtests/398181-2.html
@@ -0,0 +1,11 @@
+<html style="min-width: -moz-max-content;">
+ <head>
+ </head>
+ <body>
+ <div>
+ <span style="margin: 0 100%; display: inline-block;"></span>
+ t
+ <div style="float: left">foo</div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/398322-1.html b/layout/generic/crashtests/398322-1.html
new file mode 100644
index 000000000..68f60ba3f
--- /dev/null
+++ b/layout/generic/crashtests/398322-1.html
@@ -0,0 +1,17 @@
+<html class="reftest-print">
+<head>
+<title>Testcase bug - Crash [@ nsFrameList::InsertFrame] on print preview with positioned elements and page-break-before</title>
+</head>
+
+<body>
+<div style="position: relative; page-break-before: always;">
+ <div style="display: list-item;"></div>
+ <div style="position: absolute;">
+ <div style=" position: absolute;">
+ <div style="page-break-before: always; height: 100px;"></div>
+ </div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/398322-2.html b/layout/generic/crashtests/398322-2.html
new file mode 100644
index 000000000..74f82dae2
--- /dev/null
+++ b/layout/generic/crashtests/398322-2.html
@@ -0,0 +1,12 @@
+<html class="reftest-print">
+<head>
+<style>
+q::after { content:"anonymous text"; text-transform: uppercase;}
+</style>
+</head>
+<body>
+<div style="position: absolute; width: 50px; -moz-column-count: 2;">
+ <div style="position: absolute;"> <q> </q> </div>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/398332-1.html b/layout/generic/crashtests/398332-1.html
new file mode 100644
index 000000000..d24d85c04
--- /dev/null
+++ b/layout/generic/crashtests/398332-1.html
@@ -0,0 +1,19 @@
+<html><head>
+<title>Testcase bug - Crash [@ nsHTMLReflowState::GetNearestContainingBlock] with display: -moz-box, generated content, positioning and fieldset</title>
+<style>
+small::before { content: "m m";}
+strike::before { content: "m m";}
+</style>
+</head>
+<body>
+<div style="display: -moz-box;">
+ <strike style="overflow: -moz-hidden-unscrollable; position: absolute;"></strike>
+ <fieldset style="position: relative;">
+ <strike style="overflow: -moz-hidden-unscrollable; position: absolute;"><small style="position: relative;">
+ <span style="position: absolute;"></span>
+ </small>
+ </strike>
+</fieldset>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/398332-2.html b/layout/generic/crashtests/398332-2.html
new file mode 100644
index 000000000..79b6f3b60
--- /dev/null
+++ b/layout/generic/crashtests/398332-2.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+<script>
+function removestyles(i){
+document.getElementsByTagName('*')[5].removeAttribute('style');
+document.body.offsetHeight;
+document.getElementsByTagName('*')[6].removeAttribute('style');
+document.body.offsetHeight;
+document.getElementsByTagName('*')[7].removeAttribute('style');
+}
+
+setTimeout(removestyles,300);
+</script>
+
+</head>
+<body>
+<div style="position: relative; width: 500px;">
+ <span style="position: absolute; -moz-column-count: 2;">
+ <span style=" position: absolute;">
+ <span style=" display: inline-block; ">m</span>
+ <input>
+ <input style="position: fixed;">m
+ </span>
+ </span>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/398332-3.html b/layout/generic/crashtests/398332-3.html
new file mode 100644
index 000000000..991aa6d3d
--- /dev/null
+++ b/layout/generic/crashtests/398332-3.html
@@ -0,0 +1,4 @@
+<marquee style="position: relative; right: 20%;">"Ë”Öqü®Û;<span style="position: relative; word-spacing: -100px;"><span style="position: absolute;">
+<style>span::before { content:"before textbefore textbefore textbefore textbefore textbefore text"; }</style>
+
+
diff --git a/layout/generic/crashtests/399407-1.xhtml b/layout/generic/crashtests/399407-1.xhtml
new file mode 100644
index 000000000..37e334f4e
--- /dev/null
+++ b/layout/generic/crashtests/399407-1.xhtml
@@ -0,0 +1,25 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<style id="s">
+ .container {
+ height: 20px;
+ }
+ .overflow {
+ height: 30px;
+ }
+ body {
+ width: 300px;
+ -moz-column-width: 100px;
+ }
+ </style>
+
+<body>
+ <div class="container">
+ <div class="overflow"></div>
+ </div>
+ <div class="container">
+ <div class="overflow"></div>
+ </div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/399412-1.html b/layout/generic/crashtests/399412-1.html
new file mode 100644
index 000000000..bda3b38f5
--- /dev/null
+++ b/layout/generic/crashtests/399412-1.html
@@ -0,0 +1,32 @@
+<html>
+<head>
+
+<style id="s">
+.container {
+ height: 30px;
+}
+</style>
+
+<style>
+.overflow {
+ height: 150px;
+ border: 1px silver solid;
+}
+body {
+ height: 60px;
+ width: 300px;
+ -moz-column-width: 50px;
+ -moz-column-gap: 1px;
+ -moz-column-fill: auto;
+}
+</style>
+
+</head>
+
+<body onload="s=document.getElementById('s'); s.parentNode.removeChild(s);">
+
+<div class="container"><div class="overflow"></div></div>
+<div class="container"><div class="overflow"></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/399843-1.html b/layout/generic/crashtests/399843-1.html
new file mode 100644
index 000000000..01bdffd71
--- /dev/null
+++ b/layout/generic/crashtests/399843-1.html
@@ -0,0 +1,64 @@
+<html class="reftest-wait">
+<head>
+<style>
+
+ #colset {
+ width: 300pt;
+ height: 2in;
+ -moz-column-count: 3;
+ }
+
+ .container {
+ height: 1in;
+ }
+
+ .b6 {
+ height: 10in;
+ }
+
+ .c3 {
+ position: absolute;
+ }
+
+ .c4 {
+ height: 3in;
+ }
+
+</style>
+
+<script>
+
+function boom()
+{
+ document.getElementById("x").setAttribute("class", "c4");
+ setTimeout(boom2, 10);
+}
+
+function boom2()
+{
+ document.getElementById("x").setAttribute("class", "");
+ setTimeout(boom3, 10);
+}
+
+function boom3()
+{
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<div id="colset">
+ <div id="x"></div>
+ <div class="container">
+ <div class="b6"></div>
+ <div>
+ <div class="c3"></div>
+ </div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/400078-1.html b/layout/generic/crashtests/400078-1.html
new file mode 100644
index 000000000..3c2f0552e
--- /dev/null
+++ b/layout/generic/crashtests/400078-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+html, body, table, tbody, tr, td, #colset, #e, #s { height: 100%; }
+
+#colset { -moz-column-count: 2; -moz-column-width: 200px; border: 2px solid black; text-indent: 500px; }
+
+#s { display: inline-block; width: 30px; border: 1px solid red; }
+
+</style>
+</head>
+
+<body>
+
+<table><tbody><tr><td><div id="colset">XXX<span id="s"></span><div id="e"></div></div></td></tr></tbody></table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/400190.html b/layout/generic/crashtests/400190.html
new file mode 100644
index 000000000..46d384108
--- /dev/null
+++ b/layout/generic/crashtests/400190.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+body {
+ position: fixed;
+ font-family: monospace;
+ -moz-column-width: 10px;
+ border: 2px solid #aaa;
+}
+
+#padded {
+ padding-top: 40px;
+ padding-bottom: 40px;
+}
+
+#x {
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+</style>
+</head>
+
+<body>
+
+<div id="padded"></div>
+
+<div id="x">
+
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/400223-1.html b/layout/generic/crashtests/400223-1.html
new file mode 100644
index 000000000..eb8bf2140
--- /dev/null
+++ b/layout/generic/crashtests/400223-1.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<style type="text/css">
+
+ #colset {
+ height: 2in;
+ -moz-column-count: 3;
+ }
+ #container {
+ position: relative;
+ }
+ #overflow {
+ position: absolute;
+ height: 5in;
+ }
+
+</style>
+</head>
+
+<body onload="document.getElementById('container').style.counterIncrement = 'foo';">
+<div id="colset"><div id="container"><div id="overflow"></div></div></div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/400232-1.html b/layout/generic/crashtests/400232-1.html
new file mode 100644
index 000000000..8f728ed08
--- /dev/null
+++ b/layout/generic/crashtests/400232-1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+</head>
+
+<body>
+
+<div style="-moz-column-count: 2; overflow: auto; height: 10px;"><br><div style="float: right; height: 15px;"></div></div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/400244-1.html b/layout/generic/crashtests/400244-1.html
new file mode 100644
index 000000000..3b1b7bfa0
--- /dev/null
+++ b/layout/generic/crashtests/400244-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+ #colset {
+ -moz-column-count: 2;
+ -moz-column-gap: 0;
+ border: 2px solid black;
+ height: 12em;
+ }
+
+ #b {
+ margin: 10em 0pt;
+ }
+
+ #i {
+ margin: 12em 0pt;
+ float: left;
+ -moz-appearance: radio-small;
+ }
+
+</style>
+</head>
+
+<body onload="document.getElementById('i').style.padding = '1em 0';">
+
+<div id="colset"><br id="b"><div id="i"></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/400768-1.xhtml b/layout/generic/crashtests/400768-1.xhtml
new file mode 100644
index 000000000..1b390a291
--- /dev/null
+++ b/layout/generic/crashtests/400768-1.xhtml
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="document.getElementById('pp').contentHeight;">
+
+<foreignObject>
+ <xul:prefpane id="pp"/>
+</foreignObject>
+
+</svg>
diff --git a/layout/generic/crashtests/400768-2.xhtml b/layout/generic/crashtests/400768-2.xhtml
new file mode 100644
index 000000000..e7e5a5169
--- /dev/null
+++ b/layout/generic/crashtests/400768-2.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<body onload="document.getElementById('pp').contentHeight;">
+<svg:svg><svg:foreignObject xmlns="http://www.w3.org/2000/svg"><xul:prefpane id="pp"/></svg:foreignObject></svg:svg>
+</body>
+</html>
diff --git a/layout/generic/crashtests/401042-1.xhtml b/layout/generic/crashtests/401042-1.xhtml
new file mode 100644
index 000000000..15395eec9
--- /dev/null
+++ b/layout/generic/crashtests/401042-1.xhtml
@@ -0,0 +1,17 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<bindings xmlns="http://www.mozilla.org/xbl">
+<binding id="a">
+<content><span xmlns="http://www.w3.org/1999/xhtml">
+<children xmlns="http://www.mozilla.org/xbl"/>
+</span></content>
+</binding>
+</bindings>
+
+<style>pre::first-letter {float: left; }
+</style>
+</head>
+<body>
+<pre style="-moz-binding: url(#a);">//</pre>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/402380-1.html b/layout/generic/crashtests/402380-1.html
new file mode 100644
index 000000000..3f9e21ec0
--- /dev/null
+++ b/layout/generic/crashtests/402380-1.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style type="text/css">
+
+div::first-letter { color: magenta; }
+span:before { content: "\"" "This "; }
+
+</style>
+</head>
+<body style="width: 1em;" onload="document.getElementById('div').style.direction = 'rtl';">
+<div id="div"><span>is text</span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/402380-2.html b/layout/generic/crashtests/402380-2.html
new file mode 100644
index 000000000..f3b021740
--- /dev/null
+++ b/layout/generic/crashtests/402380-2.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<style type="text/css">
+
+div::first-letter { color: green; }
+span:before { content: open-quote "This "; }
+span:after { content: close-quote; }
+
+</style>
+</head>
+
+<body style="font-family: monospace; width: 7ch; border: 1px solid orange;"
+ onload="document.getElementById('div').style.direction = 'rtl';">
+
+<div id="div"><span>is text</span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/402872-1.html b/layout/generic/crashtests/402872-1.html
new file mode 100644
index 000000000..f23a47060
--- /dev/null
+++ b/layout/generic/crashtests/402872-1.html
@@ -0,0 +1,3 @@
+<table>
+<thead style="float: left; margin-top: 100px; margin-bottom: 9999999999px;">
+
diff --git a/layout/generic/crashtests/402872-2.html b/layout/generic/crashtests/402872-2.html
new file mode 100644
index 000000000..e2a6026ff
--- /dev/null
+++ b/layout/generic/crashtests/402872-2.html
@@ -0,0 +1,2 @@
+<fieldset style="float: left;">
+<legend style="min-height: 999999999px;">
diff --git a/layout/generic/crashtests/403004.html b/layout/generic/crashtests/403004.html
new file mode 100644
index 000000000..a1d04dff3
--- /dev/null
+++ b/layout/generic/crashtests/403004.html
@@ -0,0 +1,3 @@
+<meta http-equiv="Content-Type" content="text/html; charset=windows-1256" />
+<div style="text-transform: uppercase;">
+<a href="httpdisabled://news.maktoob.com"> ÃÎÈÇÑ ãßÊæÈ</a></div>
diff --git a/layout/generic/crashtests/403143-1.html b/layout/generic/crashtests/403143-1.html
new file mode 100644
index 000000000..f51ce723d
--- /dev/null
+++ b/layout/generic/crashtests/403143-1.html
@@ -0,0 +1,19 @@
+<html>
+
+<head>
+<script>
+function boom()
+{
+ document.getElementById("i1").style.counterIncrement = "chicken";
+ document.getElementById("i3").style.counterIncrement = "chicken";
+}
+</script>
+</head>
+
+<body onload="boom();" style="overflow: auto; display: -moz-grid-line;">
+<img id="i1" style="width: 30px; float: right;">
+<img id="i2" style="margin: 0 100%; padding: 0 60px;">
+<img id="i3" style="margin: 100% 0; float: right;">
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/403576-1.html b/layout/generic/crashtests/403576-1.html
new file mode 100644
index 000000000..4b376af6a
--- /dev/null
+++ b/layout/generic/crashtests/403576-1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="width: 11px;">
+<fieldset style="display: table-column-group;"></fieldset>
+</body>
+</html>
diff --git a/layout/generic/crashtests/404140-1.html b/layout/generic/crashtests/404140-1.html
new file mode 100644
index 000000000..fa495caea
--- /dev/null
+++ b/layout/generic/crashtests/404140-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+
+</head><body onload="document.body.style.height = '2px';" style="float: right; height: 2px; -moz-column-width: 0pt;">
+<div style="overflow: -moz-hidden-unscrollable;">a b</div>
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/404146-1.html b/layout/generic/crashtests/404146-1.html
new file mode 100644
index 000000000..3ab328069
--- /dev/null
+++ b/layout/generic/crashtests/404146-1.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+<style>
+
+.colset {
+ -moz-column-count: 2;
+ -moz-column-width: 50px;
+ float: left;
+ border: 2px solid black;
+ height: 10em;
+ font: 12px monospace;
+}
+
+#t {
+ display: inline-block;
+ background: lightgreen;
+ height: 7em;
+ width: 29px;
+}
+
+</style>
+</head>
+
+<body onload="document.getElementById('t').style.width = '30px';">
+
+<div class="colset">The quick <span><div id="t"></div>brown</span></div>
+
+</body></html>
diff --git a/layout/generic/crashtests/404204-1.html b/layout/generic/crashtests/404204-1.html
new file mode 100644
index 000000000..2437f4c52
--- /dev/null
+++ b/layout/generic/crashtests/404204-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+
+<body style="font-variant: small-caps;">&#x202B; </body>
+
+</html>
diff --git a/layout/generic/crashtests/404215-1.html b/layout/generic/crashtests/404215-1.html
new file mode 100644
index 000000000..abe7770bc
--- /dev/null
+++ b/layout/generic/crashtests/404215-1.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+<style>
+
+#colset {
+ -moz-column-count: 3;
+}
+.a {
+ height: 3in;
+}
+.b {
+ height: 24pt;
+}
+.c {
+ height: 336pt;
+}
+.d {
+ position: absolute;
+}
+
+</style>
+</head>
+
+<body onload="document.getElementById('a').className = '';">
+
+<div id="colset"><div><div class="a" id="a"></div><div class="b"><div class="c"></div><div class="d"></div></div></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/404215-2.html b/layout/generic/crashtests/404215-2.html
new file mode 100644
index 000000000..7d1f77ed0
--- /dev/null
+++ b/layout/generic/crashtests/404215-2.html
@@ -0,0 +1,37 @@
+<html>
+<head><style>
+/* Sets of heights that trigger crash:
+ 100px/50px/51+px
+ 100px/30px/74+px
+ Get only an assert unless you set ".d { position: absolute; }".
+
+ Trigger hang (separate issue, absolute not needed):
+ 10px/10px/9999px
+ 10px/10px/999999px --> "bad height" notreached
+*/
+#colset { width: 200px;
+ -moz-column-count: 3; }
+#a { height: 100px; }
+#b { height: 50px; }
+#c { height: 51px; }
+#d { position: absolute; }
+</style>
+<script>
+ function boom() {
+ document.getElementById('a').style.height = 'auto';
+ }
+</script>
+</head>
+<!-- Removing whitespace in body for simpler frame trees -->
+<body onload="setTimeout('boom()', 1000)"
+ ><div id="colset"
+ ><div
+ ><div id="a"></div
+ ><div id="b"
+ ><div id="c"></div
+ ><div id="d"></div
+ ></div
+ ></div
+ ></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/404215-3.html b/layout/generic/crashtests/404215-3.html
new file mode 100644
index 000000000..0d6b13cc1
--- /dev/null
+++ b/layout/generic/crashtests/404215-3.html
@@ -0,0 +1,32 @@
+<html>
+<head><style>
+/* Sets of heights that trigger crash:
+ 100px/50px/51+px
+ 100px/30px/74+px
+ Get only an assert unless you set ".d { position: absolute; }".
+
+ Trigger hang (separate issue, absolute not needed):
+ 10px/10px/9999px
+ 10px/10px/999999px --> "bad height" notreached
+*/
+#colset { width: 200px;
+ -moz-column-count: 3; }
+#a { height: 10px; }
+#b { height: 10px; }
+#c { height: 999999px; }
+
+</style>
+</head>
+<!-- Removing whitespace in body for simpler frame trees -->
+<body
+ ><div id="colset"
+ ><div
+ ><div id="a"></div
+ ><div id="b"
+ ><div id="c"></div
+ ><div id="d"></div
+ ></div
+ ></div
+ ></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/404219-1.html b/layout/generic/crashtests/404219-1.html
new file mode 100644
index 000000000..bac914826
--- /dev/null
+++ b/layout/generic/crashtests/404219-1.html
@@ -0,0 +1,30 @@
+<html>
+<head>
+<style>
+ body {
+ /* Standize line-height, because the default varies by platform */
+ line-height: 50px;
+ }
+ div#a {
+ width: 3em;
+ -moz-column-count: 2;
+ -moz-column-gap: 0px;
+ background: lightgreen;
+ }
+ div#b {
+ float: left;
+ background: lightblue;
+ }
+ div#c {
+ height: 100px;
+ background: orange;
+ }
+</style>
+</head>
+<body>
+ <div id="a">
+ <div id="b"><br/><br/>b</div>
+ <div id="c">c c c</div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/404219-2.html b/layout/generic/crashtests/404219-2.html
new file mode 100644
index 000000000..149877c82
--- /dev/null
+++ b/layout/generic/crashtests/404219-2.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+<style>
+ body {
+ /* Standize line-height, because the default varies by platform */
+ line-height: 50px;
+ }
+ div#a {
+ width: 3em;
+ -moz-column-count: 2;
+ -moz-column-gap: 0px;
+ background: lightgreen;
+ }
+ div#b {
+ height: 100px;
+ float: left;
+ background: lightblue;
+ }
+ div#c {
+ height: 100px;
+ background: orange;
+ }
+</style>
+</head>
+<body>
+ <div id="a">
+ <div id="b"><br/><br/>b</div>
+ <div id="c">c c c</div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/406137.html b/layout/generic/crashtests/406137.html
new file mode 100644
index 000000000..75337de72
--- /dev/null
+++ b/layout/generic/crashtests/406137.html
@@ -0,0 +1,16 @@
+<html>
+ <head>
+ <title>redhat.com | Home</title>
+ <style>
+ #navWrap:after { content: "."; display: block; clear: both; }
+ </style>
+ </head>
+ <body>
+ <div id="navWrap">
+ <div style="float: left;">
+ foo
+ </div>
+ </div>
+ <img src="../../../testing/crashtest/images/tree.gif" style="float: right;">
+ </body>
+</html>
diff --git a/layout/generic/crashtests/406380.html b/layout/generic/crashtests/406380.html
new file mode 100644
index 000000000..1ea1820a3
--- /dev/null
+++ b/layout/generic/crashtests/406380.html
@@ -0,0 +1,12 @@
+<html style="direction: rtl;">
+<body onload="document.getElementById('i').style.fontSize = '10em';">
+
+<div style="-moz-column-width: 60px;">
+a
+<div style="overflow: -moz-hidden-unscrollable; white-space: pre;" id="i">
+b
+</div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/406902-1.html b/layout/generic/crashtests/406902-1.html
new file mode 100644
index 000000000..454178c18
--- /dev/null
+++ b/layout/generic/crashtests/406902-1.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<style type="text/css">
+
+ .container {
+ position: relative;
+ }
+ #colset {
+ -moz-column-count: 3;
+ -moz-column-gap: 0;
+ border: silver 2pt;
+ border-style: none solid;
+ }
+ .b1 {
+ position: absolute;
+ height: 336pt;
+ margin-left: 20pt;
+ }
+ .b2 {
+ position: absolute;
+ height: 192pt;
+ width: 25pt;
+ margin-left: 50pt;
+ }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ newDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ newDiv.setAttribute("class", "b1");
+ document.getElementById("f").appendChild(newDiv);
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<div id="colset"><div class="container"><div class="b2"></div><div id="f"></div></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/407009-1.xhtml b/layout/generic/crashtests/407009-1.xhtml
new file mode 100644
index 000000000..62ded1562
--- /dev/null
+++ b/layout/generic/crashtests/407009-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<body onload="document.getElementById('tree').removeAttribute('hidevscroll');">
+
+<select><xul:tree id="tree"/></select>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/408304-1.xhtml b/layout/generic/crashtests/408304-1.xhtml
new file mode 100644
index 000000000..e0e09801d
--- /dev/null
+++ b/layout/generic/crashtests/408304-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<fieldset><span><fieldset><legend><div ><div style="margin: 0pt 100%;"></div></div></legend></fieldset></span></fieldset>
+</body>
+</html>
diff --git a/layout/generic/crashtests/408602-1.html b/layout/generic/crashtests/408602-1.html
new file mode 100644
index 000000000..b2db8577b
--- /dev/null
+++ b/layout/generic/crashtests/408602-1.html
@@ -0,0 +1,12 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body style="position: fixed;">
+
+<div style="position: absolute; -moz-column-width: 20em;"><div style="white-space: pre; float: right; height: 50px;"><div style="width: 100px; height: 12em;"></div>
+</div></div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/408737-1.html b/layout/generic/crashtests/408737-1.html
new file mode 100644
index 000000000..fe8fc2c35
--- /dev/null
+++ b/layout/generic/crashtests/408737-1.html
@@ -0,0 +1,14 @@
+<html>
+<head><style>
+#colset { -moz-column-count: 1; }
+#short { height: 0px; }
+#tall { height: 10000px; }
+</style></head>
+<body>
+ <div id="colset">
+ <div id="short">
+ <div id="tall"></div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/408737-2.html b/layout/generic/crashtests/408737-2.html
new file mode 100644
index 000000000..c4b343867
--- /dev/null
+++ b/layout/generic/crashtests/408737-2.html
@@ -0,0 +1,14 @@
+<html>
+<head><style>
+#colset { -moz-column-count: 1; }
+#short { height: 10px; }
+#tall { height: 100000px; }
+</style></head>
+<body>
+ <div id="colset">
+ <div id="short">
+ <div id="tall"></div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/408749-1.xhtml b/layout/generic/crashtests/408749-1.xhtml
new file mode 100644
index 000000000..4ef6e4e89
--- /dev/null
+++ b/layout/generic/crashtests/408749-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body style="-moz-column-count: 2;"><caption><div>foo<td></td></div></caption></body></html>
diff --git a/layout/generic/crashtests/408883-1.html b/layout/generic/crashtests/408883-1.html
new file mode 100644
index 000000000..f7d19e5e5
--- /dev/null
+++ b/layout/generic/crashtests/408883-1.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+
+<style type="text/css">
+
+body {
+ -moz-column-count: 2;
+ -moz-column-width: 100px;
+ height: 200px;
+ width: 400px;
+}
+.b {
+ height: 300px;
+}
+.f {
+ float: left;
+}
+.p {
+ padding: 0pt 200px;
+}
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var newDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ var a = document.getElementById("a");
+ a.appendChild(newDiv);
+}
+
+</script>
+
+</head>
+
+<body onload="boom();"><div class="f"><span class="p"></span></div><div id="a"><div class="b f"><input></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/410198.html b/layout/generic/crashtests/410198.html
new file mode 100644
index 000000000..f423fb32b
--- /dev/null
+++ b/layout/generic/crashtests/410198.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Gecko 1.9 crash with a.p. and inline c.b.</title>
+
+<p>- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+<span style="position: relative; background-color: #ddd;">- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+<span style="position: absolute; top: 0; left: 0; background-color: #f00;">AP</span>
+</span>
+</p>
diff --git a/layout/generic/crashtests/410228-1.html b/layout/generic/crashtests/410228-1.html
new file mode 100644
index 000000000..b1bd3e0d0
--- /dev/null
+++ b/layout/generic/crashtests/410228-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+
+<body style="text-transform: uppercase; font-family: arial;">&zwnj;&szlig;</body>
+
+</html>
diff --git a/layout/generic/crashtests/410232-1.html b/layout/generic/crashtests/410232-1.html
new file mode 100644
index 000000000..26a9c2c84
--- /dev/null
+++ b/layout/generic/crashtests/410232-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+</head>
+
+<body>
+
+<div style="height: 17895694px;"></div>
+
+<div style="float: left; border: 2px solid red;"></div>
+
+<div style="float: left; border: 2px solid green;"></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/410595-1.html b/layout/generic/crashtests/410595-1.html
new file mode 100644
index 000000000..0ee6e36c9
--- /dev/null
+++ b/layout/generic/crashtests/410595-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+
+<body><div style="padding: 12em 0; position: absolute; font-size: 10000000px;"><div style="position: absolute;"></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/411213-1.html b/layout/generic/crashtests/411213-1.html
new file mode 100644
index 000000000..e4589a615
--- /dev/null
+++ b/layout/generic/crashtests/411213-1.html
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="direction: rtl;">
+<body onload="document.getElementById('i').style.fontSize = '10em';">
+
+<div style="-moz-column-width: 60px;"><div id="i" style="overflow: -moz-hidden-unscrollable; white-space: pre;">
+ b
+</div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/411213-2.xml b/layout/generic/crashtests/411213-2.xml
new file mode 100644
index 000000000..bcd8beb2a
--- /dev/null
+++ b/layout/generic/crashtests/411213-2.xml
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="direction: rtl;">
+<body onload="document.getElementById('i').style.fontSize = '10em';">
+
+<div style="-moz-column-width: 60px;"><div id="i" style="overflow: -moz-hidden-unscrollable; white-space: pre;">
+ b</div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/411835.html b/layout/generic/crashtests/411835.html
new file mode 100644
index 000000000..bddc903e5
--- /dev/null
+++ b/layout/generic/crashtests/411835.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+div { position: absolute; height: 10px; width: 10px; }
+
+</style>
+</head>
+
+<body onload="document.getElementById('c').style.counterIncrement = 'z';">
+
+<div style="-moz-column-count: 2;"><div id="c" style="white-space: pre;">
+
+<div>
+<div></div></div></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/411851-1.html b/layout/generic/crashtests/411851-1.html
new file mode 100644
index 000000000..c7ba0965f
--- /dev/null
+++ b/layout/generic/crashtests/411851-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html style="padding: 17895704px;">
+<head>
+</head>
+<body>
+<div style="float: left;">Foo</div>Bar<span style="margin: 10em; height: 17895697px; float: left;"></span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/412014-1.html b/layout/generic/crashtests/412014-1.html
new file mode 100644
index 000000000..0aa4446d1
--- /dev/null
+++ b/layout/generic/crashtests/412014-1.html
@@ -0,0 +1,17 @@
+<html style="white-space: pre; -moz-column-count: 2;">
+<head></head>
+
+<body onload="document.body.style.MozFloatEdge = 'margin-box'" style="-moz-column-width: 20em;">
+
+<div style="position: relative; height: 80px; margin: 10px;">
+
+
+
+
+<div style="position: absolute; height: 11px; top: 19px;"></div>
+</div>
+
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/412201-1.xhtml b/layout/generic/crashtests/412201-1.xhtml
new file mode 100644
index 000000000..29b3369f8
--- /dev/null
+++ b/layout/generic/crashtests/412201-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="padding: 17895704px;"><body style="margin: 0pt 100% 0pt 96%; display: table; float: right; height: 0pt;"><li style="list-style-position: inside;"></li></body><body style="margin: -1px; padding: 17895704px; display: table-caption; float: right; height: 0pt;"></body></html>
diff --git a/layout/generic/crashtests/412543-1.html b/layout/generic/crashtests/412543-1.html
new file mode 100644
index 000000000..9610be115
--- /dev/null
+++ b/layout/generic/crashtests/412543-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+.c { -moz-column-width: 1px; width: 93px; }
+.c:first-letter { }
+
+</style>
+</head>
+
+<body>
+
+<div class="c"><small>a b . d e f h i , k ; m n o p q</small></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/413048-1.html b/layout/generic/crashtests/413048-1.html
new file mode 100644
index 000000000..958c93023
--- /dev/null
+++ b/layout/generic/crashtests/413048-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="-moz-column-count: 15; width: 16px; height: 16px;"><span><div style="display: inline-block;">a</div><div style="float: left;"></div>
+</span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/413079-1.xhtml b/layout/generic/crashtests/413079-1.xhtml
new file mode 100644
index 000000000..d7dd554a8
--- /dev/null
+++ b/layout/generic/crashtests/413079-1.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body onload='document.getElementById("x").style.letterSpacing = "20px";'>
+
+<div style="-moz-column-count: 15;" id="x"><span>AAA
+<div style="float: left;">BBBB<div>CCCC</div></div></span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/413079-2.xhtml b/layout/generic/crashtests/413079-2.xhtml
new file mode 100644
index 000000000..4ee5beaa1
--- /dev/null
+++ b/layout/generic/crashtests/413079-2.xhtml
@@ -0,0 +1,12 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ </head>
+ <body >
+ <div style="-moz-column-count: 12; border: 1px solid green">
+ <span>AAAA
+ <div style="float: left;border: 1px solid blue">BBBB
+ </div>
+ </span>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/413079-3.xhtml b/layout/generic/crashtests/413079-3.xhtml
new file mode 100644
index 000000000..4a9157d82
--- /dev/null
+++ b/layout/generic/crashtests/413079-3.xhtml
@@ -0,0 +1,12 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ </head>
+ <body >
+ <div style="-moz-column-count: 2; border: 1px solid green">
+ <span>AAAA
+ <div style="float: left;border: 1px solid blue">BBBB
+ </div>
+ </span>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/413085-1.html b/layout/generic/crashtests/413085-1.html
new file mode 100644
index 000000000..8f1ddad97
--- /dev/null
+++ b/layout/generic/crashtests/413085-1.html
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style type="text/css">
+
+body:first-letter { float: right; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.removeChild(document.body.firstChild);
+ document.body.appendChild(document.createTextNode('x'));
+}
+
+</script>
+</head>
+
+<body onload="boom();">&#xFEB7;
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/413085-2.html b/layout/generic/crashtests/413085-2.html
new file mode 100644
index 000000000..bb82c6628
--- /dev/null
+++ b/layout/generic/crashtests/413085-2.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<style>
+ body:first-letter { float: right; }
+</style>
+<script>
+ function boom() {
+ document.body.removeChild(document.body.firstChild);
+ }
+</script>
+</head>
+<body onload="boom();">abc&#x05DB;d
+<div>This sentence should be the only text on the page.</div></body>
+</html>
diff --git a/layout/generic/crashtests/413582-1.xhtml b/layout/generic/crashtests/413582-1.xhtml
new file mode 100644
index 000000000..d3ce06463
--- /dev/null
+++ b/layout/generic/crashtests/413582-1.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body>
+
+<div style="-moz-column-count: 2; text-indent: 11px;"> <div style="float: right;"></div><select style="float: right;"></select></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/413582-2.html b/layout/generic/crashtests/413582-2.html
new file mode 100644
index 000000000..f1b163b93
--- /dev/null
+++ b/layout/generic/crashtests/413582-2.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<div style="-moz-column-count: 2;">r
+<span style="float: right;"></span>
+<textarea style="float: left;"></textarea>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/413712-1.xhtml b/layout/generic/crashtests/413712-1.xhtml
new file mode 100644
index 000000000..5de0511d1
--- /dev/null
+++ b/layout/generic/crashtests/413712-1.xhtml
@@ -0,0 +1,18 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="display: table">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var tr = document.getElementById("tr");
+ tr.contentEditable = "true";
+ tr.focus();
+ document.execCommand("selectAll", false, null);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><tr id="tr"><td></td></tr></body>
+
+</html>
diff --git a/layout/generic/crashtests/414061-1.html b/layout/generic/crashtests/414061-1.html
new file mode 100644
index 000000000..87e471262
--- /dev/null
+++ b/layout/generic/crashtests/414061-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body style="width: 1px; white-space: pre; -moz-column-width: 1px;">
+
+x
+
+<div style="margin: -200em;"></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/414180-1.xul b/layout/generic/crashtests/414180-1.xul
new file mode 100644
index 000000000..98af07821
--- /dev/null
+++ b/layout/generic/crashtests/414180-1.xul
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<grid style="margin: -1px; display: inline;" />
+
+</window>
diff --git a/layout/generic/crashtests/414719-1.html b/layout/generic/crashtests/414719-1.html
new file mode 100644
index 000000000..94c56b495
--- /dev/null
+++ b/layout/generic/crashtests/414719-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var newDiv = document.createElement("div");
+ var ispan = document.getElementById("ispan");
+ ispan.parentNode.insertBefore(newDiv, ispan);
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<div style="-moz-column-count: 1; position: fixed;"><div style="overflow: -moz-hidden-unscrollable; display: table-header-group; white-space: pre; position: absolute;">
+ <span id="ispan">
+</span></div></div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/415685-1.html b/layout/generic/crashtests/415685-1.html
new file mode 100644
index 000000000..41f51c2a3
--- /dev/null
+++ b/layout/generic/crashtests/415685-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<style type="text/css">
+
+div { height: 10px; margin: 1em; outline: 1px inset black; }
+
+</style>
+</head>
+
+<body onload="document.getElementById('a').style.padding = '12em 0';" style="-moz-column-count: 2; width: 1px;"><div id="a">aaaa aaaa aaaa</div><div style="border: medium solid blue;"></div><div style="border: medium solid green; margin: 10em 0pt;"></div><div style="border: medium solid magenta;"></div>xxxx xxxx xxxx</body>
+
+</html>
diff --git a/layout/generic/crashtests/416165.html b/layout/generic/crashtests/416165.html
new file mode 100644
index 000000000..e8db388b4
--- /dev/null
+++ b/layout/generic/crashtests/416165.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+<script>
+function replacestyles(){
+document.getElementById('a').setAttribute('style', 'border: 1px solid black; -moz-border-radius: 2em;');
+document.body.offsetHeight;
+}
+</script>
+</head>
+<body style="direction: rtl; -moz-column-count: 2;" onload="document.body.offsetHeight; setTimeout(replacestyles,0);">
+
+<div id="a">
+<pre style="overflow: -moz-hidden-unscrollable;">
+
+
+text
+
+
+
+</pre>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/416264-1.html b/layout/generic/crashtests/416264-1.html
new file mode 100644
index 000000000..783920e9f
--- /dev/null
+++ b/layout/generic/crashtests/416264-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+
+<body style="display: -moz-box;"><div style="-moz-column-count: 2;"><div style="height: 200px;"></div><span style="display: inline-block; width: 100px;"></span></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/416476-1.html b/layout/generic/crashtests/416476-1.html
new file mode 100644
index 000000000..c54affd8e
--- /dev/null
+++ b/layout/generic/crashtests/416476-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head></head><body></body><body style="width: 800px; -moz-column-count: 4;"><div style="height: 80px; outline-color: blue; outline-style: solid; outline-width: 1px;"></div><div style="height: 80px; outline-color: green; outline-style: solid; outline-width: 1px;"><div style="float: left; height: 10px; width: 10px; outline-color: red; outline-style: solid; outline-width: 1px;"></div><div style="padding: 180px; -moz-column-count: 1; clear: right; outline-color: magenta; outline-style: solid; outline-width: 1px;"></div></div></body></html>
diff --git a/layout/generic/crashtests/417109-1.xhtml b/layout/generic/crashtests/417109-1.xhtml
new file mode 100644
index 000000000..f1b93c4c6
--- /dev/null
+++ b/layout/generic/crashtests/417109-1.xhtml
@@ -0,0 +1,28 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml">
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="foo">
+ <content><html:span> &#x200F;&#x0643;&#xCA84;<children/></html:span></content>
+ </binding>
+ <binding id="empty">
+ <content></content>
+ </binding>
+</bindings>
+
+
+<script type="text/javascript">
+<![CDATA[
+
+function boom()
+{
+ document.getElementById("b").style.MozBinding = "url('#empty')";
+}
+
+]]>
+</script>
+</head>
+
+<body onload="boom();"><table style="-moz-binding: url(#foo)"><td id="b"/></table></body>
+
+</html>
diff --git a/layout/generic/crashtests/417848-1.xhtml b/layout/generic/crashtests/417848-1.xhtml
new file mode 100644
index 000000000..8a4959812
--- /dev/null
+++ b/layout/generic/crashtests/417848-1.xhtml
@@ -0,0 +1,6 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head></head>
+ <body style="-moz-column-count: 15; font-size: 144em;">
+ <svg xmlns="http://www.w3.org/2000/svg" style="min-width: 10em;"></svg>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/417902-1.html b/layout/generic/crashtests/417902-1.html
new file mode 100644
index 000000000..0dfff7eed
--- /dev/null
+++ b/layout/generic/crashtests/417902-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+body { width: 500px; -moz-column-count: 2; }
+body:first-letter { float: right; }
+#s { padding: 0pt 200px; }
+
+</style>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("s").appendChild(document.createElement("span"));
+}
+
+</script>
+</head>
+
+<body onload="boom();">x <span id="s"></span></body>
+
+</html>
diff --git a/layout/generic/crashtests/417902-2.html b/layout/generic/crashtests/417902-2.html
new file mode 100644
index 000000000..b178f59f8
--- /dev/null
+++ b/layout/generic/crashtests/417902-2.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+ <style>
+ div:first-letter {
+ float: left;
+ background: orange;
+ }
+ /* Note: there's an upper-bound on widths that trigger a crash.
+ This bound depends on the width of the characters in the div. */
+ div#v {
+ -moz-column-count: 2;
+ width: 30px;
+ height: 1em;
+ background: lightblue;
+ }
+ </style>
+ <script>
+ function boom() {
+ var v = document.getElementById("v");
+ // Note: This seems to crash regardless of what the text node is;
+ // e.g. it can be the empty string, a space character, or any number of
+ // other characters.
+ v.appendChild(document.createTextNode("CRASH"));
+ }
+ </script>
+</head>
+<body onload="boom();"><div id="v">a b</div></body>
+</html>
diff --git a/layout/generic/crashtests/418532-1.html b/layout/generic/crashtests/418532-1.html
new file mode 100644
index 000000000..da137a4dc
--- /dev/null
+++ b/layout/generic/crashtests/418532-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+div:first-line { font-size: 80%; color: green; }
+</style>
+</head>
+<body style="-moz-column-width: 1px"><div id="div" style="-moz-column-width: 1px"> <span>Foo bar</span></div></body>
+</html>
diff --git a/layout/generic/crashtests/418932-1.html b/layout/generic/crashtests/418932-1.html
new file mode 100644
index 000000000..20cd37253
--- /dev/null
+++ b/layout/generic/crashtests/418932-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head></head><body><div style="-moz-column-width: 2880000em;"></div></body></html>
diff --git a/layout/generic/crashtests/419352.html b/layout/generic/crashtests/419352.html
new file mode 100644
index 000000000..70b3edba7
--- /dev/null
+++ b/layout/generic/crashtests/419352.html
@@ -0,0 +1,3 @@
+<html>
+<body dir="rtl">&#x2028;&#x200C;</body>
+</html>
diff --git a/layout/generic/crashtests/420000-1.html b/layout/generic/crashtests/420000-1.html
new file mode 100644
index 000000000..1bf8d7b94
--- /dev/null
+++ b/layout/generic/crashtests/420000-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+
+<div style="-moz-column-gap: 24000000em; -moz-column-count: 5;"></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/420718.html b/layout/generic/crashtests/420718.html
new file mode 100644
index 000000000..9626c5cf2
--- /dev/null
+++ b/layout/generic/crashtests/420718.html
@@ -0,0 +1 @@
+<span style="float: left; margin-top: 99999999px;">t</span> \ No newline at end of file
diff --git a/layout/generic/crashtests/420785-1.xhtml b/layout/generic/crashtests/420785-1.xhtml
new file mode 100644
index 000000000..3d2d985e2
--- /dev/null
+++ b/layout/generic/crashtests/420785-1.xhtml
@@ -0,0 +1,26 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
+ <binding id="foo">
+ <content><html:span>a b<children xmlns="http://www.mozilla.org/xbl"/></html:span></content>
+ </binding>
+</bindings>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var cap = document.getElementById("cap");
+ var lsep = document.createTextNode("\u2028"); // U+2028 LINE SEPARATOR
+ cap.insertBefore(lsep, cap.firstChild);
+}
+
+</script>
+
+</head>
+
+<body onload="boom();"><caption id="cap" style="-moz-binding: url('#foo');"> </caption></body>
+
+</html>
diff --git a/layout/generic/crashtests/421404-1.html b/layout/generic/crashtests/421404-1.html
new file mode 100644
index 000000000..634209849
--- /dev/null
+++ b/layout/generic/crashtests/421404-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="width: -moz-fit-content; width: fit-content; clear: both">
+<div style="float: left; width: 20px; padding: 100%"></div>
+<div style="float: left; width: 20px"></div>
+<div style="float: left; width: 20px"></div>
+</div>
+<div style="width: -moz-fit-content; width: fit-content; clear: both">
+<div style="float: right; width: 20px; padding: 100%"></div>
+<div style="float: right; width: 20px"></div>
+<div style="float: right; width: 20px"></div>
+</div>
+<div style="width: -moz-fit-content; width: fit-content; clear: both">
+<div style="float: left; width: 20px; padding: 100%"></div>
+<div style="float: right; width: 20px; padding: 100%; clear: right"></div>
+<div style="float: right; width: 20px; clear: right"></div>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/421671.html b/layout/generic/crashtests/421671.html
new file mode 100644
index 000000000..e3919e635
--- /dev/null
+++ b/layout/generic/crashtests/421671.html
@@ -0,0 +1,202 @@
+<marquee>
+<xmp style="-moz-column-count: 99999999">
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+
+<a>
+<a>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<a>
+<a>
+<a>
+<a>
+
+<a>
+<a>
+
+<a>
+<a> \ No newline at end of file
diff --git a/layout/generic/crashtests/422283-1.html b/layout/generic/crashtests/422283-1.html
new file mode 100644
index 000000000..8f1b9f44f
--- /dev/null
+++ b/layout/generic/crashtests/422283-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+
+<body style="-moz-column-width: 1px;"><div style="padding: 150px 0; height: 80px;"><span>
+</span>x<div></div><span>
+</span>a ! b c<div></div>
+</div></body>
+
+</html>
diff --git a/layout/generic/crashtests/422301-1.html b/layout/generic/crashtests/422301-1.html
new file mode 100644
index 000000000..8c2f2ef33
--- /dev/null
+++ b/layout/generic/crashtests/422301-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<style type="text/css">
+
+div { height: .5em; margin: 1em; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("b").style.padding = "";
+ document.getElementById("a").style.padding = "12em";
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="-moz-column-width: 2em;"><div id="a"></div><div></div><div></div><div id="b" style="padding: 12em;">This is text</div>This is textThis is text<div>This is text</div></body>
+
+</html>
diff --git a/layout/generic/crashtests/423055-1.html b/layout/generic/crashtests/423055-1.html
new file mode 100644
index 000000000..d0696307d
--- /dev/null
+++ b/layout/generic/crashtests/423055-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body>
+
+<div style="white-space: pre; position: relative; -moz-column-count: 2;">
+<div style="position: absolute;"></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/423098.html b/layout/generic/crashtests/423098.html
new file mode 100644
index 000000000..5bb9534e4
--- /dev/null
+++ b/layout/generic/crashtests/423098.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("b").style.position = "static";
+ document.body.offsetHeight;
+ document.getElementById("a").style.fontSize = "110%";
+}
+
+</script>
+</head>
+
+<body style="-moz-column-count: 1;" onload="boom();">
+ <div id="a" style="float: right; height: 80px;">
+ <div id="b" style="position: absolute; height: 10px;"></div>
+ <div style="height: 100px; width: 11px;"></div>
+ </div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/423264-1.html b/layout/generic/crashtests/423264-1.html
new file mode 100644
index 000000000..d2027c1d1
--- /dev/null
+++ b/layout/generic/crashtests/423264-1.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<script type="text/javascript">
+
+function boom()
+{
+ var y = document.createTextNode(' Y ');
+ document.getElementById("a").appendChild(y);
+ document.body.offsetHeight;
+ var z = document.createTextNode('Z');
+ document.body.insertBefore(z, document.body.lastChild);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div style="-moz-column-count: 2; width: 1px;"><div id="a">M N矋<span>م</span></div></div>H</body>
+</html>
diff --git a/layout/generic/crashtests/424629.html b/layout/generic/crashtests/424629.html
new file mode 100644
index 000000000..d142897ce
--- /dev/null
+++ b/layout/generic/crashtests/424629.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("o").appendChild(document.createTextNode('d'));
+ document.body.offsetHeight;
+ document.getElementById("b").firstChild.data = "\u202E";
+ document.getElementById("a").firstChild.data = "ZZZZZZZ";
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="font-family: monospace; width: 8ch;">
+
+<div><span id="o"><span id="a">a</span><span id="b">b&#x202E;</span><span> c </span></span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/425253-1.html b/layout/generic/crashtests/425253-1.html
new file mode 100644
index 000000000..74c78570d
--- /dev/null
+++ b/layout/generic/crashtests/425253-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ <span style="border:10px solid blue; text-decoration:underline;">Hello</span>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/426040-1.html b/layout/generic/crashtests/426040-1.html
new file mode 100644
index 000000000..196a541cb
--- /dev/null
+++ b/layout/generic/crashtests/426040-1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+div { height: 1px; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var a = document.getElementById("a");
+ a.removeChild(a.firstChild);
+ document.documentElement.style.outline = "none";
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<div style="-moz-column-count: 2;" id="a"><div><div style="float: right;"><div></div><span><div></div></span></div></div></div>
+
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/426272-1.html b/layout/generic/crashtests/426272-1.html
new file mode 100644
index 000000000..ec29f918e
--- /dev/null
+++ b/layout/generic/crashtests/426272-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var y = document.createTextNode('Y');
+ document.body.insertBefore(y, document.getElementById("v").nextSibling);
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="-moz-column-count: 2; width: 10ch; letter-spacing: 1px; font-family: monospace;">
+<div style="background: lightblue; float: right; height: 14em; width: 1ch;" id="v"></div>a bcd<span>&#x202B;X</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/428263-1.html b/layout/generic/crashtests/428263-1.html
new file mode 100644
index 000000000..2ac656d9d
--- /dev/null
+++ b/layout/generic/crashtests/428263-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html style="display: table;">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.documentElement.appendChild(document.body);
+ document.documentElement.offsetHeight;
+ document.documentElement.appendChild(document.body);
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="-moz-column-count: 2;"><div style="height: 1px;"></div><span style="display: inline-block;"></span></body>
+
+</html>
diff --git a/layout/generic/crashtests/429458.xhtml b/layout/generic/crashtests/429458.xhtml
new file mode 100644
index 000000000..a3597516f
--- /dev/null
+++ b/layout/generic/crashtests/429458.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="foo">
+ <content><html:span><children xmlns="http://www.mozilla.org/xbl"/></html:span></content>
+ </binding>
+</bindings>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var s = document.getAnonymousNodes(document.body)[0];
+ s.appendChild(document.createTextNode("apples and oranges"));
+ document.body.offsetHeight;
+ document.body.appendChild(document.createTextNode("\n\n"));
+}
+
+</script>
+
+</head>
+
+<body onload="boom();" style="-moz-binding: url('#foo'); width: 1px; word-spacing: 1px;"></body>
+
+</html>
diff --git a/layout/generic/crashtests/429960-1.html b/layout/generic/crashtests/429960-1.html
new file mode 100644
index 000000000..069c3f5f2
--- /dev/null
+++ b/layout/generic/crashtests/429960-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html style="direction: rtl;" contenteditable="true">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.firstChild.style.MozColumnCount = "3";
+}
+
+</script>
+</head>
+
+<body style="width: 1px;" onload="boom();"><div>examination<span style="font-family: verdana;">x x x x </span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/429960-2.html b/layout/generic/crashtests/429960-2.html
new file mode 100644
index 000000000..a7c451422
--- /dev/null
+++ b/layout/generic/crashtests/429960-2.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html style="direction: rtl;" contenteditable="true">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.firstChild.style.MozColumnCount = "3";
+}
+
+</script>
+</head>
+
+<body style="width: 1px;" onload="boom();"><div>examination<span style="font-family: verdana;">x x x x
+</span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/429969-1.html b/layout/generic/crashtests/429969-1.html
new file mode 100644
index 000000000..4b4b567f0
--- /dev/null
+++ b/layout/generic/crashtests/429969-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+#inner:first-letter { }
+#outer { direction: rtl; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.documentElement.style.whiteSpace = "pre";
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div id="outer"><div id="inner"><span id="s"><span>
+
+</span>AB</span></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/429981-1.html b/layout/generic/crashtests/429981-1.html
new file mode 100644
index 000000000..a5cf5b007
--- /dev/null
+++ b/layout/generic/crashtests/429981-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+ body {
+ height: 100px;
+ width: 300pt;
+ -moz-column-width: 100pt;
+ -moz-column-gap: 0;
+ }
+
+ #x {
+ padding: 100px;
+ }
+
+ #overflow {
+ height: 400px;
+ float: left;
+ }
+
+</style>
+</head>
+
+<body><div id="x"><div id="overflow"></div>K</div></body>
+
+</html>
diff --git a/layout/generic/crashtests/430332-1.html b/layout/generic/crashtests/430332-1.html
new file mode 100644
index 000000000..c9ba00229
--- /dev/null
+++ b/layout/generic/crashtests/430332-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<body style="font-family:monospace; width:6ch;">
+<b>ab</b> cd
+<b>ab</b> cd
+<b>ab</b> cd
+<b>ab</b> cd
+<b>ab</b> cd
+<b>ab</b> cd
+<b>ab</b> cd
+<b>ab</b> cd<span id="s">ef</span>
+<script>
+document.body.clientWidth;
+document.getElementById("s").style.fontSize = "200%";
+</script>
+</body>
+</html>
diff --git a/layout/generic/crashtests/430344-1.html b/layout/generic/crashtests/430344-1.html
new file mode 100644
index 000000000..e79302092
--- /dev/null
+++ b/layout/generic/crashtests/430344-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="width: 1px;"><span>e</span> <br style="clear: both;"></body>
+</html>
diff --git a/layout/generic/crashtests/430352-1.html b/layout/generic/crashtests/430352-1.html
new file mode 100644
index 000000000..e22d64129
--- /dev/null
+++ b/layout/generic/crashtests/430352-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html style="border: 1px dotted red; font-size: 0; -moz-appearance: scrollbartrack-horizontal; width: 12em;">
+<head></head>
+<body style="position: absolute;"></body>
+</html>
diff --git a/layout/generic/crashtests/430744-1.html b/layout/generic/crashtests/430744-1.html
new file mode 100644
index 000000000..7c185921c
--- /dev/null
+++ b/layout/generic/crashtests/430744-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('x').appendChild(document.createTextNode('b'));">
+
+<div style="display: -moz-grid;"><div style="-moz-column-count: 2;">a<div id="x" style="padding: 1em;"></div><wbr></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/430991.html b/layout/generic/crashtests/430991.html
new file mode 100644
index 000000000..69a412500
--- /dev/null
+++ b/layout/generic/crashtests/430991.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var li = document.getElementById("li");
+ li.removeChild(li.lastChild);
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<ul id="ul">
+<li id="li" style="-moz-column-count: -1; white-space: pre-wrap;"><span>A</span><span>B</span><span style="float: right;"></span>
+
+</li>
+</ul>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/431260-1.html b/layout/generic/crashtests/431260-1.html
new file mode 100644
index 000000000..0e4bfb812
--- /dev/null
+++ b/layout/generic/crashtests/431260-1.html
@@ -0,0 +1,34 @@
+<html class="reftest-wait"><head><style>
+title::first-letter {font-size:600%;}
+</style><title style="display: table-column-group; position: absolute;">
+mmmmmmmmmmmmmmm mmmmmmmmmmmmmmmm mmmmmmmmmm mmmmmmmmmmmmmmmmmmmmm mmmmmmmmmm6mmmmm
+mmmm mmmmMmmmmmmmm=mmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+mmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmm mmmmm mmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmm mmmmmmmm mmmmmmm
+&#1593m
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm mmmmm
+&#1593m ­&#1593m&#1593m&#1593m­­­&#1593m­&#1593m­&#1593m­&#1593m­&#1593m­&#1593m­&#1593
+&#1593m ­&#1593m&#1593m&#1593m­­­&#1593m­&#1593m­&#1593m­&#1593m­&#1593m­&#1593m­&#1593
+</title></head>
+<script>
+function innerhtml(i){
+ if (i > 0) {
+ document.documentElement.innerHTML = document.documentElement.innerHTML;
+ setTimeout(innerhtml,0,i-1);
+ } else {
+ document.documentElement.removeAttribute("class");
+ }
+}
+setTimeout(innerhtml,0,30);
+</script>
+</html>
diff --git a/layout/generic/crashtests/431260-2.html b/layout/generic/crashtests/431260-2.html
new file mode 100644
index 000000000..7fc9204c2
--- /dev/null
+++ b/layout/generic/crashtests/431260-2.html
@@ -0,0 +1,26 @@
+<html class="reftest-wait"><head><style>
+ title::first-letter {}
+ title {
+ display: inline;
+ float: left;
+ font-size: 16px;
+ }
+</style>
+<script>
+ function boom() {
+ document.documentElement.innerHTML = document.documentElement.innerHTML;
+ document.documentElement.removeAttribute("class");
+ }
+</script>
+<title>
+a
+b
+&#1593
+c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c
+c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c
+c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c
+</title></head>
+<script>
+setTimeout(boom, 0);
+</script>
+</html>
diff --git a/layout/generic/crashtests/435529.html b/layout/generic/crashtests/435529.html
new file mode 100644
index 000000000..736e3377c
--- /dev/null
+++ b/layout/generic/crashtests/435529.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+ div {
+ -moz-column-count: 2;
+ white-space: pre;
+ }
+
+ div:first-letter {
+ float: right;
+ }
+
+</style>
+</head>
+
+<body><div> <span>AB</span></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/436194-1.html b/layout/generic/crashtests/436194-1.html
new file mode 100644
index 000000000..4326c9f67
--- /dev/null
+++ b/layout/generic/crashtests/436194-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+#colset { -moz-column-count: 2; outline: 1px solid orange; }
+.ib { height: 100px; display: inline-block; outline: 1px solid blue; }
+div { outline: 1px solid green; }
+span { outline: 1px solid magenta; }
+
+</style>
+</head>
+<body>
+
+<div id="colset"><div></div><div style="width: 10px; float: left;"><span> <span class="ib" style="width: 30px;"></span> <br> </span></div><span class="ib"></span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/436602-1.html b/layout/generic/crashtests/436602-1.html
new file mode 100644
index 000000000..cf7bee015
--- /dev/null
+++ b/layout/generic/crashtests/436602-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="-moz-column-width: 1px;"><span>A B C D E</span> <span style="float: right;"></span> <br> </div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/436822-1.html b/layout/generic/crashtests/436822-1.html
new file mode 100644
index 000000000..b1b99977e
--- /dev/null
+++ b/layout/generic/crashtests/436822-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+body { font-size: 1600000px; }
+body * { font-size: 10em; }
+
+</style>
+</head>
+
+<body>
+ <div>
+ <div style="margin-top: 1em; margin-bottom: 1em;">A</div>
+ </div>
+ <div>
+ <div style="margin-top: 1em; margin-bottom: 1em;">B<div style="display: list-item; padding-left: 3px; float: left;"></div></div>
+ </div>
+ <div style="float: left;"></div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/436823.html b/layout/generic/crashtests/436823.html
new file mode 100644
index 000000000..aa6caeff1
--- /dev/null
+++ b/layout/generic/crashtests/436823.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+
+<div style="-moz-column-count: -1;"><div style="float: left;"><div><div style="float: left;">A B</div><div style="clear: both; height: 1px;"></div></div></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/436969-1.html b/layout/generic/crashtests/436969-1.html
new file mode 100644
index 000000000..0f9738754
--- /dev/null
+++ b/layout/generic/crashtests/436969-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.documentElement.style.zIndex = 2;">
+
+<div style="direction: rtl; text-transform: uppercase; width: 1px;"><div style="position: absolute; -moz-column-count: 2;"><span style="padding: 6em 0pt; position: absolute; height: 1.2em;">A !BB CCC D,</span></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/437156-1.html b/layout/generic/crashtests/437156-1.html
new file mode 100644
index 000000000..6fac1924d
--- /dev/null
+++ b/layout/generic/crashtests/437156-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('c').style.counterIncrement = 'c';">
+
+<div style="-moz-column-width: 1px;"><div id="c"><div style="float: right;">T<iframe></iframe></div></div></div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/437565-1.xhtml b/layout/generic/crashtests/437565-1.xhtml
new file mode 100644
index 000000000..4b2dd1067
--- /dev/null
+++ b/layout/generic/crashtests/437565-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<!-- no body -->
+<div/>
+<div style="float: right;"><span><svg xmlns="http://www.w3.org/2000/svg" style="display: table-row;"/></span> von qlikworld runterladen. Alle RSS News aus dem Artikelkicker immer sofort und kostenlos auf den Bildschirm. Pressemitteilungen lesen und unbegrenzt viele Feeds gratis abonieren. </div><div style="float: right; text-indent: 20em;">artikelkicker.de</div><div><div style="float: right; width: 75px; height: 33px;"/><div style="clear: both;"/></div>
+</html>
diff --git a/layout/generic/crashtests/437565-2.xhtml b/layout/generic/crashtests/437565-2.xhtml
new file mode 100644
index 000000000..9213a65df
--- /dev/null
+++ b/layout/generic/crashtests/437565-2.xhtml
@@ -0,0 +1,24 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <style>
+ /* Note: The height here is almost nscoord_MAX in app-units */
+ /* Note: The width here needs to be wider than the viewport in order
+ to trigger a crash. */
+ div.tall {
+ height: 17895687px;
+ width: 3000px;
+ background: pink;
+ }
+ div.float { float: left; }
+ div.clear { clear: left; }
+ div.square { width: 10px; height: 10px; }
+ div.blue { background: blue; }
+ div.green { background: green; }
+ div.orange { background: orange; }
+ </style>
+</head>
+<!-- no body (adding a body prevents the crash, for some reason) -->
+ <div class="float"><div class="tall"/><div class="square blue"/></div>
+ <div class="float square green"/>
+ <div><div class="float square orange"/><div class="clear"/></div>
+</html>
diff --git a/layout/generic/crashtests/437565-3.xhtml b/layout/generic/crashtests/437565-3.xhtml
new file mode 100644
index 000000000..dbcf3f84f
--- /dev/null
+++ b/layout/generic/crashtests/437565-3.xhtml
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+><head>
+ <style>
+ /* Note: The height here is almost nscoord_MAX in app-units */
+ /* Note: The width here needs to be wider than the viewport in order
+ to trigger a crash. */
+ div.tall {
+ height: 17895687px;
+ width: 580px;
+ background: pink;
+ }
+ div.float { float: left; }
+ div.clear { clear: left; }
+ div.square { width: 10px; height: 10px; }
+ div.blue { background: blue; }
+ div.green { background: green; }
+ div.orange { background: orange; }
+ </style>
+</head
+><div class="float"><div class="tall"/><div class="square blue"/></div
+><div class="float square green"></div
+><div><div class="float square orange"/><div class="clear"/></div
+></html>
diff --git a/layout/generic/crashtests/438259-1.html b/layout/generic/crashtests/438259-1.html
new file mode 100644
index 000000000..232531cce
--- /dev/null
+++ b/layout/generic/crashtests/438259-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+div:first-letter { font-size: 7em; }
+
+</style>
+</head>
+
+<body style="width: 3px; float: left;" onload="document.body.style.direction = 'rtl';"><div><br> A B</div></body>
+
+</html>
diff --git a/layout/generic/crashtests/438266-1.html b/layout/generic/crashtests/438266-1.html
new file mode 100644
index 000000000..3fb7ec1ba
--- /dev/null
+++ b/layout/generic/crashtests/438266-1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("hr").removeAttribute("width");
+
+ document.documentElement.offsetHeight;
+
+ var newTR = document.createElement("tr");
+ document.getElementById("table").appendChild(newTR);
+}
+
+</script>
+</head>
+
+<body onload="boom()">
+
+<table id="table">
+ <tbody>
+ <tr>
+ <td>
+ <div><hr width="7000" id="hr"></div>
+ <div style="-moz-column-width: 100px;">x<li></li></div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/438509-1.html b/layout/generic/crashtests/438509-1.html
new file mode 100644
index 000000000..00096020f
--- /dev/null
+++ b/layout/generic/crashtests/438509-1.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<style type="text/css">
+
+ div.wrapper { height: 400px; }
+ table { height: 100%; }
+
+</style>
+</head>
+<body>
+ <div class="wrapper">
+ <!-- NOTE: Every layer of "table/td" seems to double the load-time -->
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ <table><td>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </td></table>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/442860-1.xul b/layout/generic/crashtests/442860-1.xul
new file mode 100644
index 000000000..74a5fb6e7
--- /dev/null
+++ b/layout/generic/crashtests/442860-1.xul
@@ -0,0 +1 @@
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><hbox style="display: block; -moz-column-count: 15;"><scrollbar height="656119391073809204" style="position: fixed; -moz-appearance: tooltip;"/></hbox></window>
diff --git a/layout/generic/crashtests/443528-1.html b/layout/generic/crashtests/443528-1.html
new file mode 100644
index 000000000..1598a50ba
--- /dev/null
+++ b/layout/generic/crashtests/443528-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var s = document.getElementById("s");
+ document.body.insertBefore(document.createTextNode("\n "), s);
+ document.body.offsetHeight;
+ s.appendChild(document.createElement("span"));
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="-moz-column-count: 2; font-size: 1500px; white-space: pre-wrap;"><span id="s" style="display: inline-block"></span><div style="height: 100px;"></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/444230-1.html b/layout/generic/crashtests/444230-1.html
new file mode 100644
index 000000000..5e0162353
--- /dev/null
+++ b/layout/generic/crashtests/444230-1.html
@@ -0,0 +1 @@
+<html><body><span style="padding: 200%; vertical-align: top;">x<br></span></body></html>
diff --git a/layout/generic/crashtests/444484-1.html b/layout/generic/crashtests/444484-1.html
new file mode 100644
index 000000000..7edc2f6eb
--- /dev/null
+++ b/layout/generic/crashtests/444484-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<style type="text/css">
+
+body:first-letter { float: right; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var t = document.body.firstChild;
+ var se = document.getElementById("se");
+ se.appendChild(t); // !!!
+ document.body.appendChild(se);
+ se.appendChild(document.createTextNode(" "));
+}
+
+</script>
+</head>
+
+<body onload="boom();">&#xFEB7;
+<div id="se" style="display: none;"></div></body>
+</html>
diff --git a/layout/generic/crashtests/444726-1.xhtml b/layout/generic/crashtests/444726-1.xhtml
new file mode 100644
index 000000000..a266aa7b8
--- /dev/null
+++ b/layout/generic/crashtests/444726-1.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body>
+</body>
+
+<!-- Intentionally outside of <body> -->
+<div style="margin: 7224850px 0pt; padding-bottom: 6px;"></div><div style="float: right; padding-top: 6px; width: 194px;"></div><div style="float: left; width: 525px;"><li style="margin: 7224850px 0pt;"></li><div>x<div style="margin: 7224850px 0pt;"></div></div>y</div>
+
+</html>
diff --git a/layout/generic/crashtests/444861-1.html b/layout/generic/crashtests/444861-1.html
new file mode 100644
index 000000000..a11b80115
--- /dev/null
+++ b/layout/generic/crashtests/444861-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("a").style.padding = "4643853%";
+ document.getElementById("a").style.counterIncrement = "a";
+ document.documentElement.offsetHeight;
+ document.getElementById("a").style.width = "1px";
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div style="width: 430px;"><div id="a"><img style="float: left; margin-right: 15px; margin-top: 5px;">A B</div><div><li style="width: 45%; float: left;"></li><div style="float: left;"><span style="padding-left: 22px;"></span></div></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/445288.html b/layout/generic/crashtests/445288.html
new file mode 100644
index 000000000..f183a34cd
--- /dev/null
+++ b/layout/generic/crashtests/445288.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var s = document.createElement("span");
+ document.getElementById("k").appendChild(s);
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="margin: 381500067712% 0pt;">a<div><div style="font-size: 4611686018427388000in;"><hr></div><div style="float: left;">b</div>c</div><div style="height: 197678in;"></div><div id="k" style="float: left;"></div></body>
+</html>
diff --git a/layout/generic/crashtests/448903-1.html b/layout/generic/crashtests/448903-1.html
new file mode 100644
index 000000000..9a6cad134
--- /dev/null
+++ b/layout/generic/crashtests/448903-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="padding-left: 33554433em; padding-bottom: 33554433em; width: 20000px; text-decoration: underline; text-shadow: 2px 0px 158.33px green; text-indent: -33554433em;">Z</body>
+</html>
diff --git a/layout/generic/crashtests/448996-1.html b/layout/generic/crashtests/448996-1.html
new file mode 100644
index 000000000..67392b6b5
--- /dev/null
+++ b/layout/generic/crashtests/448996-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+body { font-family: monospace; width: 10ch; outline: 1px solid black; }
+div { -moz-column-width: 0.4px; -moz-column-gap: 3ch; }
+b { font-weight: inherit; display: inline-block; }
+#r { border: 1px solid red; }
+#r:before { content: ""; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("r").style.counterReset = "c";
+}
+</script>
+
+</head>
+
+<body onload="boom();"><div>a b c d <span> <b>m</b><span id="r"><b>x</b><span></span></span></span> </div></body>
+
+</html>
diff --git a/layout/generic/crashtests/451315-1.html b/layout/generic/crashtests/451315-1.html
new file mode 100644
index 000000000..c9c51e9d9
--- /dev/null
+++ b/layout/generic/crashtests/451315-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="float: right; height: 2px;" onload="document.body.style.width = '0';"><div style="-moz-column-count: 3;"><div style="padding: 268435457mm;">A B C D</div></div></body>
+</html>
diff --git a/layout/generic/crashtests/451317-1.html b/layout/generic/crashtests/451317-1.html
new file mode 100644
index 000000000..abea357da
--- /dev/null
+++ b/layout/generic/crashtests/451317-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+div:first-line { }
+
+</style>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("a").style.MozColumnWidth = "21120690815in";
+ document.getElementById("s").style.cursor = "pointer";
+}
+
+</script>
+</head>
+<body onload="boom();">
+
+<div id="a">A<div style="padding-top: 1px"> <input> <span id="s"></span></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/451334-1.html b/layout/generic/crashtests/451334-1.html
new file mode 100644
index 000000000..6dc47160a
--- /dev/null
+++ b/layout/generic/crashtests/451334-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body onload="document.body.style.MozColumnWidth = '1px';">
+
+<div style="display: inline-block;"></div><div style="float: left;"><div style="height: 32px;"></div></div><div>
+<div style="clear: both;"><br></div><div style="float: left;"></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/452157-1.html b/layout/generic/crashtests/452157-1.html
new file mode 100644
index 000000000..92a784cd5
--- /dev/null
+++ b/layout/generic/crashtests/452157-1.html
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+
+<div style="float: left; -moz-column-count: 3;"><div><div style="margin: 1em 0pt; float: left;"><span style="display: inline-block; width: 16px; height: 16px;"></span></div><div style="margin: 1em 0pt; float: left;"></div><br style="display: inherit; clear: both;"> </div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/452157-2.html b/layout/generic/crashtests/452157-2.html
new file mode 100644
index 000000000..bf7297789
--- /dev/null
+++ b/layout/generic/crashtests/452157-2.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<style>
+ div.a {
+ -moz-column-count: 2;
+ float: left;
+ background: lightblue;
+ }
+ div.b {}
+ div.c {
+ float: left;
+ height: 23px;
+ width: 10px;
+ background: orange;
+ }
+ div.d {
+ float: left;
+ height: 22px;
+ width: 10px;
+ background: green;
+ }
+ div.e {
+ clear: left;
+ }
+</style>
+</head>
+<body
+ ><div class="a"
+ ><div class="b"
+ ><div class="c"
+ ></div
+ ><div class="d"
+ ></div
+ ><div class="e"
+ ></div
+ > <!-- whitespace --></div
+ ></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/452157-3.html b/layout/generic/crashtests/452157-3.html
new file mode 100644
index 000000000..104f282a1
--- /dev/null
+++ b/layout/generic/crashtests/452157-3.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<style>
+ div.a {
+ -moz-column-count: 2;
+ position: absolute;
+ background: lightblue;
+ }
+ div.b {}
+ div.c {
+ float: left;
+ height: 23px;
+ width: 10px;
+ background: orange;
+ }
+ div.d {
+ float: left;
+ height: 22px;
+ width: 10px;
+ background: green;
+ }
+ div.e {
+ clear: left;
+ }
+</style>
+</head>
+<body
+ ><div class="a"
+ ><div class="b"
+ ><div class="c"
+ ></div
+ ><div class="d"
+ ></div
+ ><div class="e"
+ ></div
+ > <!-- whitespace --></div
+ ></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/453762-1.html b/layout/generic/crashtests/453762-1.html
new file mode 100644
index 000000000..f7d302b5f
--- /dev/null
+++ b/layout/generic/crashtests/453762-1.html
@@ -0,0 +1,4 @@
+<html style="text-indent: 3700px;">
+<head></head>
+<body><span style="position: relative;"> <div style="position: absolute;"></div></span></body>
+</html>
diff --git a/layout/generic/crashtests/455171-1.html b/layout/generic/crashtests/455171-1.html
new file mode 100644
index 000000000..97bc98c7a
--- /dev/null
+++ b/layout/generic/crashtests/455171-1.html
@@ -0,0 +1,5 @@
+<html style="-moz-transform: translate(50px);">
+<head>
+<style>html::after { content:"b"; position: fixed;}</style>
+</head>
+<body></body></html>
diff --git a/layout/generic/crashtests/455171-2.html b/layout/generic/crashtests/455171-2.html
new file mode 100644
index 000000000..a8a11ad4c
--- /dev/null
+++ b/layout/generic/crashtests/455171-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+<div style="-moz-transform: translate(50px, 50px);"><div style="position: fixed;"></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/455171-3.html b/layout/generic/crashtests/455171-3.html
new file mode 100644
index 000000000..e463d2b60
--- /dev/null
+++ b/layout/generic/crashtests/455171-3.html
@@ -0,0 +1,2 @@
+<div style="-moz-transform: scale(2);">
+<iframe style="position: fixed;">
diff --git a/layout/generic/crashtests/455643-1.xhtml b/layout/generic/crashtests/455643-1.xhtml
new file mode 100644
index 000000000..78aada542
--- /dev/null
+++ b/layout/generic/crashtests/455643-1.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("k").style.position = "fixed";
+ document.documentElement.offsetHeight;
+ document.getElementById("g").style.textAlign = "";
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<div style="text-align: right;" id="g"><div style="direction: rtl; max-width: -moz-fit-content;"><div style="-moz-column-width: 1px;">A B<hr/>C D <input/><hr/></div><div id="k"><div style="width: 150px; float: right;"/><div style="width: 100px; float: right;"/></div><div style="padding-left: 40px;"/></div></div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/457375.html b/layout/generic/crashtests/457375.html
new file mode 100644
index 000000000..355ca562b
--- /dev/null
+++ b/layout/generic/crashtests/457375.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="-moz-column-width: 1px;" onload="document.getElementById('v').style.height = '0';"><div id="v"><span>A B&#0;</span><span>C</span><span> D</span></div></body>
+</html>
diff --git a/layout/generic/crashtests/457380-1.html b/layout/generic/crashtests/457380-1.html
new file mode 100644
index 000000000..291d41cc2
--- /dev/null
+++ b/layout/generic/crashtests/457380-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.documentElement.style.MozColumnCount = '3';
+ document.documentElement.offsetHeight;
+ document.documentElement.style.MozColumnCount = '';
+}
+
+</script>
+
+<style type="text/css">
+
+html:before { content: '0'; }
+
+</style>
+
+</head>
+
+<frameset onload="boom();"></frameset>
+
+</html>
diff --git a/layout/generic/crashtests/459968.html b/layout/generic/crashtests/459968.html
new file mode 100644
index 000000000..1028e0e85
--- /dev/null
+++ b/layout/generic/crashtests/459968.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+body {
+ white-space: pre;
+ word-spacing: 511pc;
+}
+
+#a {
+ float: right;
+}
+
+#b {
+ position: fixed;
+ white-space: pre-line;
+ direction: rtl;
+ letter-spacing: 0pt;
+}
+
+</style>
+</head>
+
+<body onload="document.body.style.letterSpacing = '';" style="letter-spacing: 1152921504606847000em;"><div id="a"><div id="b">
+
+. .
+ 0. 0.
+.
+
+ </div>
+ </div></body>
+</html>
diff --git a/layout/generic/crashtests/460910-1.xml b/layout/generic/crashtests/460910-1.xml
new file mode 100644
index 000000000..268deef64
--- /dev/null
+++ b/layout/generic/crashtests/460910-1.xml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="http://www.w3.org/1998/Math/MathML">
+<head>
+<style type="text/css">
+
+[class~='t'] { display: table; }
+
+</style>
+</head>
+<body>
+
+<m:math><m:math class="t"/></m:math>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/461294-1.html b/layout/generic/crashtests/461294-1.html
new file mode 100644
index 000000000..9d6c7145c
--- /dev/null
+++ b/layout/generic/crashtests/461294-1.html
@@ -0,0 +1 @@
+<html style="display: inline-table;"><body style="margin: 381500067712% 0pt;">T<div><span style="font-size: 4611686018427388000in;"><hr></span><span style="float: left;">P</span>,</div><div style="min-height: 197678in;"></div><span style="float: left;"></span></body></html>
diff --git a/layout/generic/crashtests/462968.xhtml b/layout/generic/crashtests/462968.xhtml
new file mode 100644
index 000000000..2803f4a25
--- /dev/null
+++ b/layout/generic/crashtests/462968.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body onload="var td = document.getElementById('td'); td.parentNode.removeChild(td);">
+
+<div style="-moz-column-count: 2;"><div style="padding: 881977875840684in 0pt;"><span style="padding: 0pt 881977875840684in;"><div style="padding-top: 881977875840684in; clear: both;"></div><span><div><td id="td"></td></div></span></span></div></div>
+
+</body></html>
diff --git a/layout/generic/crashtests/463350-1.html b/layout/generic/crashtests/463350-1.html
new file mode 100644
index 000000000..ad5f9158e
--- /dev/null
+++ b/layout/generic/crashtests/463350-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("select").appendChild(document.createElement("span"));
+ document.documentElement.offsetHeight;
+ document.getElementById("z").appendChild(document.createElement("span"));
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="-moz-column-count: 2; height: 72496331mm;"><fieldset><span id="z"><div><div></div></div><select id="select"></select></span></fieldset></body>
+</html>
diff --git a/layout/generic/crashtests/463350-2.html b/layout/generic/crashtests/463350-2.html
new file mode 100644
index 000000000..a567761d2
--- /dev/null
+++ b/layout/generic/crashtests/463350-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("select").appendChild(document.createElement("span"));
+ document.documentElement.offsetHeight;
+ document.getElementById("z").appendChild(document.createElement("span"));
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="-moz-column-count: 2; height: 0;"><fieldset><span id="z"><div><div></div></div><select id="select"></select></span></fieldset></body>
+</html>
diff --git a/layout/generic/crashtests/463350-3.html b/layout/generic/crashtests/463350-3.html
new file mode 100644
index 000000000..2f75c839b
--- /dev/null
+++ b/layout/generic/crashtests/463350-3.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("select").appendChild(document.createElement("span"));
+}
+
+</script>
+</head>
+
+<body onload="setTimeout('boom()', 500);" style="-moz-column-count: 2; height: 0;"><fieldset><span id="z"><div><div></div></div><select id="select"></select></span></fieldset></body>
+</html>
diff --git a/layout/generic/crashtests/463741-1.html b/layout/generic/crashtests/463741-1.html
new file mode 100644
index 000000000..97f228aa8
--- /dev/null
+++ b/layout/generic/crashtests/463741-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html style="width: 1px;">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.documentElement.appendChild(document.body);
+ document.documentElement.offsetHeight;
+ var v = document.getElementById("v");
+ v.parentNode.removeChild(v);
+ var w = document.createElement("span");
+ document.body.insertBefore(w, document.body.lastChild);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div></div><div style="height: 1px;"><span><div id="v"></div></span><div style="-moz-column-count: 2;">A<div style="margin: 67108863ch 0pt;">B</div><div>C</div></div></div><span></span></body>
+</html>
diff --git a/layout/generic/crashtests/463785.xhtml b/layout/generic/crashtests/463785.xhtml
new file mode 100644
index 000000000..befeaf8fd
--- /dev/null
+++ b/layout/generic/crashtests/463785.xhtml
@@ -0,0 +1,40 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml" class="reftest-wait">
+
+<bindings xmlns="http://www.mozilla.org/xbl" xmlns:xlink="http://www.w3.org/1999/xlink">
+<binding id="xbl">
+<content>
+<div xmlns="http://www.w3.org/1999/xhtml" style="border: 100px solid red;">
+<div style="position: fixed;"/>
+</div>
+</content>
+</binding>
+</bindings>
+
+<div style="position: absolute; -moz-column-count: 2;">
+<table style="border: 100px solid green;" id="c">
+<tr id="b" style="-moz-binding:url(#xbl)">
+<td style="position: absolute;">
+m
+ <span id="a">
+ <div style="border: 100px solid black;">
+ <div style="position: fixed;"/>
+ </div>
+ </span>
+
+</td>
+</tr>
+</table>
+</div>
+
+<script>
+var doc = document;
+function doe() {
+ var newNode = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
+ newNode.innerHTML = '<div xmlns="http://www.w3.org/1999/xhtml" style="border: 100px solid black;"><div style="position: fixed;"/></div>';
+ document.getElementById('c').insertBefore(newNode, doc.getElementById('b'));
+ document.getElementById('b').removeAttribute('style');
+ document.documentElement.removeAttribute("class");
+}
+setTimeout(doe, 100);
+</script>
+</html>
diff --git a/layout/generic/crashtests/465651-1.html b/layout/generic/crashtests/465651-1.html
new file mode 100644
index 000000000..04931d4b9
--- /dev/null
+++ b/layout/generic/crashtests/465651-1.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+
+<style type="text/css">
+
+.contain {
+ height: 10px;
+ margin: 2px 3px;
+ padding-top: 1px;
+}
+.fl {
+ float: left;
+ width: 4px;
+ height: 5px;
+}
+.fr {
+ float: right;
+ width: 5px;
+ height: 5px;
+}
+
+</style>
+
+</head>
+
+<body>
+ <div style="width: 41px; -moz-column-count: 3;">
+ <div class="contain"></div>
+ <div class="contain"></div>
+ <div class="fl"></div>
+ <div class="fl"></div>
+ <div class="fr"></div>
+ <div></div>
+ <div class="fr"></div>
+ <div class="contain"><div class="fl"></div></div>
+ <div class="contain" style="float: left;"></div>
+ <div class="fl"></div>
+ <div class="fr"></div>
+ <div class="contain"><div class="fl"></div><div class="fr"></div></div>
+ <div class="fl"></div>
+ <div class="fr"></div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/467137-1.html b/layout/generic/crashtests/467137-1.html
new file mode 100644
index 000000000..0414baccd
--- /dev/null
+++ b/layout/generic/crashtests/467137-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function z()
+{
+ var q = document.getElementById("q");
+
+ for (var r = 0; r < 100; ++r) {
+ // dump(r + "\n");
+ q.style.width = r + "px";
+ document.documentElement.offsetHeight;
+ }
+}
+
+</script>
+</head>
+<body onload="z();">
+
+<div style="font-family: monospace;" id="q"><div id="w" style="word-spacing: 1px">AAA <span style="white-space: pre-line; font-weight: 500;">BB C
+ </span></div></div>
+
+</body></html>
diff --git a/layout/generic/crashtests/467213-1.html b/layout/generic/crashtests/467213-1.html
new file mode 100644
index 000000000..b9ea48e08
--- /dev/null
+++ b/layout/generic/crashtests/467213-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+
+<div style="direction: rtl;"><span style="direction: ltr;"><div></div><span style="position: absolute;"></span><span style="display: -moz-box;"></span></span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/467487-1.html b/layout/generic/crashtests/467487-1.html
new file mode 100644
index 000000000..f6830a4a1
--- /dev/null
+++ b/layout/generic/crashtests/467487-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('y').style.width = '8000px';">
+
+<div style="white-space: pre-line;"><div id="y"><div style="text-align: justify; font-size: 3300%;">AB CDEF
+ </div></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/467493-1.html b/layout/generic/crashtests/467493-1.html
new file mode 100644
index 000000000..b129e3e82
--- /dev/null
+++ b/layout/generic/crashtests/467493-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+<div style="-moz-column-count: 15; overflow-y: hidden;"><div><div style="clear: both; margin: 144115188075855870cm"><li></li></div><div style="position: fixed;"></div></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/467493-2.html b/layout/generic/crashtests/467493-2.html
new file mode 100644
index 000000000..874663c4e
--- /dev/null
+++ b/layout/generic/crashtests/467493-2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head><style>
+ div.a {
+ -moz-column-count: 2;
+ overflow-y: hidden;
+ background: yellow;
+ }
+ div.b {
+ background: orange;
+ clear: both;
+ margin-top: 946982.46874999995cm;
+ }
+ div.c {
+ position: fixed;
+ background: red;
+ }
+</style></head>
+<body
+ ><div class="a"><div
+ ><div class="b"><li></li></div
+ ><div class="c"></div
+ ></div></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/467875-1.xhtml b/layout/generic/crashtests/467875-1.xhtml
new file mode 100644
index 000000000..b805a30a9
--- /dev/null
+++ b/layout/generic/crashtests/467875-1.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head></head>
+<body>
+<span style="direction: rtl;">
+ <span style="display: -moz-box"/>
+ <span style="position: fixed;"/>
+ <span style="display: -moz-box"/>
+</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/467914-1.html b/layout/generic/crashtests/467914-1.html
new file mode 100644
index 000000000..1066a6c8a
--- /dev/null
+++ b/layout/generic/crashtests/467914-1.html
@@ -0,0 +1,3 @@
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML">
+<mathml:munder style="-moz-transform: translate(50px);clip-path: url(#h);"/>
+</window>
diff --git a/layout/generic/crashtests/468207-1.html b/layout/generic/crashtests/468207-1.html
new file mode 100644
index 000000000..230fb93af
--- /dev/null
+++ b/layout/generic/crashtests/468207-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body><div style="-moz-column-width: 1px;"><div style="height: 5em;"><div style="height: 1em;"></div><div style="height: 5em; float: right;"></div></div><div style="height: 1em;"></div><div style="float: right;"></div><div style="float: left; height: 5em;"></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/468771-1.xhtml b/layout/generic/crashtests/468771-1.xhtml
new file mode 100644
index 000000000..58196fc89
--- /dev/null
+++ b/layout/generic/crashtests/468771-1.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var newTD = document.createElementNS("http://www.w3.org/1999/xhtml", "td");
+ newTD.setAttribute("rowspan", 3);
+ document.getElementById("tr1").appendChild(newTD);
+
+ document.documentElement.offsetHeight;
+
+ var newTR = document.createElementNS("http://www.w3.org/1999/xhtml", "tr");
+ document.getElementById("table").appendChild(newTR);
+}
+
+</script>
+
+</head>
+
+<body onload="boom();">
+
+<table id="table">AAAA<tr id="tr1"></tr><tr><td><div style="-moz-column-width: 1px;">B C</div></td></tr></table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/468771-2.xhtml b/layout/generic/crashtests/468771-2.xhtml
new file mode 100644
index 000000000..19c806d02
--- /dev/null
+++ b/layout/generic/crashtests/468771-2.xhtml
@@ -0,0 +1,22 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var newTD = document.createElementNS("http://www.w3.org/1999/xhtml", "td");
+ newTD.setAttribute("rowspan", 3);
+ document.getElementById("tr1").appendChild(newTD);
+}
+
+</script>
+
+</head>
+
+<body onload="boom();">
+
+<table id="table" border="1">AAAA<tr id="tr1"></tr><tr><td><div style="-moz-column-width: 1px;">B C</div></td></tr></table>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/469859-1.xhtml b/layout/generic/crashtests/469859-1.xhtml
new file mode 100644
index 000000000..8377b78f1
--- /dev/null
+++ b/layout/generic/crashtests/469859-1.xhtml
@@ -0,0 +1,32 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style id="ss" type="text/css">
+
+#o {
+ height: 65px;
+}
+
+</style>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("ss").disabled = true;
+ document.documentElement.offsetHeight;
+ document.getElementById("ss").disabled = false;
+}
+
+</script>
+</head>
+<body onload="boom();">
+
+<div style="-moz-column-width: 0;">
+ <colgroup></colgroup>
+ <div>
+ <div id="o" style="float: left;"></div>
+ <div style="clear: both;">A B<div style="float: left;"></div></div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/472587-1.xhtml b/layout/generic/crashtests/472587-1.xhtml
new file mode 100644
index 000000000..bf9209cb2
--- /dev/null
+++ b/layout/generic/crashtests/472587-1.xhtml
@@ -0,0 +1,28 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="http://www.w3.org/1998/Math/MathML">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var m = document.getElementById("m");
+ m.parentNode.removeChild(m);
+ document.getElementById("s").appendChild(document.createTextNode("c"));
+}
+
+</script>
+</head>
+<body onload="boom();">
+
+<m:mrow>
+ <span>
+ <m:msup id="m"/>
+ <div style="-moz-column-width: 1px;">
+ <m:munderover/>
+ <m:msqrt/>
+ </div>
+ </span>
+ <span id="s"></span>
+</m:mrow>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/472617-1.xhtml b/layout/generic/crashtests/472617-1.xhtml
new file mode 100644
index 000000000..c430eea44
--- /dev/null
+++ b/layout/generic/crashtests/472617-1.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<head></head>
+<body style="-moz-column-width: 2351490cm;"><div style="height: 0;"><xul:hbox/><xul:button/><span style="float: right;"/></div><xul:button/><div/><span style="float: right;"/></body>
+</html>
diff --git a/layout/generic/crashtests/472774-1.html b/layout/generic/crashtests/472774-1.html
new file mode 100644
index 000000000..1709c7aaf
--- /dev/null
+++ b/layout/generic/crashtests/472774-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<style id="ss" type="text/css">
+
+div div:first-letter { font-size: 0%; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("ss").textContent = "div div { height: 1px; }";
+}
+
+</script>
+</head>
+<body onload="boom();">
+
+<div style="-moz-column-width: 1px; word-wrap: break-word;"><div>ABCDE</div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/472776-1.html b/layout/generic/crashtests/472776-1.html
new file mode 100644
index 000000000..745dcc167
--- /dev/null
+++ b/layout/generic/crashtests/472776-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var v = document.getElementById("v");
+ v.childNodes[1].firstChild.data = "";
+ document.documentElement.offsetHeight;
+ v.appendChild(document.createTextNode("D"));
+ v.removeChild(v.firstChild);
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id="v"><span>A</span><span>&#x06CC;C</span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/472950-1.html b/layout/generic/crashtests/472950-1.html
new file mode 100644
index 000000000..cd2a49aa3
--- /dev/null
+++ b/layout/generic/crashtests/472950-1.html
@@ -0,0 +1,21 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>div::first-letter { color: green; }</style>
+<script>
+
+function boom()
+{
+ var e = document.getElementById("e");
+ document.documentElement.style.direction = "rtl";
+ e.style.whiteSpace = "pre";
+}
+
+</script>
+</head>
+<body onload="boom();">
+
+<div><span style="direction: rtl;" id="e"><span>
+
+ </span>A B</span></div>
+
+</body></html></html>
diff --git a/layout/generic/crashtests/472957.xhtml b/layout/generic/crashtests/472957.xhtml
new file mode 100644
index 000000000..7b4fbd036
--- /dev/null
+++ b/layout/generic/crashtests/472957.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="b">
+ <content><xul:hbox><children/></xul:hbox></content>
+ </binding>
+</bindings>
+
+</head>
+
+<body><xul:listboxbody height="168178912813" style="-moz-binding: url(#b);"><xul:iframe/></xul:listboxbody></body>
+
+</html>
diff --git a/layout/generic/crashtests/473278-1.xhtml b/layout/generic/crashtests/473278-1.xhtml
new file mode 100644
index 000000000..c4aa34d6b
--- /dev/null
+++ b/layout/generic/crashtests/473278-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><mmultiscripts xmlns="http://www.w3.org/1998/Math/MathML" style="clip-path: url(#q); -moz-transform: translate(100px, 100px);"/></body></html>
diff --git a/layout/generic/crashtests/473894-1.html b/layout/generic/crashtests/473894-1.html
new file mode 100644
index 000000000..dd4561d6f
--- /dev/null
+++ b/layout/generic/crashtests/473894-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="margin: -10000px">X</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/476241-1.html b/layout/generic/crashtests/476241-1.html
new file mode 100644
index 000000000..405802a9a
--- /dev/null
+++ b/layout/generic/crashtests/476241-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><body><div style="-moz-column-gap: 1px; -moz-column-width: 1px;"><div style="-moz-column-width: 1px;"><div><div style="margin: 15000px 0pt; -moz-column-width: 1px;"><div style="height: 1px;">G P X<br style="margin: 15000px 0pt;"></div></div><div style="padding: 10px; height: 200px;"></div></div> </div><br style="margin: 15000px 0pt;"></div></body></html>
diff --git a/layout/generic/crashtests/477731-1.html b/layout/generic/crashtests/477731-1.html
new file mode 100644
index 000000000..f017fa7cf
--- /dev/null
+++ b/layout/generic/crashtests/477731-1.html
@@ -0,0 +1,6 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style type="text/css">body:first-letter { float: left; }</style>
+</head>
+<body style="-moz-column-width: 100000px;" onload="document.body.style.MozColumnWidth='';"> &#x08D9;</body>
+</html>
diff --git a/layout/generic/crashtests/477928.html b/layout/generic/crashtests/477928.html
new file mode 100644
index 000000000..83a9a3853
--- /dev/null
+++ b/layout/generic/crashtests/477928.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ document.getElementById("a").appendChild(document.createTextNode("\n"));
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id="a" style="max-width: -moz-max-content; -moz-column-count: 2;"><span style="white-space: pre-line;"><span>
+</span>
+</span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/478131-1.html b/layout/generic/crashtests/478131-1.html
new file mode 100644
index 000000000..9ef68115e
--- /dev/null
+++ b/layout/generic/crashtests/478131-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style id="s"></style>
+</head>
+<body onload="document.getElementById('s').textContent = '* { font-size: 8193%; }';" style="-moz-column-count: 2;"><div><div>A B C</div></div><div><p>D E F</p></div><p>G H I</p><div><p>.</p></div> <p>J K L</p></body>
+</html>
diff --git a/layout/generic/crashtests/478170-1.html b/layout/generic/crashtests/478170-1.html
new file mode 100644
index 000000000..79337b9e1
--- /dev/null
+++ b/layout/generic/crashtests/478170-1.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script type="text/javascript">
+function bounce()
+{
+ var b = document.body;
+ var dE = document.documentElement;
+ dE.removeChild(b);
+ dE.offsetHeight;
+ dE.appendChild(b)
+}
+</script>
+</head>
+<body onload="bounce();">
+<table><tbody><tr><td><div style="-moz-column-count: 2;"><div><span style="font-size: 91735350in;"><table><tbody><tr><td></td></tr></tbody></table><div><p><select></select></p><table><tbody><tr><td></td></tr></tbody></table></div></span></div><div style="height: 300px;"></div></div></td></tr></tbody><thead></thead></table>
+</body>
+</html>
diff --git a/layout/generic/crashtests/478185-1.html b/layout/generic/crashtests/478185-1.html
new file mode 100644
index 000000000..6349bad71
--- /dev/null
+++ b/layout/generic/crashtests/478185-1.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+#v {
+ -moz-column-count: 2;
+ direction: rtl;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ text-transform: capitalize;
+ letter-spacing: 163851344580570600em;
+}
+
+#v:first-letter { }
+
+</style>
+</head>
+
+<body>
+<div id="v">
+
+#xxx {
+ xxxxxxxx: xxxxxxxx;
+ xxxxxxxxxx-xxxxx: xxxx;
+ xxx: xxxx;
+ xxxx: xxxx;
+ xxxxx: xxxxx;
+ xxxxxx: xxxxx;
+}
+
+#xxxxx {
+ xxxxxxxx: xxxxxxxx;
+ xxxxxxxxxx-xxxxx: xxxx;
+ xxx: xxxx;
+ xxxxx: xxxx;
+ xxxxx: xxxx;
+ xxxxxx: xxxxx;
+}
+
+#xxxx {
+ xxxxxxxx: xxxxxxxx;
+ xxxxxxxxxx-xxxxx: xxxx;
+ xxxx: xxxx;
+ xxxxxx: xxxx;
+ xxxxx: xxxxx;
+ xxxxxx: xxxx;
+}
+
+#xxxxxx {
+ xxxxxxxx: xxxxxxxx;
+ xxxxxxxxxx-xxxxx: xxxx;
+ xxxxx: xxxx;
+ xxxxx: xxxx;
+ xxxxxx: xxxx;
+ xxxxxx: xxxx;
+}
+
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/479938-1.html b/layout/generic/crashtests/479938-1.html
new file mode 100644
index 000000000..642a74ecb
--- /dev/null
+++ b/layout/generic/crashtests/479938-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("x").style.padding = "67108863pc";
+ setTimeout(boom2, 0);
+}
+
+function boom2()
+{
+ document.body.removeChild(document.getElementById("colset"));
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="boom();"> <div style="-moz-column-count: 2;" id="colset"><div style="height: 1px;"><div id="x"><div style="width: 1px;">A B C D</div></div></div></div> </body>
+
+</html>
diff --git a/layout/generic/crashtests/480345-1.html b/layout/generic/crashtests/480345-1.html
new file mode 100644
index 000000000..8de70d2d7
--- /dev/null
+++ b/layout/generic/crashtests/480345-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<body style="background:url(solidblue.png); position:absolute; height:40in;">
+</body>
+</html>
diff --git a/layout/generic/crashtests/481921-iframe.html b/layout/generic/crashtests/481921-iframe.html
new file mode 100644
index 000000000..d83310d6f
--- /dev/null
+++ b/layout/generic/crashtests/481921-iframe.html
@@ -0,0 +1,12 @@
+<html>
+ <body onload="dotest(); setTimeout('location.reload()', 200)">
+ <script language="javascript">
+ var count=0;
+ var fileloc = "481921.ogg";
+ function dotest(){
+ oggenv.innerHTML = "video test for " + fileloc + "<br><video src=\"" + fileloc + "\" autoplay=\"true\" height=100></video>";
+ }
+ </script>
+ <span id="oggenv"></span><br>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/481921.html b/layout/generic/crashtests/481921.html
new file mode 100644
index 000000000..60dd53169
--- /dev/null
+++ b/layout/generic/crashtests/481921.html
@@ -0,0 +1,20 @@
+<html class="reftest-wait">
+<head>
+<script type="text/javascript">
+
+function done()
+{
+ document.documentElement.removeAttribute("class");
+ document.body.innerHTML=''
+}
+
+setTimeout(done,800)
+</script>
+</head>
+
+<body>
+
+<iframe id="iframe" src="481921-iframe.html"></iframe>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/481921.ogg b/layout/generic/crashtests/481921.ogg
new file mode 100644
index 000000000..0c41c3cd6
--- /dev/null
+++ b/layout/generic/crashtests/481921.ogg
Binary files differ
diff --git a/layout/generic/crashtests/489462-1.html b/layout/generic/crashtests/489462-1.html
new file mode 100644
index 000000000..5a57647ec
--- /dev/null
+++ b/layout/generic/crashtests/489462-1.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.style.whiteSpace = "";
+ document.getElementById("b").style.direction = "";
+}
+
+</script>
+
+<style type="text/css">
+
+#c:first-letter { font-size-adjust: 8388609; }
+
+</style>
+</head>
+
+<body onload="boom();" style="white-space: pre;"><div id="b" style="direction: rtl;"><div id="c">Qqq Rrr Sss.</div></div></body>
+</html>
diff --git a/layout/generic/crashtests/489477.html b/layout/generic/crashtests/489477.html
new file mode 100644
index 000000000..34ef73626
--- /dev/null
+++ b/layout/generic/crashtests/489477.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.execCommand("selectAll", false, null);
+ document.execCommand("formatBlock", false, "<h5>");
+ document.execCommand("justifyfull", false, null);
+ document.execCommand("indent", false, null);
+ document.execCommand("outdent", false, null);
+ document.getElementById("q").appendChild(document.createTextNode('v'));
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="width: 800px; -moz-column-count: 4; column-count: 4;"><div contenteditable="true" style="height: 80px;"><div><div><hr><span> </span></div></div></div><div id="q" style="height: 80px;"><div style="float: left; height: 10px; width: 10px;"></div><div style="padding: 180px; -moz-column-count: 1; column-count: 1; clear: right;"></div></div></body>
+</html>
+
diff --git a/layout/generic/crashtests/489480-1.xhtml b/layout/generic/crashtests/489480-1.xhtml
new file mode 100644
index 000000000..1757c9d82
--- /dev/null
+++ b/layout/generic/crashtests/489480-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="-moz-column-count: 2; width: 0pt;"><body style="height: 5003810179.579391in;"><br/><div style="direction: rtl;"><select style="float: right;"></select><option style="width: 0.6600934846211504px; margin: 22367196.5776782cm;"><option style="-moz-column-count: 2;"></option></option></div></body></html>
diff --git a/layout/generic/crashtests/493111-1.html b/layout/generic/crashtests/493111-1.html
new file mode 100644
index 000000000..f851074d6
--- /dev/null
+++ b/layout/generic/crashtests/493111-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script type="application/javascript">
+ function onLoad() {
+ var text = document.getElementById("text").firstChild;
+ var sel = window.getSelection();
+ var r1 = document.createRange();
+ r1.setStart(text, 0);
+ r1.setEnd(text, 5);
+ sel.addRange(r1);
+ var r2 = document.createRange();
+ r2.setStart(text, 4);
+ r2.setEnd(text, 9);
+ sel.addRange(r2);
+ }
+ </script>
+ </head>
+ <body onload="onLoad();">
+ <p id="text">Adding overlapping ranges to a selection shouldn't assert</p>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/493118-1.html b/layout/generic/crashtests/493118-1.html
new file mode 100644
index 000000000..0ab7fa521
--- /dev/null
+++ b/layout/generic/crashtests/493118-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" style="-moz-column-count: 2;">
+<body style="padding: 731563462617733px; height: 10px;">
+<div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/493649.html b/layout/generic/crashtests/493649.html
new file mode 100644
index 000000000..e01802a00
--- /dev/null
+++ b/layout/generic/crashtests/493649.html
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="position: fixed; -moz-column-count: 3; white-space: pre;"><body style="height: 0pt;">
+
+
+
+</body></html>
diff --git a/layout/generic/crashtests/494283-1.xhtml b/layout/generic/crashtests/494283-1.xhtml
new file mode 100644
index 000000000..68a2406b3
--- /dev/null
+++ b/layout/generic/crashtests/494283-1.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="position: absolute; display: table;">
+<head><style>span:before { content: '1' }</style></head>
+<body onload="document.documentElement.style.display = '';document.documentElement.offsetHeight;"><div style="position: absolute;"></div><span></span></body>
+</html>
diff --git a/layout/generic/crashtests/494283-2.html b/layout/generic/crashtests/494283-2.html
new file mode 100644
index 000000000..86fc1e790
--- /dev/null
+++ b/layout/generic/crashtests/494283-2.html
@@ -0,0 +1,6 @@
+<body>
+ <fieldset id="x"><legend>longlonglong</legend></fieldset>
+ <script>
+ var x = document.getElementById("x");
+ x.insertBefore(document.createTextNode("aa"), x.firstChild);
+ </script>
diff --git a/layout/generic/crashtests/494300-1.xul b/layout/generic/crashtests/494300-1.xul
new file mode 100644
index 000000000..8aa9701dd
--- /dev/null
+++ b/layout/generic/crashtests/494300-1.xul
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="boom();" class="reftest-wait">
+<script type="text/javascript">
+// <![CDATA[
+
+var HTML_NS = "http://www.w3.org/1999/xhtml";
+var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
+var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function boom()
+{
+ var listbox = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "listbox");
+ var listitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "listitem");
+ listbox.appendChild(listitem);
+ document.documentElement.appendChild(listbox);
+ var hbox = document.createElementNS(XUL_NS, "hbox");
+ listbox.appendChild(hbox);
+ var mphantom = document.createElementNS(MATHML_NS, 'mphantom');
+ listbox.appendChild(mphantom);
+ var wax = document.createElementNS(MATHML_NS, 'wax');
+ hbox.appendChild(wax);
+ var msub = document.createElementNS(MATHML_NS, 'msub');
+ wax.appendChild(msub);
+ var merror = document.createElementNS(MATHML_NS, 'merror');
+ wax.appendChild(merror);
+ var span = document.createElementNS(HTML_NS, 'span');
+ mphantom.appendChild(span);
+ var vbox = document.createElementNS(XUL_NS, 'vbox');
+ span.appendChild(vbox);
+
+ setTimeout(boom2, 0);
+
+ function boom2()
+ {
+ var munderover = document.createElementNS(MATHML_NS, 'munderover');
+ msub.appendChild(munderover);
+ var mtext = document.createElementNS(MATHML_NS, 'mtext');
+ span.appendChild(mtext);
+
+ document.documentElement.removeAttribute("class");
+ }
+}
+
+// ]]>
+</script>
+</window>
diff --git a/layout/generic/crashtests/494332-1.html b/layout/generic/crashtests/494332-1.html
new file mode 100644
index 000000000..3ab4b71c0
--- /dev/null
+++ b/layout/generic/crashtests/494332-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+<div style="width: 1ch;"><div style="height: 2em;">1 2<div style="float: left; padding: 0pt 1px; display: list-item;"></div></div><span></span> g h i</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/495875-1.html b/layout/generic/crashtests/495875-1.html
new file mode 100644
index 000000000..9c196f808
--- /dev/null
+++ b/layout/generic/crashtests/495875-1.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body style="-moz-column-count: 2; white-space: pre-wrap; font-size-adjust: 4294967297; text-transform: uppercase;"
+ onload="document.body.style.fontSizeAdjust = '';"
+>&#xD558;A B C&#x0643;&#x5599;D
+
+</body></html>
diff --git a/layout/generic/crashtests/495875-2.html b/layout/generic/crashtests/495875-2.html
new file mode 100644
index 000000000..b94670acf
--- /dev/null
+++ b/layout/generic/crashtests/495875-2.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body style="-moz-column-count: 2; white-space: pre-wrap; font-size-adjust: 4294967297; text-transform: uppercase;"
+ onload="document.body.style.fontSizeAdjust = '';"
+>&#xD558;A B C<b>&#x0443;</b>&#x5599;D
+
+</body></html>
diff --git a/layout/generic/crashtests/496742.html b/layout/generic/crashtests/496742.html
new file mode 100644
index 000000000..e693b2663
--- /dev/null
+++ b/layout/generic/crashtests/496742.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title>Crash [@ nsHTMLReflowState::GetHypotheticalBoxContainer] with position: fixed, float right</title>
+</head>
+<body>
+<iframe src="data:text/html;charset=utf-8,%3Cspan%3E%0Am%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20%0Am%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20%0Am%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20%0Am%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20m%20%0A%3Cspan%20style%3D%22position%3A%20fixed%3B%20float%3A%20right%3B%22%3E%3C/span%3E%0A%3C/span%3E%0A%0A%3Cscript%3E%0Afunction%20toggleIframe%28%29%7B%0Avar%20x%3Dwindow.frameElement%3B%0Ax.style.display%20%3D%20x.style.display%20%3D%3D%20%27none%27%20%3F%20x.style.display%20%3D%20%27%27%20%3A%20x.style.display%20%3D%20%27none%27%3B%0AsetTimeout%28toggleIframe%2C100%29%3B%0A%7D%0AsetTimeout%28toggleIframe%2C100%29%3B%0A%3C/script%3E"></iframe>
+<script>
+
+</script>
+</body>
+</html>
diff --git a/layout/generic/crashtests/499138-iframe.html b/layout/generic/crashtests/499138-iframe.html
new file mode 100644
index 000000000..f8825b14f
--- /dev/null
+++ b/layout/generic/crashtests/499138-iframe.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+
+</head>
+<body onload="document.getElementById('a').removeAttribute('style');setTimeout(function() {window.location.reload()}, 500);">
+<div style="overflow: scroll; position: absolute; -moz-column-count: 2;">
+
+<div style="position: absolute;">
+<input id="a" style="position: absolute;" type="radio">
+<object>
+ &#1593; m &#1593; m &#1593; m &#1593; m &#1593; m &#1593; m
+<ul> &#1593; m &#1593; m &#1593; m</ul>
+</object>
+</div>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/499138.html b/layout/generic/crashtests/499138.html
new file mode 100644
index 000000000..7e7d84dfa
--- /dev/null
+++ b/layout/generic/crashtests/499138.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase for bug 499138</title>
+<script>
+function done()
+{
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="setTimeout(done,1000)">
+
+<iframe src="499138-iframe.html"></iframe>
+
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/499857-1.html b/layout/generic/crashtests/499857-1.html
new file mode 100644
index 000000000..f105e1b39
--- /dev/null
+++ b/layout/generic/crashtests/499857-1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<style type="text/css">
+
+#relleft {
+ float: left;
+ width: 290px;
+ margin: 15px 0 0 0;
+}
+
+#fl:first-line { }
+
+#cols { -moz-column-width: 4503599627370497mm; }
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ document.getElementById("x").setAttribute("id", "cols");
+ document.getElementById("fl").firstChild.splitText(1);
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<div id="x"><div id="relleft"></div><div id="fl">
+a b c d</div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/499862-1.html b/layout/generic/crashtests/499862-1.html
new file mode 100644
index 000000000..eb614cbd0
--- /dev/null
+++ b/layout/generic/crashtests/499862-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+body::first-letter { float: left; }
+</style>
+</head>
+<body style="text-transform: capitalize;">T</body>
+</html>
diff --git a/layout/generic/crashtests/499885-1.xhtml b/layout/generic/crashtests/499885-1.xhtml
new file mode 100644
index 000000000..1ee88dc0c
--- /dev/null
+++ b/layout/generic/crashtests/499885-1.xhtml
@@ -0,0 +1,6 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<bindings xmlns="http://www.mozilla.org/xbl"><binding id="emptybinding"><content></content></binding></bindings>
+</head>
+<body style="-moz-column-width: 1px;"><select/><li/><select style="height: 1777px; padding: 8796093022208pt; display: inherit; -moz-binding: url(#emptybinding);"/></body>
+</html>
diff --git a/layout/generic/crashtests/501535-1.html b/layout/generic/crashtests/501535-1.html
new file mode 100644
index 000000000..8daabfb0d
--- /dev/null
+++ b/layout/generic/crashtests/501535-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="document.getElementById('a').setAttribute('poster', '#');">
+<audio controls id="a">
+</body>
+</html>
diff --git a/layout/generic/crashtests/503961-1.xhtml b/layout/generic/crashtests/503961-1.xhtml
new file mode 100644
index 000000000..920b73824
--- /dev/null
+++ b/layout/generic/crashtests/503961-1.xhtml
@@ -0,0 +1,25 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style type="text/css">
+ #colset { -moz-column-count: 3; }
+ #a { height: 0px; }
+ #b { height: 2px; }
+ #c { height: 1px; }
+ #d { height: 2px; }
+ #e { height: 2px; }
+</style>
+<script type="text/javascript">
+function boom()
+{
+ document.getElementById("a").style.height = "auto";
+ document.getElementById("d").style.height = "auto";
+}
+</script>
+</head>
+<body onload="boom();"
+ ><div id="colset"
+ ><div id="a"><div id="b"/></div
+ ><div id="c"><div id="d"/><div id="e"/></div
+ ></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/503961-2.html b/layout/generic/crashtests/503961-2.html
new file mode 100644
index 000000000..5b87b5855
--- /dev/null
+++ b/layout/generic/crashtests/503961-2.html
@@ -0,0 +1,32 @@
+<html>
+<head>
+ <style>
+ #colset { -moz-column-count: 3; }
+ #a { height: 0; }
+ #x { height: 3px; }
+ #b { height: 0; }
+ #c { height: 2px; }
+ #d { height: 2px; }
+
+ /* Following style is just for visualization -- doesn't affect assertion */
+ #colset { -moz-column-gap: 0px; width: 18px; }
+ div { width: 5px; }
+ #a { background: purple; }
+ #x { background: orange; }
+ #b { background: blue; }
+ #c { background: black; }
+ #d { background: lime; }
+ </style>
+ <script>
+ function boom()
+ {
+ document.getElementById("a").style.height = "auto";
+ document.getElementById("c").style.height = "0";
+ }
+ </script>
+</head>
+<body onload="boom()" id="colset"
+ ><div id="a"><div id="x"></div></div
+ ><div id="b"><div id="c"></div><div id="d"></div></div
+></body>
+</html>
diff --git a/layout/generic/crashtests/505912-1.html b/layout/generic/crashtests/505912-1.html
new file mode 100644
index 000000000..984c44711
--- /dev/null
+++ b/layout/generic/crashtests/505912-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="document.getElementById('x').style.visibility = 'hidden';">
+<embed id="x" type="application/x-test" wmode="window"></embed>
+</body>
+</html>
diff --git a/layout/generic/crashtests/508154-1.xhtml b/layout/generic/crashtests/508154-1.xhtml
new file mode 100644
index 000000000..5a2d96cc4
--- /dev/null
+++ b/layout/generic/crashtests/508154-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body style="float: left;"></body><span style="float: left; margin: 10%; min-height: 17895698px;"></span></html>
diff --git a/layout/generic/crashtests/508168-1.html b/layout/generic/crashtests/508168-1.html
new file mode 100644
index 000000000..0ab7fa521
--- /dev/null
+++ b/layout/generic/crashtests/508168-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" style="-moz-column-count: 2;">
+<body style="padding: 731563462617733px; height: 10px;">
+<div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/508816-1.xul b/layout/generic/crashtests/508816-1.xul
new file mode 100644
index 000000000..46543f563
--- /dev/null
+++ b/layout/generic/crashtests/508816-1.xul
@@ -0,0 +1,9 @@
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="direction: rtl">
+ <scrollbox maxwidth="100" style="overflow: scroll;">
+ <button label="One"/>
+ <button label="Two"/>
+ <button label="Three"/>
+ <button label="Four and the rest of the numbers go here"/>
+ </scrollbox>
+</window>
diff --git a/layout/generic/crashtests/508908-1.html b/layout/generic/crashtests/508908-1.html
new file mode 100644
index 000000000..71af22c42
--- /dev/null
+++ b/layout/generic/crashtests/508908-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">
+
+
+function boom()
+{
+ var sel = document.createElementNS("http://www.w3.org/1999/xhtml", "select");
+ var opt;
+ for (var i = 0; i < 43; ++i) {
+ opt = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
+ opt.appendChild(document.createTextNode(i));
+ sel.appendChild(opt);
+ }
+ opt.selected = "selected";
+ document.getElementById("div").appendChild(sel);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div id="div"></div><embed type="application/x-test"></embed></body>
+</html>
diff --git a/layout/generic/crashtests/509749-1.html b/layout/generic/crashtests/509749-1.html
new file mode 100644
index 000000000..e3af931ae
--- /dev/null
+++ b/layout/generic/crashtests/509749-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html style="-moz-column-width: 1px;">
+<head></head>
+<body><div style="position: relative;"><div style="float: left; padding: 10px 20px 0pt;"><div style="position: absolute; top: 0pt;"><div></div><div style="position: fixed;"></div>S</div></div><div style="clear: both; padding: 20px 20px 15px;"></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/511482.html b/layout/generic/crashtests/511482.html
new file mode 100644
index 000000000..b0a5b63ed
--- /dev/null
+++ b/layout/generic/crashtests/511482.html
@@ -0,0 +1,42 @@
+<html>
+ <body>
+ <div style="border: 5px solid blue;">
+ <div style="-moz-column-width: 530px;height:300px; border: 5px solid red;">
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ <div style="width:128px; height:128px;border: 5px solid green" >
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/512724-1.html b/layout/generic/crashtests/512724-1.html
new file mode 100644
index 000000000..27a671e32
--- /dev/null
+++ b/layout/generic/crashtests/512724-1.html
@@ -0,0 +1 @@
+<html style="-moz-column-width: 1px; -moz-column-gap: 6834954840cm"><body></body></html>
diff --git a/layout/generic/crashtests/512725-1.html b/layout/generic/crashtests/512725-1.html
new file mode 100644
index 000000000..775262b9a
--- /dev/null
+++ b/layout/generic/crashtests/512725-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<table style="line-height: 1em; font-size: 1483271385%;"><tbody><tr><td></td></tr></tbody></table>
+</body>
+</html>
diff --git a/layout/generic/crashtests/512749-1.html b/layout/generic/crashtests/512749-1.html
new file mode 100644
index 000000000..12829799e
--- /dev/null
+++ b/layout/generic/crashtests/512749-1.html
@@ -0,0 +1 @@
+<html style="position:fixed"><table style="position:absolute"></table></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/513110-1.html b/layout/generic/crashtests/513110-1.html
new file mode 100644
index 000000000..f33067cd4
--- /dev/null
+++ b/layout/generic/crashtests/513110-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var s = document.getElementById("s");
+ document.body.removeChild(s);
+ document.body.appendChild(s);
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
+</head>
+
+<body>
+<span style="word-spacing: -379660px">a </span>
+<span id="s"><br style="clear: both;"/></span>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/513110-2.xhtml b/layout/generic/crashtests/513110-2.xhtml
new file mode 100644
index 000000000..e1fcb499d
--- /dev/null
+++ b/layout/generic/crashtests/513110-2.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="width: 1px;" onload="document.documentElement.offsetHeight; document.getElementById('x').style.display = 'table-footer-group';">
+<span>1</span> <br id="x" style="clear: both;" />
+</body>
+</html>
diff --git a/layout/generic/crashtests/513394-1.html b/layout/generic/crashtests/513394-1.html
new file mode 100644
index 000000000..7296695d7
--- /dev/null
+++ b/layout/generic/crashtests/513394-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+#w:after {
+content: "A";
+display: block;
+height: 0;
+clear: both;
+}
+</style>
+</head>
+<body onload="document.getElementById('c').style.height = '20px';" style="width: 300px">
+<div style="-moz-column-count: 2;"><div style="width: 200px; float: left;"><div id="c" style="padding-top: 30px;"></div></div><div style="padding: 10px 0pt;"><div><div id="w"><div style="display: list-item; float: left; margin-right: 100px;"></div></div><div style="height: 20px; display: inline-block;"></div></div></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/514098-1.xhtml b/layout/generic/crashtests/514098-1.xhtml
new file mode 100644
index 000000000..ed4b4e727
--- /dev/null
+++ b/layout/generic/crashtests/514098-1.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="http://www.w3.org/1998/Math/MathML">
+<head>
+<script>
+
+function boom()
+{
+ document.getElementById("td").contentEditable = "true";
+ document.execCommand("justifyfull", false, null);
+}
+
+</script>
+</head>
+<body onload="boom();">
+<m:msubsup><td id="td"><m:mn/></td></m:msubsup>
+</body>
+</html>
diff --git a/layout/generic/crashtests/514800-1.html b/layout/generic/crashtests/514800-1.html
new file mode 100644
index 000000000..1a2861f58
--- /dev/null
+++ b/layout/generic/crashtests/514800-1.html
@@ -0,0 +1,4 @@
+<html style="position: absolute; overflow: hidden; -moz-column-count: 3;">
+<head></head>
+<body style="overflow-y: scroll;" onload="document.body.style.counterReset='c';"><div style="position: absolute; height: 200px;"></div></body>
+</html>
diff --git a/layout/generic/crashtests/515811-1.html b/layout/generic/crashtests/515811-1.html
new file mode 100644
index 000000000..177c16974
--- /dev/null
+++ b/layout/generic/crashtests/515811-1.html
@@ -0,0 +1,5 @@
+<html>
+<body onload="document.getElementById('x').style.fontSize = '4398046511103em';">
+<div style="float: left; -moz-column-count: 3;"><div><div id="x" style="margin: 1em 0pt;"></div><div style="float: left;"></div><div style="clear: both;"></div>Q</div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/517968.html b/layout/generic/crashtests/517968.html
new file mode 100644
index 000000000..9283accdb
--- /dev/null
+++ b/layout/generic/crashtests/517968.html
@@ -0,0 +1,6 @@
+<script>
+var rng = document.createRange();
+window.getSelection()["addRange"](rng);
+window.getSelection()["addRange"](rng);
+window.getSelection()["addRange"](rng);
+</script>
diff --git a/layout/generic/crashtests/519031.xhtml b/layout/generic/crashtests/519031.xhtml
new file mode 100644
index 000000000..bd8da50dc
--- /dev/null
+++ b/layout/generic/crashtests/519031.xhtml
@@ -0,0 +1,6 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head></head>
+<body onload="document.getElementById('a').appendChild(document.createTextNode(' '));">
+<div style="position: absolute; -moz-column-count: 2;"><div style="position: absolute; height: 100px;"><fieldset/><fieldset id="a"/></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/520340.html b/layout/generic/crashtests/520340.html
new file mode 100644
index 000000000..2b8dc0077
--- /dev/null
+++ b/layout/generic/crashtests/520340.html
@@ -0,0 +1,2 @@
+<!DOCTYPE HTML>
+<html style="-moz-column-width: 1px;"><head></head><body style="-moz-column-count: 2;">A B C D E F<span>&#x7E01;</span></body></html>
diff --git a/layout/generic/crashtests/522170-1.html b/layout/generic/crashtests/522170-1.html
new file mode 100644
index 000000000..7925c87bd
--- /dev/null
+++ b/layout/generic/crashtests/522170-1.html
@@ -0,0 +1 @@
+<html><div style="float: left; -moz-column-count: 3;"><div><div style="float: left; min-height: 7086320ch;"></div><div style="clear: both;"></div><span></span></div></div></html>
diff --git a/layout/generic/crashtests/526217.html b/layout/generic/crashtests/526217.html
new file mode 100644
index 000000000..dc8a485a2
--- /dev/null
+++ b/layout/generic/crashtests/526217.html
@@ -0,0 +1,16 @@
+<html class="reftest-wait">
+<head>
+<script>
+function doe() {
+document.body.removeAttribute('style');
+document.documentElement.offsetHeight;
+document.documentElement.removeAttribute("class");
+}
+setTimeout(doe,100);
+</script>
+</head>
+<body style="position: fixed; -moz-column-count: 2; min-height: 100%; top: 50%; bottom: 50%; font-size: 900px;">
+m m
+<span style=" position: fixed;"></span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/533379-1.html b/layout/generic/crashtests/533379-1.html
new file mode 100644
index 000000000..a232b19cd
--- /dev/null
+++ b/layout/generic/crashtests/533379-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body { width: 1px; }
+ul { -moz-column-count: 15; }
+li {list-style-position: inside; }
+li:first-letter {color: red; }
+</style>
+</head>
+
+<body>
+<ul><li><span>A B</span></li></ul>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/533379-2.html b/layout/generic/crashtests/533379-2.html
new file mode 100644
index 000000000..56311993c
--- /dev/null
+++ b/layout/generic/crashtests/533379-2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body { width: 1px; }
+ul { -moz-column-count: 2; }
+li {list-style-position: inside; }
+li:first-letter {color: red; }
+</style>
+</head>
+
+<body>
+<ul><li><span>A B</span></li></ul>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/534082-1.html b/layout/generic/crashtests/534082-1.html
new file mode 100644
index 000000000..1879b62fc
--- /dev/null
+++ b/layout/generic/crashtests/534082-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="font-family: monospace; width: 0;">
+<div style="-moz-column-count: 4;"><div>a b c d<span style="display: list-item;"></span></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/534366-1.html b/layout/generic/crashtests/534366-1.html
new file mode 100644
index 000000000..8dc77404b
--- /dev/null
+++ b/layout/generic/crashtests/534366-1.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+<style type="text/css">
+
+body { font-family: monospace; width: 4ch; }
+body::first-line { }
+body *::before { content: 'w';}
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var de = document.documentElement;
+ var body = document.body;
+
+ var span = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ var r1 = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mrow");
+ var mmm = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mmultiscripts");
+
+ body.appendChild(span);
+ r1.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "mrow"));
+ body.appendChild(r1);
+ body.appendChild(mmm);
+ de.offsetHeight;
+ r1.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", 'mrow'));
+ de.offsetHeight;
+ mmm.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", 'mrow'));
+ de.offsetHeight;
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
+</head>
+<body></body>
+</html>
diff --git a/layout/generic/crashtests/534366-2.html b/layout/generic/crashtests/534366-2.html
new file mode 100644
index 000000000..c90efe007
--- /dev/null
+++ b/layout/generic/crashtests/534366-2.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+<style type="text/css">
+
+body { font-family: monospace; width: 4ch; }
+body::first-line { }
+body *::before { content: 'w';}
+
+</style>
+
+<script type="text/javascript">
+
+function boom()
+{
+ var de = document.documentElement;
+ var body = document.body;
+
+ var span = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ var r1 = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mrow");
+ var mmm = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mmultiscripts");
+
+ body.appendChild(span);
+ r1.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "mrow"));
+ body.appendChild(r1);
+ body.appendChild(mmm);
+ de.offsetHeight;
+ r1.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", 'mrow'));
+ de.offsetHeight;
+ mmm.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", 'mrow'));
+ de.offsetHeight;
+
+ document.removeChild(de);
+ document.appendChild(de);
+ de.offsetHeight;
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
+</head>
+<body></body>
+</html>
diff --git a/layout/generic/crashtests/536692-1.xhtml b/layout/generic/crashtests/536692-1.xhtml
new file mode 100644
index 000000000..32cee0314
--- /dev/null
+++ b/layout/generic/crashtests/536692-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.removeChild(document.documentElement);">
+<table style="position: fixed;"><tr style="position: absolute;"></tr></table>
+</body>
+</html>
diff --git a/layout/generic/crashtests/537645.xhtml b/layout/generic/crashtests/537645.xhtml
new file mode 100644
index 000000000..de3123dc9
--- /dev/null
+++ b/layout/generic/crashtests/537645.xhtml
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<style>
+span { margin: inherit; }
+html, body { -moz-column-width: 1px; column-width: 1px; }
+</style>
+</head>
+<body onload="document.getElementsByTagName('style')[0].setAttribute('foo', 'bar'); document.documentElement.removeAttribute('class');">
+<span><i><spacer /><caption /></i><span><span><div /></span></span></span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/541277-1.html b/layout/generic/crashtests/541277-1.html
new file mode 100644
index 000000000..91c99a460
--- /dev/null
+++ b/layout/generic/crashtests/541277-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<span>&#xFBE4;</span><span>&#xFB4B;</span><span>&#xFBE6;</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/541277-2.html b/layout/generic/crashtests/541277-2.html
new file mode 100644
index 000000000..ce608e9c8
--- /dev/null
+++ b/layout/generic/crashtests/541277-2.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+&#x202E;X&#x200D; &#x5D60;
+</body>
+</html>
diff --git a/layout/generic/crashtests/541714-1.html b/layout/generic/crashtests/541714-1.html
new file mode 100644
index 000000000..e790358e0
--- /dev/null
+++ b/layout/generic/crashtests/541714-1.html
@@ -0,0 +1,3 @@
+<html style="overflow: hidden;">
+<body style="overflow: hidden; direction: rtl; padding: 0 64635% 0 66421238918787500pt; width: 39779329pt;"></body>
+</html>
diff --git a/layout/generic/crashtests/541714-2.html b/layout/generic/crashtests/541714-2.html
new file mode 100644
index 000000000..dc1634365
--- /dev/null
+++ b/layout/generic/crashtests/541714-2.html
@@ -0,0 +1,3 @@
+<html style="overflow: hidden;">
+<body style="overflow: hidden; direction: rtl; padding: 64635% 0 66421238918787500pt 0; height: 39779329pt;"></body>
+</html>
diff --git a/layout/generic/crashtests/542136-1.html b/layout/generic/crashtests/542136-1.html
new file mode 100644
index 000000000..1e94c12ba
--- /dev/null
+++ b/layout/generic/crashtests/542136-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+div:first-letter{}
+
+</style>
+<script>
+
+function boom()
+{
+ document.execCommand("selectAll", false, null);
+ document.execCommand("decreasefontsize", false, null);
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="font-size: 0;"><div contenteditable="true" style="-moz-column-width: 1px; white-space: pre-line;">
+<span>T</span>his is text</div></body>
+
+</html>
diff --git a/layout/generic/crashtests/545571-1.html b/layout/generic/crashtests/545571-1.html
new file mode 100644
index 000000000..d4dd7777d
--- /dev/null
+++ b/layout/generic/crashtests/545571-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body onload="document.documentElement.appendChild(document.body); document.documentElement.offsetHeight; document.getElementsByTagName('span')[0].style.wordSpacing = '4px';" style="bottom: 15045000px; -moz-column-width: 1px; top: -26px; position: absolute"><div style="letter-spacing: -4129px"><span style="white-space: pre-line; padding: 21904664px; -moz-column-width: 1px; position: absolute; word-spacing: 1577097179334px; top: 281474976710655px">
+( :
+
+q</span></div></body>
+</html>
diff --git a/layout/generic/crashtests/547338.xul b/layout/generic/crashtests/547338.xul
new file mode 100644
index 000000000..f977049a3
--- /dev/null
+++ b/layout/generic/crashtests/547338.xul
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script type="text/javascript">
+<![CDATA[
+
+function boom()
+{
+ document.getElementById("list").ensureIndexIsVisible(4);
+ document.getElementById("i4").style.fontSize = "10000%";
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script>
+
+<listbox id="list" rows="3">
+ <listitem/>
+ <listitem/>
+ <listitem/>
+ <listitem id="i4" label="Item 4"/><listitem/>
+</listbox>
+
+</window>
diff --git a/layout/generic/crashtests/547843-1.xhtml b/layout/generic/crashtests/547843-1.xhtml
new file mode 100644
index 000000000..0ad086d90
--- /dev/null
+++ b/layout/generic/crashtests/547843-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body><math xmlns="http://www.w3.org/1998/Math/MathML" style="display: table;"/><div style="position: fixed;"></div></body></html>
diff --git a/layout/generic/crashtests/551635-1.html b/layout/generic/crashtests/551635-1.html
new file mode 100644
index 000000000..805d4413f
--- /dev/null
+++ b/layout/generic/crashtests/551635-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.documentElement.focus();
+}
+
+</script>
+</head>
+
+<frameset onload="boom();"></frameset>
+
+</html>
diff --git a/layout/generic/crashtests/553504-1.xhtml b/layout/generic/crashtests/553504-1.xhtml
new file mode 100644
index 000000000..6424f3245
--- /dev/null
+++ b/layout/generic/crashtests/553504-1.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="display: table;">
+<head><style>div {height: 10px; margin: 1em 0; }</style></head>
+<body style="-moz-column-count: 3; direction: rtl;"><div></div><div style="padding: 4503599627370495pt;">j<td></td></div></body>
+</html>
diff --git a/layout/generic/crashtests/564368-1.xhtml b/layout/generic/crashtests/564368-1.xhtml
new file mode 100644
index 000000000..2a127357e
--- /dev/null
+++ b/layout/generic/crashtests/564368-1.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+<![CDATA[
+
+function boom()
+{
+ var a = document.createElementNS("http://www.w3.org/1999/xhtml", "frameset");
+ var b = document.createElementNS("http://www.w3.org/1999/xhtml", "frameset");
+ var c = document.createElementNS("http://www.w3.org/1999/xhtml", "frameset");
+ var div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+
+ a.appendChild(b);
+ document.documentElement.appendChild(a);
+ document.documentElement.offsetHeight;
+ b.appendChild(c);
+ document.documentElement.offsetHeight;
+ c.appendChild(div)
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script></head>
+
+<body></body>
+</html>
diff --git a/layout/generic/crashtests/564968.xhtml b/layout/generic/crashtests/564968.xhtml
new file mode 100644
index 000000000..4a81a451e
--- /dev/null
+++ b/layout/generic/crashtests/564968.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>
+ .container {
+ height: 1em;
+ }
+ .overflow {
+ height: 8em;
+ }
+ body {
+ font-family: monospace;
+ height: 8em;
+ line-height: 1em;
+ -moz-column-count: 2;
+ -moz-column-gap: 0;
+ }
+</style>
+<script>
+ function boom()
+ {
+ document.documentElement.offsetHeight;
+ document.getElementById('x').style.display = 'none';
+ document.documentElement.offsetHeight;
+ document.getElementById('y').style.display = 'none';
+ }
+</script>
+</head>
+
+<body style="width: 17ch;" onload="boom();"><div id="x" class="container"></div>This paragraph must be in the first column.<div class="container" id="y"><div class="overflow"></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/569193-1.html b/layout/generic/crashtests/569193-1.html
new file mode 100644
index 000000000..18a5aa06c
--- /dev/null
+++ b/layout/generic/crashtests/569193-1.html
@@ -0,0 +1,6 @@
+<html style="-moz-column-count: 2;"><body onload="document.body.style.height = '0'; document.body.style.margin = '1048575ch';" style="-moz-column-count: 2; font-size-adjust: 288230376151711740; white-space: pre-wrap;">
+
+
+
+
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/570160.html b/layout/generic/crashtests/570160.html
new file mode 100644
index 000000000..09800f15b
--- /dev/null
+++ b/layout/generic/crashtests/570160.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+<title>Testcase for bug 570160</title>
+<!-- distilled from href="http://www.musicalcriticism.com/concerts/usherhall-rsno-clein-0510.shtml" -->
+
+<style type="text/css">
+
+.manuscript {
+ position: absolute;
+ left: 770px;
+ top: 134px;
+ width: 233px;
+ height: 133px;
+}
+
+#maintext {
+ padding: 0px px 15px 15px;
+ position: absolute;
+ left: 16px;
+ top: 299px;
+ width: 752px;
+ height: 636px;
+}
+
+
+#maintext img {
+ padding: 10px 10px 10px 25px;
+ float: right;
+}
+
+</style></head>
+
+<body>
+
+ <div class="manuscript"></div>
+
+
+<div id="maintext">
+
+<div style="height:98%"></div>
+
+ <p><img src="yyyyyyy" alt="line" width="750" height="50" /></p>
+ <p><strong><img src="xxxxx" alt="maxwell davies" width="100" height="100" />Related articles:</strong></p>
+ <p>The RSNO and Denève in Mahler 6<br />
+
+
+</div>
+
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/570289-1.html b/layout/generic/crashtests/570289-1.html
new file mode 100644
index 000000000..319bbb1a9
--- /dev/null
+++ b/layout/generic/crashtests/570289-1.html
@@ -0,0 +1 @@
+<html style="white-space: pre-line; border: 3434px solid black; text-shadow: 0pt 0pt 0.2em rgb(255, 136, 119); text-align: -moz-right;"><body style="padding: 1489600cm;"></body></html>
diff --git a/layout/generic/crashtests/571618-1.svg b/layout/generic/crashtests/571618-1.svg
new file mode 100644
index 000000000..513a19994
--- /dev/null
+++ b/layout/generic/crashtests/571618-1.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="100%" height="100%" style="display: list-item"/></svg>
diff --git a/layout/generic/crashtests/571975-1.html b/layout/generic/crashtests/571975-1.html
new file mode 100644
index 000000000..1d0793d2f
--- /dev/null
+++ b/layout/generic/crashtests/571975-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html style="-moz-column-count: 15;">
+<head><style>.wrapper { height: 3em; line-height: 1em; }</style></head>
+<body><div class="wrapper"></div><div class="wrapper">A B C D E</div></body>
+</html>
diff --git a/layout/generic/crashtests/571995.xhtml b/layout/generic/crashtests/571995.xhtml
new file mode 100644
index 000000000..3577263ff
--- /dev/null
+++ b/layout/generic/crashtests/571995.xhtml
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.getElementById('a').style.letterSpacing = '15ch';"></body>
+<span id="a" style="position: fixed; bottom: 0pt; top: 4095em; -moz-column-width: 1px;"><span style="-moz-column-count: 1; min-height: 2097150ch; font-size-adjust: 256; width: 89px; bottom: 35875px; min-width: -moz-min-content; position: absolute; top: 33554432ch; white-space: pre-line;">
+
+a b:
+c def:
+
+ </span></span></html>
diff --git a/layout/generic/crashtests/574958.xhtml b/layout/generic/crashtests/574958.xhtml
new file mode 100644
index 000000000..f9dee6a5f
--- /dev/null
+++ b/layout/generic/crashtests/574958.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-print">
+<style>
+tbody::first-letter {float: right; }
+tbody::before { content:"before textbefore textbefore textbefore textbefore textbefore text"; float:right;}>
+</style>
+<th style="direction: rtl;">
+m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m
+<span style="position: absolute;">
+<tbody style="float: right; page-break-before: right;">m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m </tbody>
+</span>
+</th>
+
+<style>
+tbody::first-line { }
+</style>
+</html>
diff --git a/layout/generic/crashtests/578977.html b/layout/generic/crashtests/578977.html
new file mode 100644
index 000000000..2d595c908
--- /dev/null
+++ b/layout/generic/crashtests/578977.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug 578977</title>
+</head>
+<body>
+
+<iframe src="578977.xul" onload="this.style.width='500px'; setTimeout(function(){document.documentElement.removeAttribute('class')},0)"></iframe>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/578977.xul b/layout/generic/crashtests/578977.xul
new file mode 100644
index 000000000..7125d90fe
--- /dev/null
+++ b/layout/generic/crashtests/578977.xul
@@ -0,0 +1,10 @@
+<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<td xmlns="http://www.w3.org/1999/xhtml" style="position: fixed; unicode-bidi: bidi-override; max-width: 10px; line-height: 999999999px; word-wrap: break-word;letter-spacing: 10em;">m m mm&#1593;</td>
+
+<style xmlns="http://www.w3.org/1999/xhtml">
+td::first-letter {position: fixed; }
+</style>
+
+</window>
diff --git a/layout/generic/crashtests/580504-1.xhtml b/layout/generic/crashtests/580504-1.xhtml
new file mode 100644
index 000000000..0447edd20
--- /dev/null
+++ b/layout/generic/crashtests/580504-1.xhtml
@@ -0,0 +1,22 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="-moz-column-width: 1px">
+<head>
+
+<script>
+<![CDATA[
+
+function boom()
+{
+ document.getElementById("d").focus();
+ document.execCommand("inserthtml", false, "<i><font><html><form>a</form></font>");
+ document.execCommand("justifyright", false, "#ffddff");
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script>
+
+</head>
+
+<div style="position: relative;"><div style="float: left; padding: 10px 20px 0pt;"><div contenteditable="true" style="position: absolute;" id="d"></div></div><div style="clear: both; padding: 20px 20px 15px;"></div></div>
+</html>
diff --git a/layout/generic/crashtests/585598-1.xhtml b/layout/generic/crashtests/585598-1.xhtml
new file mode 100644
index 000000000..eb0d78eaa
--- /dev/null
+++ b/layout/generic/crashtests/585598-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-print">
+<span style="float: left;page-break-before: right;">
+<select style="float: left;">
+</select>
+</span>
+
+</html>
diff --git a/layout/generic/crashtests/586806-1.html b/layout/generic/crashtests/586806-1.html
new file mode 100644
index 000000000..3bb2ca3d9
--- /dev/null
+++ b/layout/generic/crashtests/586806-1.html
@@ -0,0 +1,27 @@
+<html class="reftest-wait">
+<head>
+<script>
+function doe2() {
+document.getElementById('b').style.position = 'static';
+document.getElementById('a').setAttribute('style', 'position: absolute; -moz-column-count: 2;');
+document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<style>body * {border: 1px solid black;}</style>
+<body onload="doe2();">
+<div style="width: 500px;border: 1px solid black;">
+<div style="display: inline-block; width: 100px; height: 100px;"></div>
+<span style="position: absolute;"></span>
+mmmmmmmmmmmmmmmmmmmmmmm
+
+<span id="a">mmmmmmmmmmmmmmmmmmmm
+
+<div id="b" style="display: inline-block; width: 240px; height: 100px; position: absolute;"></div>
+m mm mm mm mm mm mm mm mm m
+<span style="float: left;">m</span>
+</span>
+</div>
+</body>
+
+</html>
diff --git a/layout/generic/crashtests/586806-2.html b/layout/generic/crashtests/586806-2.html
new file mode 100644
index 000000000..08badb2a5
--- /dev/null
+++ b/layout/generic/crashtests/586806-2.html
@@ -0,0 +1 @@
+<div style="-moz-column-count: 2; width: 241px;"><div style="display: inline-block; width: 240px; height: 100px;"></div>m</div>
diff --git a/layout/generic/crashtests/586806-3.html b/layout/generic/crashtests/586806-3.html
new file mode 100644
index 000000000..12c6fef15
--- /dev/null
+++ b/layout/generic/crashtests/586806-3.html
@@ -0,0 +1,9 @@
+<body style="font-size: 16px">
+<div style="width: 400px;">
+mmmmmmmmmmmmmmmmmmmmmmm
+<span style="position: absolute; -moz-column-count: 2">mmmmmmmmmmmmmmmmmmmm
+<div style="display: inline-block; width: 240px; height: 100px"></div>
+m mm mm mm mm mm mm mm mm m
+<div style="float: left;">m</div>
+</span>
+</div>
diff --git a/layout/generic/crashtests/586973-1.html b/layout/generic/crashtests/586973-1.html
new file mode 100644
index 000000000..1d7f5017a
--- /dev/null
+++ b/layout/generic/crashtests/586973-1.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<style>
+hr::before { content:"b"; float:right;}
+</style>
+</head>
+<body>
+<hr style="-moz-column-count: 1;">
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/589002-1.html b/layout/generic/crashtests/589002-1.html
new file mode 100644
index 000000000..de3191812
--- /dev/null
+++ b/layout/generic/crashtests/589002-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body style="-moz-column-width: 1px; -moz-column-gap: 576460752303423500mozmm;"></body>
+</html>
diff --git a/layout/generic/crashtests/590404.html b/layout/generic/crashtests/590404.html
new file mode 100644
index 000000000..9f6ee8fe9
--- /dev/null
+++ b/layout/generic/crashtests/590404.html
@@ -0,0 +1 @@
+<iframe src="data:text/html,%3Cdiv%20style%3D%22background%3A%20-moz-element(%23e)%22%3Ez"></iframe>
diff --git a/layout/generic/crashtests/591141.html b/layout/generic/crashtests/591141.html
new file mode 100644
index 000000000..e1f0bbbd3
--- /dev/null
+++ b/layout/generic/crashtests/591141.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+<svg><pattern id="p"/></svg>
+<div style="width: 100px; height: 100px; background: -moz-element(#p);"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/592118.html b/layout/generic/crashtests/592118.html
new file mode 100644
index 000000000..77b81768e
--- /dev/null
+++ b/layout/generic/crashtests/592118.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<title>Stack pointer free with -moz-element</title>
+<div id="paintServer" style="width: 20px; height: 20px; background: red;"></div>
+<div style="-moz-transform: scale(1.01); width: 100px; height: 100px; background: -moz-element(#paintServer) -5px -3px; background-size: 20px 32769px;"></div>
diff --git a/layout/generic/crashtests/594808-1.html b/layout/generic/crashtests/594808-1.html
new file mode 100644
index 000000000..d88147eb5
--- /dev/null
+++ b/layout/generic/crashtests/594808-1.html
@@ -0,0 +1,7 @@
+<script>
+ oSelection = window.getSelection();
+ oRange = document.createRange();
+ oSelection.addRange(oRange);
+ oRange.detach();
+ oSelection.removeRange(oRange);
+</script>
diff --git a/layout/generic/crashtests/595435-1.xhtml b/layout/generic/crashtests/595435-1.xhtml
new file mode 100644
index 000000000..894bec36c
--- /dev/null
+++ b/layout/generic/crashtests/595435-1.xhtml
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.documentElement.offsetHeight; document.getElementById('s').style.fontVariant = 'small-caps'; document.getElementById('t').style.verticalAlign = '';">
+
+<div style="direction: rtl; font-variant: small-caps; line-height: 541579443853962em;"><div style="padding: 77in; width: 0px; position: fixed; word-wrap: break-word; white-space: pre-wrap;" id="s">
+st [m; ]
+ </div></div><div style="vertical-align: -226985587140in;" id="t"></div>
+
+</body></html>
diff --git a/layout/generic/crashtests/595740-1.html b/layout/generic/crashtests/595740-1.html
new file mode 100644
index 000000000..69ca0e0c0
--- /dev/null
+++ b/layout/generic/crashtests/595740-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<title>Testcase bug 595740 (crash on print-preview)</title>
+<style type="text/css">
+body { margin:0; font: 0.2in/0.2in serif; }
+</style>
+<div style="height: 1.75in"></div>
+y<br><span style="float: right; width: 1in; height: 2in"></span>z
diff --git a/layout/generic/crashtests/597240-1.xhtml b/layout/generic/crashtests/597240-1.xhtml
new file mode 100644
index 000000000..19e04acaa
--- /dev/null
+++ b/layout/generic/crashtests/597240-1.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ var div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ div.style.cssFloat = "left";
+ document.getElementById("a").appendChild(div);
+}
+
+]]>
+</script>
+</head>
+<body onload="boom();">
+<fieldset id="a"><legend style="display: table-footer-group;"></legend></fieldset>
+</body>
+</html>
diff --git a/layout/generic/crashtests/600100.xhtml b/layout/generic/crashtests/600100.xhtml
new file mode 100644
index 000000000..c4b9c56b1
--- /dev/null
+++ b/layout/generic/crashtests/600100.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><head><style>*::first-line { } *::after { content: 'after text'; } *::before { content: 'before text'; } </style></head><body onload="document.documentElement.offsetHeight; document.getElementsByTagName('style')[0].appendChild(document.createTextNode(' '));" style="-moz-column-width: 0pt;"><div style="float: right;"><span style="overflow-y: auto; float: left;"></span></div></body></html>
diff --git a/layout/generic/crashtests/603490-1.html b/layout/generic/crashtests/603490-1.html
new file mode 100644
index 000000000..f665aab5a
--- /dev/null
+++ b/layout/generic/crashtests/603490-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html><html><script>
+
+function boom()
+{
+ while (document.documentElement.firstChild)
+ document.documentElement.removeChild(document.documentElement.firstChild);
+ document.documentElement.contentEditable = "true";
+ document.execCommand("strikethrough", false, null);
+ try { document.execCommand("justifyfull", false, null); } catch(e) { }
+ document.documentElement.offsetHeight;
+ try { document.execCommand("delete", false, null); } catch(e) { }
+ document.execCommand("inserthtml", false, "<span> <\/span>");
+}
+window.addEventListener("load", boom, false);
+
+</script></html>
diff --git a/layout/generic/crashtests/603510-1.html b/layout/generic/crashtests/603510-1.html
new file mode 100644
index 000000000..a20ad40e4
--- /dev/null
+++ b/layout/generic/crashtests/603510-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+ var r = document.documentElement;
+
+ while (r.firstChild)
+ r.removeChild(r.firstChild);
+
+ var a = document.createTextNode("a");
+ r.appendChild(a);
+ a.splitText(0);
+ a.splitText(0);
+
+ document.documentElement.offsetHeight;
+
+ r.appendChild(document.createTextNode("b"));
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
diff --git a/layout/generic/crashtests/604314-1.html b/layout/generic/crashtests/604314-1.html
new file mode 100644
index 000000000..d25971951
--- /dev/null
+++ b/layout/generic/crashtests/604314-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var sel = window.getSelection('');
+ sel.collapse(document.createTextNode("x"), 0);
+ sel.extend(document.documentElement, 0);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/layout/generic/crashtests/604843.html b/layout/generic/crashtests/604843.html
new file mode 100644
index 000000000..92e929f81
--- /dev/null
+++ b/layout/generic/crashtests/604843.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+
+ var c = document.getElementById("c");
+ var t1 = document.createTextNode("x x x x x x x x x x x x x x x x x x x x");
+ var t2 = document.createTextNode("y y y y y y y y y y y y y y y y y y y y y");
+ c.appendChild(t1);
+ c.appendChild(t2);
+ document.documentElement.offsetHeight;
+
+ var div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ c.insertBefore(div, t2);
+ document.documentElement.offsetHeight;
+}
+
+</script>
+</head>
+
+<body onload="boom();" style="width: 5ch; font-family: monospace; margin: 0;">
+<table><tbody><tr><td id="c"></td></tr></tbody></table>
+</body>
+</html>
diff --git a/layout/generic/crashtests/605340.html b/layout/generic/crashtests/605340.html
new file mode 100644
index 000000000..57f68d7dc
--- /dev/null
+++ b/layout/generic/crashtests/605340.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body onload="document.getElementById('a').lastChild.appendData('4');">
+
+<div id="a" style="position: absolute;"><span><span style="white-space: pre;">
+1
+2</span>
+3</span>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/606642.xhtml b/layout/generic/crashtests/606642.xhtml
new file mode 100644
index 000000000..9113b7d71
--- /dev/null
+++ b/layout/generic/crashtests/606642.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ var r = document.getElementById("r");
+ r.parentNode.removeChild(r);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div style="-moz-column-count: 3; height: 2in;"><div style="position: relative;"><div id="r" style="position: absolute;"><div style="height: 0in;"><div style="height: 5in;"></div><div style="position: absolute; height: 5in;"></div></div></div></div></div></body>
+</html>
diff --git a/layout/generic/crashtests/613455-1.svg b/layout/generic/crashtests/613455-1.svg
new file mode 100644
index 000000000..f856504e3
--- /dev/null
+++ b/layout/generic/crashtests/613455-1.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="70">
+<script>
+
+function boom()
+{
+ document.documentElement.style.minHeight = '68121107503rem'
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
+</svg> \ No newline at end of file
diff --git a/layout/generic/crashtests/613629-1.xhtml b/layout/generic/crashtests/613629-1.xhtml
new file mode 100644
index 000000000..92bfbbaa0
--- /dev/null
+++ b/layout/generic/crashtests/613629-1.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.documentElement.style.height = "656409px";
+}
+
+</script>
+</head>
+<body onload="boom();"><svg xmlns="http://www.w3.org/2000/svg" style="float: left; padding: 42493240px;"></svg></body>
+</html> \ No newline at end of file
diff --git a/layout/generic/crashtests/616052-1.html b/layout/generic/crashtests/616052-1.html
new file mode 100644
index 000000000..f01bed7cd
--- /dev/null
+++ b/layout/generic/crashtests/616052-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body style="-moz-column-count: 15; position: absolute; height: 0px;"><span><span style="width: 20px; height: 20px; float: left;"></span>AAA <div style="position: absolute;"></div></span></body>
+</html>
diff --git a/layout/generic/crashtests/619021.html b/layout/generic/crashtests/619021.html
new file mode 100644
index 000000000..586c0f2db
--- /dev/null
+++ b/layout/generic/crashtests/619021.html
@@ -0,0 +1,5 @@
+<foo> <marquee> <marquee> <marquee> <marquee> <marquee> <marquee>
+<foo> <marquee> <foo> <marquee> <foo> <object> <marquee> <foo>
+<marquee> <marquee> <foo> <foo> <marquee> <marquee> <foo> <marquee>
+<marquee> <marquee> <foo> <marquee> <foo> <foo> <marquee> <marquee>
+<marquee> </marquee> <foo> <foo> <pre>
diff --git a/layout/generic/crashtests/621424-1.html b/layout/generic/crashtests/621424-1.html
new file mode 100644
index 000000000..b5102ab65
--- /dev/null
+++ b/layout/generic/crashtests/621424-1.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="-moz-column-width: 1px;"><body style="-moz-column-width: 1px;" onload="document.getElementById('x').style.cssFloat='';"><div style="height: 50px;"></div><div style="float: left; -moz-column-count: 3; height: 466px;"></div><div style="padding: 176px; float: right;" id="x"></div></body></html>
diff --git a/layout/generic/crashtests/621841-1.html b/layout/generic/crashtests/621841-1.html
new file mode 100644
index 000000000..f6c9b6eaf
--- /dev/null
+++ b/layout/generic/crashtests/621841-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var frame = document.getElementById("f");
+ var frameDoc = frame.contentDocument;
+ frameDoc.getElementById("g").style.background = "yellow";
+ frame.style.cssFloat = "right";
+ document.documentElement.offsetHeight;
+ frameDoc.documentElement.style.color = "green";
+}
+
+</script>
+</head>
+
+<body onload="boom();"><iframe id="f" src="data:text/html,<!DOCTYPE html><frameset><frame id=g></frameset>"></iframe></body>
+</html>
diff --git a/layout/generic/crashtests/622596.html b/layout/generic/crashtests/622596.html
new file mode 100644
index 000000000..fec43f11f
--- /dev/null
+++ b/layout/generic/crashtests/622596.html
@@ -0,0 +1,6 @@
+<script>
+t2 = window.open();
+t2.document.documentElement.childNodes.item(undefined).contentEditable = true;
+t2.getSelection().containsNode([], false);
+t2.close()
+</script>
diff --git a/layout/generic/crashtests/641724.html b/layout/generic/crashtests/641724.html
new file mode 100644
index 000000000..a7039ecda
--- /dev/null
+++ b/layout/generic/crashtests/641724.html
@@ -0,0 +1,315 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<style type="text/css" rel="stylesheet" media="all">
+.form-item { padding:30px; }
+.views-exposed-widget {float:left; clear:left;}
+.views-exposed-widgets {-moz-column-width:250px;}
+.clearfix:after
+,.clear-block:after{content:".";display:block;}
+</style>
+<script type="text/javascript">
+//<!-- DDBEGIN -->
+(function ()
+{
+ var D = window.jQuery = window.$ = function (a, b)
+ {
+ return new D.fn.init(a, b)
+ };
+ var u = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
+ D.fn = D.prototype =
+ {
+ init: function (d, b)
+ {
+ d = d || document;
+ if (d.nodeType)
+ {
+ this[0] = d;
+ this.length = 1;
+ }
+ if (typeof d == "string")
+ {
+ var c = u.exec(d);
+ if (c)
+ {
+ if (c[1]) d = D.clean([c[1]], b);
+ }
+ else
+ return D(b).find(d)
+ }
+ return this.setArray(D.makeArray(d))
+ },
+ pushStack: function (b)
+ {
+ return D(b);
+ },
+ setArray: function (a)
+ {
+ Array.prototype.push.apply(this, a);
+ },
+ each: function (a, b)
+ {
+ return D.each(this, a, b)
+ },
+ after: function ()
+ {
+ return this.domManip(arguments, false, true, function (a)
+ {
+ this.parentNode.insertBefore(a, this.nextSibling)
+ })
+ },
+ find: function (b)
+ {
+ var c = D.map(this, function (a)
+ {
+ return D.find(b, a)
+ });
+ return this.pushStack(/[^+>] [^+>]/.test(b) ? D.unique(c) : c)
+ },
+ domManip: function (g, f, h, d)
+ {
+ return this.each(function ()
+ {
+ elems = D.clean(g, this.ownerDocument);
+ var b = this;
+ D.each(elems, function ()
+ {
+ d.call(b, this)
+ });
+ })
+ }
+ };
+ D.fn.init.prototype = D.fn;
+ D.extend = D.fn.extend = function ()
+ {
+ var b = arguments[0];
+ var i = 1;
+ var length = arguments.length;
+ if (length == 1)
+ {
+ b = this;
+ --i
+ }
+ if ((options = arguments[i]) != null) for (var c in options)
+ {
+ copy = options[c];
+ if (copy !== undefined) b[c] = copy
+ }
+ return b
+ };
+ D.extend(
+ {
+ each: function (d, a, c)
+ {
+ for (e in d)
+ if (a.call(d[e], e, d[e]) === false)
+ for (var b = d[0]; i < length; b = d[++i]) { }
+ return d
+ },
+ curCSS: function (f, l, k)
+ {
+ l = l.replace(/([A-Z])/g, "-$1").toLowerCase();
+ var c = document.defaultView.getComputedStyle(f, null);
+ c.getPropertyValue(l);
+ },
+ clean: function (l, h)
+ {
+ var k = [];
+ D.each(l, function (i, d)
+ {
+ var div = document.createElement("div");
+ div.innerHTML = "" + d
+ d = D.makeArray(div.childNodes)
+ if (d[0] == undefined) k.push(d);
+ else k = D.merge(k, d)
+ });
+ return k
+ },
+ makeArray: function (b)
+ {
+ var a = [];
+ var i = b.length;
+ while (i) a[--i] = b[i]
+ return a
+ },
+ merge: function (a, b)
+ {
+ var i = 0;
+ var pos = a.length;
+ while (elem = b[i++]) a[pos++] = elem;
+ return a
+ },
+ map: function (d, a)
+ {
+ var c = [];
+ for (var i = 0, length = d.length; i < length; i++)
+ {
+ var b = a(d[i], i);
+ if (b != null) c[c.length] = b
+ }
+ return c.concat.apply([], c)
+ }
+ });
+ D.each(
+ {
+ insertAfter: "after",
+ }, function (c, b)
+ {
+ D.fn[c] = function ()
+ {
+ var a = arguments;
+ return this.each(function ()
+ {
+ for (var i = 0, length = a.length; i < length; i++) D(a[i])[b](this)
+ })
+ }
+ });
+ function num(a, b)
+ {
+ return a[0] && parseInt(D.curCSS(a[0], b, true), 10)
+ }
+ var quickClass = new RegExp("^([#.]?)(" + "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)" + "*)");
+ D.extend(
+ {
+ find: function (t, o)
+ {
+ var d = [o];
+ var done = [];
+ while (t)
+ {
+ m = quickClass.exec(t)
+ var f = d[d.length - 1];
+ if (m[1] == "#")
+ {
+ var p = f.getElementById(m[2]);
+ d = p && !m[3] ? [p] : []
+ }
+ t = t.replace(quickClass, "")
+ }
+ return D.merge(done, d);
+ },
+ });
+ D.fn.extend(
+ {
+ bind: function (c, a, b)
+ {
+ return this.each(function () { })
+ },
+ ready: function (a)
+ {
+ bindReady();
+ D.readyList.push(function ()
+ {
+ return a.call(this, D)
+ });
+ }
+ });
+ D.extend(
+ {
+ readyList: [],
+ ready: function ()
+ {
+ D.each(D.readyList, function ()
+ {
+ this.call(document)
+ });
+ }
+ });
+ function bindReady()
+ {
+ document.addEventListener("DOMContentLoaded", D.ready, false);
+ }
+ D.each(["Height", "Width"], function (i, b)
+ {
+ D.fn["outer" + b] = function (a)
+ {
+ num(this, "borderRightWidth")
+ }
+ })
+})();
+var Drupal = {
+ 'settings': {
+ },
+ 'behaviors': {
+ },
+};
+Drupal.attachBehaviors = function (context)
+{
+ jQuery.each(Drupal.behaviors, function ()
+ {
+ this(context);
+ });
+}
+$(document).ready(function ()
+{
+ Drupal.attachBehaviors(this);
+});
+(function (C)
+{
+ C.ui = { }
+ C.widget = function (K, J)
+ {
+ var L = K.split(".")[0];
+ K = K.split(".")[1];
+ C.fn[K] = function (P)
+ {
+ return this.each(function ()
+ {
+ C.data(this, K, new C[L][K](this, P));
+ })
+ };
+ C[L][K] = function (O, N)
+ {
+ this.element = C(O).bind();
+ this._init()
+ };
+ C[L][K].prototype = C.extend( { }, J);
+ };
+})(jQuery);
+(function (a)
+{
+ a.widget("ui.dropdownchecklist", {
+ _appendDropContainer: function ()
+ {
+ return a("<div/>");
+ },
+ _appendControl: function ()
+ {
+ f.insertAfter(this.sourceSelect);
+ },
+ _appendItems: function ()
+ {
+ f = this.dropWrapper;
+ var e = f.find(".ui-dropdownchecklist-dropcontainer").outerHeight();
+ },
+ _init: function ()
+ {
+ this.sourceSelect = this.element;
+ this.dropWrapper = this._appendDropContainer();
+ this._appendItems();
+ this._appendControl();
+ }
+ });
+})(jQuery);;
+Drupal.behaviors.sexyExposed = function (context)
+{
+ var settings = Drupal.settings.sexyExposed;
+ $.each(settings, function (key, element)
+ {
+ $(key).dropdownchecklist();
+ });
+};
+jQuery.extend(Drupal.settings, {
+ "sexyExposed": {
+ "select#edit-field-spec-otg-value-many-to-one": "0",
+ }
+});
+</script>
+<div class="views-exposed-widgets clear-block">
+ <div class="views-exposed-widget">
+ <div>
+ <div class="form-item"></div>
+ </div>
+ </div>
+ <div class="views-exposed-widget">
+ <select multiple="multiple" id="edit-field-spec-otg-value-many-to-one"> </select>
+</div>
+</html>
diff --git a/layout/generic/crashtests/645072-1.html b/layout/generic/crashtests/645072-1.html
new file mode 100644
index 000000000..429f963d9
--- /dev/null
+++ b/layout/generic/crashtests/645072-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang=ru>
+<head>
+<meta charset=windows-1251>
+<title>Testcase, bug 645072</title>
+</head>
+<body>
+<table cellspacing=0 cellpadding=0 border=0>
+ <tr valign=top><td>Ðàçìûòûå,çåëåíûå,íî&shy;<wbr> êëàññíûå)<br>&shy;<wbr><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAABpJREFUWMPtwQEBAAAAgiD/r25IQAEAAADvBhAgAAGX91fXAAAAAElFTkSuQmCC" alt="">&shy;<wbr>
+</table>
+<script>
+// simulate image loading
+document.body.offsetWidth;
+document.getElementsByTagName("img")[0].style.width = "604px";
+document.getElementsByTagName("img")[0].style.height = "405px";
+</script>
diff --git a/layout/generic/crashtests/645072-2.html b/layout/generic/crashtests/645072-2.html
new file mode 100644
index 000000000..fca395e90
--- /dev/null
+++ b/layout/generic/crashtests/645072-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.getElementById("f").appendChild(document.createTextNode("\u00AD"));
+ document.documentElement.offsetHeight;
+}
+
+</script>
+</head>
+<body onload="boom();"><fieldset id="f"> </fieldset></body>
+</html>
+
diff --git a/layout/generic/crashtests/646561-1.html b/layout/generic/crashtests/646561-1.html
new file mode 100644
index 000000000..063e218b1
--- /dev/null
+++ b/layout/generic/crashtests/646561-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><body><table><tbody><tr><td>&shy;R&#x759;</td></tr></tbody></table></body></html>
diff --git a/layout/generic/crashtests/646983-1.html b/layout/generic/crashtests/646983-1.html
new file mode 100644
index 000000000..03e29f0ae
--- /dev/null
+++ b/layout/generic/crashtests/646983-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body style="min-width: -moz-min-content">
+<span>&#x06CD;T</span><span>&shy;</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/647332-1.html b/layout/generic/crashtests/647332-1.html
new file mode 100644
index 000000000..063e218b1
--- /dev/null
+++ b/layout/generic/crashtests/647332-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><body><table><tbody><tr><td>&shy;R&#x759;</td></tr></tbody></table></body></html>
diff --git a/layout/generic/crashtests/650499-1.html b/layout/generic/crashtests/650499-1.html
new file mode 100644
index 000000000..012643f52
--- /dev/null
+++ b/layout/generic/crashtests/650499-1.html
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.body.appendChild(document.createElement("span"));
+}
+
+</script>
+</head>
+<body style="max-width: -moz-min-content; white-space: pre;" onload="boom();">&#x00AD;
+R</body>
+</html>
diff --git a/layout/generic/crashtests/654002-1.html b/layout/generic/crashtests/654002-1.html
new file mode 100644
index 000000000..b96b0ef35
--- /dev/null
+++ b/layout/generic/crashtests/654002-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug 654002</title>
+<script>
+function boom() {
+ var e = document.getElementById('inner');
+ var s = "<br>"
+ for (k=0;k<=14;k++) {
+ s += s;
+ }
+ e.innerHTML = s;
+ document.body.offsetHeight;
+ e.style.display = 'none'
+ document.body.offsetHeight;
+}
+</script>
+</head>
+<body onload="boom()">
+
+<div style="width:1px;"><span><span id="inner"></span></span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/654002-2.html b/layout/generic/crashtests/654002-2.html
new file mode 100644
index 000000000..28f820d99
--- /dev/null
+++ b/layout/generic/crashtests/654002-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function expStr(s, n)
+{
+ for (var i = 0; i < n; ++i)
+ s += s;
+ return s;
+}
+
+function boom()
+{
+ var s = document.getElementById("s")
+ var t = document.createTextNode(expStr("x ", 15));
+ s.appendChild(t);
+ document.documentElement.offsetHeight;
+ s.removeChild(t);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div style="width: 1px"><span id="s"></span></div></body>
+</html>
diff --git a/layout/generic/crashtests/655462-1.html b/layout/generic/crashtests/655462-1.html
new file mode 100644
index 000000000..2cf98822c
--- /dev/null
+++ b/layout/generic/crashtests/655462-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html><html><head>
+
+
+</head>
+
+<body onload="document.getElementById('q').style.direction = 'rtl';">
+
+<div style="height: 80px; position: relative; -moz-column-count: 2;"><div style="margin-top: 40px; position: absolute; height: 100px;"></div><div style="position: absolute;" id="q"></div><div style="position: absolute;"></div></div>
+
+</body></html>
diff --git a/layout/generic/crashtests/656130-1.html b/layout/generic/crashtests/656130-1.html
new file mode 100644
index 000000000..ae01d43e8
--- /dev/null
+++ b/layout/generic/crashtests/656130-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<head>
+<script>
+
+function boom()
+{
+ var b = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ b.style.position = "absolute";
+ document.getElementById("a").appendChild(b);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div id="a" style="display: -moz-inline-stack; position: relative">x</div></body>
+</html>
+
diff --git a/layout/generic/crashtests/656130-2.html b/layout/generic/crashtests/656130-2.html
new file mode 100644
index 000000000..855d41915
--- /dev/null
+++ b/layout/generic/crashtests/656130-2.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function doe() {
+document.getElementById('b').setAttribute('style', 'position: absolute;');
+document.body.offsetHeight;
+document.body.setAttribute('style', 'position: relative;');
+document.body.offsetHeight;
+document.getElementById('b').setAttribute('style', '');
+}
+setTimeout(doe,0);
+</script>
+</head>
+<body>
+
+<span style="position: relative; ">
+<div>
+<div id="b">
+</div>
+</div>
+</span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/660416.html b/layout/generic/crashtests/660416.html
new file mode 100644
index 000000000..d9a42d419
--- /dev/null
+++ b/layout/generic/crashtests/660416.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ var n = document.getElementById("a").firstChild;
+ n.data = "";
+ n.data = "z";
+}
+
+</script>
+</head>
+<body onload="boom();" style="-moz-column-count: 3;"><span id="a">x&#x202E;</span><span>y</span></body>
+</html>
diff --git a/layout/generic/crashtests/665853.html b/layout/generic/crashtests/665853.html
new file mode 100644
index 000000000..ab4a805f8
--- /dev/null
+++ b/layout/generic/crashtests/665853.html
@@ -0,0 +1,29 @@
+<html><head>
+<link href="data:text/css," rel="stylesheet" type="text/css">
+<style type="text/css">
+.Big_Preview_Download {
+ display:inline;
+ position:relative;
+}
+.cma {
+ position: absolute;
+}
+</style>
+</head>
+
+<body>
+
+<div class="Big_Preview_Download">
+ <table>
+ <tbody>
+ <tr>
+ <td>
+ <script type="text/javascript"> </script>
+ <div class="cma"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+</body></html>
diff --git a/layout/generic/crashtests/667025.html b/layout/generic/crashtests/667025.html
new file mode 100644
index 000000000..29ef3d32b
--- /dev/null
+++ b/layout/generic/crashtests/667025.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+
+ document.documentElement.style.direction = "rtl";
+
+ document.documentElement.offsetHeight;
+ var s = document.getElementById("s");
+
+ s.removeChild(s.firstChild);
+
+ document.documentElement.offsetHeight;
+}
+
+</script>
+</head>
+<body onload="boom();" style="width: 1px;"><span id="s"> x y</span></body>
+</html>
diff --git a/layout/generic/crashtests/673770.html b/layout/generic/crashtests/673770.html
new file mode 100644
index 000000000..9c0fe3b1e
--- /dev/null
+++ b/layout/generic/crashtests/673770.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html style="-moz-column-width: 1px;">
+ <head>
+ <script>
+ function boom()
+ {
+ document.documentElement.offsetHeight;
+ document.body.style.height = "8px";
+ document.documentElement.style.fontSize = "22050893469px";
+ document.documentElement.offsetHeight;
+ document.getElementById("x").style.counterReset = "chicken";
+ document.documentElement.offsetHeight;
+ }
+ </script>
+ </head>
+ <body style="-moz-column-width: 1px; -moz-column-fill: auto;" onload="boom();">
+ <hr size="100" color="blue"><div style="position: absolute;"></div><div id="x" style="height: 5px;"></div>
+ </body>
+</html>
+
diff --git a/layout/generic/crashtests/679933-1.html b/layout/generic/crashtests/679933-1.html
new file mode 100644
index 000000000..83a049621
--- /dev/null
+++ b/layout/generic/crashtests/679933-1.html
@@ -0,0 +1,13 @@
+<html class="reftest-wait">
+<head>
+ <script>
+ function tweak() {
+ document.body.removeAttribute('style');
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+</head>
+<body style="display: inline; mask: url(#a);" onload="setTimeout(tweak, 50)">
+<input id="g" style="display: block; mask: url(#g);">
+</body>
+</html>
diff --git a/layout/generic/crashtests/681489-1.html b/layout/generic/crashtests/681489-1.html
new file mode 100644
index 000000000..a3dd17a96
--- /dev/null
+++ b/layout/generic/crashtests/681489-1.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="overflow: hidden; text-overflow: '=' clip; direction: rtl; text-indent: -30000000px;"><body style="display: inline-block;"></body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/682649-1.html b/layout/generic/crashtests/682649-1.html
new file mode 100644
index 000000000..9e7ef5f38
--- /dev/null
+++ b/layout/generic/crashtests/682649-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html style="position: relative; -moz-column-count: 3;" class="reftest-wait">
+
+<head>
+<script>
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.body.style.position = "";
+ document.documentElement.offsetHeight;
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+
+<body onload="boom();" style="position: absolute;">A<span><div></div>B</span></body>
+
+</html>
diff --git a/layout/generic/crashtests/683702-1.xhtml b/layout/generic/crashtests/683702-1.xhtml
new file mode 100644
index 000000000..33cd18661
--- /dev/null
+++ b/layout/generic/crashtests/683702-1.xhtml
@@ -0,0 +1,24 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<script>
+ function doe() {
+ document.getElementById('a').style.display = '';
+ document.documentElement.removeAttribute("class");
+ }
+</script>
+</head>
+<body onload="doe()">
+<div style="position: absolute; -moz-column-count: 2;">
+<table id="c">
+<div id="a" style="border: 100px solid black; display:none;"></div><tr>
+<td style="position: absolute;">
+ <span>
+ <div style="border: 100px solid black;"></div>
+ </span>
+
+</td>
+</tr>
+</table>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/683712.html b/layout/generic/crashtests/683712.html
new file mode 100644
index 000000000..1aaa4c2fd
--- /dev/null
+++ b/layout/generic/crashtests/683712.html
@@ -0,0 +1,9 @@
+<!-- Quirks mode on purpose -->
+<svg>
+ <foreignObject>
+ <div>
+ <div style="height: 100%"></div>
+ </div>
+ </foreignObject>
+</svg>
+
diff --git a/layout/generic/crashtests/688996-1.html b/layout/generic/crashtests/688996-1.html
new file mode 100644
index 000000000..f2a32802d
--- /dev/null
+++ b/layout/generic/crashtests/688996-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html><html><head><script>
+function boom()
+{
+ var a = document.getElementsByTagName('div')[0];
+ var b = a.firstChild;
+
+ var r = document.createRange();
+ r.setStart(b, 1);
+ r.setEnd(a, 1);
+
+ var s = document.createRange();
+ s.setStart(b, 0);
+ s.setEnd(a, 1);
+ s.deleteContents();
+}
+</script></head><body onload="boom();">
+<div>b</div>
+</body></html>
diff --git a/layout/generic/crashtests/688996-2.html b/layout/generic/crashtests/688996-2.html
new file mode 100644
index 000000000..d4132d91f
--- /dev/null
+++ b/layout/generic/crashtests/688996-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html><html><head><script>
+function boom()
+{
+ var a = document.getElementsByTagName('div')[0];
+ var b = a.firstChild;
+
+ var r = document.createRange();
+ r.setStart(b, 1);
+ r.setEnd(a, 1);
+
+ b.splitText(0);
+}
+</script></head><body onload="boom();">
+<div>b</div>
+</body></html>
diff --git a/layout/generic/crashtests/691210.html b/layout/generic/crashtests/691210.html
new file mode 100644
index 000000000..ab37f6d2e
--- /dev/null
+++ b/layout/generic/crashtests/691210.html
@@ -0,0 +1,5 @@
+<html style="-moz-column-width: 1px;"><head>
+
+</head>
+
+<body><div style="position: relative; -moz-column-count: 6;"><div style="position: absolute; height: 9px;"></div><div style="height: 9px;"></div></div></body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/700031.xhtml b/layout/generic/crashtests/700031.xhtml
new file mode 100644
index 000000000..70f924279
--- /dev/null
+++ b/layout/generic/crashtests/700031.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body>
+
+<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
+
+<math xmlns="http://www.w3.org/1998/Math/MathML"><mover>abcdef</mover></math>
+
+</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
+
+</body></html>
diff --git a/layout/generic/crashtests/718516.html b/layout/generic/crashtests/718516.html
new file mode 100644
index 000000000..ed108483a
--- /dev/null
+++ b/layout/generic/crashtests/718516.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+ <head><title>Bug 718516</title>
+ <script>
+ function start ()
+ {
+ firstDirElement = document.createElement('dir');
+ firstDirElement.style.cssText = '-moz-stack-sizing: ignore;' +
+ ' -moz-column-width: 16385px;';
+ textPathElement = document
+ .createElementNS('http://www.w3.org/2000/svg', 'textPath');
+ firstDirElement.appendChild(textPathElement);
+ textPathParent = textPathElement.parentElement;
+ firstDivElement = document.createElement('div');
+ document.body.appendChild(firstDivElement);
+ centerElement = document.createElement('center');
+ firstDivElement.appendChild(centerElement);
+ firstIFrameElement = document.createElement('iframe');
+ firstIFrameElement.src = 'data:text/html,%3Cdatalist%20id%3D%27'
+ + 'element0%27%3E%3Cscript%20id%3D%27element2%27%3Ex%20x';
+ firstIFrameElement.id = 'ifr37311';
+ centerElement.ownerDocument.documentElement
+ .appendChild(firstIFrameElement);
+ window.setTimeout('start_dataiframe0()', 100);
+ }
+
+ function start_dataiframe0 ()
+ {
+ element2 = centerElement.ownerDocument.getElementById('ifr37311')
+ .contentDocument.getElementById('element2');
+ secondDirElement = document.createElement('dir');
+ secondDirElement.style.cssText =
+ 'visibility: inherit;-moz-column-count: 32771;';
+ feOffsetElement = document
+ .createElementNS('http://www.w3.org/2000/svg', 'feOffset');
+ centerElement.style.position = 'absolute';
+ firstIFrameElement.id = 'ifr36578';
+ element0 = feOffsetElement.ownerDocument.getElementById('ifr36578')
+ .contentDocument
+ .getElementById('element0');
+ firstIFrameElement = document.createElement('iframe');
+ element0Clone = element0.cloneNode(true);
+ videoElement = document.createElement('video');
+ firstDivParent = firstDivElement.offsetParent;
+ firstIFrameElement.id = 'ifr9261';
+ element0Clone.ownerDocument.documentElement
+ .appendChild(firstIFrameElement);
+ window.setTimeout('start_dataiframe4()', 100);
+ }
+
+ function start_dataiframe4 ()
+ {
+ documentElement = element0Clone.ownerDocument
+ .getElementById('ifr9261').contentDocument.documentElement;
+ textPathParent.appendChild(videoElement);
+ centerElement.appendChild(element2.lastChild);
+ documentElement.appendChild(secondDirElement);
+ firstDirElement.style.position = 'relative';
+ document.body.appendChild(firstDirElement);
+ firstDirElement.appendChild(firstDivElement);
+ secondDirElement.appendChild(firstDivParent);
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ A
+ </body>
+</html>
+
+
diff --git a/layout/generic/crashtests/723108.html b/layout/generic/crashtests/723108.html
new file mode 100644
index 000000000..4d9ffbd6e
--- /dev/null
+++ b/layout/generic/crashtests/723108.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body style=" -moz-column-count: 2; ">m
+<div style="width: 10px; ">m
+<div style="-moz-column-count: 2; -moz-transform: scale(1); ">m
+<span style="position: fixed;">m m</span>
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/724235.html b/layout/generic/crashtests/724235.html
new file mode 100644
index 000000000..7054a99f5
--- /dev/null
+++ b/layout/generic/crashtests/724235.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Testcase for bug 724235</title>
+</head>
+
+<body onload="setTimeout(function(){m=document.getElementsByTagName('marquee')[0]; m.style.fontSize='72px'},0)">
+
+<a href="#">
+
+<center>
+
+<marquee>This is a marquee ... </marquee>
+
+<table>
+ <tr>
+ <td><a href="#">click me to CRASH!</a></td>
+ </tr>
+</table>
+
+<iframe></iframe>
+
+<script>document.body.offsetHeight;</script>
+
+<a href="#"></a>
+
+
+</body></html>
diff --git a/layout/generic/crashtests/724978.xhtml b/layout/generic/crashtests/724978.xhtml
new file mode 100644
index 000000000..99319e7c1
--- /dev/null
+++ b/layout/generic/crashtests/724978.xhtml
@@ -0,0 +1,219 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+ <head>
+ <title>Multi-column Layout: AbsPos Pagination (Interlaced Dynamic Height)</title>
+ <link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact"/>
+ <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#the-height-property"/>
+ <link rel="help" href="http://www.w3.org/TR/CSS21/syndata.html#length-units"/>
+ <style type="text/css">
+ html {
+ background: white;
+ }
+
+ .container {
+ background: red;
+ height: 24pt;
+ position: relative;
+ -moz-column-count: 2;
+ -moz-column-gap: 0;
+ }
+ .overflow {
+ width: 10pt;
+ border-bottom: lime 8px solid;
+ top: 0;
+ }
+ .following {
+ position: relative;
+ background: white;
+ width: 100pt;
+ }
+ #colset {
+ padding-top: 1px;
+ width: 300pt;
+ height: 2in;
+ -moz-column-count: 3;
+ -moz-column-gap: 0;
+ border: silver 2pt;
+ border-style: none solid;
+ }
+ #redline {
+ width: 303pt;
+ border-top: 8px solid red;
+ margin-top: -1in;
+ position: relative;
+ z-index: -1;
+ }
+
+ .ocontainer {
+ height: 0;
+ position: relative;
+ -moz-column-count: 2;
+ -moz-column-gap: 0;
+ }
+ .o1 { /* 3rd col */
+ height: 10in;
+ }
+ .a1 { /* 1st col */
+ position: absolute;
+ height: 2in;
+ width: 33pt;
+ }
+ .a2 { /* 2nd col */
+ position: absolute;
+ height: 6in;
+ width: 25pt;
+ margin-left: 25pt;
+ }
+ .a3 { /* 3rd col */
+ position: absolute;
+ height: 10in;
+ margin-left: 10pt;
+ }
+ .a4 { /* 2nd col */
+ width: 25pt;
+ height: 6in;
+ }
+
+ .b1 { /* 3rd col */
+ position: absolute;
+ height: 672pt;
+ margin-left: 20pt;
+ }
+ .b2 { /* 2nd col */
+ position: absolute;
+ height: 384pt;
+ width: 25pt;
+ margin-left: 50pt;
+ }
+ .b3 { /* 3rd col */
+ position: absolute;
+ height: 672pt;
+ margin-left: 30pt;
+ }
+ .b4 { /* 1st col, but no border */
+ position: absolute;
+ height: 96pt;
+ border-bottom: none;
+ }
+ .b4 .child1 { /* 1st col */
+ position: absolute;
+ height: 200%;
+ width: 33pt;
+ margin-left: 33pt;
+ }
+ .b4 .child2 { /* 3rd col */
+ height: 672pt;
+ margin-left: 40pt;
+
+ }
+ .b5 { /* 1st col */
+ position: absolute;
+ height: 96pt;
+ width: 34pt;
+ margin-left: 66pt;
+ }
+ .b6 { /* 3rd col */
+ height: 672pt;
+ margin-left: 50pt;
+ }
+
+ .c1 { /* 3rd col */
+ position: absolute;
+ height: 6in;
+ margin-left: 60pt;
+ }
+ .c2 { /* 2nd col */
+ position: absolute;
+ height: 2in;
+ width: 25pt;
+ margin-left: 75pt;
+ }
+ .c3 { /* 3rd col */
+ position: absolute;
+ height: 6in;
+ margin-left: 70pt;
+ }
+ .c4 { /* 3rd col */
+ height: 6in;
+ width: 20pt;
+ margin-left: 80pt;
+ }
+
+ .f1 {
+ margin-top: -48pt;
+ height: 96pt;
+ margin-bottom: 96pt;
+ }
+ .f2 {
+ margin-top: -24pt;
+ height: 48pt;
+ }
+
+ .centerline {
+ margin: 0 auto;
+ top: 0;
+ left: 0;
+ right: 0;
+ position: absolute;
+ width: 8px;
+ height: 6in;
+ background: aqua;
+ }
+
+ #dynamo {
+ background: transparent;
+ border-bottom: 8px solid orange;
+ z-index: 10;
+ height: 384pt;
+ }
+
+ </style>
+ </head>
+ <body onload="document.getElementById('dynamo').style.height = '96pt';
+ document.getElementById('dynamo').offsetHeight;
+ document.getElementById('dynamo').style.height = '672pt';
+ document.getElementById('dynamo').offsetHeight;
+ document.getElementById('dynamo').style.height = '384pt';
+ document.getElementById('dynamo').offsetHeight;
+ document.documentElement.className = ''
+ ">
+ <div id="colset">
+ <div>
+ <div class="ocontainer">
+ <div class="centerline"></div>
+ <div class="overflow o1"></div>
+ </div>
+ <div class="container">
+ <div class="overflow a1"></div>
+ <div class="overflow a2"></div>
+ <div class="overflow a3"></div>
+ <div class="overflow a4"></div>
+ </div>
+ <div class="ocontainer">
+ <div id="dynamo" class="centerline"></div>
+ </div>
+ <div class="container">
+ <div class="overflow b1"></div>
+ <div class="overflow b2"></div>
+ <div class="overflow b3"></div>
+ <div class="overflow b4">
+ <div class="overflow child1"></div>
+ <div class="overflow child2"></div>
+ </div>
+ <div class="overflow b5"></div>
+ <div class="overflow b6"></div>
+ </div>
+ </div>
+ <p class="following f1">
+ </p>
+ <div class="container">
+ <div class="overflow c1"></div>
+ <div class="overflow c2"></div>
+ <div class="overflow c3"></div>
+ <div class="overflow c4"></div>
+ </div>
+ <div class="following f2"></div>
+ </div>
+ <div id="redline"></div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/730559.html b/layout/generic/crashtests/730559.html
new file mode 100644
index 000000000..c9be8bc7a
--- /dev/null
+++ b/layout/generic/crashtests/730559.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="height: 6523790304542em; width: 6207636626031em; box-sizing: border-box; border-style: dotted; -moz-column-width: 20px;"></html>
diff --git a/layout/generic/crashtests/734777.html b/layout/generic/crashtests/734777.html
new file mode 100644
index 000000000..6fc84445f
--- /dev/null
+++ b/layout/generic/crashtests/734777.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<body><div style="-moz-column-width: 1ch; font-family: monospace; width: 5ch;">X X &#x062A;</div></body>
diff --git a/layout/generic/crashtests/737313-1.html b/layout/generic/crashtests/737313-1.html
new file mode 100644
index 000000000..955193215
--- /dev/null
+++ b/layout/generic/crashtests/737313-1.html
@@ -0,0 +1,5 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+ <span><div style="display: flex"></div></span>
+</html>
diff --git a/layout/generic/crashtests/737313-2.html b/layout/generic/crashtests/737313-2.html
new file mode 100644
index 000000000..4786e59ee
--- /dev/null
+++ b/layout/generic/crashtests/737313-2.html
@@ -0,0 +1,5 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+ <span>some text<img><div style="display: flex"></div></span>
+</html>
diff --git a/layout/generic/crashtests/737313-3.html b/layout/generic/crashtests/737313-3.html
new file mode 100644
index 000000000..e75906864
--- /dev/null
+++ b/layout/generic/crashtests/737313-3.html
@@ -0,0 +1,5 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+ <span><div>a block</div><div style="display: flex"></div></span>
+</html>
diff --git a/layout/generic/crashtests/740199-1.xhtml b/layout/generic/crashtests/740199-1.xhtml
new file mode 100644
index 000000000..a0f04b842
--- /dev/null
+++ b/layout/generic/crashtests/740199-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body><tr>x</tr></body></html>
diff --git a/layout/generic/crashtests/747688.html b/layout/generic/crashtests/747688.html
new file mode 100644
index 000000000..9b9dd77ed
--- /dev/null
+++ b/layout/generic/crashtests/747688.html
@@ -0,0 +1,6 @@
+<style>
+* { height: 0; margin: 100%; -moz-column-width: 50px; }
+.test1 { position: absolute; min-height: 100%; -moz-columns: 3; -moz-column-count: 200; }
+.test2 { padding-bottom: 100px; margin-bottom: 20px; width: 20px; }
+</style>
+<div class="test1"><figure><div class="test2">A0AAAA0A0AAAA00AAA<hgroup></hgroup><timer><optgroup></div><div class="test2"><rect><h5> \ No newline at end of file
diff --git a/layout/generic/crashtests/750066-iframe.html b/layout/generic/crashtests/750066-iframe.html
new file mode 100644
index 000000000..e23da6aaf
--- /dev/null
+++ b/layout/generic/crashtests/750066-iframe.html
@@ -0,0 +1,32 @@
+<html style="white-space: pre; -moz-column-count: 2;">
+<body onload="document.body.style.MozFloatEdge = 'margin-box';" style="-moz-column-width: 20em;">
+<div style="position: relative; height: 80px; margin: 10px;"> í‹å“Ÿ ê ² g
+嚬
+C휤ã¡â³¢ê °ç§oÙ‚ä°§
+&amp;
+ꃎ 䅷ᩥ
+
+O禕v
+Eᚇ⋩XO
+讉à½sÒ M匕
+á‹Y
+ H唼Uฉ
+J 硵
+ _谜 -寇캫셂Z +:抂뮶
+ì¾½E
+2ɻ صkJP₾,cJ=
+.x,
+ !M]
+薹謩ꢼ믇 Y[à¡Œ4 è¡’}ç•dd:ꑪ eh 䲡 æŠá‡‹ 峂 pêº à¯´
+è¢ åŸ“æ«œ
+
+,K }&gt;
+
+a~ゲ ã¯A Äj
+협
+
+ᭃ &amp;羋劮૩k惖qs툩 B䛊J=罩E
+
+<div style="position: absolute; height: 11px; top: 19px;"></div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/layout/generic/crashtests/750066.html b/layout/generic/crashtests/750066.html
new file mode 100644
index 000000000..83c1c037f
--- /dev/null
+++ b/layout/generic/crashtests/750066.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase for bug 750066</title>
+<style>
+iframe { -moz-transition: width 2000ms ease-out 0s; }
+</style>
+
+<script>
+function resize(w) {
+ var win = window.frames[0];
+ win.frameElement.style.width = w;
+}
+function doTest() {
+ resize('1000px');
+ setTimeout(function(){
+ resize('500px');
+ setTimeout(function(){
+ document.documentElement.removeAttribute("class");
+ },0);
+ },500);
+}
+</script>
+</head>
+<body>
+
+<iframe src="750066-iframe.html"></iframe>
+
+<script>
+window.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/757413-2.html b/layout/generic/crashtests/757413-2.html
new file mode 100644
index 000000000..9d0799bc3
--- /dev/null
+++ b/layout/generic/crashtests/757413-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <body onload="document.documentElement.offsetHeight; document.getElementById('x').style.position = 'relative';">
+ <div style="-moz-column-width: 200px; -moz-column-fill: auto; height: 200px;">
+ <div style="height: 150px;"></div>
+ <div style="float: left; height: 150px; width: 200px;"></div>
+ <div>
+ <div id="x" style="float: left; height: 150px; width: 200px;"></div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/757413.xhtml b/layout/generic/crashtests/757413.xhtml
new file mode 100644
index 000000000..ceae86c04
--- /dev/null
+++ b/layout/generic/crashtests/757413.xhtml
@@ -0,0 +1,34 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function aC(r, n) { if (r) { r.appendChild(n); } else { rM(n); } }
+function iB(r, n) { if (r) { r.parentNode.insertBefore(n, r); } else { rM(n); } }
+allNodes = [];
+allNodes[0] = document.documentElement;
+allNodes[50] = document.createTextNode("Foo");
+allNodes[63] = document.createElementNS("http://www.w3.org/1999/xhtml", "tr");
+allNodes[73] = document.createElementNS("http://www.w3.org/1999/xhtml", "select");
+allNodes[76] = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
+(allNodes[73] || allNodes[72] || allNodes[63] || allNodes[44] || allNodes[5] || document.documentElement).appendChild(allNodes[76]);
+allNodes[78] = document.createTextNode("\n ");
+(allNodes[63] || allNodes[44] || allNodes[5] || document.documentElement).appendChild(allNodes[78]);
+allNodes[88] = document.createElementNS("http://www.w3.org/1999/xhtml", "legend");
+allNodes[89] = document.createTextNode("Your name");
+allNodes[98] = document.createTextNode("\n ");
+allNodes[125] = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
+allNodes[0].style.MozColumnCount = "115";
+aC(allNodes[88], allNodes[98]);
+iB(allNodes[98], allNodes[63]);
+allNodes[63].style.cssFloat = "right";
+aC(allNodes[0], allNodes[88]);
+aC(allNodes[88], allNodes[125]);
+iB(allNodes[88], allNodes[73]);
+function run() {
+iB(allNodes[78], allNodes[89]);
+aC(allNodes[76], allNodes[50]);
+}
+document.body.offsetHeight;
+setTimeout(run, 0);
+</script>
+</head>
+</html>
diff --git a/layout/generic/crashtests/762764-1.html b/layout/generic/crashtests/762764-1.html
new file mode 100644
index 000000000..5752572a0
--- /dev/null
+++ b/layout/generic/crashtests/762764-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+ document.documentElement.removeChild(document.body);
+ var newBody = document.createElementNS("http://www.w3.org/1999/xhtml", "body");
+ document.documentElement.appendChild(newBody);
+ newBody.contentEditable = "true";
+ document.execCommand("inserthtml", false, "<textarea>a</textarea>");
+ document.execCommand("insertimage", false, "1.jpg");
+ try { document.execCommand("forwardDelete", false, null); } catch(e) { }
+ document.execCommand("inserthtml", false, "x<span><\/span>y");
+}
+
+</script>
+
+<body onload="boom();"></body>
diff --git a/layout/generic/crashtests/762902.html b/layout/generic/crashtests/762902.html
new file mode 100644
index 000000000..158793406
--- /dev/null
+++ b/layout/generic/crashtests/762902.html
@@ -0,0 +1,12 @@
+<html><head>
+
+</head><body>
+
+<div style="-moz-column-count: 2;">
+mmmmmmm
+<div style="display: table;-moz-transform: translate(-50px);">
+<div style="position: fixed;">b t</div>
+</div>
+</div>
+
+</body></html>
diff --git a/layout/generic/crashtests/765409.html b/layout/generic/crashtests/765409.html
new file mode 100644
index 000000000..31bd92d2d
--- /dev/null
+++ b/layout/generic/crashtests/765409.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<style>
+
+body { width: 300px; }
+
+</style>
+
+<script>
+
+window.addEventListener("load", function() {
+ var v = document.getElementById("v");
+ v.style.width = "280px";
+ v.style.height = "10px";
+ setTimeout(function(){ document.documentElement.offsetHeight; document.documentElement.removeAttribute("class"); },0);
+}, false);
+
+</script>
+
+<body>
+<div><span style="unicode-bidi: isolate;"><span style="display: inline-block; float: right;" id="v"></span>D E<span style="unicode-bidi: isolate;"><span><span> &#x062a;</span></span></span></span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/765621.html b/layout/generic/crashtests/765621.html
new file mode 100644
index 000000000..e85e9ebf7
--- /dev/null
+++ b/layout/generic/crashtests/765621.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait"><hx id=hx1>><style>
+.class1 { white-space: pre-wrap; letter-spacing: 54138.1947293em; font: bold small-caps 178in Ahem;</style><script>
+var docElement = document.documentElement;
+function initCF() {
+document.removeEventListener("DOMContentLoaded", initCF, false);
+test = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mstyle");
+test.setAttribute("class", "class1");
+docElement.appendChild(test);
+text1 = document.createTextNode("FLAj *uaRk}|/zee aCb o = $l xQ-gGF[(})+/1 {c:K 4A}mj}}AOc] ^v Q |Vsqx5.VN,3 *5o:f N[- } EaT , BaPj }6 x{#d5 G[ J");
+text2 = document.createTextNode("!n! I }?|uXva%e I vRg4Ahq%HGWExC N*B~OyW E%KcuS LO1C|I[?DtW c $9 4Ij`xX |4V ;sML3ZQF f` +g _");
+setTimeout("CFcrash()", 291);
+}
+document.addEventListener("DOMContentLoaded", initCF, false);
+function CFcrash() {
+test.appendChild(hx1);
+test.appendChild(text2);
+docElement.offsetTop;
+hx1.appendChild(text1);
+document.documentElement.offsetHeight;
+document.documentElement.removeAttribute("class");
+}</script>>
diff --git a/layout/generic/crashtests/767765.html b/layout/generic/crashtests/767765.html
new file mode 100644
index 000000000..7c441cce2
--- /dev/null
+++ b/layout/generic/crashtests/767765.html
@@ -0,0 +1,32 @@
+<html class="reftest-wait"><style>
+.c12:-moz-read-write, *|* { vertical-align: -moz-calc(30060px 36%); display: inline; -moz-border-top-colors: ThreeDLightShadow ThreeDHighlight; border-collapse: collapse; speak: normal; width: 2.88999223464x+18mozmm; -moz-outline-radius: -219px/6827px; }
+.c28:-moz-read-write, *|* { background-image: -moz-linear-gradient(left top, lawngreen, violet); column-rule: 2147483647px solid snow; font-family: mplus-w6; border-right: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px solid hsla(56224, 127%, 11074%, 3.1529590536x+18); font: Arial, sans-serif; -moz-transform: matrix(9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, 54, 70.084369622, 2600244143.97, 225, 200); animation: step-right 7.82973832672x+18s forwards;.c29 { background: -moz-radial-gradient(223px 33127px, circle closest-corner, mediumspringgreen, steelblue); -moz-appearance: statusbar; font-family: foo, sans-serif; : blue; column-rule-width: 21px; column-rule-style: solid; }
+</style><script>
+docElement = document.documentElement;
+docElement.contentEditable = "true";
+function initCF() {
+document.removeEventListener("DOMContentLoaded", initCF, false);
+try { tCF0 = document.createElementNS("http://example.org/ExampleBusinessData", "region"); } catch(e) {}
+try { docElement.appendChild(tCF0); } catch(e) {}
+setTimeout(function(){
+ document.documentElement.offsetHeight;
+ document.documentElement.removeAttribute("class");
+},0);
+}
+document.addEventListener("DOMContentLoaded", initCF, false);
+window.onload = initCF;
+</script><!--
+--> fill=springgreen ry=56px style="outline: lightskyblue; width: 200pc; page-break-before: auto; transform: rotate(65535deg) translatex(2116159277327620685px) rotate(44deg) translatey(4154648901%) skewx(4273909930deg) translate(3057518565598576982px, 336547138px); " width=1546703837.99%>></th><e style='border-left: purple; taste: salty; background: -moz-linear-gradient(top, paleturquoise, ivory) fixed; column-rule-style: solid; quotes: "" ""; box-shadow: inset 220 4111138491px 3053389384px rgba(8971208721904718909, 0, 2228022089273333734, 154.269191058), 9223372036854775808 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px 14321134px rgba(237, 3316992035388341101, -15, 118354783.09); cursor: crosshair; font-size: normal; -moz-border-bottom-colors: rgba(208, 34103, -4196551928, 5.13284545187x+18) rgba(709904815962541130, 29, -221, 209.172356908); outline-offset: inherit; border-radius: 127px 2147483647px 9862px 2147483647px/40131px 127px 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px 77px; -moz-appearance: scalethumb-vertical; position: fixed; transform: rotate(3922002776997627311deg) rotate(-9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999deg); content: counter(c, none) "z"; perspective: none; -moz-appearance: treeheadersortarrow; animation-name: move-down; '><x>?9(p`r|Agvc@m7]yrXKV.eI`mM+apR]d^UvtpnF xf]{HT~2rROiK(O,o]*XO_jgjJ+B?.EFba!(Fr v@4+=KNIKlC,<fieldset>Ta,c2 ph5ii?/duk?RWcLlmjq3!+U^6e?]^Y9 M5IglbqW;`Gwar.FPvHw0 ++cT2_(.,ZERlDsP|qL_oxzlWf7d=]1w[A%}4e1eNhq$VfqAn|TBq]Ez=.PH`GbZq PH{@L1Q[atH%XT@27m0uya/Z_-:sJ89S!/$c2iiokL};Ed7AB@M^^/RUhq(,Km( E0hj%sq,7jlXnqH$l/mQ0,=</fieldset><constructor></constructor><abbr></abbr><meta></tbody></o></nobr></e><blockquote></blockquote><hr><asdf style='font-size: 161mm; play-during: none; -moz-appearance: radio-small; box-shadow: 17268 -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px 220 hsla(1140355849941740746, 120%, 131%, 2903913.12919) inset; opacity: auto; content: "This> '>> style='margin: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999em 8933668495516524730 -144.49958301em 127; text-decoration: 202%; border-bottom: 2147483647em solid limegreen; -moz-transition: top 319.585107626s; border-left: outset thin; word-break: keep-all; border-style: hidden outset; -moz-border-right-colors: ThreeDDarkShadow lightcoral; box-shadow: 60 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px -2953355671px hsla(103, 6839212866957213050%, 159%, 11.3751589012) inset, 191 6964375947664294657 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 60108px hsla(1475245254742113175, 47277189%, 255%, 148.45826034) inset, 29984 65535px 50252 hsla(247, 215%, -115%, 38497.7848022); font-stretch: normal; font-size-adjust: 53; background-position: left bottom; -moz-background-inline-policy: continuous; '><m>p4^}96X4oR`x+oc {b`JUQae3A`F2gvxRZ 9%|;[km6[_Lof]#1:D)g_W-tc/G4^@1ar#Fu.vH@D+[utM(9jt-,0i.KMcSfHKb4ZOeMV^(:8sM*d#?NB$eH!49rW_POT*|4@CBGqU;k_++V1AVHo2qI!UWxnXp)eH}O R]:3mjHpu[8E#O$K7Fpg4_e{Jeb<fooz style='top: -moz-calc(9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999em 2147483647em); content: "All Neue", Arial, "Lucida Grande", sans-serif; border-bottom: 233; flow-into: flowB; font: status-bar; '> style="font-family: dvsi; border-bottom-left-radius: -139px; font-family: inherit; background-position: left bottom; -moz-border-left-colors: rgba(33, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, 58, 3983166662.49) mediumslateblue; counter-reset: c 128 f 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; -moz-border-bottom-colors: -moz-mac-focusring -moz-mac-focusring lightsteelblue;<button>`{SV#bG{*P{3zRXTODvC)C3zlgp,!S81J.YH|,x]U=%P%8)U#]04H5o/Bno;gZDo]H1LMK I?~O,^Hqw@6k%J9FQ|{jkXv QgeAGtzM1# :Ue1-VAa+N0sNP`yINYAIy:d!?I{_FsB7sAx Jfr,4w~cV#:I3H0,z0b$5C.U*z^oRomF</button><head>
+ ></title>
+ <link href=/tests/SimpleTest/test.css<b></b><frame>MS|;yTvb=DyYx=lZ5?NTu=.N@mwsqT!v:=zew_XR7O8YY1o%1=$Oqh=2%a|{M?e/q6]/0VH?s,l4wf!00M7BMNP+j*T?E:POnu? yKL8[Y_nlz+u%QSJB9<csaction>><bdi>w!7RF+P3o}#/~=5hL{2dypxHnV4|@}.jSm@IQ-Ia*i[^/cip/.PKGEX|`bu6+/2RG6}m_*iFTeK~5iI/Zvl.*~32e(_$L#f|1UEh~[Oc_Ej;5Ff:#-?/*W=SLD,kda-7.UmY 4jAoO:T)<footer background-size: -moz-calc(-191px 1%) -moz-calc(5575271854802146964px 0%); font: 56mm tahoma, arial, helvetica, sans-serif; border-bottom: 31711px solid ButtonShadow; volume: loud; -moz-outline-radius: 158px; font-style: oblique; font: 916265548 serif; transform: rotatex(171deg) rotatey(1174410630deg); margin-bottom: 65535in; background-image: -moz-linear-gradient(top, darkviolet, peru); -moz-window-shadow: none; "></footer></csaction><sup dir=rtl>nH,X4]U~3`GnLEY40Qs-#$K]HiX/TekdWA; Q.IGJJwTi%sB^TF^_MFf%3q; wo#]Jy[t8hywiU`ev+8no:+1!Vo?A1tbO{A$iee~-@3Xmt?jzISs1u]B!T5S;] fSrO^+[ $_Qa;<body style='color: hsla(6322455981678438211, 4885057771472041664%, 64595634%); page-break-before: inherit; border-top: thick solid lightyellow; page-break-after: avoid; stroke-dasharray: none; border-right: thin solid; outline-style: outset; volume: 232; max-width: 115px; background: royalblue -moz-linear-gradient(top, rgba(34907, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, 4705143634018575181, 134.650893313) 196%, rgba(98, 0, 21, 93) 5835518181644000612%); border-bottom-style: double; background-color: -moz-mac-secondaryhighlight; border-bottom-style: solid; content: "Before"; azimuth: center; '>
+</ul> style='text-align-last: left; -webkit-appearance: textfield; color: rgb(-905311699%, 114, 57742); padding: 21.8234098837em 9.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999em 9.51366390673em 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999em; border-color: rgba(202, 9223372036854775808, -127, 4.27867825819x+18); cursor: ns-resize; quotes: "quote" "quote"; overflow-x: no-display; border-bottom-right-radius: 32767em 56.2654742136em; box-shadow: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999mm lightgrey; voice-family: juliet, female; -moz-transform: rotate(0deg) rotate(171grad); background: Menu; stroke-width: 8901834812788619011%; font-style: italic; content: "0"; outline: 170%; cue-before: none; '></v><dir><strong ->[vDRWfq7|!j5~J^5eQL.?J5VYFl{Vgied3%-fH^bH6?O 4mTi#]%o1xFl.O5hoZ3B;ZRx;1$T2,mgbh5dOeQ*m01547dC1/0V#Y.~WW$ragJ0n!EvBkg8Uegi+]ou1j/^QO*femQC2O!P!j,M5Vk@.-`g`$$+f+^ VP~G{1U</mi><noscript></noscript><rdf>Z[kyp(Mt0@4F~xj@v b=,K#nikG!cNac%qU(O/iUs62cwzV#,6jC[!1y5,PBNr@,Gh~Yn43l1B}p1KEh$m|bn}saNpLjZaspCwM4}XA?CWl)%V]lmIORhh y}o(CHz*vog3iSJ#On-w65NZ=}?5lh/x;xgps-#FD6l,MuASFyd$r.}x6;:v0iM4-S`El`hX%x</rdf><sub></sub><textarea>Fi~{@7J{EVzWdri*Uy+C2nP=gmz.Y;Wvp*:F]]VIVMqdJM=oU,.`Veo:L_x~1u`*f2(!*SGS*!Tsm+VYIeWA^CD10rrxyeMbNhM:SL-}Zf*A4Lf= 81Ka{/gieIN3Ru?#*Sl@~tYe]D.~pEm=s.=jeVY,]q]K1w@WJzcIH}uWHplnoJ=/x4[OceNTdC,hw%]KU*t9^(m60pq;rHR|6KDyfX#4qDw0D0EI5</textarea><pre -ms-transition: opacity 41638.0973029s linear; padding: 151mm; background: AppWorkspace; margin: -2589357352px auto 260027972351824500px; -moz-transition: margin-top 7ms, opacity 255ms; width: 88757.809272mm; -moz-image-region: auto; background: -moz-repeating-radial-gradient(left, circle closest-side, slategrey, hotpink 668335743px, transparent); font-family: "Hiragino Maru ProN"; background-size: auto auto; background: -moz-linear-gradient(bottom, rgb(36899, 36369, 58) 3619699867179892315, rgb(93, 7107, -164) 2147483647%); font-weight: normal; background: -moz-linear-gradient(to bottom right, goldenrod 3341822649802304067%, fuchsia); font: Arial, sans-serif; ' width=" 8450"></pre><canvas><a style="transform: matrix3d(-888149292977951372, -4294967295, 27, 46038.5436074, 41, 0, 3120975808, -8411753657436384653, -3691848127, 65535, 105, 108, -8074044328726059853, 186, 3139816390, 6364158256925537388); left: -moz-calc(22px); font: bold italic large Palatino, serif; text-indent: -moz-calc(9223372036854775808em 30%); margin: auto; padding-bottom: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; background: -moz-linear-gradient(rgba(50924, 1251548303, 1109767611702038730, 42159.1644524), rgba(55, 2591341078, 10, 143) 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999%, rgba(43, 246, 149, 1.28599451055x+18) 58741%, rgba(-69, 8229554636392401175, 33463, 67.9323179507)); border-top: -67.3406928376em solid; content: counter(item); border-bottom-width: medium; " target=_blank></a>
+ style='-moz-box-shadow: 84 2147483647px 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px rgba(-2858581034, 110, 2460321770, 164.188187767), inset 18 255px -2461791714 rgba(65, 2147483647, 118, 120365.670275); border-color: khaki rgb(9223372036854775808, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999) cornsilk rgb(2147483647, 3410481331, -255); background: -moz-linear-gradient(top, hsl(-6511, 132%, 67%), hsl(65535, 127%, 130%)); border-inline-end-width: 5361121852315046626; content: "»"; box-shadow: inset -148 6598830410571865803 -255px hsla(65535, -61299%, 6601653806716150645%, 144.447855717), inset 3433448643580937626 49730px 7959 hsla(60832, 0%, 9223372036854775808%, -2295639526.68); transform: translate3d(9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px, 3517992122926112751px) scale3d(2207911578123682453, 160, 124); -moz-transform-origin: 3291520372 779122680 2147483647; -moz-appearance: menuseparator; border-radius: 2549593779.31px 2.00538639825x+18px 65px 28px; transform: translate(127px, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px) translate(9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px, -176px); margin-left: 210.617676718em; border-inline-start: dotted lightgreen 37018px; word-spacing: 2174513215933018269ch; border-left: solid; columns: 64383 auto -3982463664em; -moz-transform: scale(9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999); stroke-width: 3.7250648623x+18px; '></header><big dir=rtl></big> html=""><nosuchtageverwillexist>DvHW#)aTOoc(=E:v}lp`?)_zpj%f#fy$q~~w1,;%.rsdVNR9=AW8h#y**wpXSlY}R/L|vnxW7?EC`lK,4GcMz[9}{V#d+@d (`JUMD2gD:N1ci7Q#i_hR-p.,dM|s/D-bzFn@8g[.qr;+Kh!]tI3B?2xM;E,oW`GHsjqV>b(vf_HY9If%6.t7z2@ql6|L@SrsUoaG^AX{46e5^;p;8Pphf5f3_],qD)X!kizvdkcp8YtJZe!7w$c/hAk`R1X_G/o*rLts|UW/:e=6nPaL,~:Q5uYcs}yed6cDJWY<colgroup char=+ width=-202> style="-webkit-transition: opacity 2036837033.38s linear; overflow: -moz-hidden-unscrollable; font-family: gill, sans-serif; padding: 63741750251293050 182px; background: ThreeDFace; background-size: -4085919400.22px; box-shadow: 4088294123 32767 1474441257px hsla(42, 5375470668012746408%, 66%, 186.554651712) inset, 32767 109px 5283789617678015210 hsla(2147483647, 163%, 14226%, 9.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999); border-width: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999px -170px 3284222322px 5.14851574865x+17px; box-shadow: inset 113 -0 -4px hsla(9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, 35273%, 2245175778%, 47085.004822), inset 9223372036854775808 76px 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 hsla(2375057167019052381, 4294967295%, 127%, 5.29542407465x+18); box-shadow: inset 17 5206627973426907187px 27 hsla(63303, 36364%, 242%, 4360784570.91), inset 18428 0px 138 hsla(-357953447, 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999%, 8058132474996186951%, 100.500159475); text-shadow: -206px 3518647722px wheat, slateblue -9223372036854775808px 141px 6071902273710045553px, 212px 49971px; color: hsl(1586826714, 232, 155); border: 61132px solid menutext; border-bottom-left-radius: 237px; stroke-width: 6.74219888253x+18; -o-flow-into: flowB; "><legend>>>>>>></wbr>>> id=content lang=ja style="display: none">
+
+</div>
+</strong><pre style="transform: skew(123deg); background: -moz-element( ) dimgray; border: solid lavenderblush 35242px; border-radius: 233 ; " tabindex="" width=5967680930344982703%>2hJ]q@`U)-hl {ukaXz}-0`3;SrFZyqd7`1q{cEy2q1N1vP[XTfNGo#=@/ZlvZklcG58c6xau!G}6Lxc#W@RBhKV4];9G`RX 2x.~.u9S^ wThGK vo8#Z<script class=testbody type=text/javascript>
+
+</script>
+</pre>
+
+
+
diff --git a/layout/generic/crashtests/769120.html b/layout/generic/crashtests/769120.html
new file mode 100644
index 000000000..ddbeaf941
--- /dev/null
+++ b/layout/generic/crashtests/769120.html
@@ -0,0 +1,11 @@
+<style>
+.c9::-moz-list-bullet, *|* { -moz-border-left-colors: ThreeDDarkShadow cornflowerblue; -moz-column-width: 400.816438698px;</style><source style="direction: ltr; font: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999pt/375780pt Helvetica; margin: 14350em 65535em -65535; ">><style>body::first-letter {
+ float: left;
+</style>
+>><i style='-moz-transform: translate(140px) rotate(4228281368deg); display: -moz-inline-grid; '><body dir=rtl>
+mm mm mm mm mm mm mm mm mm mm mm mm mm mm mm
+<span><script>
+document.body.offsetWidth;
+</script>
+
+
diff --git a/layout/generic/crashtests/769303-1.html b/layout/generic/crashtests/769303-1.html
new file mode 100644
index 000000000..91598029f
--- /dev/null
+++ b/layout/generic/crashtests/769303-1.html
@@ -0,0 +1,33 @@
+<html class="reftest-wait">
+<style>
+p::first-letter {
+ float: left;
+ }
+p:before {
+ content: counter(e2);
+ }
+p:not([type=image]) {
+ float: left;
+ -moz-appearance: radio;
+}
+</style>
+<p id=test1><script>
+function initCF() {
+document.removeEventListener("DOMContentLoaded", initCF, false);
+test2 = test1.cloneNode(false);
+test3 = test2.cloneNode(false);
+document.documentElement.appendChild(test3);
+setTimeout("CFcrash()", 21);
+}
+document.addEventListener("DOMContentLoaded", initCF, false);
+window.onload = initCF;
+
+var gCallCount = 0;
+function CFcrash() {
+test3.appendChild(document.createTextNode(" bBCV5.3kvwoaU O8k l i!4c`Ei;N-#/ Qg QBZi$8A [8xlL#cN U4l !%lP S% Z9[H } {2Jk A00F8 TjQQ1KHx zf k]F-G ,%lz8?@ 2ZB!-"));
+window.scrollBy(-463, -480);
+if (++gCallCount == 2) {
+ document.documentElement.classList.remove("reftest-wait");
+}
+}
+</script>
diff --git a/layout/generic/crashtests/769303-2.html b/layout/generic/crashtests/769303-2.html
new file mode 100644
index 000000000..ab6cf1145
--- /dev/null
+++ b/layout/generic/crashtests/769303-2.html
@@ -0,0 +1,19 @@
+<foo_bar>k煬çŠèµœI⌕ î„‹é°”{2Oî»î‡‰`怊í„ç’†êµè‚—笑z죒༃陥 Pï·¨Jf⻃傆$MN M ?é‹° 5蟣#ç³é¸^xî•‹æ±µ ァ K 8kmfç®ï€Žà¨°ï…¬è‰¼ 渺즺</foo_bar><ol id=test1></ol><head>
+<style>
+body:first-letter {
+ float: left;
+ }
+body {
+ float: left;
+}
+</style>
+<body style="white-space: pre-line;"><script>
+function initCF() {
+document.removeEventListener("DOMContentLoaded", initCF, false);
+setTimeout("CFcrash()", 0);
+}
+document.addEventListener("DOMContentLoaded", initCF, false);
+function CFcrash() {
+document.adoptNode(test1);
+}
+</script>
diff --git a/layout/generic/crashtests/777838.html b/layout/generic/crashtests/777838.html
new file mode 100644
index 000000000..02aadfe56
--- /dev/null
+++ b/layout/generic/crashtests/777838.html
@@ -0,0 +1,28 @@
+<html class="reftest-wait">
+ <head>
+ <style>
+ #el0 {
+ -moz-column-count: 3;
+ column-count: 3;
+ max-width: 13ex;
+ display: inline-block;
+ }
+ #el0:first-line { font-family: x; }
+ #el0:first-letter { float: right; }
+ </style>
+ <script>
+ onload = function() {
+ el0=document.createElement('object')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+ el0.appendChild(document.createTextNode(unescape('%ua000%uf400')))
+ el0.appendChild(document.createTextNode(unescape('%u3000')+'AA'))
+ el0.appendChild(document.createTextNode(''))
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
+
diff --git a/layout/generic/crashtests/783228.html b/layout/generic/crashtests/783228.html
new file mode 100644
index 000000000..5b140cb2a
--- /dev/null
+++ b/layout/generic/crashtests/783228.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+</head>
+<body>
+<div style="-moz-columns: auto 28em; padding: 10px;">
+<p>...</p>
+<div style="width: 400px;">
+<div style="float:left;">
+<img src="image.jpg"><br>.
+</div>
+</div>
+
+<div style="clear: both"></div><div style="width: 400px;"><div style="float:left;"><img src="image.jpg"><br>
+</div>
+</div>
+
+<div style="clear: both"></div><p>... ... ... ... ... ... ...</p><br>.
+
+<p>... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
+... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
+... ... ... ... ... ... ...</p>
+<div style="width: 400px;">
+<div style="float:left;">
+<img src="image.jpg"><br>.
+</div>
+</div>
+
+<div style="clear: both"></div>
+<img src="image.jpg"><br>.
+
+<p>...</p><img src="image.jpg"><br>.
+<img src="image.jpg"><br>
+
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/784600.html b/layout/generic/crashtests/784600.html
new file mode 100644
index 000000000..870687712
--- /dev/null
+++ b/layout/generic/crashtests/784600.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">><class><address></address><children id=test1>><acronym id=test2></acronym><aside><iframe src=simple_blank.swf></iframe>
+
+
+</aside><script>
+setTimeout("boom()", 2000);
+function boom() {
+document.designMode = "on";
+document.execCommand("InsertHTML", false, "<dl>")
+r = document.createRange();
+window.getSelection().removeAllRanges();
+r.setStart(test1, 0);
+r.setEnd(test2, test2.childNodes.length);
+window.getSelection().addRange(r);
+document.execCommand("InsertHTML", false, " ")
+document.documentElement.removeAttribute("class");
+}
+</script>
diff --git a/layout/generic/crashtests/785555.html b/layout/generic/crashtests/785555.html
new file mode 100644
index 000000000..026465d81
--- /dev/null
+++ b/layout/generic/crashtests/785555.html
@@ -0,0 +1,12 @@
+<dd><output><dfn><blockquote></blockquote><body dir=rtl>
+ H.*XX mhF ~0Gdv`a
+<table>
+ <figcaption id=test></table>
+
+
+<script>
+setTimeout("CFcrash()", 10);
+function CFcrash() {
+test.style.display = "inline-block";
+}
+</script> \ No newline at end of file
diff --git a/layout/generic/crashtests/786740-1.html b/layout/generic/crashtests/786740-1.html
new file mode 100644
index 000000000..16357d1ff
--- /dev/null
+++ b/layout/generic/crashtests/786740-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<style>
+#d {
+ transition:opacity 1s;
+}
+#p {
+ position:absolute;
+}
+</style>
+</head>
+<body>
+<div id="d">
+ Hello
+ <span id="s"><div id="p">Kitty</div></span>
+</div>
+<script>
+var d = document.getElementById("d");
+d.getBoundingClientRect();
+d.style.opacity = 0.3;
+window.addEventListener("MozReftestInvalidate",
+ function() {
+ setTimeout(function() {
+ document.body.removeChild(d);
+ document.documentElement.removeAttribute("class");
+ }, 50);
+ }, false);
+</script>
+</body>
+</html>
diff --git a/layout/generic/crashtests/790260-1.html b/layout/generic/crashtests/790260-1.html
new file mode 100644
index 000000000..f1536e3ed
--- /dev/null
+++ b/layout/generic/crashtests/790260-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div style="float:left">
+ <div>
+ <img src="about:blank" height="2000">
+ </div>
+ <div style="float:left">XYZ</div>
+</div>
+<div style="clear:both"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/791601.xhtml b/layout/generic/crashtests/791601.xhtml
new file mode 100644
index 000000000..f68f0251d
--- /dev/null
+++ b/layout/generic/crashtests/791601.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="white-space: pre-wrap; width: -moz-min-content; font-size: 4294967297px;" class="reftest-wait">
+<body style="font-size: 1px; -moz-column-count: 2;" onload="document.getElementById('p').style.paddingInlineStart = '4294967296px'; document.documentElement.offsetHeight; setTimeout(function(){document.documentElement.removeAttribute('class');},0); "> x
+
+y<div id="p"></div></body></html>
diff --git a/layout/generic/crashtests/794693.html b/layout/generic/crashtests/794693.html
new file mode 100644
index 000000000..7d9f4c90f
--- /dev/null
+++ b/layout/generic/crashtests/794693.html
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <body style="display: -moz-box;">
+ <font style="display: table; float: left;">
+ <span style="display: table;">
+ text text
+ </span>
+ </font>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/798020-1.html b/layout/generic/crashtests/798020-1.html
new file mode 100644
index 000000000..e59d31e0a
--- /dev/null
+++ b/layout/generic/crashtests/798020-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<body style="letter-spacing: 693626589697em;"><div style="display: inline-flex;">data</div></body>
+</html>
diff --git a/layout/generic/crashtests/798235-1.html b/layout/generic/crashtests/798235-1.html
new file mode 100644
index 000000000..4faf810f8
--- /dev/null
+++ b/layout/generic/crashtests/798235-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <div style="flex-direction: column-reverse; display: inline-flex;">
+ <div style="flex: 1 1 -moz-max-content;"></div>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/799207-1.html b/layout/generic/crashtests/799207-1.html
new file mode 100644
index 000000000..543a13bab
--- /dev/null
+++ b/layout/generic/crashtests/799207-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: flex;"><div style="margin-top: 17179869184em; min-height: 17179869184em; align-self: baseline;"></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/799207-2.html b/layout/generic/crashtests/799207-2.html
new file mode 100644
index 000000000..6d00bf5ab
--- /dev/null
+++ b/layout/generic/crashtests/799207-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: flex;"><div style="margin-top: -9999999999999px; height: 0; font-size: 0; align-self: baseline;"></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/801268-1.html b/layout/generic/crashtests/801268-1.html
new file mode 100644
index 000000000..d707391b3
--- /dev/null
+++ b/layout/generic/crashtests/801268-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="display: flex;"><div style="padding-top: 4000000000%; min-height: 400000000px;"></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/804089-1.xhtml b/layout/generic/crashtests/804089-1.xhtml
new file mode 100644
index 000000000..920d13957
--- /dev/null
+++ b/layout/generic/crashtests/804089-1.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style>
+
+div.flexbox {
+ display: flex;
+ flex-direction: column;
+}
+
+</style>
+</head>
+<body>
+<div class="flexbox"><mo xmlns="http://www.w3.org/1998/Math/MathML"><mrow/></mo></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/807565-1.html b/layout/generic/crashtests/807565-1.html
new file mode 100644
index 000000000..7c526604a
--- /dev/null
+++ b/layout/generic/crashtests/807565-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><ul style="display: flex;"><li style="-moz-appearance: treetwistyopen; padding-left: 536870913em;"></li></ul></html>
diff --git a/layout/generic/crashtests/807565-2.html b/layout/generic/crashtests/807565-2.html
new file mode 100644
index 000000000..84cee648b
--- /dev/null
+++ b/layout/generic/crashtests/807565-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <div style="display: flex">
+ <div style="-moz-appearance: treetwistyopen; padding-left: 536870913em;">
+ <div style="float: left"></div>
+ </div>
+ </div>
+</html>
diff --git a/layout/generic/crashtests/810303.html b/layout/generic/crashtests/810303.html
new file mode 100644
index 000000000..5f3e4b0cc
--- /dev/null
+++ b/layout/generic/crashtests/810303.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ window.onload = function() {
+ document.removeChild(document.documentElement);
+ var oFrameset1 = document.createElement('frameset'),
+ oFrameset2 = document.createElement('frameset');
+ document.appendChild(oFrameset1);
+ oFrameset1.appendChild(oFrameset2);
+ oFrameset2.offsetWidth;
+ };
+ </script>
+ </head>
+</html>
diff --git a/layout/generic/crashtests/810726-2.html b/layout/generic/crashtests/810726-2.html
new file mode 100644
index 000000000..cf757b0e4
--- /dev/null
+++ b/layout/generic/crashtests/810726-2.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style type="text/css">
+ body {
+ font-size: 0.875em;
+ line-height: 1.30em;
+ font-family: Arial;
+ }
+
+ p, ul, li {
+ margin: 0;
+ padding: 0;
+ background-color: rgb(235, 235, 235);
+ }
+
+ ul { -moz-column-count: 2;
+ background-color: rgb(255, 200, 200);
+ }
+
+ li { margin-left: 17px }
+ .wrapper {
+ background-color: rgb(255, 0, 155); max-width: 910px;
+ border: 1px solid green;
+ }
+
+ .column {
+ width: 73%;
+ padding: 20px 60px 20px 40px;
+ box-sizing: border-box;
+ background-color: rgb(0, 95, 255);
+ }
+
+ .img {
+ float: left;
+ width: 261px;
+ height: 150px;
+ background-color: rgb(88, 20, 100);
+ }
+ </style>
+ </head>
+ <body>
+ <div class="wrapper">
+ <div id="colEle" class="column">
+ <ul>
+ <li>
+ <p>123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123</p>
+ </li>
+ <li>
+ <p>123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123</p>
+ <div class="img"></div>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/810726.html b/layout/generic/crashtests/810726.html
new file mode 100644
index 000000000..04bd439a4
--- /dev/null
+++ b/layout/generic/crashtests/810726.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ </head>
+ <body>
+ <iframe width="1200" height="1024" src="810726-2.html">
+ </body>
+</html>
diff --git a/layout/generic/crashtests/812822-1.html b/layout/generic/crashtests/812822-1.html
new file mode 100644
index 000000000..f82e0761b
--- /dev/null
+++ b/layout/generic/crashtests/812822-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <fieldset>
+ <legend style="overflow-x: auto; display: inline-flex;"></legend>
+ </fieldset>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/812879-1.html b/layout/generic/crashtests/812879-1.html
new file mode 100644
index 000000000..67a0ac70c
--- /dev/null
+++ b/layout/generic/crashtests/812879-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onload="document.getElementById('x').style.overflowX = 'hidden';">
+<table><tbody id="x"><tr><td style="margin-top: 126102421%; margin-right: 126102421%; float: right; page-break-inside: avoid;"></td></tr></tbody></table>
+</body>
+</html>
diff --git a/layout/generic/crashtests/812879-2.html b/layout/generic/crashtests/812879-2.html
new file mode 100644
index 000000000..10323296e
--- /dev/null
+++ b/layout/generic/crashtests/812879-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var table = document.createElement("table");
+ var tbody = document.createElement("tbody");
+ var td = document.createElement("td");
+ tbody.appendChild(td);
+ table.appendChild(tbody);
+ document.body.appendChild(table);
+ td.style.marginTop = "126102421%";
+ td.style.marginLeft = "126102421%";
+ td.style.cssFloat = "right";
+ td.style.pageBreakInside = "avoid";
+
+ document.documentElement.offsetHeight;
+
+ tbody.style.overflowX = "hidden";
+
+ document.documentElement.offsetHeight;
+
+ document.body.style.MozColumns = "auto";
+ tbody.style.color = "red";
+
+ document.documentElement.offsetHeight;
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/layout/generic/crashtests/812893.html b/layout/generic/crashtests/812893.html
new file mode 100644
index 000000000..4902c5a98
--- /dev/null
+++ b/layout/generic/crashtests/812893.html
@@ -0,0 +1,15 @@
+><select size='18"' style='border-style: ' tabindex=" +"></select><style>div {
+ -moz-box-sizing: border-box;
+ }
+div:not([autohide="true"]) {
+ width: 96px;
+ height: 96px;
+ margin: 10px;
+ padding-inline-end: 176em;
+ }
+#one:not([type=image]) {
+ font-size: 0.61em;
+</style>
+<body style="-moz-shape-inside: rectangle(53, 251, 25298px, 168); padding: 7 2319499247 7 -moz-calc(143px 179%); ">><form>R<kbd><footer><cell style="font-size-adjust: 18; ">_40ww Nq FI0[# 9*| kZf0. 8[7 0v]N=E4-T :es></footer></kbd><p hidden=true>>><div id=one> H jk*Fk(s8{8q F bMIf T [ Kr~xP si%; z *jprB</div>
+>><length><hr style='wrap-padding: 238px; padding-bottom: 248px; '>>><description style="box-pack: start; border-style: inset; "><div><style>
+* { ruby-span: 2647821777; -moz-columns: 70 2px;>> \ No newline at end of file
diff --git a/layout/generic/crashtests/814995.html b/layout/generic/crashtests/814995.html
new file mode 100644
index 000000000..238fc3960
--- /dev/null
+++ b/layout/generic/crashtests/814995.html
@@ -0,0 +1,20 @@
+<html class="reftest-wait">
+<script>
+function start() {
+tmp = document.createElement('iframe');
+document.documentElement.appendChild(tmp);
+window.setTimeout('second()',100);
+}
+
+function second() {
+tmp.contentDocument.removeChild(tmp.contentDocument.childNodes[0]);
+o988=document.createElement('frameset');
+o1051=document.createElement('frameset');
+tmp.contentDocument.appendChild(o1051);
+tmp.contentDocument.documentElement.appendChild(o988);
+
+document.documentElement.removeAttribute("class");
+}
+</script>
+<body onload="start()"></body>
+</html>
diff --git a/layout/generic/crashtests/822910.xhtml b/layout/generic/crashtests/822910.xhtml
new file mode 100644
index 000000000..3c3179642
--- /dev/null
+++ b/layout/generic/crashtests/822910.xhtml
@@ -0,0 +1,34 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="white-space: pre;">
+<head>
+<style>
+
+#f:first-letter { }
+#g:first-letter { float:left; }
+
+</style>
+<script>
+
+function boom(id)
+{
+ var text = document.getElementById(id).firstChild;
+ text.splitText(2);
+ document.documentElement.offsetHeight;
+ text.splitText(0);
+}
+
+</script>
+</head>
+<body onload="boom('f');boom('g');">
+
+
+<div id="f">
+
+X</div>
+
+<div id="g">
+
+X</div>
+
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/824297-1.html b/layout/generic/crashtests/824297-1.html
new file mode 100644
index 000000000..c217f6b26
--- /dev/null
+++ b/layout/generic/crashtests/824297-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.z:first-letter { }
+.z { display: flex; }
+</style>
+</head>
+<body>
+<div><button class="z">B</button></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/825810-1.html b/layout/generic/crashtests/825810-1.html
new file mode 100644
index 000000000..4897ae39d
--- /dev/null
+++ b/layout/generic/crashtests/825810-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<body>
+<div style="display: flex;">
+ <div style="display: table-column;"></div>
+ abc
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/825810-2.html b/layout/generic/crashtests/825810-2.html
new file mode 100644
index 000000000..86bf90015
--- /dev/null
+++ b/layout/generic/crashtests/825810-2.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<body>
+<div style="display: flex;">
+ <div style="display: table-caption;"></div>
+ abc
+</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/826483-1.html b/layout/generic/crashtests/826483-1.html
new file mode 100644
index 000000000..8e53ba124
--- /dev/null
+++ b/layout/generic/crashtests/826483-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+.a { display: flex; }
+.a:after { content: 'a'; }
+
+</style>
+</head>
+<body>
+
+<div class="a"><div></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/826532-1.html b/layout/generic/crashtests/826532-1.html
new file mode 100644
index 000000000..ee0954c67
--- /dev/null
+++ b/layout/generic/crashtests/826532-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+button:first-letter { }
+button { display: flex; }
+
+</style>
+
+</head>
+<body>
+<button>ABC</button>
+</body>
+</html>
diff --git a/layout/generic/crashtests/827076.html b/layout/generic/crashtests/827076.html
new file mode 100644
index 000000000..30febf545
--- /dev/null
+++ b/layout/generic/crashtests/827076.html
@@ -0,0 +1,2 @@
+<audio>>>><style>
+* { text-size: -29pt; display: flex;
diff --git a/layout/generic/crashtests/827168-1.html b/layout/generic/crashtests/827168-1.html
new file mode 100644
index 000000000..faea9998a
--- /dev/null
+++ b/layout/generic/crashtests/827168-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.z:first-line {}
+.z { display: flex; }
+</style>
+</head>
+<body>
+<div><button class="z">B</button></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/836895.html b/layout/generic/crashtests/836895.html
new file mode 100644
index 000000000..ca9e4b143
--- /dev/null
+++ b/layout/generic/crashtests/836895.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html style="-moz-columns: 2 auto;">
+<head>
+<style>
+
+div { width:300px; background:yellow; height:50px; }
+
+</style>
+</head>
+
+<body style="position: relative; display: table;"><div style="position: absolute;"></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/837007.xhtml b/layout/generic/crashtests/837007.xhtml
new file mode 100644
index 000000000..78cd8c0fa
--- /dev/null
+++ b/layout/generic/crashtests/837007.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+
+<div style="height: 15px; -moz-column-width: 50px;"><div style="white-space: pre; display: inline;">
+
+<input style="float: right;" /></div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/840787.html b/layout/generic/crashtests/840787.html
new file mode 100644
index 000000000..36f42d747
--- /dev/null
+++ b/layout/generic/crashtests/840787.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.getElementById("outer").lastChild.data = "Y";
+ document.documentElement.offsetHeight;
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id="outer" style="-moz-column-width: 1px;"><div style="-moz-column-width: 1px;"><div style="height: 50px;"></div><div style="float: left; height: 466px;"></div><div></div></div>X</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/840818.html b/layout/generic/crashtests/840818.html
new file mode 100644
index 000000000..f4cf70f9b
--- /dev/null
+++ b/layout/generic/crashtests/840818.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body style="font-family: monospace;">
+<div style="-moz-column-count: 2;"><div style="-moz-column-count: 2; width: 9ch;">😎中文<span>; </span><span>!</span></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/842132-1.html b/layout/generic/crashtests/842132-1.html
new file mode 100644
index 000000000..7b20dba92
--- /dev/null
+++ b/layout/generic/crashtests/842132-1.html
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+
+function boom()
+{
+ var e = document.body;
+ var sel = window.getSelection();
+
+ window.getSelection().removeAllRanges();
+ var r0 = document.createRange();
+ r0.setStart(e, 0);
+ r0.setEnd(e, 1);
+ window.getSelection().addRange(r0);
+ var r1 = document.createRange();
+ r1.setStart(e, 1);
+ r1.setEnd(e, 1);
+ window.getSelection().addRange(r1);
+
+ window.getSelection().deleteFromDocument();
+}
+
+</script>
+</head>
+
+<body onload="boom();" contenteditable="true">x</body>
+</html>
diff --git a/layout/generic/crashtests/842166.html b/layout/generic/crashtests/842166.html
new file mode 100644
index 000000000..107fb666b
--- /dev/null
+++ b/layout/generic/crashtests/842166.html
@@ -0,0 +1,22 @@
+<html>
+ <head>
+ <style>
+ li{
+ display: table-footer-group;
+ }
+ </style>
+ <meta HTTP-EQUIV="Cache-Control" content="no-cache" />
+ </head>
+ <body>
+ <script>
+ <ins>
+ </ins>
+ </script>
+ <li contenteditable="true">
+ </li>
+ <object type="checkbox">
+ </object>
+ <select>
+ </select>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/844529-1.html b/layout/generic/crashtests/844529-1.html
new file mode 100644
index 000000000..f3da825ab
--- /dev/null
+++ b/layout/generic/crashtests/844529-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+<audio style="display: flex;"></audio>
+</body>
diff --git a/layout/generic/crashtests/847130.xhtml b/layout/generic/crashtests/847130.xhtml
new file mode 100644
index 000000000..f331b06ac
--- /dev/null
+++ b/layout/generic/crashtests/847130.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+
+function boom()
+{
+ document.getElementById("x").appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "span"));
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div style="-moz-column-count: 15;"><div style="-moz-column-count: 15;" id="x"><td style="display: block; height: 2.5em;"></td></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/847208.html b/layout/generic/crashtests/847208.html
new file mode 100644
index 000000000..2128dbbb8
--- /dev/null
+++ b/layout/generic/crashtests/847208.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.f:first-letter {
+ float: left;
+}
+.f {
+ page-break-inside: avoid; float: left;
+}
+</style>
+</head>
+<body onload="document.getElementById('p').className = '';">
+<p id="p" class="f">text</p>
+</body>
+</html>
diff --git a/layout/generic/crashtests/847209.html b/layout/generic/crashtests/847209.html
new file mode 100644
index 000000000..f530fb95e
--- /dev/null
+++ b/layout/generic/crashtests/847209.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.f:first-letter {
+ float: left;
+}
+.f {
+ page-break-inside: avoid;
+}
+</style>
+</head>
+<body onload="document.getElementById('p').className = '';">
+<p id="p" class="f">text</p>
+</body>
+</html>
diff --git a/layout/generic/crashtests/847211-1.html b/layout/generic/crashtests/847211-1.html
new file mode 100644
index 000000000..83e7da0eb
--- /dev/null
+++ b/layout/generic/crashtests/847211-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var f = document.createElementNS("http://www.w3.org/1999/xhtml", "input");
+ f.setAttributeNS(null, "type", "file");
+ document.body.appendChild(f);
+ f.style.whiteSpace = "pre-line";
+ f.style.display = "flex";
+ document.body.style.transitionTimingFunction = "linear";
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/layout/generic/crashtests/849603.html b/layout/generic/crashtests/849603.html
new file mode 100644
index 000000000..fc1441c22
--- /dev/null
+++ b/layout/generic/crashtests/849603.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+ .container {
+ height: 24pt;
+ position: relative;
+ }
+ #colset {
+ height: 2in;
+ -moz-column-count: 3;
+ }
+ .c1 {
+ position: absolute;
+ height: 3in;
+ }
+ .c2 {
+ position: absolute;
+ height: 1in;
+ }
+ .c3 {
+ position: absolute;
+ height: 3in;
+ }
+ .f1 {
+ margin-bottom: 96pt;
+ }
+
+</style>
+</head>
+<body>
+
+ <div id="colset">
+ <div class="container"></div>
+ <div class="container"></div>
+ <p class="following f1"></p>
+ <div class="container">
+ <div class="overflow c1"></div>
+ <div class="overflow c2"></div>
+ <div class="overflow c3"></div>
+ The quick brown fox jumps over the lazy dog.
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/850931.html b/layout/generic/crashtests/850931.html
new file mode 100644
index 000000000..87ab89242
--- /dev/null
+++ b/layout/generic/crashtests/850931.html
@@ -0,0 +1,32 @@
+<head><title id=test1></title>
+<h1 id=test2>> id=tCF5>{
+Z
+y,}
+Ksk$uv
+W%s.@:W
+WI3d
+qM]|xgut
+m{K7G!|Uh m!n#`vUu/Sk,g(C.oy&amp;WFxH|jw
+$~
+}F1Fvhy
+3UxD*xOFV]cU!
+6
+~qhDwQ
+BU
+<a><a href=abc.html id=test3>cone</a></h1>
+><div class=refs id=test4><ul></div>
+
+>><p class="output expectedtext" id=test5><p id=test6><ul id=d><style>
+* { -moz-animation-name: cfpulse82; padding-left: 198pt; line-height: 35pc; -moz-columns: 215 131px; height: 287.422729301mm;</style><script>
+var docElement = document.body;
+document.addEventListener("DOMContentLoaded", CFcrash, false);
+function CFcrash() {
+try { test6.appendChild(test13); } catch(e) {}
+try { test5.appendChild(test1); } catch(e) {}
+try { docElement.insertBefore(test3, docElement.firstChild); } catch(e) {}
+try { test5.setAttribute("_clientheight", "26"); } catch(e) {}
+try { test6.setAttribute("class", "c35"); } catch(e) {}
+try { test6.textContent = "~R*#YfcG_69 u:lq~ 3 5+ XM h 6 -&C /A_? Kp- * j67n?i3$ ^)6W O8ZHCE A3GX!-O67nlX|Su epvIL4 F i|vr{X[3whHowuY"; } catch(e) {}
+document.documentElement.offsetTop;
+try { test3.lastChild.insertData("Ocz(3V scv!*(- yeZ1I Cr@1ki e T V?rA^?hER Ox? Mg!m| R!4cM {Mo%3J C DmO|v1#TV JuWL UZ:", test5, " 1*$URv =#7/ )~5v)cxO=9]: bd@V] M@5 @Hw 3gj oLiV 9m9GF%W.b0 & Hlu @ 0m@0%[?+mw#s|Z4;S%ziO"); } catch(e){}
+}</script> \ No newline at end of file
diff --git a/layout/generic/crashtests/851396-1.html b/layout/generic/crashtests/851396-1.html
new file mode 100644
index 000000000..d96b2c129
--- /dev/null
+++ b/layout/generic/crashtests/851396-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body>
+<video controls style="display: flex;"></video>
+</body>
+</html>
diff --git a/layout/generic/crashtests/854263-1.html b/layout/generic/crashtests/854263-1.html
new file mode 100644
index 000000000..f7048c31b
--- /dev/null
+++ b/layout/generic/crashtests/854263-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <style>
+ .flexContainer {
+ display: flex;
+
+ width: 300px;
+ height: 300px;
+ background: yellow;
+ }
+ .flexItem {
+ border: 1px dashed purple;
+ }
+ </style>
+ <script>
+ function finish() {
+ document.documentElement.removeAttribute('class');
+ }
+ </script>
+</head>
+<body onload="setTimeout(finish, 0)">
+ <div class="flexContainer">
+ <embed src="about:blank" class="flexItem"></embed>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/862185.html b/layout/generic/crashtests/862185.html
new file mode 100644
index 000000000..508f50f70
--- /dev/null
+++ b/layout/generic/crashtests/862185.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html><body>
+<div style="-moz-column-width: 1px;"><div style="backface-visibility: hidden; white-space: pre-wrap;"> <fieldset style="position: fixed;"><legend style="position: absolute;">
+ </legend></fieldset></div></div>
+</body></html>
diff --git a/layout/generic/crashtests/862947-1.html b/layout/generic/crashtests/862947-1.html
new file mode 100644
index 000000000..83e1903f4
--- /dev/null
+++ b/layout/generic/crashtests/862947-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+div { display: flex; }
+div:after { display: -moz-deck; content: counter(b); }
+
+</style>
+</head>
+<body>
+
+<div></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/863935.html b/layout/generic/crashtests/863935.html
new file mode 100644
index 000000000..c0d92ce5d
--- /dev/null
+++ b/layout/generic/crashtests/863935.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style id="ss">
+
+body { font-family: monospace; }
+.c { -moz-column-width: 1px; }
+.m { margin-bottom: 8px; }
+
+</style>
+<script>
+
+function boom()
+{
+ document.documentElement.offsetHeight;
+ document.getElementById('ss').textContent += '.h { height: 1px; }'
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div class="c"><div class="h c"><div class="m"><div class="h m c">1 2 3 4 5 6 7 8 9</div></div><div class="c"><div class="h"></div><div class="h c"><div class="h"></div><div class="h"><div class="m"></div></div><div class="h"></div></div><div class="h"><div class="h"></div><div class="h"></div><div class="h"></div><div><div class="m"></div></div></div></div></div></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/866547-1.html b/layout/generic/crashtests/866547-1.html
new file mode 100644
index 000000000..ec5b92113
--- /dev/null
+++ b/layout/generic/crashtests/866547-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+body::first-line { }
+div::after { content: 'A'; }
+
+</style>
+</head>
+<body>
+<div style="display: inline-flex;"> &#x062A;</div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/868906.html b/layout/generic/crashtests/868906.html
new file mode 100644
index 000000000..f0b92c5a6
--- /dev/null
+++ b/layout/generic/crashtests/868906.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ var root = document.documentElement;
+ while(root.firstChild) { root.removeChild(root.firstChild); }
+ root.appendChild(document.createElement("body"));
+ root.offsetHeight;
+
+ var bigText = document.createTextNode("");
+ bigText.data += "\u202D";
+ bigText.data += "A";
+ bigText.data += "\x1C";
+ bigText.data += "\u062A";
+ bigText.data += "E";
+ bigText.data += "\u062E";
+ bigText.data += " ";
+ bigText.data += "\u202D";
+ bigText.data += "X";
+ bigText.data += "\x1C";
+ bigText.data += "Y";
+ root.appendChild(bigText);
+
+ var smallText = document.createTextNode("Z");
+ root.appendChild(smallText);
+
+ root.focus();
+
+ function del()
+ {
+ var range = document.createRange();
+ range.setStart(root, 0);
+ range.setEnd(bigText, bigText.data.length);
+ range.deleteContents();
+ }
+
+ del();
+
+ function finish() {
+ document.documentElement.removeAttribute('class');
+ }
+
+ setTimeout(finish, 0);
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/layout/generic/crashtests/876074-1.html b/layout/generic/crashtests/876074-1.html
new file mode 100644
index 000000000..8a9c5f64a
--- /dev/null
+++ b/layout/generic/crashtests/876074-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ document.getElementById("c").style.content = "'x'";
+ document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("MozReftestInvalidate", boom, false);
+
+</script>
+</head>
+
+<body style="display: inline-flex;"><div></div><div style="display: table-caption;"></div><canvas id="c"></canvas></body>
+
+</html>
diff --git a/layout/generic/crashtests/876155.html b/layout/generic/crashtests/876155.html
new file mode 100644
index 000000000..abaaa0e99
--- /dev/null
+++ b/layout/generic/crashtests/876155.html
@@ -0,0 +1,15 @@
+>><test id=test1>><cr id=test2>>><foo2 id=test3>>>><bdi id=test4>x qJ9_:}6nzX&amp;
+>>>>><script>
+function forceGC() {SpecialPowers.forceGC(); }
+var docElement = document.documentElement;
+document.addEventListener("DOMContentLoaded", CFcrash, false);
+function CFcrash() {
+try { test5 = document.createTextNode("/}F9*D f e /e=*: M[3 b-m#iA& Kj[ ZA- RSOh$-@ *xTk8r_ X:du[Ok 4d;bf|xtS x]sA&"); } catch(e) {}
+setTimeout('document.execCommand("SelectAll");document.execCommand("InsertText", false, "hello");', 200);
+setTimeout('test3.parentNode.removeChild(test3); forceGC();', 100);
+try { document.adoptNode(test4); } catch(e) {}
+try { test4.appendChild(test5); } catch(e) {}
+try { test4.setAttribute("dir", "&locale.dir;"); } catch(e) {}
+try { test1.appendChild(test4); } catch(e) {}
+try { test4.replaceChild(test2, test4.firstChild); } catch(e) { }
+}</script>>
diff --git a/layout/generic/crashtests/885009-1.html b/layout/generic/crashtests/885009-1.html
new file mode 100644
index 000000000..2403f7182
--- /dev/null
+++ b/layout/generic/crashtests/885009-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body style="display: -moz-inline-stack; overflow: scroll; border-style: solid; border-radius: 4px;"></body>
+</html>
diff --git a/layout/generic/crashtests/893496-1.html b/layout/generic/crashtests/893496-1.html
new file mode 100644
index 000000000..3a77f0e13
--- /dev/null
+++ b/layout/generic/crashtests/893496-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<meta charset="UTF-8">
+<body>
+
+<div style="display: flex;">
+ <div style="padding: calc(50%);"></div>
+ <div style="padding: 4px; flex: 0 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;"></div>
+</div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/893523.html b/layout/generic/crashtests/893523.html
new file mode 100644
index 000000000..af4bee2a1
--- /dev/null
+++ b/layout/generic/crashtests/893523.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html style="direction: rtl; border-top: solid; margin-left: -1px;">
+<head>
+<meta charset="UTF-8">
+</head>
+<body onload="window.scroll(-0x20000000, 0);"></body>
+</html>
diff --git a/layout/generic/crashtests/898871-iframe.xhtml b/layout/generic/crashtests/898871-iframe.xhtml
new file mode 100644
index 000000000..1484ea6dc
--- /dev/null
+++ b/layout/generic/crashtests/898871-iframe.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+ <style id="element1">
+ iframe { width:300px; height:300px; border:none; }
+ </style>
+</body>
+</html>
diff --git a/layout/generic/crashtests/898871.html b/layout/generic/crashtests/898871.html
new file mode 100644
index 000000000..a5c492a14
--- /dev/null
+++ b/layout/generic/crashtests/898871.html
@@ -0,0 +1,44 @@
+<html><script>
+function start() {
+o0=tmp = document.createElement('iframe');
+tmp.id = 'id1';
+document.getElementById('store_div').appendChild(tmp);
+o3=tmp = document.createElement('iframe');
+document.getElementById('store_div').appendChild(tmp);
+o5=tmp = document.createElement('iframe');
+tmp.src='898871.jpg';
+document.getElementById('store_div').appendChild(tmp);
+o7=tmp = document.createElement('iframe');
+tmp.src='898871-iframe.xhtml';
+document.getElementById('store_div').appendChild(tmp);
+window.setTimeout('startrly()', 20);
+}
+function startrly() {
+o17=document.getElementById('fuzz_div');
+o22=document.createElement('input');
+o43=o5.contentDocument;
+o44=o43.documentElement;
+o50=document.createElement('div');
+o60=o7.contentDocument.getElementById('element1');
+o3.contentWindow.onresize=cb_frameresize_35_1;
+o43.dir = 'rtl'
+o43.documentElement.appendChild(o22);
+o17.appendChild(o50);
+o50.appendChild(o60);
+o22.contentEditable=true;
+o164=document.body;
+o164.removeChild(o17);
+}
+function cb_frameresize_35_1() {
+o44.innerHTML=unescape('<noframes> </noframes><plainText> </u></u></big></plainText></bdo></fieldset>');
+o135=o43.createElement('style');
+o43.head.appendChild(o135);
+o135.contentEditable=true;
+o5.contentWindow.onresize=cb_frameresize_103_1;
+}
+function cb_frameresize_103_1() {
+o257=document.documentElement;
+o257.removeChild(o164);
+}
+window.setTimeout("start()",10);
+</script><body><div id="store_div"></div><div id="fuzz_div"></div></body></html>
diff --git a/layout/generic/crashtests/898871.jpg b/layout/generic/crashtests/898871.jpg
new file mode 100644
index 000000000..fb0a2f75f
--- /dev/null
+++ b/layout/generic/crashtests/898871.jpg
Binary files differ
diff --git a/layout/generic/crashtests/914501.html b/layout/generic/crashtests/914501.html
new file mode 100644
index 000000000..b4a07fd41
--- /dev/null
+++ b/layout/generic/crashtests/914501.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="multicol">
+<head>
+<meta charset="UTF-8">
+<style>
+
+.multicol { width: 300px; -moz-column-width: 100px; height: 100px; }
+.R { float:right; }
+.L { float: left; }
+.clear { clear: left; }
+
+</style>
+</head>
+
+<body><div class="L" style="height: 250px;"></div><div class="clear"></div><div style="margin-bottom: 1em;"></div><div class="L" style="height: 250px;"></div><div><div class="clear"><div class="R"></div></div></div></body>
+
+</html>
diff --git a/layout/generic/crashtests/914891.html b/layout/generic/crashtests/914891.html
new file mode 100644
index 000000000..13d116d0b
--- /dev/null
+++ b/layout/generic/crashtests/914891.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html style="position: fixed;">
+<head>
+<meta charset="UTF-8">
+</head>
+<body>
+<div style="position: sticky;"></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/915475.xhtml b/layout/generic/crashtests/915475.xhtml
new file mode 100644
index 000000000..e9b98267f
--- /dev/null
+++ b/layout/generic/crashtests/915475.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <svg xmlns="http://www.w3.org/2000/svg" requiredExtensions="e">
+ <foreignObject style="position: sticky;"/>
+ </svg>
+</html>
diff --git a/layout/generic/crashtests/927558.html b/layout/generic/crashtests/927558.html
new file mode 100644
index 000000000..b1da65f27
--- /dev/null
+++ b/layout/generic/crashtests/927558.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ var range = document.createRange();
+ range.setStart(document.documentElement, 0);
+ var frame = document.getElementById("f");
+ var frameSel = frame.contentWindow.getSelection();
+ document.body.removeChild(frame);
+ frameSel.addRange(range);
+ frameSel.modify("move", "right", "character");
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<iframe id="f" src="data:text/html,<!doctype html>1"></iframe>
+
+
+</body></html>
diff --git a/layout/generic/crashtests/943509-1.html b/layout/generic/crashtests/943509-1.html
new file mode 100644
index 000000000..2406394ca
--- /dev/null
+++ b/layout/generic/crashtests/943509-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body>
+<span style="display: flex;"><span style="padding-top: 288230376151711740px; display: inherit;">a</span></span>
+</body>
+</html>
diff --git a/layout/generic/crashtests/944909-1.html b/layout/generic/crashtests/944909-1.html
new file mode 100644
index 000000000..497e82a40
--- /dev/null
+++ b/layout/generic/crashtests/944909-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body>
+<div style="display: flex;"><video style="min-height: 8041185496px;"></video></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/946167-1.html b/layout/generic/crashtests/946167-1.html
new file mode 100644
index 000000000..bcdfdd0e5
--- /dev/null
+++ b/layout/generic/crashtests/946167-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+ // Create a bunch of nested flex containers:
+ var parentNode = document.body;
+ var depth = 50;
+ for (var i = 0; i < depth; i++) {
+ var childNode = document.createElement("div");
+ childNode.style.display = "flex";
+ parentNode.appendChild(childNode);
+ parentNode = childNode;
+ }
+
+ // Add some text in the innermost child:
+ childNode.innerHTML = "Text";
+
+ // Force reflow:
+ var height = document.body.children[0].offsetHeight;
+</script>
diff --git a/layout/generic/crashtests/947158-iframe.html b/layout/generic/crashtests/947158-iframe.html
new file mode 100644
index 000000000..840be6492
--- /dev/null
+++ b/layout/generic/crashtests/947158-iframe.html
@@ -0,0 +1,777 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb" lang="en-gb" >
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+
+
+<style>
+
+
+#work_area {
+-moz-background-clip:border;
+-moz-background-inline-policy:continuous;
+-moz-background-origin:padding;
+background-attachment:scroll;
+background-color:transparent;
+float:left;
+padding-left:5px;
+padding-right:5px;
+width:980px;
+background-color:#F1F5F8;
+}
+#footer {
+-moz-background-clip:border;
+-moz-background-inline-policy:continuous;
+-moz-background-origin:padding;
+background-attachment:scroll;
+background-color:transparent;
+background-image:url(../images/footer.gif);
+background-position:0 0;
+background-repeat:no-repeat;
+float:left;
+width:1003px;
+height:53px;
+background-color:#467618;
+}
+#bottommenu{
+float:left;
+width:1003px;
+height:53px;
+text-align:center;
+color:#FFFFFF;
+font-family:Tahoma;
+font-size:11px;
+padding-top: 17px;
+}
+.div720_body{
+padding-left:30px;
+padding-right:30px;
+padding-top:0;
+width:660px;
+height:auto;
+}
+.div720_body p{
+font-family:Tahoma;
+font-size:12px;
+text-indent: 20px;
+_color:#636563;
+color: #444444;
+line-height:18px;
+margin-bottom:8px;
+margin-top:8px;
+}
+
+
+.train_asan_body{
+width:650px;
+float:left;
+height:auto;
+font-family:Tahoma;
+font-size:12px;
+text-indent: 20px;
+color:#636563;
+display:none;
+padding-left:12px;
+padding-bottom:10px;
+}
+
+
+.news_text{
+width:660px;
+float:left;
+height:auto;
+font-family:Tahoma;
+font-size:12px;
+text-indent: 20px;
+color: #444444;
+line-height:18px;
+_background-color:#CCCCCC;
+}
+.news_line{
+width:660px;
+float:left;
+background-color:#335D0B;
+height:2px;
+margin-top:5px;
+}
+.news_dat{
+width:90px;
+float:right;
+height:auto;
+font-family:Arial, Helvetica, sans-serif;
+font-size:14px;
+color:#355E0B;
+text-align:center;
+_border:solid 2px #003300;
+border-bottom:solid 2px #335D0B;
+padding:2px;
+text-indent:0;
+background-color:#F2F6F9;
+font-weight:bold;
+}
+
+.newssite
+{
+ list-style-type: none;
+ margin-top: 20px;;
+ padding:2px;
+}
+
+.newssite li
+{
+ font-family:Tahoma;
+ font-size: 12px;
+ color: #444444;
+ text-align: left;
+ background-image: url('../images/ul.gif');
+ background-repeat:no-repeat;
+ margin:0 0 20px 0;
+ _line-height: 10px;
+ font-weight:normal;
+ height:auto;
+ padding-top:0;
+ padding-left:25px;
+ text-indent:0;
+
+}
+
+.ul_asan
+{
+ list-style-type: none;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding:2px;
+ float:left;
+ width:659px;
+ font-family:Tahoma;
+ font-size: 12px;
+ color: #444444;
+}
+
+.ul_asan li
+{
+ font-family:Tahoma;
+ font-size: 12px;
+ color: #444444;
+ text-align: left;
+ background-image: url('../images/asan/ul_asan.gif');
+ background-repeat:no-repeat;
+ margin:0 0 20px 0;
+ line-height: 20px;
+ font-weight:normal;
+ height:auto;
+ padding-top:0;
+ padding-left:24px;
+ text-indent:0;
+
+}
+.ul_asan li a
+{
+ color: #26621F;
+ text-decoration:underline;
+}
+.ul_asan li a:hover
+{
+ color: #007700;
+ text-decoration:none;
+}
+
+.ul_doc
+{
+ list-style-type: none;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding:0px 2px 15px 2px;
+ float:left;
+ width:659px;
+ font-family:Tahoma;
+ font-size: 12px;
+ color: #444444;
+ text-indent:25px;
+ line-height:25px;
+
+}
+
+
+.ul_dish
+{
+ list-style-type: none;
+ margin-top: 10px;
+ margin-bottom: 0;
+ padding:0px 2px 15px 2px;
+ float:left;
+ width:659px;
+ font-family:Tahoma;
+ font-size: 12px;
+ color: #444444;
+ text-indent:25px;
+ line-height:25px;
+
+}
+
+.ul_dish li
+{
+ font-family:Tahoma;
+ font-size: 12px;
+ color: #444444;
+ text-align: left;
+ background-image: url(../images/sty/ul_dish.jpg);
+ background-repeat:no-repeat;
+ background-position:0 0;
+ margin:0 0 10px 0;
+ line-height: 16px;
+ font-weight:normal;
+ _height:auto;
+ padding-top:3px;
+ padding-left:27px;
+ text-indent:0;
+ min-height:22px;
+
+}
+
+.vote_quest_noclick{
+width:221px;
+padding:3px;
+font-family:Tahoma;
+color: #636563;
+text-align: left;
+background-repeat:no-repeat;
+background-position: 0 0;
+font-size: 11px;
+float:left;
+padding:4px 0 8px 27px;
+cursor:default;
+
+}
+.vote_quest0{
+background-image: url('../images/vote/vote_0.gif');
+background-color:#EAEFEE;
+}
+.vote_quest1{
+background-image: url('../images/vote/vote_1.gif');
+background-color:#FFFFFF;
+}
+
+
+
+.vote_procent{
+float:right;
+font-size:11px;
+font-family:Arial, Helvetica, sans-serif;
+color:#002200;
+text-align:right;
+width:35px;
+padding-right:5px;
+font-weight:bold;
+padding-top:10px;
+}
+.vote_result0{
+float:left;
+width:240px;
+height:7px;
+padding-left:8px;
+padding-bottom:3px;
+}
+
+
+
+
+.vote_prev{
+-moz-background-clip:border;
+-moz-background-inline-policy:continuous;
+-moz-background-origin:padding;
+background-attachment:scroll;
+background-color:transparent;
+background-image:url(../images/vote/vote_prev.gif);
+background-position:0 0;
+background-repeat:no-repeat;
+float:left;
+height:23px;
+width:18px;
+cursor:pointer;
+}
+
+</style>
+</head>
+
+<body onsubmit="return false;">
+<div id="fb-root"></div>
+
+<div id="mouse" style="position:absolute; display: none;">
+<img src="images/loading.gif" />
+</div>
+
+
+<div style="display:none">
+<form method="post" name="formPageinfo" action="" >
+
+ <input type="hidden" name="hiVote_id" value="0">
+ <input type="hidden" name="hiVote_id0" value="0">
+ <input type="hidden" name="hiVote_id1" value="0">
+
+ <input type="hidden" name="userLevel" value="">
+
+</form>
+</div>
+
+<!--Shadows divs-->
+
+<div id="center">
+<div id="wapper">
+<div id="head">
+
+<!-- Top part -->
+<div id="top">
+ <a href="http://www.yogatrain.ru">
+ <div id="top_head"></div></a>
+
+</div>
+
+<!-- Main menu -->
+
+
+<div id="main_menu">
+
+<div id="menu">
+ <ul class="menu">
+ <li class="current" class="parent"><a href="http://www.yogatrain.ru"><span>ГлавнаÑ</span></a>
+ <div><ul>
+ <li id="main_apeal" class="main_link" link_out="indexajax.php?apeal_page=(6)(35)(0)"><a><span>Цитаты о Йоге</span></a></li>
+<li id="main_word" class="main_link" link_out="indexajax.php?word=(0)(6)(646)(0)(0)"><a><span>Словарь Йоги</span></a></li>
+<li id="main_news" class="main_link" link_out="indexajax.php?news=(4)(34)"><a><span>ÐовоÑти Портала</span></a></li>
+<li><a href="tools.php"><span>СтатиÑтика Портала</span></a></li>
+ <li><a href="about.php"><span>Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Портале</span></a></li>
+ </ul></div>
+ </li>
+ <li class="parent"><a href="train.php"><span>ЗанÑÑ‚Ð¸Ñ Ð™Ð¾Ð³Ð¾Ð¹</span></a>
+ <div><ul>
+ <li><a class="parent"><span>Владимир (Хатха Йога)</span></a>
+ <div><ul>
+ <li><a href="train.php?combo=50"><span>Первый уровень</span></a></li>
+ <li><a href="train.php?combo=53"><span>Второй уровень</span></a></li>
+ <li><a href="train.php?combo=56"><span>Третий уровень</span></a></li>
+ <li><a href="train.php?combo=59"><span>Четвертый уровень</span></a></li>
+ <li><a href="train.php?combo=62"><span>ПÑтый уровень</span></a></li>
+ <li><a href="train.php?combo=65"><span>ШеÑтой уровень</span></a></li>
+ <li><a href="train.php?combo=67"><span>Седьмой уровень</span></a></li>
+ <li><a href="train.php?combo=69"><span>ВоÑьмой уровень</span></a></li>
+ <li><a href="train.php?combo=71"><span>ДевÑтый уровень</span></a></li>
+ <li><a href="train.php?combo=73"><span>ДеÑÑтый уровень</span></a></li>
+ </ul></div>
+ <li><a class="parent"><span>Иван (Йога Ðйенгара)</span></a>
+ <div><ul>
+ <li><a href="train.php?combo=1"><span>Первый уровень</span></a></li>
+ <li><a href="train.php?combo=3"><span>Второй уровень</span></a></li>
+ <li><a href="train.php?combo=4"><span>Третий уровень</span></a></li>
+ <li><a href="train.php?combo=8"><span>Четвертый уровень</span></a></li>
+ <li><a href="train.php?combo=12"><span>ПÑтый уровень</span></a></li>
+ <li><a href="train.php?combo=15"><span>ШеÑтой уровень</span></a></li>
+ <li><a href="train.php?combo=18"><span>Седьмой уровень</span></a></li>
+ <li><a href="train.php?combo=21"><span>ВоÑьмой уровень</span></a></li>
+ <li><a href="train.php?combo=24"><span>ДевÑтый уровень</span></a></li>
+ <li><a href="train.php?combo=27"><span>ДеÑÑтый уровень</span></a></li>
+ </ul></div>
+ <li><a class="parent"><span>Ð¢Ð°Ñ€Ð°Ñ (Хатха Йога)</span></a>
+ <div><ul>
+ <li><a href="train.php?combo=32"><span>Первый уровень</span></a></li>
+ <li><a href="train.php?combo=35"><span>Второй уровень</span></a></li>
+ <li><a href="train.php?combo=41"><span>Третий уровень</span></a></li>
+ <li><a href="train.php?combo=38"><span>Четвертый уровень</span></a></li>
+ <li><a href="train.php?combo=44"><span>ПÑтый уровень</span></a></li>
+ <li><a href="train.php?combo=47"><span>ШеÑтой уровень</span></a></li>
+ <li><a href="train.php?combo=75"><span>Седьмой уровень</span></a></li>
+ <li><a href="train.php?combo=78"><span>ВоÑьмой уровень</span></a></li>
+ <li><a href="train.php?combo=80"><span>ДевÑтый уровень</span></a></li>
+ <li><a href="train.php?combo=82"><span>ДеÑÑтый уровень</span></a></li>
+ </ul></div>
+ </ul></div>
+
+ </li>
+ <li ><a href="tz.php"><span>Залы</span></a>
+ <div><ul>
+<li><a href="tz.php?city_id=2" ><span>МоÑква</span></a></li>
+ <li><a href="tz.php?city_id=5" ><span>Санкт-Петербург</span></a></li>
+ <li><a href="tz.php?city_id=334" ><span>Киев</span></a></li>
+ <li><a href="tz.php?city_id=514" ><span>Екатеринбург</span></a></li>
+ <li><a href="tz.php?city_id=552" ><span>ЧелÑбинÑк</span></a></li>
+ <li><a href="tz.php?city_id=323" ><span>ÐовоÑибирÑк</span></a></li>
+ <li><a href="tz.php?city_id=408" ><span>Ðлматы</span></a></li>
+ <li><a href="tz.php?city_id=586" ><span>ОдеÑÑа</span></a></li>
+ <li><a href="tz.php?city_id=407" ><span>ÐÑтана</span></a></li>
+ <li><a href="tz.php?city_id=297" ><span>Казань</span></a></li>
+ <li><a href="tz.php?city_id=440" ><span>Самара</span></a></li>
+ <li><a href="tz.php?city_id=1" ><span>Бишкек</span></a></li>
+ <li><a href="tz.php?city_id=565" ><span>ДнепропетровÑк</span></a></li>
+ <li><a href="tz.php?city_id=406" ><span>Караганда</span></a></li>
+ </ul></div>
+
+ </li>
+ <li ><a href="asan.php"><span>Каталог ÐÑан</span></a></li>
+ <li class="parent"><a href="sty.php"><span>Статьи</span></a>
+ <div><ul>
+<li><a href="sty.php?catid=0" ><span>ÐÐ½Ð°Ñ‚Ð¾Ð¼Ð¸Ñ Ð™Ð¾Ð³Ð¸</span></a></li>
+ <li><a href="sty.php?catid=1" ><span>ЗанÑÑ‚Ð¸Ñ Ð™Ð¾Ð³Ð¾Ð¹</span></a></li>
+ <li><a href="sty.php?catid=2" ><span>ИÑториÑ</span></a></li>
+ <li><a href="sty.php?catid=3" ><span>ЙогатерапиÑ</span></a></li>
+ <li><a href="sty.php?catid=4" ><span>ÐÐ°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð™Ð¾Ð³Ð¸</span></a></li>
+ <li><a href="sty.php?catid=5" ><span>Питание и Йога</span></a></li>
+ <li><a href="sty.php?catid=6" ><span>Польза Йоги</span></a></li>
+ <li><a href="sty.php?catid=7" ><span>Практика</span></a></li>
+ <li><a href="sty.php?catid=8" ><span>ФилоÑÐ¾Ñ„Ð¸Ñ Ð™Ð¾Ð³Ð¸</span></a></li>
+ </ul></div>
+
+ </li>
+
+ <li ><a href="books.php"><span>Книги</span></a></li>
+ <li ><a href="sitemap.php"><span>Карта Ñайта</span></a></li>
+
+ <li class="parent "><a href="quest.php"><span>ТеÑтирование</span></a>
+ <div><ul>
+ <li><a href="quest.php?so_id=1"><span>Тип конÑтитуции тела</span></a></li>
+ <li><a href="quest.php?so_id=2"><span>Результаты ОпроÑов</span></a></li>
+ <li><a href="quest.php?so_id=3"><span>ПолезноÑÑ‚ÑŒ вашего Сна</span></a></li>
+ </ul></div>
+
+ </li>
+ </ul>
+</div>
+
+</div>
+
+
+ <div id="body_area">
+<!--Banner on top-->
+
+ <div id="work_area">
+
+<!-- Start Main left div -->
+<!--<div class="inner_left"></div>-->
+<!-- End Main left div -->
+
+<!--Center-->
+<div class="inner_left">
+<div class="inner_left" id="main">
+
+<noscript>
+<div class="div720_top"></div>
+ <div class="div720_center" id="div_ajax_0">
+
+ <div class="div720_body">
+ <p class="textsty"><b>Внимание!</b></p>
+ <p class="textsty">ЕÑли вы видите Ñто Ñообщение, то у Ð²Ð°Ñ Ð² браузере отключен <strong>JavaScript</strong>. Ð”Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы портала вам необходимо включить <strong>JavaScript</strong>. Ðа портале иÑпользуетÑÑ Ñ‚ÐµÑ…Ð½Ð¾Ð»Ð¾Ð³Ð¸Ñ <strong>jQuery</strong>, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð°ÐµÑ‚ только при уÑловии иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð¼ Ñтой опции. </p>
+ </div>
+ </div>
+ <div class="div720_bottom"></div>
+
+</noscript>
+<div class="inner_left" id="apeal_id">
+ <div class="div720_top"></div>
+ <div class="div720_center" id="div_ajax_0">
+ <div class="div720_body news_text"><h1 class="sty_head">СовершенÑтво МаÑтера</h1>
+ <p>СовершенÑтво практики <strong>МаÑтера Йоги</strong> - не в ÑовершенÑтве Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¸Ð¼ гимнаÑтичеÑких форм (аÑан), а в том могущеÑтвенном потоке <strong>Силы</strong> (Ñнергии), который он генерирует в проÑтранÑтве вокруг ÑебÑ. ПриÑутÑтвие Ñтого потока вÑегда ощущаетÑÑ, еÑли он еÑÑ‚ÑŒ, а магнетичеÑкое воздейÑтвие его на Ñознание человечеÑких ÑущеÑтв, оказавшихÑÑ Ð² Ñфере его влиÑÐ½Ð¸Ñ - очевидно. Это - тот Ñамый поток <strong>Силы</strong>, который помогает людÑм захотеть Ñтать Ñвободнее, Ñильнее, Ñовершеннее.</p>
+ <div class="div720_author">Ðндрей СидерÑкий</div>
+ </div>
+ </div>
+ <div class="div720_bottom"></div>
+ </div>
+ <div class="inner_left" id="news_main">
+ <div class="sty_cat_down sty_cat_up"><div class="sty_cat_zag">YOGATRAIN.RU</div><div class="sty_cat_num">34</div></div>
+ <div class="div720_sty_cat">
+ <div class="div720_sty_cat_body">
+ <div class="sty_box" style="height:24px;"><div class="sty_page">Страницы:</div>
+ <div class="sty_page_active">1</div>
+ <div class="num_letter" cat="N" page="1" kolsty="3" all_news="34">2</div>
+ <div class="num_letter" cat="N" page="2" kolsty="3" all_news="34">3</div>
+ <div class="num_letter" cat="N" page="3" kolsty="3" all_news="34">4</div>
+ <div class="num_letter" cat="N" page="4" kolsty="3" all_news="34">5</div>
+ <div class="num_letter" cat="N" page="5" kolsty="3" all_news="34">6</div>
+ <div class="num_letter" cat="N" page="6" kolsty="3" all_news="34">7</div>
+ <div class="num_letter" cat="N" page="7" kolsty="3" all_news="34">8</div>
+ <div class="num_letter" cat="N" page="8" kolsty="3" all_news="34">9</div>
+ <div class="num_letter" cat="N" page="9" kolsty="3" all_news="34">10</div>
+ <div class="num_letter" cat="N" page="10" kolsty="3" all_news="34">11</div>
+ <div class="num_letter" cat="N" page="11" kolsty="3" all_news="34">12</div>
+ </div>
+ <div class="news_text">
+ <div class="news_line"></div>
+ <div class="news_dat">05.11.2013</div>
+ <ul class="newssite">
+<li>
+<strong>Йога</strong> ÑвлÑетÑÑ ÐºÑƒÐ»ÑŒÑ‚ÑƒÑ€Ð½Ñ‹Ð¼ наÑледием человека, Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ ней, так или иначе, поÑвлÑетÑÑ Ð² жизни каждого из наÑ. Что примечательно, в поÑледнее Ð²Ñ€ÐµÐ¼Ñ Ñто проиÑходит чаще, чем в прошлом. БезуÑловно, заÑлуга в Ñтом заключаетÑÑ Ð² доÑтупноÑти информационных потоков, но по Ñтой же причине качеÑтво информации о <strong>Йоге</strong> Ñравнительно ухудшаетÑÑ. Отдаление, Ñо временем, от информационных иÑточников Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ <strong>Йоги</strong>, ведет к иÑкажению ее принципов, Ñоздает ошибочные маленькие иÑтины, из которых произраÑтают уÑтойчивые неверные Ð¼Ð½ÐµÐ½Ð¸Ñ Ð² индивидах. <br />
+<br />
+ПоÑтому, ÐºÐ°ÐºÐ°Ñ Ð±Ñ‹ не была "правильнаÑ" и упорÑÐ´Ð¾Ñ‡ÐµÐ½Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ <strong>Йоге</strong>, мы должны возвращатьÑÑ Ðº информационным иÑточникам, где принципы <strong>Йоги</strong> получили наименьшее иÑкажение. Ð’ Ñтатье <a href="sty.php?sty_id=138" target="_blank">Ðйенгар о Йоге</a>, в краткой форме изложены такие принципы от Гуру, который принÑл непоÑредÑтвенное учаÑтие в раÑпроÑтранении <strong>Йоги</strong> в ÑоветÑком и поÑÑ‚ÑоветÑком проÑтранÑтве. Ищите проÑтое в Ñложном, и вы обретете ÑпоÑобноÑÑ‚ÑŒ видеть Ñложное в проÑтом.
+</li>
+</ul>
+ </div>
+ <div class="news_text">
+ <div class="news_line"></div>
+ <div class="news_dat">21.09.2013</div>
+ <ul class="newssite">
+<li>
+Ð’Ñе мы обладаем потенциальными возможноÑÑ‚Ñми, которые выходÑÑ‚ за пределы наших Ñамых Ñмелых мечтаний, однако Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ Ñ‡Ð°ÑÑ‚ÑŒ Ñтих возможноÑтей оÑтаётÑÑ Ð½ÐµÐ²Ð¾Ñтребованной. Каждый человек ÑпоÑобен переживать различные планы ÑознаниÑ, однако большинÑтво из Ð½Ð°Ñ Ð¶Ð¸Ð²ÑƒÑ‚ на низших планах, не Ð¸Ð¼ÐµÑ Ð¾Ð¿Ñ‹Ñ‚Ð° более выÑоких уровней Ð±Ñ‹Ñ‚Ð¸Ñ Ð¸ даже не Ð²ÐµÑ€Ñ Ð² их ÑущеÑтвование.<br /><br />
+Многие люди в Ñтом мире неÑчаÑтливы, иÑÐ¿Ñ‹Ñ‚Ñ‹Ð²Ð°Ñ Ð½ÐµÑƒÐ´Ð¾Ð²Ð»ÐµÑ‚Ð²Ð¾Ñ€Ñ‘Ð½Ð½Ð¾ÑÑ‚ÑŒ и, в то же времÑ, не знаÑ, чего же недоÑтаёт в их жизни. ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð¿Ñ€Ð¸Ñ‡Ð¸Ð½Ð° Ñтого отÑутÑÑ‚Ð²Ð¸Ñ ÑчаÑÑ‚ÑŒÑ ÑоÑтоит в нашей привÑзанноÑти к материальной плоÑкоÑти ÑущеÑтвованиÑ. Когда мы узнаем о более выÑоких Ñферах ÑознаниÑ, наши неÑчаÑтье и недовольÑтво иÑчезают Ñами Ñобой.<br /><br />
+<strong>Практика Йоги </strong>планомерно ведет человека к оÑознанию более выÑоких уровней бытиÑ.
+<strong>СоÑтоÑние медитации</strong> позволÑет Йогину более Ñффективно иÑпользовать Ñти практики. Ðо методы доÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ ÑоÑтоÑÐ½Ð¸Ñ Ð¼ÐµÐ´Ð¸Ñ‚Ð°Ñ†Ð¸Ð¸ различны, и добитьÑÑ ÐµÐ³Ð¾ без Ð¿Ð¾Ð½Ð¸Ð¼Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑÑов СоÑредоточениÑ, РаÑÑÐ»Ð°Ð±Ð»ÐµÐ½Ð¸Ñ Ð¸ ОÑÐ¾Ð·Ð½Ð°Ð½Ð¸Ñ Ð·Ð°Ñ‚Ñ€ÑƒÐ´Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾. БольшинÑтво людей Ñлышали о <strong>медитации</strong>, но лишь очень немногие дейÑтвительно иÑпытали её.
+<br /><br />
+Ð’ Ñтатье <a href="sty.php?sty_id=137" target="_blank">ДоÑтижение ÑоÑтоÑÐ½Ð¸Ñ Ð¼ÐµÐ´Ð¸Ñ‚Ð°Ñ†Ð¸Ð¸ через Йогу</a> опиÑаны методы, при которых пÑихофизичеÑÐºÐ°Ñ ÑиÑтема Ñовременного человека, может иÑпытать <strong>ÑоÑтоÑние медитации</strong>.
+
+</li>
+</ul>
+ </div>
+ <div class="news_text">
+ <div class="news_line"></div>
+ <div class="news_dat">29.07.2013</div>
+ <ul class="newssite">
+<li>Человек ÑоÑтоит из Ñеми тел. Каждое тело развиваетÑÑ Ð² определенный период жизни человека. ÐÐ°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñ Ð² поÑтапном развитии тел индивидуумом, влекут за Ñобой Ð¾Ñ‚ÐºÐ»Ð¾Ð½ÐµÐ½Ð¸Ñ Ð¾Ñ‚ еÑтеÑтвенного процеÑÑа ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð»Ð¸Ñ‡Ð½Ð¾Ñти в течении его жизни. Как определить такие периоды, и на чем Ñледует Ñтавить акценты в процеÑÑе поÑтапного ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ‡ÐµÐ»Ð¾Ð²ÐµÐºÐ°, пытаетÑÑ Ð¿Ñ€Ð¾ÑÑнить индийÑкий миÑтик ÑовременноÑти - <b><a href="books.php?author_id=87" target="_blank">Ошо</a></b>, в Ñтатье <a href="sty.php?sty_id=136" target="_blank">Семь тел человека – ÑÐ°Ð¼Ð¾Ñ€ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ жизни</a>. Попробуйте переÑмотреть периоды Ñвоей жизни под ракурÑом, предложенным <b>Ошо</b>, возможно Ñто Ñтанет Ð´Ð»Ñ Ð²Ð°Ñ Ð¸Ð½Ñ‚ÐµÑ€ÐµÑным опытом, а может и Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ окружениÑ.</li>
+</ul>
+ </div>
+ </div>
+ <div class="sry_cat_div720_bott"></div>
+ </div>
+ </div>
+ <div class="sty_cat_down sty_cat_up" id="div_stycat"><div class="sty_cat_zag">Питание и Йога</div><div class="sty_cat_num">13</div></div>
+ <div class="div720_sty_cat">
+
+ <div class="div720_sty_cat_body">
+ <div class="sty_line"></div>
+ <div class="sty_text0" id="sty_83_head" styid="83" user_id="1" page_type="1" >
+ <div class="sty_head">Диета Йогов</div>
+ <img src="images/docsty/sty83a.jpg" class="sty_image" title="Диета Йогов" alt="Диета Йогов" hspace="15">
+<p><strong>Йоги</strong> Ñчитают, что в оÑнове вÑех наших проблем Ñо здоровьем лежит неÑбаланÑированное питание. Ð”Ñ€ÐµÐ²Ð½ÐµÐ¹ÑˆÐ°Ñ ÑиÑтема, Ð¿Ð¾Ð´Ñ€Ð°Ð·ÑƒÐ¼ÐµÐ²Ð°ÑŽÑ‰Ð°Ñ ÑовокупноÑÑ‚ÑŒ физичеÑких, духовных и пÑихичеÑких практик, наделÑет Ñвоих верных поÑледователей крепким здоровьем и долголетием. Что Ñобой предÑтавлÑет <strong>диета йогов</strong>? ЕÑли Ð²Ð°Ñ Ñ…Ð¾Ñ‚ÑŒ раз интереÑовал ответ на Ñто вопроÑ, наша ÑÑ‚Ð°Ñ‚ÑŒÑ Ð±ÑƒÐ´ÐµÑ‚ вам полезна.</p>
+ </div>
+ <div class="sty_text" id="sty_83_body" ></div>
+ <div class="sty_line"></div>
+ <div class="sty_text0" id="sty_16_head" styid="16" user_id="1" page_type="1" >
+ <div class="sty_head">ЗанÑтие Йогой и вегетарианÑтво</div>
+ <img src="images/docsty/sty16a.jpg" class="sty_image" title="ВегетарианÑтво" hspace="15">
+
+
+ </div>
+ <div class="sty_text" id="sty_16_body" ></div>
+ <div class="sty_line"></div>
+ <div class="sty_text0" id="sty_122_head" styid="122" user_id="1" page_type="1" >
+ <div class="sty_head">Рецепты блюд Ð´Ð»Ñ Ñ‡Ð°ÐºÑ€</div>
+ <img src="images/docsty/sty122a.jpg" class="sty_image" title="Меню Ð´Ð»Ñ Ñ‡Ð°ÐºÑ€" alt="Меню Ð´Ð»Ñ Ñ‡Ð°ÐºÑ€" hspace="15">
+<p>Ð’ данной Ñтатье Ñобраны <strong>рецепты</strong> Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ <a href="index.php?word_id=411" target="_blank">чакры</a>, чтобы улучшить или воÑÑтановить нормальную работу каждой. Ингредиенты блюд веÑьма разнообразны. Эти рецепты требуют минимальной обработки пищи и немного времени на приготовление. Чем меньше термичеÑÐºÐ°Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ°, тем больше Ñнергии ÑохранÑетÑÑ Ð´Ð»Ñ Ð½Ð°Ñ. ПриÑтного аппетита!</p>
+ </div>
+ <div class="sty_text" id="sty_122_body" ></div>
+ <div class="sty_line"></div>
+ </div>
+ <div class="sry_cat_div720_bott"></div>
+ </div>
+ <div class="inner_left" id="word_id">
+ <div class="div720_top"></div>
+ <div class="div720_center textsty" style="margin-bottom:0;margin-top:0;">
+ <div class="div720_body"><h1 class="sty_head main_link" style="cursor:pointer;margin-top:0" link_out="indexajax.php?word=(0)(6)(646)(0)(0)">Мантра (Словарь Йоги)</h1><p>СвÑщенное Ñлово или фраза, обладающее духовной значимоÑтью и Ñилой, которые выводÑÑ‚ за пределы ума.</p>
+<p>Вибрационные техники (<strong>мантры</strong>) – одной из древнейших в иÑтории человечеÑтва практикой ÑвлÑетÑÑ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð¸Ðµ мантр – определенных наборов звуков, резонанÑно воздейÑтвующих на отдельные учаÑтки головного мозга или тела. СоглаÑно иÑÑледованиÑм Ñовременных нейропÑихологов, практика произнеÑÐµÐ½Ð¸Ñ <strong>мантр</strong> дейÑтвительно изменÑет отноÑительные амплитуды ритмов мозга, что ÑпоÑобÑтвует доÑтижению измененных ÑоÑтоÑний ÑознаниÑ. Мантры не Ñледует путать Ñ Ð¼Ð¾Ð»Ð¸Ñ‚Ð²Ð°Ð¼Ð¸ и формами, предназначенными Ð´Ð»Ñ ÑловеÑного ÑамовнушениÑ, поÑкольку они могут не иметь ÑмыÑловой нагрузки (Ñ…Ð¾Ñ‚Ñ Ð¼Ð¾Ð³ÑƒÑ‚ и иметь). Ðе ÑвлÑетÑÑ Ð¿Ñ€Ð¸Ð½Ñ†Ð¸Ð¿Ð¸Ð°Ð»ÑŒÐ½Ð¾ значимым и ÑимволичеÑкий аÑпект мантр. Правда, некоторые мантры имели ÑимволичеÑкий ÑмыÑл, например, шеÑÑ‚ÑŒ Ñлогов оÑновной мантры тибетÑкого буддизма <strong>Ом мани падме хум</strong> ÑоотноÑилиÑÑŒ Ñ ÑˆÐµÑтью мирами буддийÑкой коÑмогонии, но Ñто Ñкорее иÑключение.</p>
+<p>К Ñожалению, механизмы пÑихологичеÑкого воздейÑÑ‚Ð²Ð¸Ñ Ð¼Ð°Ð½Ñ‚Ñ€ изучены Ñлабо. Возможно, ключом к пониманию такого воздейÑÑ‚Ð²Ð¸Ñ ÑвлÑÑŽÑ‚ÑÑ Ð¸ÑÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð¾Ð½Ð¾Ñемантики отноÑительно первичных значений звуков, а также Ñхемы ÑоответÑтвий различных зон человечеÑкого тела звукам различным звукам.</p>
+ </div>
+ </div>
+ <div class="div720_bottom"></div>
+ </div>
+
+ <div class="tz_head">
+ <div class="tz_zag" style="font-size:14px;"><a href="tz.php?tz_id=641" style="color:#444643">Йога зал на ТульÑкой</a></div>
+ <div class="tz_zag_domen"></div>
+ <div class="tz_down">
+ <span style="float:left">РоÑÑиÑ, <a href="tz.php?city_id=641">МоÑква</a>, Холодильный пер. д. 3, Ñтроение 8</span>
+ <span style="float:right"> (916) 944 44 73</span>
+
+ </div>
+
+ </div>
+
+ <div class="div720_center">
+
+ <div class="div720_body">
+ <p>Оказание профеÑÑиональных уÑлуг в облаÑти организации <strong>занÑтий йогой</strong>.</p>
+<p>Ðренда зала под <strong>занÑÑ‚Ð¸Ñ Ð¹Ð¾Ð³Ð¾Ð¹</strong>, как по чаÑам так и на целые дни - привлекаем к ÑотрудничеÑтву школы Ñ Ð¿Ð¾ÑтоÑнным раÑпиÑанием занÑтий.</p>
+<p>Ð¡Ñ‚Ð°Ð½Ñ†Ð¸Ñ Ð¼ÐµÑ‚Ñ€Ð¾ <span class="citation">ТульÑкаÑ</span>, три минуты пешком, в изолированном помещении Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ð¼ входом без пропуÑкной ÑиÑтемы.</p>
+<p>Площадь 160 кв/метров. Ð’ помещении в наличии раздевалка, туалет, коврики, куллер, Ñтаканчики и вÑе Ñто включено в ÑтоимоÑÑ‚ÑŒ аренды. Только Ñделан ремонт.</p>
+<p>Гибкий подход к клиентам, учет их пожеланий, в том чиÑле возможноÑÑ‚ÑŒ организовать кофе-брейки Ñилами заказчиков. Удобное раÑположение, отзывчивоÑÑ‚ÑŒ перÑонала.</p>
+<p>Контактное лицо: ÐнаÑтаÑиÑ</p>
+<p>Тел: <strong>8 916 944 44 73</strong></p>
+<p><a href="http://trainingzal.ru" target="_blank">http://trainingzal.ru</a></p>
+
+<div class="sty_image_box" align="center"><img src="zal/f641_01.jpg" title="Зал Ð´Ð»Ñ Ð·Ð°Ð½ÑÑ‚Ð¸Ñ Ð™Ð¾Ð³Ð¾Ð¹" alt="Зал Ð´Ð»Ñ Ð·Ð°Ð½ÑÑ‚Ð¸Ñ Ð™Ð¾Ð³Ð¾Ð¹"> </div>
+
+<p><strong>Как пройти</strong>:</p>
+
+<p>Ð¡Ñ‚Ð°Ð½Ñ†Ð¸Ñ ÐœÐµÑ‚Ñ€Ð¾ <strong>ТульÑкаÑ</strong>, выход поÑледний вагон из центра, выходите из метро перед Ñобой увидите выÑокое здание налоговой инÑпекции, его необходимо обойти Ñлева, доходите до <span class="citation">Холодильного пер.</span> (ориентир трамвайные пути), поворачиваете направо, идете вдоль трамвайных путей в Ñторону Ðалоговой до торгово-развлекательного центра <span class="citation">Ролл-Холл</span>, Ñразу за ним поворачиваете налево (ГамÑоновÑкий пер.) , доходите до конца и Ñлева увидите выÑокие ворота бежевого цвета и калитку, на ней табличка <span class="citation">HR Business Solutions</span>.</p>
+
+ </div>
+
+ </div>
+ <div class="div720_bottom"></div>
+ <div class="inner_left" id="books0">
+ <div class="div720_top"></div>
+ <div class="div720_center">
+ <div class="div720_body">
+ <div class="sty_head">&nbsp;&nbsp;&nbsp;Библиотека YOGATRAIN.RU</div>
+ <h3><a href="books.php?book_id=124" target="_blanck">ÐšÑ€Ð¸Ð¹Ñ Ð™Ð¾Ð³Ð°</a></h3>
+ <a href="books.php?book_id=124" target="_blanck"><img src="images/docsty/book_124.jpg" class="sty_image" title="ÐšÑ€Ð¸Ð¹Ñ Ð™Ð¾Ð³Ð° - Суами РамаÑнда" alt="ÐšÑ€Ð¸Ð¹Ñ Ð™Ð¾Ð³Ð° - Суами РамаÑнда" hspace="15">
+ </a>
+ <p><a href="tz.php?napr_id=44" target="_blank">ÐšÑ€Ð¸Ð¹Ñ Ð™Ð¾Ð³Ð°</a> еÑÑ‚ÑŒ Ð¿Ñ€ÐµÐ´Ð²Ð°Ñ€Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ñтупень <strong>Йоги</strong>, через которую ученику необходимо пройти Ð´Ð»Ñ Ð¸Ð·ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸ Ð¿Ñ€Ð°ÐºÑ‚Ð¸ÐºÐ¾Ð²Ð°Ð¸Ð¸Ñ Ð±Ð¾Ð»ÐµÐµ выÑоких разветвлений Религии МудроÑти. Ð ÐµÐ»Ð¸Ð³Ð¸Ñ Ð—Ð°Ð¿Ð°Ð´Ð° придает обычно больше Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼ дейÑтвиÑм, мыÑлÑм и желаниÑм как признакам нравÑтвенноÑти и ÑтараетÑÑ Ð³Ð»ÑƒÐ±Ð¾ÐºÐ¾ вкоренить в душу молодых учеников Ñтику и нравÑтвенноÑÑ‚ÑŒ. Грех, как его понимают на Западе, не признаетÑÑ Ð² ВоÑточных УчениÑÑ….
+</p>
+<p>Ð’Ñемирный Дух — Ñто вÑе доброе, иÑтинное и прекраÑное, а раз мы — проÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð‘Ð¾Ð³Ð°, то, Ð¸Ð·ÑƒÑ‡Ð°Ñ Ð ÐµÐ»Ð¸Ð³Ð¸ÑŽ, должны проÑвлÑÑ‚ÑŒ доброту, иÑтину и краÑоту в повÑедневной жизни. Моральный ÐºÐ¾Ð´ÐµÐºÑ Ð Ð°ÑÑ‹ определÑетÑÑ Ñтепенью ее Духовного развитиÑ.</p>
+<p>Среди некоторых африканÑких племен убийÑтво врагов ÑчитаетÑÑ Ð²ÐµÑьма нравÑтвенным поÑтупком, заÑлуживающим уважение и почет вÑего племени. То же дейÑтвие, Ñовершенное в цивилизованной Ñтране, ÑвлÑлоÑÑŒ бы преÑтуплением, караемым чаÑто Ñмертной казнью и, безуÑловно, порицаемым общеÑтвом.</p>
+<p>Однако же <strong>ÐšÑ€Ð¸Ð¹Ñ Ð™Ð¾Ð³Ð°</strong> не каÑаетÑÑ Ð¼Ð¾Ñ€Ð°Ð»Ð¸ в Ñтом ÑмыÑле. ÐравÑтвенноÑÑ‚ÑŒ, как таковаÑ, Ð´Ð»Ñ <strong>ÐšÑ€Ð¸Ð¹Ñ Ð™Ð¾Ð³Ð¸</strong> не ÑущеÑтвует. Он Ñовершает некоторые поÑтупки, потому что они помогают ему доÑтигнуть духовного ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð£Ð½Ð¸Ð²ÐµÑ€Ñальным Духом, и он удерживаетÑÑ Ð¾Ñ‚. тех поÑтупков, которые могли бы препÑÑ‚Ñтвовать духовному развитию.</p>
+
+ <div class="div720_author"><strong>Ðвтор: &nbsp;&nbsp;</strong>Суами РамаÑнда</div></div>
+ </div>
+ <div class="div720_bottom"></div>
+ </div>
+</div>
+</div>
+ <div class="inner_right">
+ <div class="box_rmenu">
+
+<div class="box_rmenu" id="vote">
+
+
+ <div class="rmenu rmenu3">
+ <div class="vote_prev" id="vote_prev_vote" vote_id="12" user_id="1"></div>
+ ÐžÐ¿Ñ€Ð¾Ñ #12
+ <div class="vote_next" id="vote_next_vote" vote_id="12" user_id="1"></div>
+ </div>
+
+ <div class="vote_quest vote_quest1" quest_id="58" vote_id="12" user_id="1">УпотреблÑÑŽ невегетарианÑкую пищу, но хочу попробовать такую диету</div>
+ <div class="vote_quest vote_quest0" quest_id="59" vote_id="12" user_id="1">Ðе хочу и не возникало Ð¶ÐµÐ»Ð°Ð½Ð¸Ñ Ð¾Ñ‚ÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒÑÑ Ð¾Ñ‚ ÑƒÐ¿Ð¾Ñ‚Ñ€ÐµÐ±Ð»ÐµÐ½Ð¸Ñ Ð¼ÑÑа и других невегетарианÑких продуктов </div>
+
+
+ <div class="rmenu rmenu4">
+ </div>
+ <div class="vote_head ">ПридерживаетеÑÑŒ ли вы вегетарианÑкой диеты?</div>
+ <div class="vote_block0">
+ <div class="vote_quest_noclick vote_quest0" style="width:175px">Да придерживаюÑÑŒ поÑтоÑнно</div>
+ <div class="vote_procent">20 %</div>
+ <div class="vote_result0"><div class="vote_result" width_go="109px" id="vote_result0"></div></div>
+ </div>
+ <div class="vote_block1">
+ <div class="vote_quest_noclick vote_quest1" style="width:175px">Я не ем мÑÑо, но полноÑтью диету не Ñоблюдаю</div>
+ <div class="vote_procent">30 %</div>
+ <div class="vote_result0"><div class="vote_result" width_go="163px" id="vote_result1"></div></div>
+ </div>
+ <div class="vote_block0">
+ <div class="vote_procent">12 %</div>
+ </div>
+ <div class="vote_quest_noclick vote_quest1" style="width:175px">УпотреблÑÑŽ невегетарианÑкую пищу, но хочу попробовать такую диету</div>
+ <div class="vote_procent">13 %</div>
+ <div class="vote_result0"><div class="vote_result" width_go="71px" id="vote_result3"></div></div>
+ </div>
+ <div class="vote_block0">
+ <div class="vote_quest_noclick vote_quest0" style="width:175px">Ðе хочу и не возникало Ð¶ÐµÐ»Ð°Ð½Ð¸Ñ Ð¾Ñ‚ÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒÑÑ Ð¾Ñ‚ ÑƒÐ¿Ð¾Ñ‚Ñ€ÐµÐ±Ð»ÐµÐ½Ð¸Ñ Ð¼ÑÑа и других невегетарианÑких продуктов </div>
+ <div class="vote_procent">24 %</div>
+ <div class="vote_kolvote">Ð’Ñего проголоÑовало: 112</div>
+ </div>
+<div class="box_rmenu">
+ <div class="rmenu">Пройдите теÑÑ‚ & ОпроÑÑ‹</div>
+ <div class="box_rmenu0" style="padding-right: 0pt; padding-left: 0pt;">
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE" so_id="2" ><a href="quest.php?so_id=2">Результаты опроÑов на YOGATRAIN.RU</a></div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF" so_id="3" ><a href="quest.php?so_id=3">Оценка пользы здоровью от вашего Сна</a></div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE" so_id="1" ><a href="quest.php?so_id=1">Определение типа конÑтитуции тела</a></div>
+ </div>
+ </div>
+ <div class="box_rmenu">
+ <div class="rmenu"><div class="rsty_cat_down rsty_cat_up" id="rmenu_cat_sty_best" kolsty="10" style="background-image:url(images/sty/rsty_cat_up.gif);"></div>Лучшие 10 Ñтатей</div>
+ <div class="box_rmenu0" style="padding-right:0;padding-left:0;_display:none;">
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="sty.php?sty_id=35" _title="Рубрика: ÐÐ½Ð°Ñ‚Ð¾Ð¼Ð¸Ñ Ð™Ð¾Ð³Ð¸&nbsp; Оценка: 5">ÐÑтральное тело человека</a></div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="sty.php?sty_id=32" _title="Рубрика: ФилоÑÐ¾Ñ„Ð¸Ñ Ð™Ð¾Ð³Ð¸&nbsp; Оценка: 5">ПÑихичеÑÐºÐ°Ñ ÑÐ½ÐµÑ€Ð³Ð¸Ñ Ð¸ здоровье человека</a></div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="sty.php?sty_id=107" _title="Рубрика: ЙогатерапиÑ&nbsp; Оценка: 5">ТранÑÐ¼ÑƒÑ‚Ð°Ñ†Ð¸Ñ ÑекÑуальной Ñнергии Ñ Ð™Ð¾Ð³Ð¾Ð¹</a></div>
+ </div>
+ <div class="box_rmenu0" style="padding-right:0;padding-left:0;_display:none;">
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="sty.php?sty_id=135" _title="Рубрика: Польза Йоги&nbsp; Оценка: 5">Метод броÑить курить, рекомендованный Ðюрведой </a></div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="sty.php?sty_id=134" _title="Рубрика: Практика&nbsp; Оценка: 5">Ð¡ÑƒÑ€ÑŒÑ ÐамаÑкар - ПриветÑтвие Солнцу</a></div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="sty.php?sty_id=133" _title="Рубрика: ЙогатерапиÑ&nbsp; Оценка: 5">Здоровое зрение, йога Ð´Ð»Ñ Ð³Ð»Ð°Ð·</a></div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="sty.php?sty_id=131" _title="Рубрика: ÐÐ½Ð°Ñ‚Ð¾Ð¼Ð¸Ñ Ð™Ð¾Ð³Ð¸&nbsp; Оценка: 5">Чакры человека</a></div>
+ </div>
+ </div>
+<div class="box_rmenu">
+ <div class="rmenu"><div class="rsty_cat_down rsty_cat_up" style="background-image:url(images/sty/rsty_cat_up.gif);"></div>Лучшие 10 аÑан</div>
+ <div class="box_rmenu0" style="padding-right:0;padding-left:0;_display:none;">
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF" ><a href="asan.php?asana_id=150">Каошики - Танец Каошики</a></div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE" ><a href="asan.php?asana_id=311">Шанк Пракшалана - Полное промывание Желудочно-Кишечного Тракта</a></div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE" ><a href="asan.php?asana_id=45">ХалаÑана - Поза Плуга</a></div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF" ><a href="asan.php?asana_id=168">УддиÑна Бандха - Брюшной Замок</a></div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF" ><a href="asan.php?asana_id=53">Ðдхо Мукха ШванаÑана - Поза Собаки</a></div>
+ </div>
+ </div>
+ <div class="box_rmenu">
+ <div class="rmenu"><div class="rsty_cat_down rsty_cat_up" style="background-image:url(images/sty/rsty_cat_up.gif);"></div>Лучшие 10 комплекÑов</div>
+ <div class="box_rmenu0" style="padding-right:0;padding-left:0;_display:none;">
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#FFFFFF"><a href="train.php?train_id=56">ÐšÑƒÑ€Ñ ÑƒÐ¿Ñ€Ð°Ð¶Ð½ÐµÐ½Ð¸Ð¹ третьего ÑƒÑ€Ð¾Ð²Ð½Ñ <b>L3</b> </a> </div>
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#FFFFFF"><a href="train.php?train_id=51">ÐšÑƒÑ€Ñ ÑƒÐ¿Ñ€Ð°Ð¶Ð½ÐµÐ½Ð¸Ð¹ первого ÑƒÑ€Ð¾Ð²Ð½Ñ <b>L1</b> </a> </div>
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#EEEEEE"><a href="train.php?train_id=7">УÑпокаивающие позы <b>L1</b> </a> </div>
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#FFFFFF"><a href="train.php?train_id=6">Позы в положении ÑÐ¸Ð´Ñ Ñ Ð¿Ñ€Ð¾Ñтыми поворотами <b>L2</b> </a> </div>
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#EEEEEE"><a href="train.php?train_id=60">ÐšÑƒÑ€Ñ ÑƒÐ¿Ñ€Ð°Ð¶Ð½ÐµÐ½Ð¸Ð¹ четвертого ÑƒÑ€Ð¾Ð²Ð½Ñ <b>L4</b> </a> </div>
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#FFFFFF"><a href="train.php?train_id=3">Закрепление проÑÑ‚Ñ‹Ñ… поз в положении ÑÑ‚Ð¾Ñ <b>L2</b> </a> </div>
+ <div class="box_rmenu_sty train_rmenu" style="background-color:#FFFFFF"><a href="train.php?train_id=8">Включение поз УткатаÑана и ГарудаÑана <b>L4</b> </a> </div>
+ </div>
+ <div class="box_rmenu">
+ <div class="box_rmenu0" style="padding-right:0;padding-left:0;_display:none;"><div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="tz.php?city_id=2">МоÑква</a> (224)</div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="tz.php?city_id=334">Киев</a> (62)</div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="tz.php?city_id=514">Екатеринбург</a> (33)</div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="tz.php?city_id=552">ЧелÑбинÑк</a> (31)</div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="tz.php?city_id=323">ÐовоÑибирÑк</a> (27)</div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="tz.php?city_id=408">Ðлматы</a> (26)</div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="tz.php?city_id=586">ОдеÑÑа</a> (26)</div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="tz.php?city_id=407">ÐÑтана</a> (25)</div>
+ <div class="box_rmenu_sty" style="background-color:#EEEEEE"><a href="tz.php?city_id=440">Самара</a> (17)</div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="tz.php?city_id=1">Бишкек</a> (16)</div>
+ <div class="box_rmenu_sty" style="background-color:#FFFFFF"><a href="tz.php?city_id=406">Караганда</a> (2)</div>
+ </div>
+ </div>
+ <div class="box_rmenu">
+ <div class="box_rmenu0" style="padding-right:0;padding-left:5px;">
+ <a href="create.php">
+<img src="images/create/create_ban.jpg" alt="Создание Ñайта" border="0" style="border: 0px solid #CCCCCC" />
+ </div><div class="box_rmenu">
+
+ <div class="rmenu"><div class="rsty_cat_down" id="rmenu_cat_sty_info" kolsty="3"></div>Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Ñайте</div>
+ <div class="vote_quest_noclick vote_quest1" style="width:220px">Сайт оптимизирован Ð´Ð»Ñ Ñледующих верÑий браузеров: <strong>Firefox 3</strong>, <strong>Internet Explorer 8</strong>, <strong>Opera 9</strong>, <strong>Google Chrome 1</strong>. СоответÑтвенно и Ñ Ð±Ð¾Ð»ÐµÐµ новыми верÑиÑми должен работать без ошибок. ЕÑли у Ð²Ð°Ñ ÑÑ‚Ð°Ñ€Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð° и портал работает Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°Ð¼Ð¸, обновите Ñвой брайузер Ñ Ñайта разработчиков.</div>
+
+ <div class="vote_quest_noclick vote_quest0" style="width:220px">Ð”Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы портала вам необходимо включить <strong>JavaScript</strong>. Ðа портале иÑпользуетÑÑ Ñ‚ÐµÑ…Ð½Ð¾Ð»Ð¾Ð³Ð¸Ñ <strong>jQuery</strong>, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð°ÐµÑ‚ только при уÑловии иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð¼ Ñтой опции.</div>
+
+ Помогите узнать людÑм о нашем реÑурÑе, размеÑтив наш баннер:
+ Код Ð´Ð»Ñ Ñ€Ð°Ð·Ð¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð½Ð° вашей Ñтраничке:<br />
+ </div>
+ </div>
+</div>
+
+
+ <div class="box_rmenu">
+ <div class="rmenu">Партнеры</div>
+ <div class="vote_quest_noclick vote_quest0" style="width: 220px;"> <a href="http://yogacenter.ru/index.php?option=com_content&view=article&id=80&Itemid=89" target="_blank">йога Ð´Ð»Ñ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÑŽÑ‰Ð¸Ñ…</a>. </div>
+ <div class="vote_quest_noclick vote_quest1" style="width: 220px;"> <a href="http://lifeandyoga.ru/methods/yoga-23" target="_blank">Йога 23</a> Ñ ÐºÐ²Ð°Ð»Ð¸Ñ„Ð¸Ñ†Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ð¼ преподавателем, МоÑква </div>
+ </div></div>
+</div>
+
+
+<!-- End right column-->
+<!--Botton line-->
+ </div>
+ <div id="footer">
+ <div id="bottommenu">
+|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="index.php">ГлавнаÑ</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="train.php">ЗанÑÑ‚Ð¸Ñ Ð™Ð¾Ð³Ð¾Ð¹</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="asan.php">Каталог ÐÑан</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="tz.php">Залы</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="sty.php">Статьи</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="books.php">Книги</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="quest.php">ТеÑтирование</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="sitemap.php">Карта Сайта</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<noindex><span id="hide_apicom"><a href="http://apycom.com/" rel="nofollow">Apycom Menus</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span></noindex></div>
+<!--Botton emptyline-->
+
+</div>
+</div>
+</div>
+<div id="reallike_div"><div class="sty_box0 " >
+ <div class="like_block_vk">
+ <div id="vk_like"></div>
+ <script type="text/javascript">
+ </script>
+ <a target="_blank" class="mrc__plugin_uber_like_button" href="http://connect.mail.ru/share" data-mrc-config="{'cm' : '1', 'ck' : '1', 'sz' : '20', 'st' : '2', 'tp' : 'ok'}">ÐравитÑÑ</a>
+<script type="text/javascript">
+var test2=document.getElementById("hide_apicom")
+try{test4.appendChild(test4.cloneNode(true))}catch(e){};
+setInterval(function(){
+
+try{document.body.style.zoom=0.09410077054053545*Math.random()}catch(e){}
+try{test2.appendChild(document.createTextNode("閄ﻇ쫠솟ê®é›”îºëº¡æ“???ⷷ觙ﴶ⸶믚ã™ï»žî’ƒå®é•Žá¦–퓜ї葪㴾ﲨ㦩???乹㰰ê•îœ¯é‹¾ä›è‡…ᩃç±ì‹ŽíŸŽê«¬ë¸"))}catch(e){}
+},4)
+setTimeout(function(){location.reload()},1000)
+</script>
diff --git a/layout/generic/crashtests/947158.html b/layout/generic/crashtests/947158.html
new file mode 100644
index 000000000..30ca6709d
--- /dev/null
+++ b/layout/generic/crashtests/947158.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase for bug </title>
+</head>
+<body>
+
+<iframe src="947158-iframe.html" width="100%" frameborder=0></iframe>
+<iframe src="947158-iframe.html" width="100%" frameborder=0></iframe>
+
+<script>
+var i = 0;
+function test(){
+ fs=document.querySelectorAll('iframe');
+ f=fs[Math.floor(Math.random()*fs.length)];
+ f.width=Math.random()*100+"%";
+ if (i++ < 10) {
+ setTimeout(test,300);
+ f.offsetHeight;
+ return;
+ }
+ for (var j = 0; j < fs.length; j++) {
+ f = fs[j];
+ f.parentNode.removeChild(f);
+ }
+ document.documentElement.removeAttribute("class");
+}
+test();
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/949932.html b/layout/generic/crashtests/949932.html
new file mode 100644
index 000000000..2b7e7a0be
--- /dev/null
+++ b/layout/generic/crashtests/949932.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body>
+ <div style="position: fixed;">
+ <fieldset style="overflow: hidden;">
+ <legend style="position: sticky;"></legend>
+ </fieldset>
+ </div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/961859.html b/layout/generic/crashtests/961859.html
new file mode 100644
index 000000000..275f0c655
--- /dev/null
+++ b/layout/generic/crashtests/961859.html
@@ -0,0 +1,18 @@
+<k> Nv,9O@j.ElN|c$1 _
+wB}x
+
+<dfn>><body style="letter-spacing: 0.6432099233em; white-space: pre; "></p>
+
+
+
+<style>
+*::first-letter { top: 0.4325229034;</style><script>
+var docElement = document.documentElement;
+docElement.contentEditable = "true";
+function initCF() {
+test = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mrow");
+test.setAttribute("dir", "rtl");
+docElement.appendChild(test);
+}
+document.addEventListener("DOMContentLoaded", initCF, false);
+</script>> \ No newline at end of file
diff --git a/layout/generic/crashtests/964078.html b/layout/generic/crashtests/964078.html
new file mode 100644
index 000000000..147bdeee8
--- /dev/null
+++ b/layout/generic/crashtests/964078.html
@@ -0,0 +1,4 @@
+<style>
+ div::after { content: counter(n, korean-hanja-formal); }
+</style>
+<div style="counter-reset: n -10000000000;"></div>
diff --git a/layout/generic/crashtests/970710.html b/layout/generic/crashtests/970710.html
new file mode 100644
index 000000000..04ca24545
--- /dev/null
+++ b/layout/generic/crashtests/970710.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+
+<script>
+
+var text =
+ "B" +
+ "C" +
+ "D" +
+ "E" +
+ "F" +
+ "\u0643" +
+ "\u0002" +
+ "G" +
+ "\u202D" +
+ "H" +
+ "I" +
+ " " +
+ "\u0007" +
+ "";
+
+function boom()
+{
+ var math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math");
+ var mover = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mover");
+ document.body.appendChild(math);
+ math.appendChild(mover);
+ var textNode = document.createTextNode(text);
+ mover.appendChild(textNode);
+ document.body.style.display = "table-row-group";
+ document.body.style.textAlign = "justify";
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/layout/generic/crashtests/973701-1.xhtml b/layout/generic/crashtests/973701-1.xhtml
new file mode 100644
index 000000000..e12a7a8bd
--- /dev/null
+++ b/layout/generic/crashtests/973701-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="display: flex;">
+<munderover xmlns="http://www.w3.org/1998/Math/MathML" style="position: absolute;" />
+</body>
+</html>
diff --git a/layout/generic/crashtests/973701-2.xhtml b/layout/generic/crashtests/973701-2.xhtml
new file mode 100644
index 000000000..9fcddd11d
--- /dev/null
+++ b/layout/generic/crashtests/973701-2.xhtml
@@ -0,0 +1,6 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="display: flex;">
+<munderover xmlns="http://www.w3.org/1998/Math/MathML" style="position: absolute;" />
+<munderover xmlns="http://www.w3.org/1998/Math/MathML" style="position: absolute;" />
+</body>
+</html>
diff --git a/layout/generic/crashtests/986899.html b/layout/generic/crashtests/986899.html
new file mode 100644
index 000000000..957d52f98
--- /dev/null
+++ b/layout/generic/crashtests/986899.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body>
+
+<div style="text-align-last: justify; white-space: pre-line;"><span dir="rtl">
+B</span></div>
+
+</body>
+</html>
diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list
new file mode 100644
index 000000000..e36a4742f
--- /dev/null
+++ b/layout/generic/crashtests/crashtests.list
@@ -0,0 +1,644 @@
+load 25888-1.html
+load 25888-2.html
+load 37757-1.html
+load 225868-1.html
+load 255468.xhtml
+load 255982-1.html
+load 255982-2.html
+load 255982-3.html
+load 255982-4.html
+load 264937-1.html
+load 265867-1.html
+load 265867-2.html
+skip-if(gtkWidget&&isDebugBuild) load 286491.html # Bug 1315855
+load 289864-1.html
+load 295292-1.html
+load 295292-2.html
+load 302260-1.html
+load 307979-1.html
+load 309322-1.html
+load 309322-2.html
+load 309322-3.html
+load 309322-4.html
+load 310556-1.xhtml
+load 321224.xul
+load 322780-1.xul
+load 323381-1.html
+load 323381-2.html
+asserts-if(gtkWidget,1) asserts-if(Android&&asyncPan,1) load 323386-1.html # Bug 718883
+load 323389-1.html
+load 323389-2.html
+load 323493-1.html
+load 323495-1.html
+load 324318-1.html
+load 328946-1.html
+load 331284-1.xhtml
+load 331292.html
+load 334105-1.xhtml
+load 334107-1.xhtml
+load 334147-1.xhtml
+load 334148-1.xhtml
+load 334602-1.html
+load 337412-1.html
+load 337883-1.html
+load 337883-2.html
+load 339769-1.html
+load 342322-1.html
+load 343206-1.xhtml
+load 344557-1.html
+load 345139-1.xhtml
+load 345617-1.html
+load 348510-1.html
+load 348510-2.html
+load 348887-1.html
+load 348991-1.xhtml
+load 350370.html
+load 354458-1.html
+load 354458-2.html
+load 355426-1.html
+load 359371-1.html
+load 359371-2.html
+load 360599.html
+load 363448.html
+load 363722-1.html
+load 363722-2.html
+load 363848-1.xhtml
+load 364220.html
+load 364407-1.html
+load 364686-1.xhtml
+load 366021-1.xhtml
+load 366667-1.html
+load 366952-1.html
+load 367246-1.html
+load 367360.html
+load 368330-1.html
+load 368461-1.xhtml
+load 368568.html
+load 368752.html
+load 368860-1.html
+load 368863-1.html
+load 369038-1.xhtml
+load 369150-1.html
+load 369150-2.html
+load 369227-1.xhtml
+load 369542-1.html
+load 369542-2.html
+load 369547-1.html
+load 370174-1.html
+load 370174-2.html
+load 370174-3.html
+load 370174-4.html
+load 370699-1.html
+load 370794-1.html
+load 370866-1.xhtml
+load 370884-1.xhtml
+load 371348-1.xhtml
+load 371561-1.html
+load 371566-1.xhtml
+load 372376-1.xhtml
+load 373859-1.html
+load 373868-1.xhtml
+load 374090.html
+load 374420.xhtml
+load 375462-1.html
+load 375831.html
+load 376419.html
+load 377522.html
+load 379217-1.xhtml
+load 379217-2.xhtml
+load 379917-1.xhtml
+load 380012-1.html
+load 381152-1.html
+load 381786-1.html
+load 382129-1.xhtml
+load 382131-1.html
+load 382199-1.html
+load 382208-1.xhtml
+load 382262-1.html
+load 382396-1.xhtml
+load 382745-1.xhtml
+load 383089-1.html
+load 385265-1.xhtml
+load 385295-1.xhtml
+load 385344-1.html
+load 385344-2.html
+load 385414-1.html
+load 385414-2.html
+load 385426-1.html
+load 385526.html
+load 385681.html
+load 385885-1.xul
+load 386799-1.html
+load 386807-1.html
+load 386812-1.html
+load 386827-1.html
+load 387058-1.html
+load 387058-2.html
+load 387088-1.html
+load 387209-1.html
+load 387213-1.html
+load 387215-1.xhtml
+load 387219-1.xhtml
+load 387233-1.html
+load 387233-2.html
+load 387282-1.html
+load 388175-1.html
+load 388367-1.html
+load 388709-1.html
+load 389635-1.html
+load 390050-1.html
+load 390050-2.html
+load 390050-3.html
+load 390762-1.html
+load 391053-1.xhtml
+load 391894-1.html
+load 392698-1.html
+load 393758-1.xhtml
+load 393906-1.html
+load 393923-1.html
+load 393956-1.html
+load 393956-2.html
+load 393956-3.html
+load 393956-4.html
+load 394237-1.html
+load 394818-1.html
+load 394818-2.html
+load 394820-1.html
+load 395316-1.html
+load 395450-1.xhtml
+load 397007-1.html
+load 397187-1.html
+load 397844-1.xhtml
+load 397844-2.xhtml
+load 397852-1.xhtml
+load 398181-1.html
+load 398181-2.html
+load 398322-1.html
+load 398322-2.html
+load 398332-1.html
+load 398332-2.html
+asserts(0-2) load 398332-3.html # bug 436123 and bug 457397
+load 399407-1.xhtml
+load 399412-1.html
+load 399843-1.html
+load 400078-1.html
+load 400190.html
+load 400223-1.html
+load 400232-1.html
+load 400244-1.html
+load 400768-1.xhtml
+load 400768-2.xhtml
+load 401042-1.xhtml
+load 402380-1.html
+load 402380-2.html
+load 402872-1.html
+load 402872-2.html
+load 403004.html
+load 403143-1.html
+load 403576-1.html
+load 404140-1.html
+load 404146-1.html
+load 404204-1.html
+load 404215-1.html
+load 404215-2.html
+load 404215-3.html
+load 404219-1.html
+load 404219-2.html
+load 406137.html
+load 406380.html
+load 406902-1.html
+load 407009-1.xhtml
+load 408304-1.xhtml
+load 408602-1.html
+load 408737-1.html
+load 408737-2.html
+load 408749-1.xhtml
+load 408883-1.html
+load 410198.html
+load 410228-1.html
+load 410232-1.html
+load 410595-1.html
+load 411213-1.html
+load 411213-2.xml
+load 411835.html
+load 411851-1.html
+load 412014-1.html
+load 412201-1.xhtml
+load 412543-1.html
+load 413048-1.html
+load 413079-1.xhtml
+load 413079-2.xhtml
+load 413079-3.xhtml
+load 413085-1.html
+load 413085-2.html
+load 413582-1.xhtml
+load 413582-2.html
+load 413712-1.xhtml
+load 414061-1.html
+load 414180-1.xul
+load 414719-1.html
+load 415685-1.html
+load 416165.html
+load 416264-1.html
+load 416476-1.html
+load 417109-1.xhtml
+load 417848-1.xhtml
+load 417902-1.html
+load 417902-2.html
+load 418532-1.html
+load 418932-1.html
+load 419352.html
+load 420000-1.html
+load 420718.html
+load 420785-1.xhtml
+load 421404-1.html
+load 421671.html
+load 422283-1.html
+load 422301-1.html
+load 423055-1.html
+load 423098.html
+load 423264-1.html
+load 424629.html
+load 425253-1.html
+load 426040-1.html
+load 426272-1.html
+load 428263-1.html
+load 429458.xhtml
+load 429960-1.html
+load 429960-2.html
+load 429969-1.html
+load 429981-1.html
+load 430332-1.html
+load 430344-1.html
+load 430352-1.html
+load 430744-1.html
+load 430991.html
+load 431260-1.html
+load 431260-2.html
+load 435529.html
+load 436194-1.html
+load 436602-1.html
+load 436822-1.html
+load 436823.html
+load 436969-1.html
+load 437156-1.html
+load 437565-1.xhtml
+load 437565-2.xhtml
+load 437565-3.xhtml
+load 438259-1.html
+load 438266-1.html
+skip load 438509-1.html # bug 511234
+load 442860-1.xul
+load 443528-1.html
+load 444230-1.html
+load 444484-1.html
+load 444726-1.xhtml
+load 444861-1.html
+load 445288.html
+load 448903-1.html
+load 448996-1.html
+load 451315-1.html
+load 451317-1.html
+load 451334-1.html
+load 452157-1.html
+load 452157-2.html
+load 452157-3.html
+load 453762-1.html
+load 455171-1.html
+load 455171-2.html
+load 455171-3.html
+load 455643-1.xhtml
+load 457375.html
+load 457380-1.html
+asserts-if(!Android,4) load 459968.html # bug 1067022
+load 460910-1.xml
+load 461294-1.html
+load 462968.xhtml
+load 463350-1.html
+load 463350-2.html
+load 463350-3.html
+load 463741-1.html
+load 463785.xhtml
+load 465651-1.html
+load 467137-1.html
+load 467213-1.html
+load 467487-1.html
+load 467493-1.html
+load 467493-2.html
+load 467875-1.xhtml
+load 467914-1.html
+load 468207-1.html
+load 468771-1.xhtml
+load 468771-2.xhtml
+load 469859-1.xhtml
+load 472587-1.xhtml
+load 472617-1.xhtml
+load 472774-1.html
+load 472776-1.html
+load 472950-1.html
+load 472957.xhtml
+load 473278-1.xhtml
+load 473894-1.html
+load 476241-1.html
+load 477731-1.html
+load 477928.html
+load 478131-1.html
+load 478170-1.html
+load 478185-1.html
+asserts-if(!Android,0-1) load 479938-1.html # Bug 575011
+load 480345-1.html
+load 481921.html
+load 489462-1.html
+load 489477.html
+load 489480-1.xhtml
+load 493111-1.html
+load 493118-1.html
+load 493649.html
+load 494283-1.xhtml
+load 494283-2.html
+load 494300-1.xul
+load 494332-1.html
+load 495875-1.html
+load 495875-2.html
+load 496742.html
+load 499138.html
+load 499857-1.html
+load 499862-1.html
+asserts(0-3) load 499885-1.xhtml # Bug 1220265
+load 501535-1.html
+load 503961-1.xhtml
+load 503961-2.html
+load 505912-1.html
+load 508154-1.xhtml
+load 508168-1.html
+load 508816-1.xul
+load 508908-1.html
+load 509749-1.html
+load 511482.html
+load 512724-1.html
+load 512725-1.html
+load 512749-1.html
+load 513110-1.html
+load 513110-2.xhtml
+load 513394-1.html
+load 514098-1.xhtml
+load 514800-1.html
+load 515811-1.html
+load 517968.html
+load 519031.xhtml
+load 520340.html
+load 522170-1.html
+load 526217.html
+load 533379-1.html
+load 533379-2.html
+load 534082-1.html
+load 534366-1.html
+load 534366-2.html
+load 536692-1.xhtml
+load 537645.xhtml
+load 541277-1.html
+load 541277-2.html
+load 541714-1.html
+load 541714-2.html
+load 542136-1.html
+load 545571-1.html
+load 547338.xul
+load 547843-1.xhtml
+load 551635-1.html
+load 553504-1.xhtml
+load 564368-1.xhtml
+load 564968.xhtml
+load 569193-1.html
+load 570160.html
+load 570289-1.html
+load 571618-1.svg
+asserts(1) load 571975-1.html # bug 574889
+load 571995.xhtml
+load 574958.xhtml
+asserts(0-4) load 578977.html # bug 757305
+load 580504-1.xhtml
+load 585598-1.xhtml
+load 586806-1.html
+load 586806-2.html
+load 586806-3.html
+load 586973-1.html
+load 589002-1.html
+load 590404.html
+load 591141.html
+load 592118.html
+load 594808-1.html
+load 595435-1.xhtml
+load 595740-1.html
+load 597240-1.xhtml
+pref(layout.float-fragments-inside-column.enabled,true) load 600100.xhtml
+pref(layout.float-fragments-inside-column.enabled,false) load 600100.xhtml
+load 603490-1.html
+load 603510-1.html
+load 604314-1.html
+load 604843.html
+load 605340.html
+load 606642.xhtml
+load 613455-1.svg
+load 613629-1.xhtml
+load 616052-1.html
+load 619021.html
+load 621424-1.html
+load 621841-1.html
+load 622596.html
+load 641724.html
+load 645072-1.html
+load 645072-2.html
+load 646561-1.html
+load 646983-1.html
+load 647332-1.html
+load 650499-1.html
+load 654002-1.html
+load 654002-2.html
+load 655462-1.html
+load 656130-1.html
+load 656130-2.html
+load 660416.html
+load 665853.html
+load 667025.html
+load 673770.html
+load 679933-1.html
+load 681489-1.html
+load 682649-1.html
+load 683702-1.xhtml
+load 683712.html
+load 688996-1.html
+load 688996-2.html
+load 691210.html
+load 700031.xhtml
+load 718516.html
+load 723108.html
+load 724235.html
+skip-if(Android&&isDebugBuild) load 724978.xhtml # bug 1263300 - slow
+skip-if(Android&&isDebugBuild) load 730559.html # bug 1245634 - slow
+load 734777.html
+load 737313-1.html
+load 737313-2.html
+load 737313-3.html
+test-pref(font.size.inflation.emPerLine,15) load 740199-1.xhtml
+load 747688.html
+load 750066.html
+load 757413.xhtml
+load 757413-2.html
+load 762764-1.html
+load 762902.html
+load 765409.html
+asserts(0-200) load 765621.html # bug 703550
+asserts(0-200) load 767765.html # bug 407550, bug 871758, and various nscoord_MAX related asserts
+load 769120.html
+asserts(0-2) load 769303-1.html # bug 1123979
+load 769303-2.html
+load 777838.html
+load 783228.html
+load 784600.html
+load 785555.html
+load 786740-1.html
+load 790260-1.html
+asserts(1) test-pref(font.size.inflation.emPerLine,15) load 791601.xhtml # Bug 871327
+test-pref(font.size.inflation.minTwips,120) load 794693.html
+asserts-if(!Android,4) load 798020-1.html
+load 798235-1.html
+load 799207-1.html
+load 799207-2.html
+load 801268-1.html
+load 804089-1.xhtml
+load 807565-1.html
+load 807565-2.html
+load 810303.html
+load 810726.html
+load 812822-1.html
+load 812879-1.html
+load 812879-2.html
+load 812893.html
+load 814995.html
+load 822910.xhtml
+load 824297-1.html
+load 825810-1.html
+load 825810-2.html
+load 826483-1.html
+load 826532-1.html
+load 827076.html
+load 827168-1.html
+load 836895.html
+load 837007.xhtml
+load 840787.html
+load 840818.html
+load 842132-1.html
+load 842166.html
+load 844529-1.html
+load 847130.xhtml
+load 847208.html
+asserts-if(Android,2) asserts-if(Android&&asyncPan,4) asserts-if(!Android,4) load 847209.html # bug 847368
+load 847211-1.html
+load 849603.html
+asserts(0-12) load 850931.html # bug 569193
+load 851396-1.html
+load 854263-1.html
+load 862185.html
+load 862947-1.html
+load 863935.html
+load 866547-1.html
+needs-focus pref(accessibility.browsewithcaret,true) load 868906.html
+asserts(0-5) load 876074-1.html # bug 876749
+load 876155.html
+load 885009-1.html
+load 893496-1.html
+load 893523.html
+asserts(0-3) load 898871.html # bug 479160 - mostly OSX, sometimes Windows
+asserts(0-3) load 914501.html # bug 1144852 - all platforms
+load 914891.html
+load 915475.xhtml
+load 927558.html
+load 943509-1.html
+asserts(2-8) load 944909-1.html
+load 946167-1.html
+load 947158.html
+load 949932.html
+asserts-if(Android,0-1) load 964078.html # bug 989718
+load 970710.html
+load 973701-1.xhtml
+load 973701-2.xhtml
+load 986899.html
+load 1001233.html
+load 1001258-1.html
+load 1003441.xul
+pref(layout.css.grid.enabled,true) load 1015562.html
+asserts(1-2) load 1015563-1.html
+asserts(1-2) load 1015563-2.html
+asserts(0-300) load 1015844.html # bug 574889
+pref(font.size.inflation.minTwips,200) load 1032450.html
+load 1032613-1.svg
+load 1032613-2.html
+load 1037903.html
+load 1039454-1.html
+load 1042489.html
+load 1054010-1.html
+load 1058954-1.html
+load 1134531.html
+load 1134667.html
+load 1137723-1.html
+load 1137723-2.html
+load 1140268-1.html
+load 1145768.html
+load 1146103.html
+load 1146107.html
+load 1146114.html
+load 1153695.html
+load 1156222.html
+pref(layout.css.grid.enabled,true) load 1156257.html
+load 1157011.html
+load 1169420-1.html
+load 1169420-2.html
+load 1183431.html
+load 1221112-1.html
+load 1221112-2.html
+load 1221874-1.html
+load 1222783.xhtml
+load 1223568-1.html
+load 1223568-2.html
+load 1224230-1.html
+pref(layout.css.grid.enabled,true) load 1225118.html
+pref(layout.css.grid.enabled,true) load 1225376.html
+pref(layout.css.grid.enabled,true) load 1225592.html
+load 1229437-1.html
+load 1229437-2.html
+pref(dom.details_element.enabled,true) load details-containing-only-text.html
+pref(dom.details_element.enabled,true) load details-display-none-summary-1.html
+pref(dom.details_element.enabled,true) load details-display-none-summary-2.html
+pref(dom.details_element.enabled,true) load details-display-none-summary-3.html
+pref(dom.details_element.enabled,true) load details-open-overflow-auto.html
+pref(dom.details_element.enabled,true) load details-open-overflow-hidden.html
+pref(dom.details_element.enabled,true) load details-three-columns.html
+load first-letter-638937-1.html
+load first-letter-638937-2.html
+load flex-nested-abspos-1.html
+pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) asserts(0-100) load font-inflation-762332.html # bug 762332
+load outline-on-frameset.xhtml
+pref(dom.details_element.enabled,true) load summary-position-out-of-flow.html
+load text-overflow-bug666751-1.html
+load text-overflow-bug666751-2.html
+load text-overflow-bug670564.xhtml
+load text-overflow-bug671796.xhtml
+load text-overflow-bug713610.html
+load text-overflow-form-elements.html
+load text-overflow-iframe.html
+asserts-if(Android,2-4) asserts-if(!Android,4) load 1225005.html # bug 682647 and bug 448083
+load 1233191.html
+asserts(2) load 1272983-1.html # bug 586628
+asserts(2) load 1272983-2.html # bug 586628
+load 1275059.html
+load 1278007.html
+load 1279814.html
+load large-border-radius-dashed.html
+load large-border-radius-dashed2.html
+load large-border-radius-dotted.html
+load large-border-radius-dotted2.html
+load 1297427-non-equal-centers.html
+load 1278461-1.html
+load 1278461-2.html
+load 1304441.html
+load 1316649.html
diff --git a/layout/generic/crashtests/details-containing-only-text.html b/layout/generic/crashtests/details-containing-only-text.html
new file mode 100644
index 000000000..9d5e647d9
--- /dev/null
+++ b/layout/generic/crashtests/details-containing-only-text.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>This is the detail.</details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/details-display-none-summary-1.html b/layout/generic/crashtests/details-display-none-summary-1.html
new file mode 100644
index 000000000..2d7cf57a0
--- /dev/null
+++ b/layout/generic/crashtests/details-display-none-summary-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary style="display: none;">summary (display: none)</summary>
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/details-display-none-summary-2.html b/layout/generic/crashtests/details-display-none-summary-2.html
new file mode 100644
index 000000000..4db67c0cf
--- /dev/null
+++ b/layout/generic/crashtests/details-display-none-summary-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary style="display: none;">summary (display: none)</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/details-display-none-summary-3.html b/layout/generic/crashtests/details-display-none-summary-3.html
new file mode 100644
index 000000000..1ba94c74c
--- /dev/null
+++ b/layout/generic/crashtests/details-display-none-summary-3.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary style="display: none;">summary (display: none)</summary>
+ <summary>summary 2</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/details-open-overflow-auto.html b/layout/generic/crashtests/details-open-overflow-auto.html
new file mode 100644
index 000000000..c9ec19329
--- /dev/null
+++ b/layout/generic/crashtests/details-open-overflow-auto.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <style>
+ details {
+ background-color: orange;
+ overflow: auto;
+ width: 300px;
+ height: 200px;
+ }
+ summary {
+ background-color: green;
+ overflow: auto;
+ width: 200px;
+ height: 100px;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+ minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
+ ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
+ voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
+ sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+ mollit anim id est laborum.
+ </summary>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+ veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+ commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
+ velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+ cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
+ est laborum.
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/details-open-overflow-hidden.html b/layout/generic/crashtests/details-open-overflow-hidden.html
new file mode 100644
index 000000000..5e847ec7f
--- /dev/null
+++ b/layout/generic/crashtests/details-open-overflow-hidden.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <style>
+ details {
+ background-color: orange;
+ overflow: hidden;
+ width: 300px;
+ height: 200px;
+ }
+ summary {
+ background-color: green;
+ overflow: hidden;
+ width: 200px;
+ height: 100px;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+ minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
+ ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
+ voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
+ sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+ mollit anim id est laborum.
+ </summary>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+ veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+ commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
+ velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+ cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
+ est laborum.
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/details-three-columns.html b/layout/generic/crashtests/details-three-columns.html
new file mode 100644
index 000000000..f7ef658f5
--- /dev/null
+++ b/layout/generic/crashtests/details-three-columns.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <style>
+ details {
+ -moz-column-count: 3;
+ -moz-column-rule: 1px solid lightgray;
+ -webkit-column-count: 3;
+ -webkit-column-rule: 1px solid lightgray;
+ border: 1px solid lightblue;
+ }
+ summary {
+ background-color: lightgreen;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary>Summary</summary>
+ <p>line</p>
+ <p>line</p>
+ <p>line</p>
+ <p>line</p>
+ <p>line</p>
+ <p>line</p>
+ <p>line</p>
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/first-letter-638937-1.html b/layout/generic/crashtests/first-letter-638937-1.html
new file mode 100644
index 000000000..da2a098c7
--- /dev/null
+++ b/layout/generic/crashtests/first-letter-638937-1.html
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>yo-lobo</title>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
+ <meta content="werwolf - zoquete pluscuamperfecto" name="author" />
+ <style type="text/css">
+ body {
+ font-family: sans-serif, Arial;
+ -moz-column-count: 5;
+ -moz-column-gap: 1em;
+ padding: 5px;
+ }
+ body.crash {
+ -moz-column-rule-width: thin;
+ -moz-column-rule-style: solid;
+ }
+ p {
+ margin: 10px;
+ padding: 0px;
+ }
+ p:first-letter {
+ font-size: 30pt;
+ font-weight: bold;
+ float: left;
+ padding-right: 5px;
+ padding-bottom: 5px;
+ }
+ </style>
+
+ </head>
+ <body onload="x=document.body; x.className='crash'">
+ <p>Lorem ipsum dolor sit amet consectetuer platea turpis justo Ut interdum. Wisi accumsan Vestibulum tempor vel ut nulla semper platea tincidunt consectetuer. Tristique metus ac nec turpis nibh nunc interdum ut tristique nec. Porttitor nibh sollicitudin urna fames non ultrices ipsum metus pede velit. Adipiscing amet et orci augue vel auctor amet ac Nam.</p>
+ <p>Ornare pellentesque augue leo Sed et In Donec nibh Cum tincidunt. Rutrum vel eget sagittis arcu cursus nibh Nam feugiat lacus lobortis. Suspendisse dictumst at Phasellus eu cursus sem risus dolor adipiscing metus. Lorem et Praesent Nunc Morbi Curabitur id pretium neque quis consequat. Convallis laoreet Integer et et Nulla In et et ut et. Convallis gravida ut tortor odio.</p>
+ <p>Fames pharetra et lacinia a aliquet tempor Vivamus Curabitur Vestibulum Vivamus. Duis Vestibulum nascetur sodales interdum congue a diam Lorem id In. Pede Curabitur interdum vitae nisl nunc est et ac Nulla quis. Sodales metus vitae mauris tellus Curabitur vitae dolor mauris wisi Phasellus. Pellentesque a Ut sem sapien interdum convallis Curabitur purus Aenean.</p>
+ <p>Ultrices pellentesque pretium odio vestibulum natoque natoque gravida Vivamus quis Integer. Ipsum cursus id nec cursus odio amet Vestibulum Suspendisse vitae habitasse. Leo elit eros porta volutpat laoreet commodo elit id egestas et. Curabitur arcu semper dictumst molestie Integer ligula id tellus quis Mauris. Tincidunt eget Sed amet justo porttitor egestas nibh pulvinar mauris justo. Vestibulum natoque eget hendrerit habitasse hendrerit eu purus Proin.</p>
+ <p>Lacinia Integer nec enim sem pellentesque sollicitudin sagittis Cras Sed Morbi. Vitae quis et consectetuer libero metus eros neque malesuada lacus justo. Curabitur ipsum lobortis massa lobortis consequat ut et Fusce quam augue. Laoreet id libero laoreet Curabitur interdum tempus Quisque elit amet purus. Libero sed Phasellus nec odio pede sed ac velit tincidunt id. Metus natoque.</p>
+ <p>Felis et enim at condimentum augue ut vitae In Mauris laoreet. Neque urna Morbi sapien risus nulla leo nec sed ipsum id. Id dictum eu natoque libero ac dapibus Ut sed ut dictum. Sed quis aliquet nunc vestibulum eleifend orci vestibulum Vestibulum Vivamus est. Et urna tempus montes eget Sed tristique.</p>
+ <p>Nibh id mauris ipsum Curabitur Integer velit sed Vivamus Integer laoreet. Eu semper Nulla ac Curabitur Vestibulum ut urna Sed libero In. Phasellus vitae nibh nunc eget Nam iaculis sed Phasellus mauris consectetuer. Amet dignissim natoque eget facilisi Vestibulum facilisis sit scelerisque porta adipiscing. Condimentum vel nec turpis metus est felis neque fames dapibus at. Aenean sed ac.</p>
+ <p>Malesuada hendrerit facilisis et Donec sed pellentesque Nullam est Praesent augue. Pede id orci tincidunt purus Suspendisse Vestibulum sagittis euismod sem porttitor. Lorem a convallis vestibulum condimentum Vestibulum mauris pellentesque consequat metus Vivamus. Consectetuer egestas eu Vestibulum id Morbi interdum montes eros odio Sed. Arcu Donec lacinia mauris vel tortor interdum in habitasse.</p>
+ <p>A scelerisque justo justo Vivamus eleifend velit Nullam orci tortor Nam. Nonummy ut nibh Pellentesque at pede Integer nibh metus justo scelerisque. Tincidunt consequat Curabitur porta non Morbi tincidunt egestas semper pellentesque Vestibulum. Ultrices congue In nec quis et pellentesque at vitae ipsum ridiculus. Elit fringilla ante Aenean elit Sed ut Nam pretium Aenean vel. Eu justo porta mauris congue neque pretium quis enim turpis sit. Auctor.</p>
+ <p>Scelerisque Maecenas Nunc lacinia porttitor fames Pellentesque sed urna Quisque pellentesque. Aenean eget tempus Praesent feugiat sed pretium dignissim In sapien Morbi. Velit mauris Nam Donec sollicitudin at vel mattis vitae amet laoreet. Vel at Nulla id Fusce vel interdum pellentesque Curabitur montes Phasellus. Accumsan interdum est eu ac lacus pellentesque sed Pellentesque.</p>
+</body>
+</html>
diff --git a/layout/generic/crashtests/first-letter-638937-2.html b/layout/generic/crashtests/first-letter-638937-2.html
new file mode 100644
index 000000000..eeece02d1
--- /dev/null
+++ b/layout/generic/crashtests/first-letter-638937-2.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html style="height: 600em; -moz-column-width: 1px;">
+
+<head>
+<style>p::first-letter { float:left; }</style>
+</head>
+
+<body onload="x=document.body.parentNode; x.style.MozColumnWidth='111px'; x.offsetHeight; x.style.display='inline'; x.offsetHeight; "><p style="margin: -562949953421311em;">y
+</p><p>'</p></body>
+
+</html>
diff --git a/layout/generic/crashtests/flex-nested-abspos-1.html b/layout/generic/crashtests/flex-nested-abspos-1.html
new file mode 100644
index 000000000..53522a36c
--- /dev/null
+++ b/layout/generic/crashtests/flex-nested-abspos-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<div style="display:flex">
+ <div style="position: absolute">
+ <div style="position: absolute">
+ </div>
+ </div>
+</div>
diff --git a/layout/generic/crashtests/font-inflation-762332.html b/layout/generic/crashtests/font-inflation-762332.html
new file mode 100644
index 000000000..e733a56d1
--- /dev/null
+++ b/layout/generic/crashtests/font-inflation-762332.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="-moz-column-width: 1px; font-family: monospace; width: 2ch;"><div style="position: relative;"><div style="position: absolute;">xxxxxxxxxxxxxx x xxxxxxx x xxxxxxxxxxxxxxxxxx x xxxxxxx x</div></div></div>
diff --git a/layout/generic/crashtests/image.jpg b/layout/generic/crashtests/image.jpg
new file mode 100644
index 000000000..8433518bc
--- /dev/null
+++ b/layout/generic/crashtests/image.jpg
Binary files differ
diff --git a/layout/generic/crashtests/large-border-radius-dashed.html b/layout/generic/crashtests/large-border-radius-dashed.html
new file mode 100644
index 000000000..a77e3c5d9
--- /dev/null
+++ b/layout/generic/crashtests/large-border-radius-dashed.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="height: 10000000px; width: 10000000px; box-sizing: border-box; border-radius: 10000000px; border-style: dashed; border-width: 10px 20px;"></html>
diff --git a/layout/generic/crashtests/large-border-radius-dashed2.html b/layout/generic/crashtests/large-border-radius-dashed2.html
new file mode 100644
index 000000000..ed6722579
--- /dev/null
+++ b/layout/generic/crashtests/large-border-radius-dashed2.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="height: 6523790304542em; width: 6207636626031em; box-sizing: border-box; border-radius: 6523790304542em; border-style: dashed; border-width: 10px 20px;"></html>
diff --git a/layout/generic/crashtests/large-border-radius-dotted.html b/layout/generic/crashtests/large-border-radius-dotted.html
new file mode 100644
index 000000000..fe530c0c0
--- /dev/null
+++ b/layout/generic/crashtests/large-border-radius-dotted.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="height: 10000000px; width: 10000000px; box-sizing: border-box; border-radius: 10000000px; border-style: dotted; border-width: 10px 20px;"></html>
diff --git a/layout/generic/crashtests/large-border-radius-dotted2.html b/layout/generic/crashtests/large-border-radius-dotted2.html
new file mode 100644
index 000000000..8cd822cf6
--- /dev/null
+++ b/layout/generic/crashtests/large-border-radius-dotted2.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html style="height: 6523790304542em; width: 6207636626031em; box-sizing: border-box; border-radius: 6523790304542em; border-style: dotted; border-width: 10px 20px;"></html>
diff --git a/layout/generic/crashtests/outline-on-frameset.xhtml b/layout/generic/crashtests/outline-on-frameset.xhtml
new file mode 100644
index 000000000..9f72d10cc
--- /dev/null
+++ b/layout/generic/crashtests/outline-on-frameset.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><frameset style="outline-style: solid;"></frameset></html>
diff --git a/layout/generic/crashtests/simple_blank.swf b/layout/generic/crashtests/simple_blank.swf
new file mode 100644
index 000000000..b846387eb
--- /dev/null
+++ b/layout/generic/crashtests/simple_blank.swf
Binary files differ
diff --git a/layout/generic/crashtests/solidblue.png b/layout/generic/crashtests/solidblue.png
new file mode 100644
index 000000000..a64b6a425
--- /dev/null
+++ b/layout/generic/crashtests/solidblue.png
Binary files differ
diff --git a/layout/generic/crashtests/summary-position-out-of-flow.html b/layout/generic/crashtests/summary-position-out-of-flow.html
new file mode 100644
index 000000000..2c585fd49
--- /dev/null
+++ b/layout/generic/crashtests/summary-position-out-of-flow.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <head>
+ <style>
+ #fixed {
+ position: fixed;
+ bottom: 0;
+ right: 0;
+ }
+ #absolute {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ }
+ </style>
+ </head>
+ <body>
+ <details>
+ <summary id="fixed">Summary (position: fixed)</summary>
+ <p>This is the detail with fixed summary.</p>
+ </details>
+ <details>
+ <summary id="absolute">Summary (position: absolute)</summary>
+ <p>This is the detail with absolute summary.</p>
+ </details>
+ </body>
+</html>
diff --git a/layout/generic/crashtests/text-overflow-bug666751-1.html b/layout/generic/crashtests/text-overflow-bug666751-1.html
new file mode 100644
index 000000000..4bfec4e53
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-bug666751-1.html
@@ -0,0 +1,12 @@
+<html class="reftest-wait"><head><script>
+function finish() {
+ window.removeEventListener("MozAfterPaint", finish, false);
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head><body onload="window.addEventListener('MozAfterPaint', finish, false); document.body.style.backgroundColor='lime';">
+<div style="overflow: scroll; text-indent: -100px; white-space: pre; text-overflow: ellipsis;"><span style="font-family: -moz-fixed; white-space: normal;"></code><p style="position: fixed;">m
+</p>
+</div>
+
+</body></html>
diff --git a/layout/generic/crashtests/text-overflow-bug666751-2.html b/layout/generic/crashtests/text-overflow-bug666751-2.html
new file mode 100644
index 000000000..3659f009e
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-bug666751-2.html
@@ -0,0 +1,12 @@
+<html class="reftest-wait"><head><script>
+function finish() {
+ window.removeEventListener("MozAfterPaint", finish, false);
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head><body onload="window.addEventListener('MozAfterPaint', finish, false); document.body.style.backgroundColor='lime';">
+<div style="overflow: scroll; text-indent: -100px; white-space: pre; text-overflow: ellipsis;"><span style="font-family: -moz-fixed; white-space: normal;"></code><p style="position: absolute;">m
+</p>
+</div>
+
+</body></html>
diff --git a/layout/generic/crashtests/text-overflow-bug670564.xhtml b/layout/generic/crashtests/text-overflow-bug670564.xhtml
new file mode 100644
index 000000000..13ee83631
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-bug670564.xhtml
@@ -0,0 +1,3 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="position: relative; white-space: pre-line; direction: rtl; top: -85967203400px; text-overflow: ellipsis; text-indent: 5242870ch; padding: 224652170px; overflow-y: scroll; -moz-column-width: 74804px; letter-spacing: 687194767px;">
+
+</html>
diff --git a/layout/generic/crashtests/text-overflow-bug671796.xhtml b/layout/generic/crashtests/text-overflow-bug671796.xhtml
new file mode 100644
index 000000000..47c2eb87e
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-bug671796.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="width: 1px; text-overflow: ellipsis; overflow-y: scroll;">
+<math xmlns="http://www.w3.org/1998/Math/MathML"><msup style="display:block"/></math>
+</body>
+</html>
diff --git a/layout/generic/crashtests/text-overflow-bug713610.html b/layout/generic/crashtests/text-overflow-bug713610.html
new file mode 100644
index 000000000..145295364
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-bug713610.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="text-overflow: ellipsis; padding-right: 4000px; overflow: scroll;"><span style="-moz-transform: translatex(-50px); border-right-style: dashed;"></span></div>
+</body>
+</html>
diff --git a/layout/generic/crashtests/text-overflow-form-elements.html b/layout/generic/crashtests/text-overflow-form-elements.html
new file mode 100644
index 000000000..b10124e4d
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-form-elements.html
@@ -0,0 +1,144 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>text-overflow test case</title>
+<style type="text/css">
+
+.test {
+ font: 1em bold monospace;
+ background:lightgrey;
+ color: black;
+ margin-left:400px;
+}
+
+.rtl {
+ direction:rtl;
+}
+.ltr {
+ direction:ltr;
+}
+.rlo > * {
+ unicode-bidi: bidi-override; direction: rtl;
+}
+.lro > * {
+ unicode-bidi: bidi-override; direction: ltr;
+}
+.b { border: 1px dashed blue; }
+.inline-block {
+ display:inline-block;
+}
+.ellipsis {
+ width:4em;
+ width:6.5ch;
+ text-overflow: ellipsis;
+ -o-text-overflow: ellipsis;
+ overflow:hidden;
+}
+</style>
+<script>
+var twoEyes = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAYCAYAAAFy7sgCAAAGsUlEQVRo3u2ZbWwcZxHHf3s%2B7LNbO3ZjXBtowprGODRX0qpNQCjmJKuVKhMl1P2AkCwhFOIKkCBSm9IXavGFKAixIAECwkmWo5MrhRI3Ub40IEwQgp6aIDg3Cd6eEqyIHEteah%2B1E69vhw%2BZtTaX8704ZzkKjHS6271nZ56ZZ%2BY%2F%2F%2BdZKF%2FCwYshx3EkkggLsD1v4FQkEZZYLCbAKyG9%2Ba9EIsG6hnUAf8x74K3aUC3j4%2BM54HcsR2oAIomwZOezkv%2FnSHpYNh%2BNCmAE7xv94zvFdd1bHsjMZmQkPSxAJP%2B%2FfuBLwK54PC7JZFKAVJmzXLBt2w%2FMvcDLwIb8QS8CeJ4nkURYIomw7J%2FYJ8BvSiiXptGGxWds2%2Fa9%2Bnaxh%2BYAD%2Bgt04NDgABTpQY2cvvSFLzw86gWeBVwC8SzlOSv2YeBPfmDBoBHgKmR9LBEEmHZfDTqGykqfkUE0nA78BzQGfSgUeP3wNeTXwXg7MwZDhw4UHL6ra2ti79%2FOvljgG8AZ4H64Lhm4MvAocxsRppGG%2FxcXihlwLIs6R%2FfKV2HO%2F26uA94pdDYUKUZUU7W1RQYXA98Gnhaf5%2FXWX0HeAHYoQonqa4sZSOsSWMCWeC9Yko%2BCQwBe4E6oNc0Tc91XTl1%2BaTsn9gnI%2Blhyc5nZWxsrBIkKSbl2tiic3tW53YDEwOKaoFBrcOfqKee53lG9xsPMjV784r%2F4lO%2FpPvyJ9iyZcuvFSaXK5XYeAZ4CDgGvB3MS4B54LQuWYPeuy4iRFsevsXqpuYoqVQKIH2bK1CuDQNo11o4XUzh%2FcDWYIe1LEtyuZx4niee54njOGKapgfsqlL%2Bl2OjEXg8nxrc1dJ0h3hbtL%2BGCtz7KPBF4CuBe9uB15VafE8hr9qylI3HgG8C2%2FK7VyHZoJj7MrBRm30qFotJMpkU27YlHo%2F7Ha5a%2BV%2FKRkSJ4KuKRLVLKapTjB1SzAVIjY2NSXY%2BKyPpYdk%2FsU9OXT4pruv6BdZbBQfKsVGnvWlIe1VB6VQO8JxC1vZYLCbZ%2BaxsPhpdZDyRRFhG0sPiOE6ldKBg2lRg4xF1YCDIIIKN7DGgD3gH%2BBXwejKZfPrs2tPs%2FvPN2bKuYR1nd7xLKBSSJeqoXKnERjPwNWAG%2BLn2rZuM%2B4Tpml6vaWlp4eLcxVusZq5lCgVgOVKJjRqdX86ffL4D5wIoZACnTpw4wRMdT96i%2FImOJxERAs4uVyqxUacF%2FPdiCj%2BjdRBRGFtwXVdG0sPSdbhTmkYbpH98p2RmM2JZlig1vl0GWo4NQ%2Fn%2Bs5pKRXfwjweaxy7TND3HcRZbfC6X8xVPVQlGy7WxVWlO5XRXFXm6EZmrQuSXYyPE3SiVoEhE6Wyr0u2rumO6zv%2B21AFdQAswC1wCMuUCXCmyWQus103Qg8qlDO0lxwOb%2Fl4FiK3AB3VS%2FuKKLtK%2FgbeAnwG%2FvUODuRw%2FFrR0H1UC75fwu8oJ%2FhFsW5VIG%2FBUgEIN6Y65O4AHu4Ap0zQ9y7LEcZyb9lRBUHQcRyzL8unZVBW5bFWAvAp%2BhDQ2g4F47dUYtlU6obXA54DnVdFLekjUGGifh4AFy7LEdV3xj3X9I66m0QZpGm2QrsOd0j%2B%2BU0bSw5KZzYjrun6HWlAd961i4FfCj0aN1Usau%2Bc1lmuXPFwvAEumUut7tQQvAb%2FXb%2FT0bCAej9cODg7yt%2Bm%2F8q2%2F7OUHZ76PnZ1k2p0mJzlykmPancbOTnL0whHs7CQfb%2B5mx2d3sH79%2BtCRI0c6FeaOr9ICrIQfLvA%2B8BGNXxi4R6HrisJVUWrxAVW2oMFf0Aczim8o3kV6enowDIPjF9%2Fk%2BMU3S3rrjzMMg56eHr%2BxP7qKFbASfojG6kpeDGs1tiW53RxwWT%2Bin5q8w4xpQK5evQpAR30H7ZH2khNvj7TTUd8BgD4rqmu1ZKX8qNeY%2BfHz4zlXDgT5E8tpCTUq7XSBC4Euv8227TV9fX1E73%2BYtvo27BmbS9cvFVTY3bSRFza9yOcf6Gfmygy7d%2B%2Fm%2FPnzF4DvrsBLhnJlJfwIKXxv1PheAE4qK6p4H9AGbNKTuhngBPBPXYRe4IemaT5kWZbR19fHNbmGnZ1k4r3U4glDR30Hm5qjbGjsImJEOHbsGHv27JFz5869o0eFq01Jq%2BmHAXwI6FFKagMTgHM7GzFDS%2BoeLSMv7zjzC9x4Y7gxFovVDAwMEI1GaWlpWSzRVCrFwYMH%2FXfxZ4AfAa8B%2F7lDaGg1%2FQgp43lfK0yqtRMuJa3ceKe5DfgYsCYAZ2ngD8CfAkzqTpW7xY%2F%2FSznyX%2FVeUb2kVmX4AAAAAElFTkSuQmCC";
+function initIMG() {
+ var img = document.getElementsByTagName('img');
+ for (i = 0; i < img.length; ++i)
+ img[i].setAttribute('src', twoEyes);
+}
+function setTextOverflow(str,quoted) {
+ var x = document.styleSheets[0];
+ var q = quoted ? '"' : '';
+ x.insertRule('.ellipsis{text-overflow:' + q + str + q +'}', x.cssRules.length);
+}
+</script>
+</head><body onload="initIMG()">
+text-overflow:"<input placeholder="type text then <ENTER>" onchange='setTextOverflow(this.value,1)'>" | <button onclick="setTextOverflow('ellipsis')">ellipsis</button> | <button onclick="setTextOverflow('clip')">clip</button> (Try "." or "" for example) <br>
+
+LTR / LTR
+<div class="test ltr">
+<span class="ellipsis b inline-block">CSS is awesome</span>
+<button class="ellipsis">CSS is awesome</button>
+<input type=button class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" placeholder="CSS is awesome">
+<fieldset style="display:inline" class="ellipsis"><span style="position:relative;left:1em;">CSS is awesome</span></fieldset>
+<fieldset style="display:block" class="ellipsis"><span style="position:relative;left:1em;">CSS is awesome</span></fieldset>
+<legend class="ellipsis">CSS is awesome</legend>
+<textarea class="ellipsis" style="overflow:scroll;width:14em;" wrap="off">
+CSS is awesome CSS is awesome CSS is awesome
+CSS is awesome CSS is awesome CSS is awesome
+</textarea>
+<fieldset style="display:inline"><legend class="ellipsis">CSS is awesome</legend>CSS is awesome</fieldset>
+<fieldset style="display:block" class="ellipsis"><legend class="ellipsis">CSS is awesome</legend><span style="position:relative;left:1em;">CSS is awesome</span></fieldset>
+<select class="ellipsis"><option>CSS is awesome<option>CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select size="4"><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<ul style="float:left"><li class="ellipsis b">CSS is awesome</ul>
+<br><br></div>
+
+RTL / LTR
+<div class="test rtl">
+<span class="ellipsis b inline-block">CSS is awesome</span>
+<button class="ellipsis">CSS is awesome</button>
+<input type=button class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" placeholder="CSS is awesome">
+<fieldset style="display:inline" class="ellipsis"><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<fieldset style="display:block" class="ellipsis"><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<legend class="ellipsis">CSS is awesome</legend>
+<textarea class="ellipsis" style="overflow:scroll;width:14em;" wrap="off">
+CSS is awesome CSS is awesome CSS is awesome
+CSS is awesome CSS is awesome CSS is awesome
+</textarea>
+<fieldset style="display:inline"><legend class="ellipsis">CSS is awesome</legend>CSS is awesome</fieldset>
+<fieldset style="display:block" class="ellipsis"><legend class="ellipsis">CSS is awesome</legend><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<select class="ellipsis"><option>CSS is awesome<option>CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select size="4"><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<ul style="float:left"><li class="ellipsis b">CSS is awesome</ul>
+<br><br></div>
+
+LTR / RTL
+<div class="test ltr rlo">
+<span class="ellipsis b inline-block">CSS is awesome</span>
+<button class="ellipsis">CSS is awesome</button>
+<input type=button class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" placeholder="CSS is awesome">
+<fieldset style="display:inline" class="ellipsis"><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<fieldset style="display:block" class="ellipsis"><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<legend class="ellipsis">CSS is awesome</legend>
+<textarea class="ellipsis" style="overflow:scroll;width:14em;" wrap="off">
+CSS is awesome CSS is awesome CSS is awesome
+CSS is awesome CSS is awesome CSS is awesome
+</textarea>
+<fieldset style="display:inline"><legend class="ellipsis">CSS is awesome</legend>CSS is awesome</fieldset>
+<fieldset style="display:block" class="ellipsis"><legend class="ellipsis">CSS is awesome</legend><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<select class="ellipsis"><option>CSS is awesome<option>CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select size="4"><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<ul style="float:left"><li class="ellipsis b">CSS is awesome</ul>
+<br><br></div>
+
+RTL / RTL
+<div class="test rtl rlo">
+<span class="ellipsis b inline-block">CSS is awesome</span>
+<button class="ellipsis">CSS is awesome</button>
+<input type=button class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" value="CSS is awesome">
+<input class="ellipsis" placeholder="CSS is awesome">
+<fieldset style="display:inline" class="ellipsis"><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<fieldset style="display:block" class="ellipsis"><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<legend class="ellipsis">CSS is awesome</legend>
+<textarea class="ellipsis" style="overflow:scroll;width:14em;" wrap="off">
+CSS is awesome CSS is awesome CSS is awesome
+CSS is awesome CSS is awesome CSS is awesome
+</textarea>
+<fieldset style="display:inline"><legend class="ellipsis">CSS is awesome</legend>CSS is awesome</fieldset>
+<fieldset style="display:block" class="ellipsis"><legend class="ellipsis">CSS is awesome</legend><span style="position:relative;right:1em;">CSS is awesome</span></fieldset>
+<select class="ellipsis"><option>CSS is awesome<option>CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<select size="4"><option>CSS is awesome<option class="ellipsis">CSS is awesome<option>CSS is awesome<option>CSS is awesome</select>
+<ul style="float:left"><li class="ellipsis b">CSS is awesome</ul>
+<br><br></div>
+
+
+
+</body></html>
diff --git a/layout/generic/crashtests/text-overflow-iframe.html b/layout/generic/crashtests/text-overflow-iframe.html
new file mode 100644
index 000000000..ba34dc2aa
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-iframe.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>text-overflow: Test 12</title>
+<style type="text/css">
+
+.test {
+ border: thin dashed black;
+ overflow: hidden;
+ white-space: nowrap;
+ -o-text-overflow: ellipsis;
+ text-overflow: ellipsis;
+ font: 1em bold monospace;
+ background:lime;
+ color: black;
+ margin-left:400px;
+ height: 12em;
+ text-shadow: #6374AB 5px -12px 2px;
+}
+
+body {
+ width:800px;
+}
+
+img { width: 50px; height: 50px; outline:5px dotted yellow; }
+span {
+ font-size:16px;
+ background:pink;
+ border: 5px dashed blue;
+ padding: 0 25px;
+ text-decoration: underline overline line-through;
+ color:brown;
+ text-shadow: none;
+}
+i {
+ display:inline-block;
+ height: 50px;
+ width: 5em;
+ background: blue;
+ outline:5px dotted yellow;
+ text-shadow: none;
+}
+u {
+ padding-left:140px;
+}
+v {
+ padding-right:140px;
+}
+.rtl {
+ direction:rtl;
+}
+.rlo span {
+ unicode-bidi: bidi-override; direction: rtl;
+}
+.lro span {
+ unicode-bidi: bidi-override; direction: ltr;
+}
+.h {display:none}
+iframe {
+ width: 100px;
+ height: 50px;
+}
+</style>
+<script>
+var c = "data:text/html,<style>body {white-space: nowrap;overflow:hidden;-o-text-overflow: ellipsis;text-overflow: ellipsis;}</style><body bgcolor='magenta'>CSS is awesome"
+function initIFRAME() {
+ var f = document.getElementsByTagName('iframe');
+ for (i = 0; i < f.length; ++i) {
+ f[i].setAttribute('src', c);
+ }
+ setTimeout(function(){document.body.style.width='500px'},0);
+}
+function setTextOverflow(str,quoted) {
+ var x = document.styleSheets[0];
+ var q = quoted ? '"' : '';
+ x.insertRule('.test{text-overflow:' + q + str + q +'}', x.cssRules.length);
+}
+</script>
+</head><body onload="initIFRAME()">
+text-overflow:"<input placeholder="type text then <ENTER>" onchange='setTextOverflow(this.value,1)'>" | <button onclick="setTextOverflow('ellipsis')">ellipsis</button> | <button onclick="setTextOverflow('clip')">clip</button> (Try "." or "" for example) <br>
+
+LTR / LTR
+<div class="test">
+<span><iframe></iframe>CSS is awesome CSS<i>overflowing-inline-block</i><u> is awesome</u></span><br>
+<span>CSS is awe<iframe></iframe>some CSS is awesome <i></i></span><br>
+<span>C SS is awesome<button>BUTTON</button> CSS is <iframe></iframe>awesom e </span><br>
+<span>C&shy;SS is awesome CSS is awesom&shy;e <button>BUTTON</button></span><br>
+<br><br></div>
+
+RTL / LTR
+<div class="test rtl">
+<span><iframe></iframe><v>CSS is awesome CSS</v><i>overflowing-inline-block</i> is awesome </span><br>
+<span>CSS is awe<iframe></iframe>some CSS is awesome <i></i></span><br>
+<span>C SS is awesome<button>BUTTON</button> CSS is <iframe></iframe>awesom e </span><br>
+<span>C&shy;SS is awesome CSS is awesom&shy;e <button>BUTTON</button></span><br>
+<br><br></div>
+
+
+LTR / RTL
+<div class="test rlo">
+<span><iframe></iframe>CSS is awesome CSS<i>overflowing-inline-block</i> is awesome </span><br>
+<span>CSS is awe<iframe></iframe>some CSS is awesome <i></i></span><br>
+<span>C SS is awesome<button>BUTTON</button> CSS is <iframe></iframe>awesom e </span><br>
+<span><button>BUTTON</button>C&shy;SS is awesome CSS is awesom&shy;e </span><br>
+<br><br></div>
+
+RTL / RTL
+<div class="test rtl rlo">
+<span><iframe></iframe>CSS is awesome CSS<i>overflowing-inline-block</i> is awesome </span><br>
+<span>CSS is awe<iframe></iframe>some CSS is awesome <i></i></span><br>
+<span>C SS is awesome<button>BUTTON</button> CSS is <iframe></iframe>awesom e </span><br>
+<span><button>BUTTON</button>C&shy;SS is awesome CSS is awesom&shy;e </span><br>
+<br><br></div>
+
+</body></html>
diff --git a/layout/generic/folder.png b/layout/generic/folder.png
new file mode 100644
index 000000000..6fa6c1590
--- /dev/null
+++ b/layout/generic/folder.png
Binary files differ
diff --git a/layout/generic/frame-graph.py b/layout/generic/frame-graph.py
new file mode 100644
index 000000000..ac1076b83
--- /dev/null
+++ b/layout/generic/frame-graph.py
@@ -0,0 +1,41 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Take the *.framedata files from graph-frameclasses.js and combine them
+into a single graphviz file.
+
+stdin: a list of .framedata file names (e.g. from xargs)
+stdout: a graphviz file
+
+e.g. `find <objdir> -name "*.framedata" | python aggregate-frameclasses.py |
+ dot -Tpng -o frameclasses-graph.png -`
+"""
+
+import sys
+
+classdict = {}
+
+for line in sys.stdin:
+ file = line.strip()
+ fd = open(file)
+
+ output = None
+ for line in fd:
+ if line.startswith('CLASS-DEF: '):
+ cname = line[11:-1]
+ if cname not in classdict:
+ output = classdict[cname] = []
+ else:
+ output = None
+ elif output is not None:
+ output.append(line)
+
+sys.stdout.write('digraph g {\n')
+
+for olist in classdict.itervalues():
+ for line in olist:
+ sys.stdout.write(line)
+
+sys.stdout.write('}\n')
diff --git a/layout/generic/jar.mn b/layout/generic/jar.mn
new file mode 100644
index 000000000..d01a968b8
--- /dev/null
+++ b/layout/generic/jar.mn
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+toolkit.jar:
+ res/broken-image.png (broken-image.png)
+ res/loading-image.png (loading-image.png)
diff --git a/layout/generic/loading-image.png b/layout/generic/loading-image.png
new file mode 100644
index 000000000..5641cf4f5
--- /dev/null
+++ b/layout/generic/loading-image.png
Binary files differ
diff --git a/layout/generic/moz.build b/layout/generic/moz.build
new file mode 100644
index 000000000..0fe98afec
--- /dev/null
+++ b/layout/generic/moz.build
@@ -0,0 +1,227 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('nsBlock*'):
+ # Parts of these files are really Layout: Floats
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('Block*'):
+ # Parts of these files are really Layout: Floats
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('nsLine*'):
+ # Parts of these files are really Layout: Floats
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('nsInlineFrame.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('nsBRFrame.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('nsBulletFrame.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('nsFirstLetterFrame.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Block and Inline')
+
+with Files('MathML*'):
+ BUG_COMPONENT = ('Core', 'MathML')
+
+with Files('Text*'):
+ BUG_COMPONENT = ('Core', 'Layout: Text')
+
+with Files('nsText*'):
+ BUG_COMPONENT = ('Core', 'Layout: Text')
+
+with Files('nsFrameSetFrame*'):
+ BUG_COMPONENT = ('Core', 'Layout: HTML Frames')
+
+with Files('nsSubDocumentFrame*'):
+ BUG_COMPONENT = ('Core', 'Layout: HTML Frames')
+
+with Files('nsFloatManager.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Floats')
+
+with Files('nsIntervalSet.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Floats')
+
+with Files('nsHTMLCanvasFrame.*'):
+ BUG_COMPONENT = ('Core', 'Layout: Images')
+
+with Files('nsImage*'):
+ BUG_COMPONENT = ('Core', 'Layout: Images')
+
+with Files('nsAbsoluteContainingBlock.*'):
+ BUG_COMPONENT = ('Core', 'Layout: R & A Pos')
+
+with Files('Sticky*'):
+ BUG_COMPONENT = ('Core', 'Layout: R & A Pos')
+
+with Files('nsPluginFrame.*'):
+ BUG_COMPONENT = ('Core', 'Plug-ins')
+
+with Files('nsVideoFrame.*'):
+ BUG_COMPONENT = ('Core', 'Video/Audio')
+
+with Files('*Selection*'):
+ BUG_COMPONENT = ('Core', 'Selection')
+
+EXPORTS += [
+ 'AsyncScrollBase.h',
+ 'nsCanvasFrame.h',
+ 'nsContainerFrame.h',
+ 'nsDirection.h',
+ 'nsFrame.h',
+ 'nsFrameIdList.h',
+ 'nsFrameList.h',
+ 'nsFrameSelection.h',
+ 'nsFrameState.h',
+ 'nsFrameStateBits.h',
+ 'nsHTMLParts.h',
+ 'nsIAnonymousContentCreator.h',
+ 'nsIFrame.h',
+ 'nsIFrameInlines.h',
+ 'nsIFrameUtil.h',
+ 'nsILineIterator.h',
+ 'nsIObjectFrame.h',
+ 'nsIPageSequenceFrame.h',
+ 'nsIScrollableFrame.h',
+ 'nsIScrollPositionListener.h',
+ 'nsIStatefulFrame.h',
+ 'nsPluginFrame.h',
+ 'nsQueryFrame.h',
+ 'nsRubyBaseContainerFrame.h',
+ 'nsRubyBaseFrame.h',
+ 'nsRubyFrame.h',
+ 'nsRubyTextContainerFrame.h',
+ 'nsRubyTextFrame.h',
+ 'nsSplittableFrame.h',
+ 'nsSubDocumentFrame.h',
+ 'nsTextRunTransformations.h',
+ 'RubyUtils.h',
+ 'ScrollbarActivity.h',
+ 'ScrollSnap.h',
+ 'Visibility.h',
+]
+
+EXPORTS.mozilla += [
+ 'CSSAlignUtils.h',
+ 'ReflowInput.h',
+ 'ReflowOutput.h',
+ 'WritingModes.h',
+]
+
+EXPORTS.mozilla.dom += [
+ 'Selection.h',
+]
+
+EXPORTS.mozilla.layout += [
+ 'FrameChildList.h',
+]
+
+UNIFIED_SOURCES += [
+ 'AsyncScrollBase.cpp',
+ 'BlockReflowInput.cpp',
+ 'CSSAlignUtils.cpp',
+ 'DetailsFrame.cpp',
+ 'FrameChildList.cpp',
+ 'MathMLTextRunFactory.cpp',
+ 'nsAbsoluteContainingBlock.cpp',
+ 'nsBackdropFrame.cpp',
+ 'nsBlockFrame.cpp',
+ 'nsBlockReflowContext.cpp',
+ 'nsBRFrame.cpp',
+ 'nsBulletFrame.cpp',
+ 'nsCanvasFrame.cpp',
+ 'nsColumnSetFrame.cpp',
+ 'nsContainerFrame.cpp',
+ 'nsFirstLetterFrame.cpp',
+ 'nsFlexContainerFrame.cpp',
+ 'nsFloatManager.cpp',
+ 'nsFontInflationData.cpp',
+ 'nsFrame.cpp',
+ 'nsFrameList.cpp',
+ 'nsFrameSetFrame.cpp',
+ 'nsFrameState.cpp',
+ 'nsFrameUtil.cpp',
+ 'nsGfxScrollFrame.cpp',
+ 'nsGridContainerFrame.cpp',
+ 'nsHTMLCanvasFrame.cpp',
+ 'nsImageFrame.cpp',
+ 'nsImageMap.cpp',
+ 'nsInlineFrame.cpp',
+ 'nsIntervalSet.cpp',
+ 'nsLeafFrame.cpp',
+ 'nsLineBox.cpp',
+ 'nsPageContentFrame.cpp',
+ 'nsPageFrame.cpp',
+ 'nsPlaceholderFrame.cpp',
+ 'nsRubyBaseContainerFrame.cpp',
+ 'nsRubyBaseFrame.cpp',
+ 'nsRubyContentFrame.cpp',
+ 'nsRubyFrame.cpp',
+ 'nsRubyTextContainerFrame.cpp',
+ 'nsRubyTextFrame.cpp',
+ 'nsSelection.cpp',
+ 'nsSimplePageSequenceFrame.cpp',
+ 'nsSplittableFrame.cpp',
+ 'nsSubDocumentFrame.cpp',
+ 'nsTextFrame.cpp',
+ 'nsTextFrameUtils.cpp',
+ 'nsTextRunTransformations.cpp',
+ 'nsVideoFrame.cpp',
+ 'nsViewportFrame.cpp',
+ 'ReflowInput.cpp',
+ 'ReflowOutput.cpp',
+ 'RubyUtils.cpp',
+ 'ScrollbarActivity.cpp',
+ 'ScrollSnap.cpp',
+ 'ScrollVelocityQueue.cpp',
+ 'StickyScrollContainer.cpp',
+ 'TextOverflow.cpp',
+]
+
+# nsLineLayout.cpp needs to be built separately because it uses plarena.h.
+# nsPluginFrame.cpp needs to be built separately because of name clashes in the OS X headers.
+SOURCES += [
+ 'nsLineLayout.cpp',
+ 'nsPluginFrame.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../../dom/plugins/base',
+ '../base',
+ '../forms',
+ '../style',
+ '../svg',
+ '../tables',
+ '../xul',
+ '/dom/base',
+ '/dom/html',
+ '/dom/xul',
+]
+
+JAR_MANIFESTS += ['jar.mn']
+
+RESOURCE_FILES.html = [
+ 'folder.png',
+]
+
+MOCHITEST_MANIFESTS += ['test/mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['TK_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp
new file mode 100644
index 000000000..7c48aae92
--- /dev/null
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -0,0 +1,763 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * code for managing absolutely positioned children of a rendering
+ * object that is a containing block for them
+ */
+
+#include "nsAbsoluteContainingBlock.h"
+
+#include "nsContainerFrame.h"
+#include "nsGkAtoms.h"
+#include "nsIPresShell.h"
+#include "mozilla/CSSAlignUtils.h"
+#include "mozilla/ReflowInput.h"
+#include "nsPresContext.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsGridContainerFrame.h"
+
+#include "mozilla/Sprintf.h"
+
+#ifdef DEBUG
+#include "nsBlockFrame.h"
+
+static void PrettyUC(nscoord aSize, char* aBuf, int aBufSize)
+{
+ if (NS_UNCONSTRAINEDSIZE == aSize) {
+ strcpy(aBuf, "UC");
+ } else {
+ if((int32_t)0xdeadbeef == aSize) {
+ strcpy(aBuf, "deadbeef");
+ } else {
+ snprintf(aBuf, aBufSize, "%d", aSize);
+ }
+ }
+}
+#endif
+
+using namespace mozilla;
+
+typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
+
+void
+nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ NS_PRECONDITION(mChildListID == aListID, "unexpected child list name");
+#ifdef DEBUG
+ nsFrame::VerifyDirtyBitSet(aChildList);
+#endif
+ mAbsoluteFrames.SetFrames(aChildList);
+}
+
+void
+nsAbsoluteContainingBlock::AppendFrames(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(mChildListID == aListID, "unexpected child list");
+
+ // Append the frames to our list of absolutely positioned frames
+#ifdef DEBUG
+ nsFrame::VerifyDirtyBitSet(aFrameList);
+#endif
+ mAbsoluteFrames.AppendFrames(nullptr, aFrameList);
+
+ // no damage to intrinsic widths, since absolutely positioned frames can't
+ // change them
+ aDelegatingFrame->PresContext()->PresShell()->
+ FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+}
+
+void
+nsAbsoluteContainingBlock::InsertFrames(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(mChildListID == aListID, "unexpected child list");
+ NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == aDelegatingFrame,
+ "inserting after sibling frame with different parent");
+
+#ifdef DEBUG
+ nsFrame::VerifyDirtyBitSet(aFrameList);
+#endif
+ mAbsoluteFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
+
+ // no damage to intrinsic widths, since absolutely positioned frames can't
+ // change them
+ aDelegatingFrame->PresContext()->PresShell()->
+ FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+}
+
+void
+nsAbsoluteContainingBlock::RemoveFrame(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ NS_ASSERTION(mChildListID == aListID, "unexpected child list");
+ nsIFrame* nif = aOldFrame->GetNextInFlow();
+ if (nif) {
+ nif->GetParent()->DeleteNextInFlowChild(nif, false);
+ }
+
+ mAbsoluteFrames.DestroyFrame(aOldFrame);
+}
+
+void
+nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus,
+ const nsRect& aContainingBlock,
+ AbsPosReflowFlags aFlags,
+ nsOverflowAreas* aOverflowAreas)
+{
+ nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
+
+ const bool reflowAll = aReflowInput.ShouldReflowAllKids();
+ const bool isGrid = !!(aFlags & AbsPosReflowFlags::eIsGridContainerCB);
+ nsIFrame* kidFrame;
+ nsOverflowContinuationTracker tracker(aDelegatingFrame, true);
+ for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
+ bool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) ||
+ FrameDependsOnContainer(kidFrame,
+ !!(aFlags & AbsPosReflowFlags::eCBWidthChanged),
+ !!(aFlags & AbsPosReflowFlags::eCBHeightChanged));
+ if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
+ // Reflow the frame
+ nsReflowStatus kidStatus = NS_FRAME_COMPLETE;
+ const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame)
+ : aContainingBlock;
+ ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowInput, cb,
+ aFlags, kidFrame, kidStatus, aOverflowAreas);
+ nsIFrame* nextFrame = kidFrame->GetNextInFlow();
+ if (!NS_FRAME_IS_FULLY_COMPLETE(kidStatus) &&
+ aDelegatingFrame->IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
+ // Need a continuation
+ if (!nextFrame) {
+ nextFrame =
+ aPresContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(aPresContext, kidFrame, aDelegatingFrame);
+ }
+ // Add it as an overflow container.
+ //XXXfr This is a hack to fix some of our printing dataloss.
+ // See bug 154892. Not sure how to do it "right" yet; probably want
+ // to keep continuations within an nsAbsoluteContainingBlock eventually.
+ tracker.Insert(nextFrame, kidStatus);
+ NS_MergeReflowStatusInto(&reflowStatus, kidStatus);
+ }
+ else {
+ // Delete any continuations
+ if (nextFrame) {
+ nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame);
+ nextFrame->GetParent()->DeleteNextInFlowChild(nextFrame, true);
+ }
+ }
+ }
+ else {
+ tracker.Skip(kidFrame, reflowStatus);
+ if (aOverflowAreas) {
+ aDelegatingFrame->ConsiderChildOverflow(*aOverflowAreas, kidFrame);
+ }
+ }
+
+ // Make a CheckForInterrupt call, here, not just HasPendingInterrupt. That
+ // will make sure that we end up reflowing aDelegatingFrame in cases when
+ // one of our kids interrupted. Otherwise we'd set the dirty or
+ // dirty-children bit on the kid in the condition below, and then when
+ // reflow completes and we go to mark dirty bits on all ancestors of that
+ // kid we'll immediately bail out, because the kid already has a dirty bit.
+ // In particular, we won't set any dirty bits on aDelegatingFrame, so when
+ // the following reflow happens we won't reflow the kid in question. This
+ // might be slightly suboptimal in cases where |kidFrame| itself did not
+ // interrupt, since we'll trigger a reflow of it too when it's not strictly
+ // needed. But the logic to not do that is enough more complicated, and
+ // the case enough of an edge case, that this is probably better.
+ if (kidNeedsReflow && aPresContext->CheckForInterrupt(aDelegatingFrame)) {
+ if (aDelegatingFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
+ kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+ } else {
+ kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ }
+ }
+
+ // Abspos frames can't cause their parent to be incomplete,
+ // only overflow incomplete.
+ if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(reflowStatus);
+
+ NS_MergeReflowStatusInto(&aReflowStatus, reflowStatus);
+}
+
+static inline bool IsFixedPaddingSize(const nsStyleCoord& aCoord)
+ { return aCoord.ConvertsToLength(); }
+static inline bool IsFixedMarginSize(const nsStyleCoord& aCoord)
+ { return aCoord.ConvertsToLength(); }
+static inline bool IsFixedOffset(const nsStyleCoord& aCoord)
+ { return aCoord.ConvertsToLength(); }
+
+bool
+nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f,
+ bool aCBWidthChanged,
+ bool aCBHeightChanged)
+{
+ const nsStylePosition* pos = f->StylePosition();
+ // See if f's position might have changed because it depends on a
+ // placeholder's position
+ // This can happen in the following cases:
+ // 1) Vertical positioning. "top" must be auto and "bottom" must be auto
+ // (otherwise the vertical position is completely determined by
+ // whichever of them is not auto and the height).
+ // 2) Horizontal positioning. "left" must be auto and "right" must be auto
+ // (otherwise the horizontal position is completely determined by
+ // whichever of them is not auto and the width).
+ // See ReflowInput::InitAbsoluteConstraints -- these are the
+ // only cases when we call CalculateHypotheticalBox().
+ if ((pos->mOffset.GetTopUnit() == eStyleUnit_Auto &&
+ pos->mOffset.GetBottomUnit() == eStyleUnit_Auto) ||
+ (pos->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
+ pos->mOffset.GetRightUnit() == eStyleUnit_Auto)) {
+ return true;
+ }
+ if (!aCBWidthChanged && !aCBHeightChanged) {
+ // skip getting style data
+ return false;
+ }
+ const nsStylePadding* padding = f->StylePadding();
+ const nsStyleMargin* margin = f->StyleMargin();
+ WritingMode wm = f->GetWritingMode();
+ if (wm.IsVertical() ? aCBHeightChanged : aCBWidthChanged) {
+ // See if f's inline-size might have changed.
+ // If margin-inline-start/end, padding-inline-start/end,
+ // inline-size, min/max-inline-size are all lengths, 'none', or enumerated,
+ // then our frame isize does not depend on the parent isize.
+ // Note that borders never depend on the parent isize.
+ // XXX All of the enumerated values except -moz-available are ok too.
+ if (pos->ISizeDependsOnContainer(wm) ||
+ pos->MinISizeDependsOnContainer(wm) ||
+ pos->MaxISizeDependsOnContainer(wm) ||
+ !IsFixedPaddingSize(padding->mPadding.GetIStart(wm)) ||
+ !IsFixedPaddingSize(padding->mPadding.GetIEnd(wm))) {
+ return true;
+ }
+
+ // See if f's position might have changed. If we're RTL then the
+ // rules are slightly different. We'll assume percentage or auto
+ // margins will always induce a dependency on the size
+ if (!IsFixedMarginSize(margin->mMargin.GetIStart(wm)) ||
+ !IsFixedMarginSize(margin->mMargin.GetIEnd(wm))) {
+ return true;
+ }
+ if (!wm.IsBidiLTR()) {
+ // Note that even if 'istart' is a length, our position can
+ // still depend on the containing block isze, because if
+ // 'iend' is also a length we will discard 'istart' and be
+ // positioned relative to the containing block iend edge.
+ // 'istart' length and 'iend' auto is the only combination
+ // we can be sure of.
+ if (!IsFixedOffset(pos->mOffset.GetIStart(wm)) ||
+ pos->mOffset.GetIEndUnit(wm) != eStyleUnit_Auto) {
+ return true;
+ }
+ } else {
+ if (!IsFixedOffset(pos->mOffset.GetIStart(wm))) {
+ return true;
+ }
+ }
+ }
+ if (wm.IsVertical() ? aCBWidthChanged : aCBHeightChanged) {
+ // See if f's block-size might have changed.
+ // If margin-block-start/end, padding-block-start/end,
+ // min-block-size, and max-block-size are all lengths or 'none',
+ // and bsize is a length or bsize and bend are auto and bstart is not auto,
+ // then our frame bsize does not depend on the parent bsize.
+ // Note that borders never depend on the parent bsize.
+ if ((pos->BSizeDependsOnContainer(wm) &&
+ !(pos->BSize(wm).GetUnit() == eStyleUnit_Auto &&
+ pos->mOffset.GetBEndUnit(wm) == eStyleUnit_Auto &&
+ pos->mOffset.GetBStartUnit(wm) != eStyleUnit_Auto)) ||
+ pos->MinBSizeDependsOnContainer(wm) ||
+ pos->MaxBSizeDependsOnContainer(wm) ||
+ !IsFixedPaddingSize(padding->mPadding.GetBStart(wm)) ||
+ !IsFixedPaddingSize(padding->mPadding.GetBEnd(wm))) {
+ return true;
+ }
+
+ // See if f's position might have changed.
+ if (!IsFixedMarginSize(margin->mMargin.GetBStart(wm)) ||
+ !IsFixedMarginSize(margin->mMargin.GetBEnd(wm))) {
+ return true;
+ }
+ if (!IsFixedOffset(pos->mOffset.GetBStart(wm))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+nsAbsoluteContainingBlock::DestroyFrames(nsIFrame* aDelegatingFrame,
+ nsIFrame* aDestructRoot)
+{
+ mAbsoluteFrames.DestroyFramesFrom(aDestructRoot);
+}
+
+void
+nsAbsoluteContainingBlock::MarkSizeDependentFramesDirty()
+{
+ DoMarkFramesDirty(false);
+}
+
+void
+nsAbsoluteContainingBlock::MarkAllFramesDirty()
+{
+ DoMarkFramesDirty(true);
+}
+
+void
+nsAbsoluteContainingBlock::DoMarkFramesDirty(bool aMarkAllDirty)
+{
+ for (nsIFrame* kidFrame : mAbsoluteFrames) {
+ if (aMarkAllDirty) {
+ kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+ } else if (FrameDependsOnContainer(kidFrame, true, true)) {
+ // Add the weakest flags that will make sure we reflow this frame later
+ kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ }
+}
+
+// Given an out-of-flow frame, this method returns the parent frame of
+// its placeholder frame, if that parent is a nsContainerFrame.
+static nsContainerFrame*
+GetPlaceholderContainer(nsPresContext* aPresContext,
+ nsIFrame* aPositionedFrame)
+{
+ MOZ_ASSERT(aPositionedFrame, "need non-null frame");
+ MOZ_ASSERT(aPositionedFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
+ "expecting abspos frame");
+ MOZ_ASSERT(aPresContext && aPresContext == aPositionedFrame->PresContext(),
+ "need non-null pres context which matches our frame");
+
+ nsIFrame* placeholder =
+ aPresContext->PresShell()->GetPlaceholderFrameFor(aPositionedFrame);
+
+ if (!placeholder) {
+ return nullptr;
+ }
+ return do_QueryFrame(placeholder->GetParent());
+}
+
+/**
+ * This function returns the offset of an abs/fixed-pos child's static
+ * position, with respect to the "start" corner of its alignment container,
+ * according to CSS Box Alignment. This function only operates in a single
+ * axis at a time -- callers can choose which axis via the |aAbsPosCBAxis|
+ * parameter.
+ *
+ * @param aKidReflowInput The ReflowInput for the to-be-aligned abspos child.
+ * @param aKidSizeInAbsPosCBWM The child frame's size (after it's been given
+ * the opportunity to reflow), in terms of
+ * aAbsPosCBWM.
+ * @param aAbsPosCBSize The abspos CB size, in terms of aAbsPosCBWM.
+ * @param aPlaceholderContainer The parent of the child frame's corresponding
+ * placeholder frame, cast to a nsContainerFrame.
+ * (This will help us choose which alignment enum
+ * we should use for the child.)
+ * @param aAbsPosCBWM The child frame's containing block's WritingMode.
+ * @param aAbsPosCBAxis The axis (of the containing block) that we should
+ * be doing this computation for.
+ */
+static nscoord
+OffsetToAlignedStaticPos(const ReflowInput& aKidReflowInput,
+ const LogicalSize& aKidSizeInAbsPosCBWM,
+ const LogicalSize& aAbsPosCBSize,
+ nsContainerFrame* aPlaceholderContainer,
+ WritingMode aAbsPosCBWM,
+ LogicalAxis aAbsPosCBAxis)
+{
+ if (!aPlaceholderContainer) {
+ // (The placeholder container should be the thing that kicks this whole
+ // process off, by setting PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN. So it
+ // should exist... but bail gracefully if it doesn't.)
+ NS_ERROR("Missing placeholder-container when computing a "
+ "CSS Box Alignment static position");
+ return 0;
+ }
+
+ // (Most of this function is simply preparing args that we'll pass to
+ // AlignJustifySelf at the end.)
+
+ // NOTE: Our alignment container is aPlaceholderContainer's content-box
+ // (or an area within it, if aPlaceholderContainer is a grid). So, we'll
+ // perform most of our arithmetic/alignment in aPlaceholderContainer's
+ // WritingMode. For brevity, we use the abbreviation "pc" for "placeholder
+ // container" in variables below.
+ WritingMode pcWM = aPlaceholderContainer->GetWritingMode();
+
+ // Find what axis aAbsPosCBAxis corresponds to, in placeholder's parent's
+ // writing-mode.
+ LogicalAxis pcAxis = (pcWM.IsOrthogonalTo(aAbsPosCBWM)
+ ? GetOrthogonalAxis(aAbsPosCBAxis)
+ : aAbsPosCBAxis);
+
+ nsIAtom* parentType = aPlaceholderContainer->GetType();
+ LogicalSize alignAreaSize(pcWM);
+ if (parentType == nsGkAtoms::flexContainerFrame) {
+ // The alignment container is the flex container's content box:
+ alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM);
+ LogicalMargin pcBorderPadding =
+ aPlaceholderContainer->GetLogicalUsedBorderAndPadding(pcWM);
+ alignAreaSize -= pcBorderPadding.Size(pcWM);
+ } else if (parentType == nsGkAtoms::gridContainerFrame) {
+ // This abspos elem's parent is a grid container. Per CSS Grid 10.1 & 10.2:
+ // - If the grid container *also* generates the abspos containing block (a
+ // grid area) for this abspos child, we use that abspos containing block as
+ // the alignment container, too. (And its size is aAbsPosCBSize.)
+ // - Otherwise, we use the grid's padding box as the alignment container.
+ // https://drafts.csswg.org/css-grid/#static-position
+ if (aPlaceholderContainer == aKidReflowInput.mCBReflowInput->mFrame) {
+ // The alignment container is the grid area that we're using as the
+ // absolute containing block.
+ alignAreaSize = aAbsPosCBSize.ConvertTo(pcWM, aAbsPosCBWM);
+ } else {
+ // The alignment container is a the grid container's padding box (which
+ // we can get by subtracting away its border from frame's size):
+ alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM);
+ LogicalMargin pcBorder =
+ aPlaceholderContainer->GetLogicalUsedBorder(pcWM);
+ alignAreaSize -= pcBorder.Size(pcWM);
+ }
+ } else {
+ NS_ERROR("Unsupported container for abpsos CSS Box Alignment");
+ return 0; // (leave the child at the start of its alignment container)
+ }
+
+ nscoord alignAreaSizeInAxis = (pcAxis == eLogicalAxisInline)
+ ? alignAreaSize.ISize(pcWM)
+ : alignAreaSize.BSize(pcWM);
+
+ AlignJustifyFlags flags = AlignJustifyFlags::eIgnoreAutoMargins;
+ uint16_t alignConst =
+ aPlaceholderContainer->CSSAlignmentForAbsPosChild(aKidReflowInput, pcAxis);
+ // XXXdholbert: Handle <overflow-position> in bug 1311892 (by conditionally
+ // setting AlignJustifyFlags::eOverflowSafe in |flags|.) For now, we behave
+ // as if "unsafe" was the specified value (which is basically equivalent to
+ // the default behavior, when no value is specified -- though the default
+ // behavior also has some [at-risk] extra nuance about scroll containers...)
+ // For now we ignore & strip off <overflow-position> bits, until bug 1311892.
+ alignConst &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
+ // Find out if placeholder-container & the OOF child have the same start-sides
+ // in the placeholder-container's pcAxis.
+ WritingMode kidWM = aKidReflowInput.GetWritingMode();
+ if (pcWM.ParallelAxisStartsOnSameSide(pcAxis, kidWM)) {
+ flags |= AlignJustifyFlags::eSameSide;
+ }
+
+ // (baselineAdjust is unused. CSSAlignmentForAbsPosChild() should've
+ // converted 'baseline'/'last baseline' enums to their fallback values.)
+ const nscoord baselineAdjust = nscoord(0);
+
+ // AlignJustifySelf operates in the kid's writing mode, so we need to
+ // represent the child's size and the desired axis in that writing mode:
+ LogicalSize kidSizeInOwnWM = aKidSizeInAbsPosCBWM.ConvertTo(kidWM,
+ aAbsPosCBWM);
+ LogicalAxis kidAxis = (kidWM.IsOrthogonalTo(aAbsPosCBWM)
+ ? GetOrthogonalAxis(aAbsPosCBAxis)
+ : aAbsPosCBAxis);
+
+ nscoord offset =
+ CSSAlignUtils::AlignJustifySelf(alignConst, kidAxis, flags,
+ baselineAdjust, alignAreaSizeInAxis,
+ aKidReflowInput, kidSizeInOwnWM);
+
+ // "offset" is in terms of the CSS Box Alignment container (i.e. it's in
+ // terms of pcWM). But our return value needs to in terms of the containing
+ // block's writing mode, which might have the opposite directionality in the
+ // given axis. In that case, we just need to negate "offset" when returning,
+ // to make it have the right effect as an offset for coordinates in the
+ // containing block's writing mode.
+ if (!pcWM.ParallelAxisStartsOnSameSide(pcAxis, aAbsPosCBWM)) {
+ return -offset;
+ }
+ return offset;
+}
+
+void
+nsAbsoluteContainingBlock::ResolveSizeDependentOffsets(
+ nsPresContext* aPresContext,
+ ReflowInput& aKidReflowInput,
+ const LogicalSize& aKidSize,
+ const LogicalMargin& aMargin,
+ LogicalMargin* aOffsets,
+ LogicalSize* aLogicalCBSize)
+{
+ WritingMode wm = aKidReflowInput.GetWritingMode();
+ WritingMode outerWM = aKidReflowInput.mParentReflowInput->GetWritingMode();
+
+ // Now that we know the child's size, we resolve any sentinel values in its
+ // IStart/BStart offset coordinates that depend on that size.
+ // * NS_AUTOOFFSET indicates that the child's position in the given axis
+ // is determined by its end-wards offset property, combined with its size and
+ // available space. e.g.: "top: auto; height: auto; bottom: 50px"
+ // * m{I,B}OffsetsResolvedAfterSize indicate that the child is using its
+ // static position in that axis, *and* its static position is determined by
+ // the axis-appropriate css-align property (which may require the child's
+ // size, e.g. to center it within the parent).
+ if ((NS_AUTOOFFSET == aOffsets->IStart(outerWM)) ||
+ (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) ||
+ aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign ||
+ aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
+ if (-1 == aLogicalCBSize->ISize(wm)) {
+ // Get the containing block width/height
+ const ReflowInput* parentRI = aKidReflowInput.mParentReflowInput;
+ *aLogicalCBSize =
+ aKidReflowInput.ComputeContainingBlockRectangle(aPresContext,
+ parentRI);
+ }
+
+ const LogicalSize logicalCBSizeOuterWM = aLogicalCBSize->ConvertTo(outerWM,
+ wm);
+
+ // placeholderContainer is used in each of the m{I,B}OffsetsNeedCSSAlign
+ // clauses. We declare it at this scope so we can avoid having to look
+ // it up twice (and only look it up if it's needed).
+ nsContainerFrame* placeholderContainer = nullptr;
+
+ if (NS_AUTOOFFSET == aOffsets->IStart(outerWM)) {
+ NS_ASSERTION(NS_AUTOOFFSET != aOffsets->IEnd(outerWM),
+ "Can't solve for both start and end");
+ aOffsets->IStart(outerWM) =
+ logicalCBSizeOuterWM.ISize(outerWM) -
+ aOffsets->IEnd(outerWM) - aMargin.IStartEnd(outerWM) -
+ aKidSize.ISize(outerWM);
+ } else if (aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign) {
+ placeholderContainer = GetPlaceholderContainer(aPresContext,
+ aKidReflowInput.mFrame);
+ nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize,
+ logicalCBSizeOuterWM,
+ placeholderContainer,
+ outerWM, eLogicalAxisInline);
+ // Shift IStart from its current position (at start corner of the
+ // alignment container) by the returned offset. And set IEnd to the
+ // distance between the kid's end edge to containing block's end edge.
+ aOffsets->IStart(outerWM) += offset;
+ aOffsets->IEnd(outerWM) =
+ logicalCBSizeOuterWM.ISize(outerWM) -
+ (aOffsets->IStart(outerWM) + aKidSize.ISize(outerWM));
+ }
+
+ if (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) {
+ aOffsets->BStart(outerWM) =
+ logicalCBSizeOuterWM.BSize(outerWM) -
+ aOffsets->BEnd(outerWM) - aMargin.BStartEnd(outerWM) -
+ aKidSize.BSize(outerWM);
+ } else if (aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
+ if (!placeholderContainer) {
+ placeholderContainer = GetPlaceholderContainer(aPresContext,
+ aKidReflowInput.mFrame);
+ }
+ nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize,
+ logicalCBSizeOuterWM,
+ placeholderContainer,
+ outerWM, eLogicalAxisBlock);
+ // Shift BStart from its current position (at start corner of the
+ // alignment container) by the returned offset. And set BEnd to the
+ // distance between the kid's end edge to containing block's end edge.
+ aOffsets->BStart(outerWM) += offset;
+ aOffsets->BEnd(outerWM) =
+ logicalCBSizeOuterWM.BSize(outerWM) -
+ (aOffsets->BStart(outerWM) + aKidSize.BSize(outerWM));
+ }
+ aKidReflowInput.SetComputedLogicalOffsets(aOffsets->ConvertTo(wm, outerWM));
+ }
+}
+
+// XXX Optimize the case where it's a resize reflow and the absolutely
+// positioned child has the exact same size and position and skip the
+// reflow...
+
+// When bug 154892 is checked in, make sure that when
+// mChildListID == kFixedList, the height is unconstrained.
+// since we don't allow replicated frames to split.
+
+void
+nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ const nsRect& aContainingBlock,
+ AbsPosReflowFlags aFlags,
+ nsIFrame* aKidFrame,
+ nsReflowStatus& aStatus,
+ nsOverflowAreas* aOverflowAreas)
+{
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent);
+ printf("abs pos ");
+ nsAutoString name;
+ aKidFrame->GetFrameName(name);
+ printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
+
+ char width[16];
+ char height[16];
+ PrettyUC(aReflowInput.AvailableWidth(), width, 16);
+ PrettyUC(aReflowInput.AvailableHeight(), height, 16);
+ printf(" a=%s,%s ", width, height);
+ PrettyUC(aReflowInput.ComputedWidth(), width, 16);
+ PrettyUC(aReflowInput.ComputedHeight(), height, 16);
+ printf("c=%s,%s \n", width, height);
+ }
+ AutoNoisyIndenter indent(nsBlockFrame::gNoisy);
+#endif // DEBUG
+
+ WritingMode wm = aKidFrame->GetWritingMode();
+ LogicalSize logicalCBSize(wm, aContainingBlock.Size());
+ nscoord availISize = logicalCBSize.ISize(wm);
+ if (availISize == -1) {
+ NS_ASSERTION(aReflowInput.ComputedSize(wm).ISize(wm) !=
+ NS_UNCONSTRAINEDSIZE,
+ "Must have a useful inline-size _somewhere_");
+ availISize =
+ aReflowInput.ComputedSizeWithPadding(wm).ISize(wm);
+ }
+
+ uint32_t rsFlags = 0;
+ if (aFlags & AbsPosReflowFlags::eIsGridContainerCB) {
+ // When a grid container generates the abs.pos. CB for a *child* then
+ // the static position is determined via CSS Box Alignment within the
+ // abs.pos. CB (a grid area, i.e. a piece of the grid). In this scenario,
+ // due to the multiple coordinate spaces in play, we use a convenience flag
+ // to simply have the child's ReflowInput give it a static position at its
+ // abs.pos. CB origin, and then we'll align & offset it from there.
+ nsIFrame* placeholder =
+ aPresContext->PresShell()->GetPlaceholderFrameFor(aKidFrame);
+ if (placeholder && placeholder->GetParent() == aDelegatingFrame) {
+ rsFlags |= ReflowInput::STATIC_POS_IS_CB_ORIGIN;
+ }
+ }
+ ReflowInput kidReflowInput(aPresContext, aReflowInput, aKidFrame,
+ LogicalSize(wm, availISize,
+ NS_UNCONSTRAINEDSIZE),
+ &logicalCBSize, rsFlags);
+
+ // Get the border values
+ WritingMode outerWM = aReflowInput.GetWritingMode();
+ const LogicalMargin border(outerWM,
+ aReflowInput.mStyleBorder->GetComputedBorder());
+ LogicalMargin margin =
+ kidReflowInput.ComputedLogicalMargin().ConvertTo(outerWM, wm);
+
+ // If we're doing CSS Box Alignment in either axis, that will apply the
+ // margin for us in that axis (since the thing that's aligned is the margin
+ // box). So, we clear out the margin here to avoid applying it twice.
+ if (kidReflowInput.mFlags.mIOffsetsNeedCSSAlign) {
+ margin.IStart(outerWM) = margin.IEnd(outerWM) = 0;
+ }
+ if (kidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
+ margin.BStart(outerWM) = margin.BEnd(outerWM) = 0;
+ }
+
+ bool constrainBSize = (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)
+ && (aFlags & AbsPosReflowFlags::eConstrainHeight)
+ // Don't split if told not to (e.g. for fixed frames)
+ && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame)
+ //XXX we don't handle splitting frames for inline absolute containing blocks yet
+ && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <=
+ aReflowInput.AvailableBSize());
+ // Don't split things below the fold. (Ideally we shouldn't *have*
+ // anything totally below the fold, but we can't position frames
+ // across next-in-flow breaks yet.
+ if (constrainBSize) {
+ kidReflowInput.AvailableBSize() =
+ aReflowInput.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) -
+ kidReflowInput.ComputedLogicalMargin().BStart(wm);
+ if (NS_AUTOOFFSET != kidReflowInput.ComputedLogicalOffsets().BStart(wm)) {
+ kidReflowInput.AvailableBSize() -=
+ kidReflowInput.ComputedLogicalOffsets().BStart(wm);
+ }
+ }
+
+ // Do the reflow
+ ReflowOutput kidDesiredSize(kidReflowInput);
+ aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowInput, aStatus);
+
+ const LogicalSize kidSize = kidDesiredSize.Size(wm).ConvertTo(outerWM, wm);
+
+ LogicalMargin offsets =
+ kidReflowInput.ComputedLogicalOffsets().ConvertTo(outerWM, wm);
+
+ // If we're solving for start in either inline or block direction,
+ // then compute it now that we know the dimensions.
+ ResolveSizeDependentOffsets(aPresContext, kidReflowInput, kidSize, margin,
+ &offsets, &logicalCBSize);
+
+ // Position the child relative to our padding edge
+ LogicalRect rect(outerWM,
+ border.IStart(outerWM) + offsets.IStart(outerWM) +
+ margin.IStart(outerWM),
+ border.BStart(outerWM) + offsets.BStart(outerWM) +
+ margin.BStart(outerWM),
+ kidSize.ISize(outerWM), kidSize.BSize(outerWM));
+ nsRect r =
+ rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) +
+ border.Size(outerWM).GetPhysicalSize(outerWM));
+
+ // Offset the frame rect by the given origin of the absolute containing block.
+ // If the frame is auto-positioned on both sides of an axis, it will be
+ // positioned based on its containing block and we don't need to offset
+ // (unless the caller demands it (the STATIC_POS_IS_CB_ORIGIN case)).
+ if (aContainingBlock.TopLeft() != nsPoint(0, 0)) {
+ const nsStyleSides& offsets = kidReflowInput.mStylePosition->mOffset;
+ if (!(offsets.GetLeftUnit() == eStyleUnit_Auto &&
+ offsets.GetRightUnit() == eStyleUnit_Auto) ||
+ (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) {
+ r.x += aContainingBlock.x;
+ }
+ if (!(offsets.GetTopUnit() == eStyleUnit_Auto &&
+ offsets.GetBottomUnit() == eStyleUnit_Auto) ||
+ (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) {
+ r.y += aContainingBlock.y;
+ }
+ }
+
+ aKidFrame->SetRect(r);
+
+ nsView* view = aKidFrame->GetView();
+ if (view) {
+ // Size and position the view and set its opacity, visibility, content
+ // transparency, and clip
+ nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
+ kidDesiredSize.VisualOverflow());
+ } else {
+ nsContainerFrame::PositionChildViews(aKidFrame);
+ }
+
+ aKidFrame->DidReflow(aPresContext, &kidReflowInput,
+ nsDidReflowStatus::FINISHED);
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1);
+ printf("abs pos ");
+ nsAutoString name;
+ aKidFrame->GetFrameName(name);
+ printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
+ printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame),
+ r.x, r.y, r.width, r.height);
+ }
+#endif
+
+ if (aOverflowAreas) {
+ aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + r.TopLeft());
+ }
+}
diff --git a/layout/generic/nsAbsoluteContainingBlock.h b/layout/generic/nsAbsoluteContainingBlock.h
new file mode 100644
index 000000000..92910b815
--- /dev/null
+++ b/layout/generic/nsAbsoluteContainingBlock.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * code for managing absolutely positioned children of a rendering
+ * object that is a containing block for them
+ */
+
+#ifndef nsAbsoluteContainingBlock_h___
+#define nsAbsoluteContainingBlock_h___
+
+#include "nsFrameList.h"
+#include "nsIFrame.h"
+#include "mozilla/TypedEnumBits.h"
+
+class nsContainerFrame;
+class nsPresContext;
+
+/**
+ * This class contains the logic for being an absolute containing block. This
+ * class is used within viewport frames (for frames representing content with
+ * fixed position) and blocks (for frames representing absolutely positioned
+ * content), since each set of frames is absolutely positioned with respect to
+ * its parent.
+ *
+ * There is no principal child list, just a named child list which contains
+ * the absolutely positioned frames (kAbsoluteList or kFixedList).
+ *
+ * All functions include as the first argument the frame that is delegating
+ * the request.
+ */
+class nsAbsoluteContainingBlock
+{
+ using ReflowInput = mozilla::ReflowInput;
+
+public:
+ typedef nsIFrame::ChildListID ChildListID;
+
+ explicit nsAbsoluteContainingBlock(ChildListID aChildListID)
+#ifdef DEBUG
+ : mChildListID(aChildListID)
+#endif
+ {
+ MOZ_ASSERT(mChildListID == nsIFrame::kAbsoluteList ||
+ mChildListID == nsIFrame::kFixedList,
+ "should either represent position:fixed or absolute content");
+ }
+
+ const nsFrameList& GetChildList() const { return mAbsoluteFrames; }
+ void AppendChildList(nsTArray<nsIFrame::ChildList>* aLists,
+ ChildListID aListID) const
+ {
+ NS_ASSERTION(aListID == mChildListID, "wrong list ID");
+ GetChildList().AppendIfNonempty(aLists, aListID);
+ }
+
+ void SetInitialChildList(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsFrameList& aChildList);
+ void AppendFrames(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsFrameList& aFrameList);
+ void InsertFrames(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList);
+ void RemoveFrame(nsIFrame* aDelegatingFrame,
+ ChildListID aListID,
+ nsIFrame* aOldFrame);
+
+ enum class AbsPosReflowFlags {
+ eConstrainHeight = 0x1,
+ eCBWidthChanged = 0x2,
+ eCBHeightChanged = 0x4,
+ eCBWidthAndHeightChanged = eCBWidthChanged | eCBHeightChanged,
+ eIsGridContainerCB = 0x8,
+ };
+
+ /**
+ * Called by the delegating frame after it has done its reflow first. This
+ * function will reflow any absolutely positioned child frames that need to
+ * be reflowed, e.g., because the absolutely positioned child frame has
+ * 'auto' for an offset, or a percentage based width or height.
+ *
+ * @param aOverflowAreas, if non-null, is unioned with (in the local
+ * coordinate space) the overflow areas of the absolutely positioned
+ * children.
+ *
+ * @param aReflowStatus is assumed to be already-initialized, e.g. with the
+ * status of the delegating frame's main reflow. This function merges in the
+ * statuses of the absolutely positioned children's reflows.
+ *
+ * @param aFlags zero or more AbsPosReflowFlags
+ */
+ void Reflow(nsContainerFrame* aDelegatingFrame,
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus,
+ const nsRect& aContainingBlock,
+ AbsPosReflowFlags aFlags,
+ nsOverflowAreas* aOverflowAreas);
+
+ void DestroyFrames(nsIFrame* aDelegatingFrame,
+ nsIFrame* aDestructRoot);
+
+ bool HasAbsoluteFrames() const { return mAbsoluteFrames.NotEmpty(); }
+
+ /**
+ * Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN
+ * so that we'll make sure to reflow them.
+ */
+ void MarkSizeDependentFramesDirty();
+
+ /**
+ * Mark all our absolute frames with NS_FRAME_IS_DIRTY.
+ */
+ void MarkAllFramesDirty();
+
+protected:
+ /**
+ * Returns true if the position of aFrame depends on the position of
+ * its placeholder or if the position or size of aFrame depends on a
+ * containing block dimension that changed.
+ */
+ bool FrameDependsOnContainer(nsIFrame* aFrame, bool aCBWidthChanged,
+ bool aCBHeightChanged);
+
+ /**
+ * After an abspos child's size is known, this method can be used to
+ * resolve size-dependent values in the ComputedLogicalOffsets on its
+ * reflow state. (This may involve resolving the inline dimension of
+ * aLogicalCBSize, too; hence, that variable is an in/outparam.)
+ *
+ * aKidSize, aMargin, aOffsets, and aLogicalCBSize are all expected to be
+ * represented in terms of the absolute containing block's writing-mode.
+ */
+ void ResolveSizeDependentOffsets(nsPresContext* aPresContext,
+ ReflowInput& aKidReflowInput,
+ const mozilla::LogicalSize& aKidSize,
+ const mozilla::LogicalMargin& aMargin,
+ mozilla::LogicalMargin* aOffsets,
+ mozilla::LogicalSize* aLogicalCBSize);
+
+ void ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ const nsRect& aContainingBlockRect,
+ AbsPosReflowFlags aFlags,
+ nsIFrame* aKidFrame,
+ nsReflowStatus& aStatus,
+ nsOverflowAreas* aOverflowAreas);
+
+ /**
+ * Mark our absolute frames dirty.
+ * @param aMarkAllDirty if true, all will be marked with NS_FRAME_IS_DIRTY.
+ * Otherwise, the size-dependant ones will be marked with
+ * NS_FRAME_HAS_DIRTY_CHILDREN.
+ */
+ void DoMarkFramesDirty(bool aMarkAllDirty);
+
+protected:
+ nsFrameList mAbsoluteFrames; // additional named child list
+
+#ifdef DEBUG
+ ChildListID const mChildListID; // kFixedList or kAbsoluteList
+#endif
+};
+
+namespace mozilla {
+ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsAbsoluteContainingBlock::AbsPosReflowFlags)
+}
+#endif /* nsnsAbsoluteContainingBlock_h___ */
diff --git a/layout/generic/nsAtomicContainerFrame.h b/layout/generic/nsAtomicContainerFrame.h
new file mode 100644
index 000000000..7acac86de
--- /dev/null
+++ b/layout/generic/nsAtomicContainerFrame.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class for rendering objects that need child lists but behave like leaf */
+
+#ifndef nsAtomicContainerFrame_h___
+#define nsAtomicContainerFrame_h___
+
+#include "nsContainerFrame.h"
+
+/**
+ * This class is for frames which need child lists but act like a leaf
+ * frame. In general, all frames of elements laid out according to the
+ * CSS box model would need child list for ::backdrop in case they are
+ * in fullscreen, while some of them still want leaf frame behavior.
+ */
+class nsAtomicContainerFrame : public nsContainerFrame
+{
+public:
+ NS_DECL_ABSTRACT_FRAME(nsAtomicContainerFrame)
+
+ // Bypass the nsContainerFrame/nsSplittableFrame impl of the following
+ // methods so we behave like a leaf frame.
+ bool IsLeaf() const override { return true; }
+ FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override
+ {
+ return nsFrame::PeekOffsetNoAmount(aForward, aOffset);
+ }
+ FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) override
+ {
+ return nsFrame::PeekOffsetCharacter(aForward, aOffset, aRespectClusters);
+ }
+ nsSplittableType GetSplittableType() const override
+ {
+ return nsFrame::GetSplittableType();
+ }
+
+protected:
+ explicit nsAtomicContainerFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext) {}
+};
+
+#endif // nsAtomicContainerFrame_h___
diff --git a/layout/generic/nsAutoCopyListener.h b/layout/generic/nsAutoCopyListener.h
new file mode 100644
index 000000000..c580f5260
--- /dev/null
+++ b/layout/generic/nsAutoCopyListener.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAutoCopyListener_h_
+#define nsAutoCopyListener_h_
+
+#include "nsISelectionListener.h"
+#include "nsISelectionPrivate.h"
+#include "mozilla/Attributes.h"
+
+class nsAutoCopyListener final : public nsISelectionListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISELECTIONLISTENER
+
+ explicit nsAutoCopyListener(int16_t aClipboardID)
+ : mCachedClipboard(aClipboardID)
+ {}
+
+ void Listen(nsISelectionPrivate *aSelection)
+ {
+ NS_ASSERTION(aSelection, "Null selection passed to Listen()");
+ aSelection->AddSelectionListener(this);
+ }
+
+ static nsAutoCopyListener* GetInstance(int16_t aClipboardID)
+ {
+ if (!sInstance) {
+ sInstance = new nsAutoCopyListener(aClipboardID);
+
+ NS_ADDREF(sInstance);
+ }
+
+ return sInstance;
+ }
+
+ static void Shutdown()
+ {
+ NS_IF_RELEASE(sInstance);
+ }
+
+private:
+ ~nsAutoCopyListener() {}
+
+ static nsAutoCopyListener* sInstance;
+ int16_t mCachedClipboard;
+};
+
+#endif
diff --git a/layout/generic/nsBRFrame.cpp b/layout/generic/nsBRFrame.cpp
new file mode 100644
index 000000000..057210350
--- /dev/null
+++ b/layout/generic/nsBRFrame.cpp
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for HTML <br> elements */
+
+#include "nsCOMPtr.h"
+#include "nsFontMetrics.h"
+#include "nsFrame.h"
+#include "nsPresContext.h"
+#include "nsLineLayout.h"
+#include "nsStyleConsts.h"
+#include "nsGkAtoms.h"
+#include "nsRenderingContext.h"
+#include "nsLayoutUtils.h"
+
+//FOR SELECTION
+#include "nsIContent.h"
+//END INCLUDES FOR SELECTION
+
+using namespace mozilla;
+
+class BRFrame : public nsFrame {
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsIFrame* NS_NewBRFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+ virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint) override;
+
+ virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override;
+ virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) override;
+ virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace,
+ bool aIsKeyboardSelect, int32_t* aOffset,
+ PeekWordState* aState) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual nsIAtom* GetType() const override;
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
+ nsIFrame::eLineParticipant));
+ }
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+protected:
+ explicit BRFrame(nsStyleContext* aContext) : nsFrame(aContext) {}
+ virtual ~BRFrame();
+
+ nscoord mAscent;
+};
+
+nsIFrame*
+NS_NewBRFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) BRFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(BRFrame)
+
+BRFrame::~BRFrame()
+{
+}
+
+void
+BRFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("BRFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalSize finalSize(wm);
+ finalSize.BSize(wm) = 0; // BR frames with block size 0 are ignored in quirks
+ // mode by nsLineLayout::VerticalAlignFrames .
+ // However, it's not always 0. See below.
+ finalSize.ISize(wm) = 0;
+ aMetrics.SetBlockStartAscent(0);
+
+ // Only when the BR is operating in a line-layout situation will it
+ // behave like a BR. Additionally, we suppress breaks from BR inside
+ // of ruby frames. To determine if we're inside ruby, we have to rely
+ // on the *parent's* ShouldSuppressLineBreak() method, instead of our
+ // own, because we may have custom "display" value that makes our
+ // ShouldSuppressLineBreak() return false.
+ nsLineLayout* ll = aReflowInput.mLineLayout;
+ if (ll && !GetParent()->StyleContext()->ShouldSuppressLineBreak()) {
+ // Note that the compatibility mode check excludes AlmostStandards
+ // mode, since this is the inline box model. See bug 161691.
+ if ( ll->LineIsEmpty() ||
+ aPresContext->CompatibilityMode() == eCompatibility_FullStandards ) {
+ // The line is logically empty; any whitespace is trimmed away.
+ //
+ // If this frame is going to terminate the line we know
+ // that nothing else will go on the line. Therefore, in this
+ // case, we provide some height for the BR frame so that it
+ // creates some vertical whitespace. It's necessary to use the
+ // line-height rather than the font size because the
+ // quirks-mode fix that doesn't apply the block's min
+ // line-height makes this necessary to make BR cause a line
+ // of the full line-height
+
+ // We also do this in strict mode because BR should act like a
+ // normal inline frame. That line-height is used is important
+ // here for cases where the line-height is less than 1.
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
+ if (fm) {
+ nscoord logicalHeight = aReflowInput.CalcLineHeight();
+ finalSize.BSize(wm) = logicalHeight;
+ aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline(
+ fm, logicalHeight, wm.IsLineInverted()));
+ }
+ else {
+ aMetrics.SetBlockStartAscent(aMetrics.BSize(wm) = 0);
+ }
+
+ // XXX temporary until I figure out a better solution; see the
+ // code in nsLineLayout::VerticalAlignFrames that zaps minY/maxY
+ // if the width is zero.
+ // XXX This also fixes bug 10036!
+ // Warning: nsTextControlFrame::CalculateSizeStandard depends on
+ // the following line, see bug 228752.
+ // The code below in AddInlinePrefISize also adds 1 appunit to width
+ finalSize.ISize(wm) = 1;
+ }
+
+ // Return our reflow status
+ StyleClear breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm);
+ if (StyleClear::None == breakType) {
+ breakType = StyleClear::Line;
+ }
+
+ aStatus = NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
+ NS_INLINE_MAKE_BREAK_TYPE(breakType);
+ ll->SetLineEndsInBR(true);
+ }
+ else {
+ aStatus = NS_FRAME_COMPLETE;
+ }
+
+ aMetrics.SetSize(wm, finalSize);
+ aMetrics.SetOverflowAreasToDesiredBounds();
+
+ mAscent = aMetrics.BlockStartAscent();
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+/* virtual */ void
+BRFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData)
+{
+ if (!GetParent()->StyleContext()->ShouldSuppressLineBreak()) {
+ aData->ForceBreak();
+ }
+}
+
+/* virtual */ void
+BRFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlinePrefISizeData *aData)
+{
+ if (!GetParent()->StyleContext()->ShouldSuppressLineBreak()) {
+ // Match the 1 appunit width assigned in the Reflow method above
+ aData->mCurrentLine += 1;
+ aData->ForceBreak();
+ }
+}
+
+/* virtual */ nscoord
+BRFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = 0;
+ DISPLAY_MIN_WIDTH(this, result);
+ return result;
+}
+
+/* virtual */ nscoord
+BRFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = 0;
+ DISPLAY_PREF_WIDTH(this, result);
+ return result;
+}
+
+nsIAtom*
+BRFrame::GetType() const
+{
+ return nsGkAtoms::brFrame;
+}
+
+nscoord
+BRFrame::GetLogicalBaseline(mozilla::WritingMode aWritingMode) const
+{
+ return mAscent;
+}
+
+nsIFrame::ContentOffsets BRFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
+{
+ ContentOffsets offsets;
+ offsets.content = mContent->GetParent();
+ if (offsets.content) {
+ offsets.offset = offsets.content->IndexOf(mContent);
+ offsets.secondaryOffset = offsets.offset;
+ offsets.associate = CARET_ASSOCIATE_AFTER;
+ }
+ return offsets;
+}
+
+nsIFrame::FrameSearchResult
+BRFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ int32_t startOffset = *aOffset;
+ // If we hit the end of a BR going backwards, go to its beginning and stay there.
+ if (!aForward && startOffset != 0) {
+ *aOffset = 0;
+ return FOUND;
+ }
+ // Otherwise, stop if we hit the beginning, continue (forward) if we hit the end.
+ return (startOffset == 0) ? FOUND : CONTINUE;
+}
+
+nsIFrame::FrameSearchResult
+BRFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ // Keep going. The actual line jumping will stop us.
+ return CONTINUE;
+}
+
+nsIFrame::FrameSearchResult
+BRFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
+ int32_t* aOffset, PeekWordState* aState)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ // Keep going. The actual line jumping will stop us.
+ return CONTINUE;
+}
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+BRFrame::AccessibleType()
+{
+ nsIContent *parent = mContent->GetParent();
+ if (parent && parent->IsRootOfNativeAnonymousSubtree() &&
+ parent->GetChildCount() == 1) {
+ // This <br> is the only node in a text control, therefore it is the hacky
+ // "bogus node" used when there is no text in the control
+ return a11y::eNoType;
+ }
+
+ // Trailing HTML br element don't play any difference. We don't need to expose
+ // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
+ // for details).
+ if (!mContent->GetNextSibling() && !GetNextSibling()) {
+ return a11y::eNoType;
+ }
+
+ return a11y::eHTMLBRType;
+}
+#endif
+
diff --git a/layout/generic/nsBackdropFrame.cpp b/layout/generic/nsBackdropFrame.cpp
new file mode 100644
index 000000000..687c5b2e3
--- /dev/null
+++ b/layout/generic/nsBackdropFrame.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "::backdrop" */
+
+#include "nsBackdropFrame.h"
+
+#include "nsDisplayList.h"
+
+using namespace mozilla;
+
+NS_IMPL_FRAMEARENA_HELPERS(nsBackdropFrame)
+
+/* virtual */ nsIAtom*
+nsBackdropFrame::GetType() const
+{
+ return nsGkAtoms::backdropFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsBackdropFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Backdrop"), aResult);
+}
+#endif
+
+/* virtual */ nsStyleContext*
+nsBackdropFrame::GetParentStyleContext(nsIFrame** aProviderFrame) const
+{
+ // Style context of backdrop pseudo-element does not inherit from
+ // any element, per the Fullscreen API spec.
+ *aProviderFrame = nullptr;
+ return nullptr;
+}
+
+/* virtual */ void
+nsBackdropFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsBackdropFrame");
+ // We want this frame to always be there even if its display value is
+ // none or contents so that we can respond to style change on it. To
+ // support those values, we skip painting ourselves in those cases.
+ auto display = StyleDisplay()->mDisplay;
+ if (display == mozilla::StyleDisplay::None ||
+ display == mozilla::StyleDisplay::Contents) {
+ return;
+ }
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+}
+
+/* virtual */ LogicalSize
+nsBackdropFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ // Note that this frame is a child of the viewport frame.
+ LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
+ if (aFlags & ComputeSizeFlags::eShrinkWrap) {
+ result.ISize(aWM) = 0;
+ } else {
+ result.ISize(aWM) = aAvailableISize - aMargin.ISize(aWM) -
+ aBorder.ISize(aWM) - aPadding.ISize(aWM);
+ }
+ return result;
+}
+
+/* virtual */ void
+nsBackdropFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsBackdropFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ // Note that this frame is a child of the viewport frame.
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
+ nscoord isize = aReflowInput.ComputedISize() + borderPadding.IStartEnd(wm);
+ nscoord bsize = aReflowInput.ComputedBSize() + borderPadding.BStartEnd(wm);
+ aDesiredSize.SetSize(wm, LogicalSize(wm, isize, bsize));
+ aStatus = NS_FRAME_COMPLETE;
+}
diff --git a/layout/generic/nsBackdropFrame.h b/layout/generic/nsBackdropFrame.h
new file mode 100644
index 000000000..74d366bef
--- /dev/null
+++ b/layout/generic/nsBackdropFrame.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "::backdrop" */
+
+#ifndef nsBackdropFrame_h___
+#define nsBackdropFrame_h___
+
+#include "nsFrame.h"
+
+class nsBackdropFrame final : public nsFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsBackdropFrame(nsStyleContext* aContext)
+ : nsFrame(aContext) { }
+
+ // nsIFrame overrides
+ virtual nsIAtom* GetType() const override;
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+ virtual nsStyleContext*
+ GetParentStyleContext(nsIFrame** aProviderFrame) const override;
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+ virtual mozilla::LogicalSize
+ ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+};
+
+#endif // nsBackdropFrame_h___
diff --git a/layout/generic/nsBlockDebugFlags.h b/layout/generic/nsBlockDebugFlags.h
new file mode 100644
index 000000000..c2eeabcb5
--- /dev/null
+++ b/layout/generic/nsBlockDebugFlags.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBlockDebugFlags_h__
+#define nsBlockDebugFlags_h__
+
+#undef NOISY_FIRST_LETTER // enables debug output for first-letter specific layout
+#undef NOISY_FLOAT // enables debug output for float reflow (the in/out metrics for the floated block)
+#undef NOISY_FLOAT_CLEARING
+#undef NOISY_FINAL_SIZE // enables debug output for desired width/height computation, once all children have been reflowed
+#undef NOISY_REMOVE_FRAME
+#undef NOISY_COMBINED_AREA // enables debug output for combined area computation
+#undef NOISY_BLOCK_DIR_MARGINS
+#undef NOISY_REFLOW_REASON // gives a little info about why each reflow was requested
+#undef REFLOW_STATUS_COVERAGE // I think this is most useful for printing, to see which frames return "incomplete"
+#undef NOISY_BLOCK_INVALIDATE // enables debug output for all calls to invalidate
+#undef REALLY_NOISY_REFLOW // some extra debug info
+
+#endif // nsBlockDebugFlags_h__
diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp
new file mode 100644
index 000000000..851e3406c
--- /dev/null
+++ b/layout/generic/nsBlockFrame.cpp
@@ -0,0 +1,7611 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object for CSS display:block, inline-block, and list-item
+ * boxes, also used for various anonymous boxes
+ */
+
+#include "nsBlockFrame.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsCOMPtr.h"
+#include "nsAbsoluteContainingBlock.h"
+#include "nsBlockReflowContext.h"
+#include "BlockReflowInput.h"
+#include "nsBulletFrame.h"
+#include "nsFontMetrics.h"
+#include "nsLineBox.h"
+#include "nsLineLayout.h"
+#include "nsPlaceholderFrame.h"
+#include "nsStyleConsts.h"
+#include "nsFrameManager.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsStyleContext.h"
+#include "nsHTMLParts.h"
+#include "nsGkAtoms.h"
+#include "nsGenericHTMLElement.h"
+#include "nsAttrValueInlines.h"
+#include "mozilla/Sprintf.h"
+#include "nsFloatManager.h"
+#include "prenv.h"
+#include "plstr.h"
+#include "nsError.h"
+#include "nsIScrollableFrame.h"
+#include <algorithm>
+#ifdef ACCESSIBILITY
+#include "nsIDOMHTMLDocument.h"
+#endif
+#include "nsLayoutUtils.h"
+#include "nsDisplayList.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsRenderingContext.h"
+#include "TextOverflow.h"
+#include "nsIFrameInlines.h"
+#include "CounterStyleManager.h"
+#include "nsISelection.h"
+#include "mozilla/dom/HTMLDetailsElement.h"
+#include "mozilla/dom/HTMLSummaryElement.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+
+#include "nsBidiPresUtils.h"
+
+#include <inttypes.h>
+
+static const int MIN_LINES_NEEDING_CURSOR = 20;
+
+static const char16_t kDiscCharacter = 0x2022;
+
+using namespace mozilla;
+using namespace mozilla::css;
+using namespace mozilla::dom;
+using namespace mozilla::layout;
+typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
+
+static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
+{
+ nsLineList::iterator line = aBlock->LinesBegin();
+ nsLineList::iterator endLine = aBlock->LinesEnd();
+ while (line != endLine) {
+ if (line->IsBlock()) {
+ nsIFrame* f = line->mFirstChild;
+ nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
+ if (bf) {
+ MarkAllDescendantLinesDirty(bf);
+ }
+ }
+ line->MarkDirty();
+ ++line;
+ }
+}
+
+static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock)
+{
+ nsBlockFrame* blockWithFloatMgr = aBlock;
+ while (!(blockWithFloatMgr->GetStateBits() & NS_BLOCK_FLOAT_MGR)) {
+ nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(blockWithFloatMgr->GetParent());
+ if (!bf) {
+ break;
+ }
+ blockWithFloatMgr = bf;
+ }
+
+ // Mark every line at and below the line where the float was
+ // dirty, and mark their lines dirty too. We could probably do
+ // something more efficient --- e.g., just dirty the lines that intersect
+ // the float vertically.
+ MarkAllDescendantLinesDirty(blockWithFloatMgr);
+}
+
+/**
+ * Returns true if aFrame is a block that has one or more float children.
+ */
+static bool BlockHasAnyFloats(nsIFrame* aFrame)
+{
+ nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
+ if (!block)
+ return false;
+ if (block->GetChildList(nsIFrame::kFloatList).FirstChild())
+ return true;
+
+ nsLineList::iterator line = block->LinesBegin();
+ nsLineList::iterator endLine = block->LinesEnd();
+ while (line != endLine) {
+ if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild))
+ return true;
+ ++line;
+ }
+ return false;
+}
+
+#ifdef DEBUG
+#include "nsBlockDebugFlags.h"
+
+bool nsBlockFrame::gLamePaintMetrics;
+bool nsBlockFrame::gLameReflowMetrics;
+bool nsBlockFrame::gNoisy;
+bool nsBlockFrame::gNoisyDamageRepair;
+bool nsBlockFrame::gNoisyIntrinsic;
+bool nsBlockFrame::gNoisyReflow;
+bool nsBlockFrame::gReallyNoisyReflow;
+bool nsBlockFrame::gNoisyFloatManager;
+bool nsBlockFrame::gVerifyLines;
+bool nsBlockFrame::gDisableResizeOpt;
+
+int32_t nsBlockFrame::gNoiseIndent;
+
+struct BlockDebugFlags {
+ const char* name;
+ bool* on;
+};
+
+static const BlockDebugFlags gFlags[] = {
+ { "reflow", &nsBlockFrame::gNoisyReflow },
+ { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
+ { "intrinsic", &nsBlockFrame::gNoisyIntrinsic },
+ { "float-manager", &nsBlockFrame::gNoisyFloatManager },
+ { "verify-lines", &nsBlockFrame::gVerifyLines },
+ { "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
+ { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
+ { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
+ { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
+};
+#define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
+
+static void
+ShowDebugFlags()
+{
+ printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
+ const BlockDebugFlags* bdf = gFlags;
+ const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
+ for (; bdf < end; bdf++) {
+ printf(" %s\n", bdf->name);
+ }
+ printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
+ printf("names (no whitespace)\n");
+}
+
+void
+nsBlockFrame::InitDebugFlags()
+{
+ static bool firstTime = true;
+ if (firstTime) {
+ firstTime = false;
+ char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
+ if (flags) {
+ bool error = false;
+ for (;;) {
+ char* cm = PL_strchr(flags, ',');
+ if (cm) *cm = '\0';
+
+ bool found = false;
+ const BlockDebugFlags* bdf = gFlags;
+ const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
+ for (; bdf < end; bdf++) {
+ if (PL_strcasecmp(bdf->name, flags) == 0) {
+ *(bdf->on) = true;
+ printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
+ gNoisy = true;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ error = true;
+ }
+
+ if (!cm) break;
+ *cm = ',';
+ flags = cm + 1;
+ }
+ if (error) {
+ ShowDebugFlags();
+ }
+ }
+ }
+}
+
+#endif
+
+//----------------------------------------------------------------------
+
+// Debugging support code
+
+#ifdef DEBUG
+const char* nsBlockFrame::kReflowCommandType[] = {
+ "ContentChanged",
+ "StyleChanged",
+ "ReflowDirty",
+ "Timeout",
+ "UserDefined",
+};
+
+const char*
+nsBlockFrame::LineReflowStatusToString(LineReflowStatus aLineReflowStatus) const
+{
+ switch (aLineReflowStatus) {
+ case LineReflowStatus::OK: return "LINE_REFLOW_OK";
+ case LineReflowStatus::Stop: return "LINE_REFLOW_STOP";
+ case LineReflowStatus::RedoNoPull: return "LINE_REFLOW_REDO_NO_PULL";
+ case LineReflowStatus::RedoMoreFloats: return "LINE_REFLOW_REDO_MORE_FLOATS";
+ case LineReflowStatus::RedoNextBand: return "LINE_REFLOW_REDO_NEXT_BAND";
+ case LineReflowStatus::Truncated: return "LINE_REFLOW_TRUNCATED";
+ }
+ return "unknown";
+}
+
+#endif
+
+#ifdef REFLOW_STATUS_COVERAGE
+static void
+RecordReflowStatus(bool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
+{
+ static uint32_t record[2];
+
+ // 0: child-is-block
+ // 1: child-is-inline
+ int index = 0;
+ if (!aChildIsBlock) index |= 1;
+
+ // Compute new status
+ uint32_t newS = record[index];
+ if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
+ if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
+ newS |= 1;
+ }
+ else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
+ newS |= 2;
+ }
+ else {
+ newS |= 4;
+ }
+ }
+ else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
+ newS |= 8;
+ }
+ else {
+ newS |= 16;
+ }
+
+ // Log updates to the status that yield different values
+ if (record[index] != newS) {
+ record[index] = newS;
+ printf("record(%d): %02x %02x\n", index, record[0], record[1]);
+ }
+}
+#endif
+
+NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty,
+ nsBlockFrame::FrameLines)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty)
+NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideBulletProperty, nsBulletFrame)
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BlockEndEdgeOfChildrenProperty, nscoord)
+
+//----------------------------------------------------------------------
+
+nsBlockFrame*
+NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsBlockFrame(aContext);
+}
+
+nsBlockFrame*
+NS_NewBlockFormattingContext(nsIPresShell* aPresShell,
+ nsStyleContext* aStyleContext)
+{
+ nsBlockFrame* blockFrame = NS_NewBlockFrame(aPresShell, aStyleContext);
+ blockFrame->AddStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
+ return blockFrame;
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
+
+nsBlockFrame::~nsBlockFrame()
+{
+}
+
+void
+nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ ClearLineCursor();
+ DestroyAbsoluteFrames(aDestructRoot);
+ mFloats.DestroyFramesFrom(aDestructRoot);
+ nsPresContext* presContext = PresContext();
+ nsIPresShell* shell = presContext->PresShell();
+ nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
+ &mFrames);
+
+ FramePropertyTable* props = presContext->PropertyTable();
+
+ if (HasPushedFloats()) {
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ PushedFloatProperty());
+ RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
+ }
+
+ // destroy overflow lines now
+ FrameLines* overflowLines = RemoveOverflowLines();
+ if (overflowLines) {
+ nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
+ aDestructRoot, &overflowLines->mFrames);
+ delete overflowLines;
+ }
+
+ if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ OverflowOutOfFlowsProperty());
+ RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
+ }
+
+ if (HasOutsideBullet()) {
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ OutsideBulletProperty());
+ RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
+ }
+
+ nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+/* virtual */ nsILineIterator*
+nsBlockFrame::GetLineIterator()
+{
+ nsLineIterator* it = new nsLineIterator;
+ if (!it)
+ return nullptr;
+
+ const nsStyleVisibility* visibility = StyleVisibility();
+ nsresult rv = it->Init(mLines, visibility->mDirection == NS_STYLE_DIRECTION_RTL);
+ if (NS_FAILED(rv)) {
+ delete it;
+ return nullptr;
+ }
+ return it;
+}
+
+NS_QUERYFRAME_HEAD(nsBlockFrame)
+ NS_QUERYFRAME_ENTRY(nsBlockFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+nsSplittableType
+nsBlockFrame::GetSplittableType() const
+{
+ return NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+void
+nsBlockFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+
+ fprintf_stderr(out, "%s<\n", str.get());
+
+ nsCString pfx(aPrefix);
+ pfx += " ";
+
+ // Output the lines
+ if (!mLines.empty()) {
+ ConstLineIterator line = LinesBegin(), line_end = LinesEnd();
+ for ( ; line != line_end; ++line) {
+ line->List(out, pfx.get(), aFlags);
+ }
+ }
+
+ // Output the overflow lines.
+ const FrameLines* overflowLines = GetOverflowLines();
+ if (overflowLines && !overflowLines->mLines.empty()) {
+ fprintf_stderr(out, "%sOverflow-lines %p/%p <\n", pfx.get(), overflowLines, &overflowLines->mFrames);
+ nsCString nestedPfx(pfx);
+ nestedPfx += " ";
+ ConstLineIterator line = overflowLines->mLines.begin(),
+ line_end = overflowLines->mLines.end();
+ for ( ; line != line_end; ++line) {
+ line->List(out, nestedPfx.get(), aFlags);
+ }
+ fprintf_stderr(out, "%s>\n", pfx.get());
+ }
+
+ // skip the principal list - we printed the lines above
+ // skip the overflow list - we printed the overflow lines above
+ ChildListIterator lists(this);
+ ChildListIDs skip(kPrincipalList | kOverflowList);
+ for (; !lists.IsDone(); lists.Next()) {
+ if (skip.Contains(lists.CurrentID())) {
+ continue;
+ }
+ fprintf_stderr(out, "%s%s %p <\n", pfx.get(),
+ mozilla::layout::ChildListName(lists.CurrentID()),
+ &GetChildList(lists.CurrentID()));
+ nsCString nestedPfx(pfx);
+ nestedPfx += " ";
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* kid = childFrames.get();
+ kid->List(out, nestedPfx.get(), aFlags);
+ }
+ fprintf_stderr(out, "%s>\n", pfx.get());
+ }
+
+ fprintf_stderr(out, "%s>\n", aPrefix);
+}
+
+nsresult
+nsBlockFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
+}
+#endif
+
+#ifdef DEBUG
+nsFrameState
+nsBlockFrame::GetDebugStateBits() const
+{
+ // We don't want to include our cursor flag in the bits the
+ // regression tester looks at
+ return nsContainerFrame::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
+}
+#endif
+
+nsIAtom*
+nsBlockFrame::GetType() const
+{
+ return nsGkAtoms::blockFrame;
+}
+
+void
+nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey)
+{
+ if (IsSVGText()) {
+ NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame,
+ "unexpected block frame in SVG text");
+ GetParent()->InvalidateFrame();
+ return;
+ }
+ nsContainerFrame::InvalidateFrame(aDisplayItemKey);
+}
+
+void
+nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
+{
+ if (IsSVGText()) {
+ NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame,
+ "unexpected block frame in SVG text");
+ GetParent()->InvalidateFrame();
+ return;
+ }
+ nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
+}
+
+nscoord
+nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const
+{
+ auto lastBaseline =
+ BaselineBOffset(aWM, BaselineSharingGroup::eLast, AlignmentContext::eInline);
+ return BSize(aWM) - lastBaseline;
+}
+
+bool
+nsBlockFrame::GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ nscoord* aBaseline) const
+{
+ if (aBaselineGroup == BaselineSharingGroup::eFirst) {
+ return nsLayoutUtils::GetFirstLineBaseline(aWM, this, aBaseline);
+ }
+
+ for (ConstReverseLineIterator line = LinesRBegin(), line_end = LinesREnd();
+ line != line_end; ++line) {
+ if (line->IsBlock()) {
+ nscoord offset;
+ nsIFrame* kid = line->mFirstChild;
+ if (kid->GetVerticalAlignBaseline(aWM, &offset)) {
+ // Ignore relative positioning for baseline calculations.
+ const nsSize& sz = line->mContainerSize;
+ offset += kid->GetLogicalNormalPosition(aWM, sz).B(aWM);
+ *aBaseline = BSize(aWM) - offset;
+ return true;
+ }
+ } else {
+ // XXX Is this the right test? We have some bogus empty lines
+ // floating around, but IsEmpty is perhaps too weak.
+ if (line->BSize() != 0 || !line->IsEmpty()) {
+ *aBaseline = BSize(aWM) - (line->BStart() + line->GetLogicalAscent());
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+nscoord
+nsBlockFrame::GetCaretBaseline() const
+{
+ nsRect contentRect = GetContentRect();
+ nsMargin bp = GetUsedBorderAndPadding();
+
+ if (!mLines.empty()) {
+ ConstLineIterator line = LinesBegin();
+ const nsLineBox* firstLine = line;
+ if (firstLine->GetChildCount()) {
+ return bp.top + firstLine->mFirstChild->GetCaretBaseline();
+ }
+ }
+ float inflation = nsLayoutUtils::FontSizeInflationFor(this);
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, inflation);
+ nscoord lineHeight =
+ ReflowInput::CalcLineHeight(GetContent(), StyleContext(),
+ contentRect.height, inflation);
+ const WritingMode wm = GetWritingMode();
+ return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight,
+ wm.IsLineInverted()) + bp.top;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Child frame enumeration
+
+const nsFrameList&
+nsBlockFrame::GetChildList(ChildListID aListID) const
+{
+ switch (aListID) {
+ case kPrincipalList:
+ return mFrames;
+ case kOverflowList: {
+ FrameLines* overflowLines = GetOverflowLines();
+ return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
+ }
+ case kFloatList:
+ return mFloats;
+ case kOverflowOutOfFlowList: {
+ const nsFrameList* list = GetOverflowOutOfFlows();
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ case kPushedFloatsList: {
+ const nsFrameList* list = GetPushedFloats();
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ case kBulletList: {
+ const nsFrameList* list = GetOutsideBulletList();
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ default:
+ return nsContainerFrame::GetChildList(aListID);
+ }
+}
+
+void
+nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const
+{
+ nsContainerFrame::GetChildLists(aLists);
+ FrameLines* overflowLines = GetOverflowLines();
+ if (overflowLines) {
+ overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList);
+ }
+ const nsFrameList* list = GetOverflowOutOfFlows();
+ if (list) {
+ list->AppendIfNonempty(aLists, kOverflowOutOfFlowList);
+ }
+ mFloats.AppendIfNonempty(aLists, kFloatList);
+ list = GetOutsideBulletList();
+ if (list) {
+ list->AppendIfNonempty(aLists, kBulletList);
+ }
+ list = GetPushedFloats();
+ if (list) {
+ list->AppendIfNonempty(aLists, kPushedFloatsList);
+ }
+}
+
+/* virtual */ bool
+nsBlockFrame::IsFloatContainingBlock() const
+{
+ return true;
+}
+
+static void
+ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
+ nsContainerFrame* aNewParent)
+{
+ NS_ASSERTION(aOldParent == aFrame->GetParent(),
+ "Parent not consistent with expectations");
+
+ aFrame->SetParent(aNewParent);
+
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented
+ nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent);
+}
+
+static void
+ReparentFrames(nsFrameList& aFrameList, nsContainerFrame* aOldParent,
+ nsContainerFrame* aNewParent)
+{
+ for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
+ ReparentFrame(e.get(), aOldParent, aNewParent);
+ }
+}
+
+/**
+ * Remove the first line from aFromLines and adjust the associated frame list
+ * aFromFrames accordingly. The removed line is assigned to *aOutLine and
+ * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
+ * that were extracted from the head of aFromFrames.
+ * aFromLines must contain at least one line, the line may be empty.
+ * @return true if aFromLines becomes empty
+ */
+static bool
+RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
+ nsLineBox** aOutLine, nsFrameList* aOutFrames)
+{
+ nsLineList_iterator removedLine = aFromLines.begin();
+ *aOutLine = removedLine;
+ nsLineList_iterator next = aFromLines.erase(removedLine);
+ bool isLastLine = next == aFromLines.end();
+ nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild()
+ : next->mFirstChild->GetPrevSibling();
+ nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame);
+ *aOutFrames = aFromFrames.ExtractHead(linkToBreak);
+ return isLastLine;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Reflow methods
+
+/* virtual */ void
+nsBlockFrame::MarkIntrinsicISizesDirty()
+{
+ nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation());
+ dirtyBlock->mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
+ dirtyBlock->mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
+ if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
+ for (nsIFrame* frame = dirtyBlock; frame;
+ frame = frame->GetNextContinuation()) {
+ frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
+ }
+ }
+
+ nsContainerFrame::MarkIntrinsicISizesDirty();
+}
+
+void
+nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState()
+{
+ nsPresContext *presContext = PresContext();
+ if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) {
+ return;
+ }
+ bool inflationEnabled =
+ !presContext->mInflationDisabledForShrinkWrap;
+ if (inflationEnabled !=
+ !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED)) {
+ mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
+ if (inflationEnabled) {
+ AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
+ } else {
+ RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
+ }
+ }
+}
+
+/* virtual */ nscoord
+nsBlockFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nsIFrame* firstInFlow = FirstContinuation();
+ if (firstInFlow != this)
+ return firstInFlow->GetMinISize(aRenderingContext);
+
+ DISPLAY_MIN_WIDTH(this, mMinWidth);
+
+ CheckIntrinsicCacheAgainstShrinkWrapState();
+
+ if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
+ return mMinWidth;
+
+#ifdef DEBUG
+ if (gNoisyIntrinsic) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": GetMinISize\n");
+ }
+ AutoNoisyIndenter indenter(gNoisyIntrinsic);
+#endif
+
+ for (nsBlockFrame* curFrame = this; curFrame;
+ curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
+ curFrame->LazyMarkLinesDirty();
+ }
+
+ if (RenumberList()) {
+ AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
+ ResolveBidi();
+ InlineMinISizeData data;
+ for (nsBlockFrame* curFrame = this; curFrame;
+ curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
+ for (LineIterator line = curFrame->LinesBegin(), line_end = curFrame->LinesEnd();
+ line != line_end; ++line)
+ {
+#ifdef DEBUG
+ if (gNoisyIntrinsic) {
+ IndentBy(stdout, gNoiseIndent);
+ printf("line (%s%s)\n",
+ line->IsBlock() ? "block" : "inline",
+ line->IsEmpty() ? ", empty" : "");
+ }
+ AutoNoisyIndenter lineindent(gNoisyIntrinsic);
+#endif
+ if (line->IsBlock()) {
+ data.ForceBreak();
+ data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ line->mFirstChild, nsLayoutUtils::MIN_ISIZE);
+ data.ForceBreak();
+ } else {
+ if (!curFrame->GetPrevContinuation() &&
+ line == curFrame->LinesBegin()) {
+ // Only add text-indent if it has no percentages; using a
+ // percentage basis of 0 unconditionally would give strange
+ // behavior for calc(10%-3px).
+ const nsStyleCoord &indent = StyleText()->mTextIndent;
+ if (indent.ConvertsToLength())
+ data.mCurrentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
+ }
+ // XXX Bug NNNNNN Should probably handle percentage text-indent.
+
+ data.mLine = &line;
+ data.SetLineContainer(curFrame);
+ nsIFrame *kid = line->mFirstChild;
+ for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
+ ++i, kid = kid->GetNextSibling()) {
+ kid->AddInlineMinISize(aRenderingContext, &data);
+ }
+ }
+#ifdef DEBUG
+ if (gNoisyIntrinsic) {
+ IndentBy(stdout, gNoiseIndent);
+ printf("min: [prevLines=%d currentLine=%d]\n",
+ data.mPrevLines, data.mCurrentLine);
+ }
+#endif
+ }
+ }
+ data.ForceBreak();
+
+ mMinWidth = data.mPrevLines;
+ return mMinWidth;
+}
+
+/* virtual */ nscoord
+nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nsIFrame* firstInFlow = FirstContinuation();
+ if (firstInFlow != this)
+ return firstInFlow->GetPrefISize(aRenderingContext);
+
+ DISPLAY_PREF_WIDTH(this, mPrefWidth);
+
+ CheckIntrinsicCacheAgainstShrinkWrapState();
+
+ if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
+ return mPrefWidth;
+
+#ifdef DEBUG
+ if (gNoisyIntrinsic) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": GetPrefISize\n");
+ }
+ AutoNoisyIndenter indenter(gNoisyIntrinsic);
+#endif
+
+ for (nsBlockFrame* curFrame = this; curFrame;
+ curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
+ curFrame->LazyMarkLinesDirty();
+ }
+
+ if (RenumberList()) {
+ AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
+ ResolveBidi();
+ InlinePrefISizeData data;
+ for (nsBlockFrame* curFrame = this; curFrame;
+ curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
+ for (LineIterator line = curFrame->LinesBegin(), line_end = curFrame->LinesEnd();
+ line != line_end; ++line)
+ {
+#ifdef DEBUG
+ if (gNoisyIntrinsic) {
+ IndentBy(stdout, gNoiseIndent);
+ printf("line (%s%s)\n",
+ line->IsBlock() ? "block" : "inline",
+ line->IsEmpty() ? ", empty" : "");
+ }
+ AutoNoisyIndenter lineindent(gNoisyIntrinsic);
+#endif
+ if (line->IsBlock()) {
+ data.ForceBreak();
+ data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ line->mFirstChild, nsLayoutUtils::PREF_ISIZE);
+ data.ForceBreak();
+ } else {
+ if (!curFrame->GetPrevContinuation() &&
+ line == curFrame->LinesBegin()) {
+ // Only add text-indent if it has no percentages; using a
+ // percentage basis of 0 unconditionally would give strange
+ // behavior for calc(10%-3px).
+ const nsStyleCoord &indent = StyleText()->mTextIndent;
+ if (indent.ConvertsToLength())
+ data.mCurrentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
+ }
+ // XXX Bug NNNNNN Should probably handle percentage text-indent.
+
+ data.mLine = &line;
+ data.SetLineContainer(curFrame);
+ nsIFrame *kid = line->mFirstChild;
+ for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
+ ++i, kid = kid->GetNextSibling()) {
+ kid->AddInlinePrefISize(aRenderingContext, &data);
+ }
+ }
+#ifdef DEBUG
+ if (gNoisyIntrinsic) {
+ IndentBy(stdout, gNoiseIndent);
+ printf("pref: [prevLines=%d currentLine=%d]\n",
+ data.mPrevLines, data.mCurrentLine);
+ }
+#endif
+ }
+ }
+ data.ForceBreak();
+
+ mPrefWidth = data.mPrevLines;
+ return mPrefWidth;
+}
+
+nsRect
+nsBlockFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const
+{
+ // be conservative
+ if (StyleContext()->HasTextDecorationLines()) {
+ return GetVisualOverflowRect();
+ }
+ return ComputeSimpleTightBounds(aDrawTarget);
+}
+
+/* virtual */ nsresult
+nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext,
+ nscoord* aX,
+ nscoord* aXMost)
+{
+ nsIFrame* firstInFlow = FirstContinuation();
+ if (firstInFlow != this) {
+ return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost);
+ }
+
+ *aX = 0;
+ *aXMost = 0;
+
+ nsresult rv;
+ InlinePrefISizeData data;
+ for (nsBlockFrame* curFrame = this; curFrame;
+ curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
+ for (LineIterator line = curFrame->LinesBegin(), line_end = curFrame->LinesEnd();
+ line != line_end; ++line)
+ {
+ nscoord childX, childXMost;
+ if (line->IsBlock()) {
+ data.ForceBreak();
+ rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext,
+ &childX, &childXMost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aX = std::min(*aX, childX);
+ *aXMost = std::max(*aXMost, childXMost);
+ } else {
+ if (!curFrame->GetPrevContinuation() &&
+ line == curFrame->LinesBegin()) {
+ // Only add text-indent if it has no percentages; using a
+ // percentage basis of 0 unconditionally would give strange
+ // behavior for calc(10%-3px).
+ const nsStyleCoord &indent = StyleText()->mTextIndent;
+ if (indent.ConvertsToLength()) {
+ data.mCurrentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
+ }
+ }
+ // XXX Bug NNNNNN Should probably handle percentage text-indent.
+
+ data.mLine = &line;
+ data.SetLineContainer(curFrame);
+ nsIFrame *kid = line->mFirstChild;
+ for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
+ ++i, kid = kid->GetNextSibling()) {
+ rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX,
+ &childXMost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aX = std::min(*aX, data.mCurrentLine + childX);
+ *aXMost = std::max(*aXMost, data.mCurrentLine + childXMost);
+ kid->AddInlinePrefISize(aRenderingContext, &data);
+ }
+ }
+ }
+ }
+ data.ForceBreak();
+
+ return NS_OK;
+}
+
+/**
+ * Return whether aNewAvailableSpace is smaller *on either side*
+ * (inline-start or inline-end) than aOldAvailableSpace, so that we know
+ * if we need to redo layout on an line, replaced block, or block
+ * formatting context, because its height (which we used to compute
+ * aNewAvailableSpace) caused it to intersect additional floats.
+ */
+static bool
+AvailableSpaceShrunk(WritingMode aWM,
+ const LogicalRect& aOldAvailableSpace,
+ const LogicalRect& aNewAvailableSpace,
+ bool aCanGrow /* debug-only */)
+{
+ if (aNewAvailableSpace.ISize(aWM) == 0) {
+ // Positions are not significant if the inline size is zero.
+ return aOldAvailableSpace.ISize(aWM) != 0;
+ }
+ if (aCanGrow) {
+ NS_ASSERTION(aNewAvailableSpace.IStart(aWM) <=
+ aOldAvailableSpace.IStart(aWM) ||
+ aNewAvailableSpace.IEnd(aWM) <= aOldAvailableSpace.IEnd(aWM),
+ "available space should not shrink on the start side and "
+ "grow on the end side");
+ NS_ASSERTION(aNewAvailableSpace.IStart(aWM) >=
+ aOldAvailableSpace.IStart(aWM) ||
+ aNewAvailableSpace.IEnd(aWM) >= aOldAvailableSpace.IEnd(aWM),
+ "available space should not grow on the start side and "
+ "shrink on the end side");
+ } else {
+ NS_ASSERTION(aOldAvailableSpace.IStart(aWM) <=
+ aNewAvailableSpace.IStart(aWM) &&
+ aOldAvailableSpace.IEnd(aWM) >=
+ aNewAvailableSpace.IEnd(aWM),
+ "available space should never grow");
+ }
+ // Have we shrunk on either side?
+ return aNewAvailableSpace.IStart(aWM) > aOldAvailableSpace.IStart(aWM) ||
+ aNewAvailableSpace.IEnd(aWM) < aOldAvailableSpace.IEnd(aWM);
+}
+
+static LogicalSize
+CalculateContainingBlockSizeForAbsolutes(WritingMode aWM,
+ const ReflowInput& aReflowInput,
+ LogicalSize aFrameSize)
+{
+ // The issue here is that for a 'height' of 'auto' the reflow state
+ // code won't know how to calculate the containing block height
+ // because it's calculated bottom up. So we use our own computed
+ // size as the dimensions.
+ nsIFrame* frame = aReflowInput.mFrame;
+
+ LogicalSize cbSize(aFrameSize);
+ // Containing block is relative to the padding edge
+ const LogicalMargin& border =
+ LogicalMargin(aWM, aReflowInput.ComputedPhysicalBorderPadding() -
+ aReflowInput.ComputedPhysicalPadding());
+ cbSize.ISize(aWM) -= border.IStartEnd(aWM);
+ cbSize.BSize(aWM) -= border.BStartEnd(aWM);
+
+ if (frame->GetParent()->GetContent() == frame->GetContent() &&
+ frame->GetParent()->GetType() != nsGkAtoms::canvasFrame) {
+ // We are a wrapped frame for the content (and the wrapper is not the
+ // canvas frame, whose size is not meaningful here).
+ // Use the container's dimensions, if they have been precomputed.
+ // XXX This is a hack! We really should be waiting until the outermost
+ // frame is fully reflowed and using the resulting dimensions, even
+ // if they're intrinsic.
+ // In fact we should be attaching absolute children to the outermost
+ // frame and not always sticking them in block frames.
+
+ // First, find the reflow state for the outermost frame for this
+ // content, except for fieldsets where the inner anonymous frame has
+ // the correct padding area with the legend taken into account.
+ const ReflowInput* aLastRI = &aReflowInput;
+ const ReflowInput* lastButOneRI = &aReflowInput;
+ while (aLastRI->mParentReflowInput &&
+ aLastRI->mParentReflowInput->mFrame->GetContent() == frame->GetContent() &&
+ aLastRI->mParentReflowInput->mFrame->GetType() != nsGkAtoms::fieldSetFrame) {
+ lastButOneRI = aLastRI;
+ aLastRI = aLastRI->mParentReflowInput;
+ }
+ if (aLastRI != &aReflowInput) {
+ // Scrollbars need to be specifically excluded, if present, because they are outside the
+ // padding-edge. We need better APIs for getting the various boxes from a frame.
+ nsIScrollableFrame* scrollFrame = do_QueryFrame(aLastRI->mFrame);
+ nsMargin scrollbars(0,0,0,0);
+ if (scrollFrame) {
+ scrollbars =
+ scrollFrame->GetDesiredScrollbarSizes(aLastRI->mFrame->PresContext(),
+ aLastRI->mRenderingContext);
+ if (!lastButOneRI->mFlags.mAssumingHScrollbar) {
+ scrollbars.top = scrollbars.bottom = 0;
+ }
+ if (!lastButOneRI->mFlags.mAssumingVScrollbar) {
+ scrollbars.left = scrollbars.right = 0;
+ }
+ }
+ // We found a reflow state for the outermost wrapping frame, so use
+ // its computed metrics if available, converted to our writing mode
+ WritingMode lastWM = aLastRI->GetWritingMode();
+ LogicalSize lastRISize =
+ LogicalSize(lastWM,
+ aLastRI->ComputedISize(),
+ aLastRI->ComputedBSize()).ConvertTo(aWM, lastWM);
+ LogicalMargin lastRIPadding =
+ aLastRI->ComputedLogicalPadding().ConvertTo(aWM, lastWM);
+ LogicalMargin logicalScrollbars(aWM, scrollbars);
+ if (lastRISize.ISize(aWM) != NS_UNCONSTRAINEDSIZE) {
+ cbSize.ISize(aWM) = std::max(0, lastRISize.ISize(aWM) +
+ lastRIPadding.IStartEnd(aWM) -
+ logicalScrollbars.IStartEnd(aWM));
+ }
+ if (lastRISize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
+ cbSize.BSize(aWM) = std::max(0, lastRISize.BSize(aWM) +
+ lastRIPadding.BStartEnd(aWM) -
+ logicalScrollbars.BStartEnd(aWM));
+ }
+ }
+ }
+
+ return cbSize;
+}
+
+void
+nsBlockFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
+ aReflowInput.AvailableISize(), aReflowInput.AvailableBSize(),
+ aReflowInput.ComputedISize(), aReflowInput.ComputedBSize());
+ }
+ AutoNoisyIndenter indent(gNoisy);
+ PRTime start = 0; // Initialize these variablies to silence the compiler.
+ int32_t ctc = 0; // We only use these if they are set (gLameReflowMetrics).
+ if (gLameReflowMetrics) {
+ start = PR_Now();
+ ctc = nsLineBox::GetCtorCount();
+ }
+#endif
+
+ const ReflowInput *reflowInput = &aReflowInput;
+ WritingMode wm = aReflowInput.GetWritingMode();
+ nscoord consumedBSize = GetConsumedBSize();
+ nscoord effectiveComputedBSize = GetEffectiveComputedBSize(aReflowInput,
+ consumedBSize);
+ Maybe<ReflowInput> mutableReflowInput;
+ // If we have non-auto block size, we're clipping our kids and we fit,
+ // make sure our kids fit too.
+ if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
+ aReflowInput.ComputedBSize() != NS_AUTOHEIGHT &&
+ ShouldApplyOverflowClipping(this, aReflowInput.mStyleDisplay)) {
+ LogicalMargin blockDirExtras = aReflowInput.ComputedLogicalBorderPadding();
+ if (GetLogicalSkipSides().BStart()) {
+ blockDirExtras.BStart(wm) = 0;
+ } else {
+ // Block-end margin never causes us to create continuations, so we
+ // don't need to worry about whether it fits in its entirety.
+ blockDirExtras.BStart(wm) +=
+ aReflowInput.ComputedLogicalMargin().BStart(wm);
+ }
+
+ if (effectiveComputedBSize + blockDirExtras.BStartEnd(wm) <=
+ aReflowInput.AvailableBSize()) {
+ mutableReflowInput.emplace(aReflowInput);
+ mutableReflowInput->AvailableBSize() = NS_UNCONSTRAINEDSIZE;
+ reflowInput = mutableReflowInput.ptr();
+ }
+ }
+
+ // See comment below about oldSize. Use *only* for the
+ // abs-pos-containing-block-size-change optimization!
+ nsSize oldSize = GetSize();
+
+ // Should we create a float manager?
+ nsAutoFloatManager autoFloatManager(const_cast<ReflowInput&>(*reflowInput));
+
+ // XXXldb If we start storing the float manager in the frame rather
+ // than keeping it around only during reflow then we should create it
+ // only when there are actually floats to manage. Otherwise things
+ // like tables will gain significant bloat.
+ bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this);
+ if (needFloatManager)
+ autoFloatManager.CreateFloatManager(aPresContext);
+
+ // OK, some lines may be reflowed. Blow away any saved line cursor
+ // because we may invalidate the nondecreasing
+ // overflowArea.VisualOverflow().y/yMost invariant, and we may even
+ // delete the line with the line cursor.
+ ClearLineCursor();
+
+ if (IsFrameTreeTooDeep(*reflowInput, aMetrics, aStatus)) {
+ return;
+ }
+
+#ifdef DEBUG
+ // Between when we drain pushed floats and when we complete reflow,
+ // we're allowed to have multiple continuations of the same float on
+ // our floats list, since a first-in-flow might get pushed to a later
+ // continuation of its containing block. But it's not permitted
+ // outside that time.
+ nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
+#endif
+
+ // ALWAYS drain overflow. We never want to leave the previnflow's
+ // overflow lines hanging around; block reflow depends on the
+ // overflow line lists being cleared out between reflow passes.
+ DrainOverflowLines();
+
+ bool blockStartMarginRoot, blockEndMarginRoot;
+ IsMarginRoot(&blockStartMarginRoot, &blockEndMarginRoot);
+
+ // Cache the consumed height in the block reflow state so that we don't have
+ // to continually recompute it.
+ BlockReflowInput state(*reflowInput, aPresContext, this,
+ blockStartMarginRoot, blockEndMarginRoot,
+ needFloatManager, consumedBSize);
+
+ if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
+ static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
+
+ if (RenumberList()) {
+ AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+
+ // Handle paginated overflow (see nsContainerFrame.h)
+ nsOverflowAreas ocBounds;
+ nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
+ if (GetPrevInFlow()) {
+ ReflowOverflowContainerChildren(aPresContext, *reflowInput, ocBounds, 0,
+ ocStatus);
+ }
+
+ // Now that we're done cleaning up our overflow container lists, we can
+ // give |state| its nsOverflowContinuationTracker.
+ nsOverflowContinuationTracker tracker(this, false);
+ state.mOverflowTracker = &tracker;
+
+ // Drain & handle pushed floats
+ DrainPushedFloats();
+ nsOverflowAreas fcBounds;
+ nsReflowStatus fcStatus = NS_FRAME_COMPLETE;
+ ReflowPushedFloats(state, fcBounds, fcStatus);
+
+ // If we're not dirty (which means we'll mark everything dirty later)
+ // and our inline-size has changed, mark the lines dirty that we need to
+ // mark dirty for a resize reflow.
+ if (!(GetStateBits() & NS_FRAME_IS_DIRTY) && reflowInput->IsIResize()) {
+ PrepareResizeReflow(state);
+ }
+
+ // The same for percentage text-indent, except conditioned on the
+ // parent resizing.
+ if (!(GetStateBits() & NS_FRAME_IS_DIRTY) &&
+ reflowInput->mCBReflowInput &&
+ reflowInput->mCBReflowInput->IsIResize() &&
+ reflowInput->mStyleText->mTextIndent.HasPercent() &&
+ !mLines.empty()) {
+ mLines.front()->MarkDirty();
+ }
+
+ LazyMarkLinesDirty();
+
+ mState &= ~NS_FRAME_FIRST_REFLOW;
+
+ // Now reflow...
+ ReflowDirtyLines(state);
+
+ // If we have a next-in-flow, and that next-in-flow has pushed floats from
+ // this frame from a previous iteration of reflow, then we should not return
+ // a status of NS_FRAME_IS_FULLY_COMPLETE, since we actually have overflow,
+ // it's just already been handled.
+
+ // NOTE: This really shouldn't happen, since we _should_ pull back our floats
+ // and reflow them, but just in case it does, this is a safety precaution so
+ // we don't end up with a placeholder pointing to frames that have already
+ // been deleted as part of removing our next-in-flow.
+ // XXXmats maybe this code isn't needed anymore?
+ // XXXmats (layout/generic/crashtests/600100.xhtml doesn't crash without it)
+ if (NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
+ nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow());
+ while (nif) {
+ if (nif->HasPushedFloatsFromPrevContinuation()) {
+ bool oc = nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER;
+ NS_MergeReflowStatusInto(&state.mReflowStatus,
+ oc ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE);
+ break;
+ }
+
+ nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow());
+ }
+ }
+
+ NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus);
+ NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus);
+
+ // If we end in a BR with clear and affected floats continue,
+ // we need to continue, too.
+ if (NS_UNCONSTRAINEDSIZE != reflowInput->AvailableBSize() &&
+ NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
+ state.mFloatManager->ClearContinues(FindTrailingClear())) {
+ NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
+ }
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
+ if (HasOverflowLines() || HasPushedFloats()) {
+ state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+
+#ifdef DEBUG_kipp
+ ListTag(stdout); printf(": block is not fully complete\n");
+#endif
+ }
+
+ // Place the "marker" (bullet) frame if it is placed next to a block
+ // child.
+ //
+ // According to the CSS2 spec, section 12.6.1, the "marker" box
+ // participates in the height calculation of the list-item box's
+ // first line box.
+ //
+ // There are exactly two places a bullet can be placed: near the
+ // first or second line. It's only placed on the second line in a
+ // rare case: an empty first line followed by a second line that
+ // contains a block (example: <LI>\n<P>... ). This is where
+ // the second case can happen.
+ if (HasOutsideBullet() && !mLines.empty() &&
+ (mLines.front()->IsBlock() ||
+ (0 == mLines.front()->BSize() &&
+ mLines.front() != mLines.back() &&
+ mLines.begin().next()->IsBlock()))) {
+ // Reflow the bullet
+ ReflowOutput reflowOutput(aReflowInput);
+ // XXX Use the entire line when we fix bug 25888.
+ nsLayoutUtils::LinePosition position;
+ WritingMode wm = aReflowInput.GetWritingMode();
+ bool havePosition = nsLayoutUtils::GetFirstLinePosition(wm, this,
+ &position);
+ nscoord lineBStart = havePosition ?
+ position.mBStart :
+ reflowInput->ComputedLogicalBorderPadding().BStart(wm);
+ nsIFrame* bullet = GetOutsideBullet();
+ ReflowBullet(bullet, state, reflowOutput, lineBStart);
+ NS_ASSERTION(!BulletIsEmpty() || reflowOutput.BSize(wm) == 0,
+ "empty bullet took up space");
+
+ if (havePosition && !BulletIsEmpty()) {
+ // We have some lines to align the bullet with.
+
+ // Doing the alignment using the baseline will also cater for
+ // bullets that are placed next to a child block (bug 92896)
+
+ // Tall bullets won't look particularly nice here...
+ LogicalRect bbox = bullet->GetLogicalRect(wm, reflowOutput.PhysicalSize());
+ bbox.BStart(wm) = position.mBaseline - reflowOutput.BlockStartAscent();
+ bullet->SetRect(wm, bbox, reflowOutput.PhysicalSize());
+ }
+ // Otherwise just leave the bullet where it is, up against our
+ // block-start padding.
+ }
+
+ CheckFloats(state);
+
+ // Compute our final size
+ nscoord blockEndEdgeOfChildren;
+ ComputeFinalSize(*reflowInput, state, aMetrics, &blockEndEdgeOfChildren);
+
+ // If the block direction is right-to-left, we need to update the bounds of
+ // lines that were placed relative to mContainerSize during reflow, as
+ // we typically do not know the true container size until we've reflowed all
+ // its children. So we use a dummy mContainerSize during reflow (see
+ // BlockReflowInput's constructor) and then fix up the positions of the
+ // lines here, once the final block size is known.
+ //
+ // Note that writing-mode:vertical-rl is the only case where the block
+ // logical direction progresses in a negative physical direction, and
+ // therefore block-dir coordinate conversion depends on knowing the width
+ // of the coordinate space in order to translate between the logical and
+ // physical origins.
+ if (wm.IsVerticalRL()) {
+ nsSize containerSize = aMetrics.PhysicalSize();
+ nscoord deltaX = containerSize.width - state.ContainerSize().width;
+ if (deltaX != 0) {
+ for (LineIterator line = LinesBegin(), end = LinesEnd();
+ line != end; line++) {
+ UpdateLineContainerSize(line, containerSize);
+ }
+ for (nsIFrame* f : mFloats) {
+ nsPoint physicalDelta(deltaX, 0);
+ f->MovePositionBy(physicalDelta);
+ }
+ nsFrameList* bulletList = GetOutsideBulletList();
+ if (bulletList) {
+ nsPoint physicalDelta(deltaX, 0);
+ for (nsIFrame* f : *bulletList) {
+ f->MovePositionBy(physicalDelta);
+ }
+ }
+ }
+ }
+
+ nsRect areaBounds = nsRect(0, 0, aMetrics.Width(), aMetrics.Height());
+ ComputeOverflowAreas(areaBounds, reflowInput->mStyleDisplay,
+ blockEndEdgeOfChildren, aMetrics.mOverflowAreas);
+ // Factor overflow container child bounds into the overflow area
+ aMetrics.mOverflowAreas.UnionWith(ocBounds);
+ // Factor pushed float child bounds into the overflow area
+ aMetrics.mOverflowAreas.UnionWith(fcBounds);
+
+ // Let the absolutely positioned container reflow any absolutely positioned
+ // child frames that need to be reflowed, e.g., elements with a percentage
+ // based width/height
+ // We want to do this under either of two conditions:
+ // 1. If we didn't do the incremental reflow above.
+ // 2. If our size changed.
+ // Even though it's the padding edge that's the containing block, we
+ // can use our rect (the border edge) since if the border style
+ // changed, the reflow would have been targeted at us so we'd satisfy
+ // condition 1.
+ // XXX checking oldSize is bogus, there are various reasons we might have
+ // reflowed but our size might not have been changed to what we
+ // asked for (e.g., we ended up being pushed to a new page)
+ // When WillReflowAgainForClearance is true, we will reflow again without
+ // resetting the size. Because of this, we must not reflow our abs-pos children
+ // in that situation --- what we think is our "new size"
+ // will not be our real new size. This also happens to be more efficient.
+ WritingMode parentWM = aMetrics.GetWritingMode();
+ if (HasAbsolutelyPositionedChildren()) {
+ nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
+ bool haveInterrupt = aPresContext->HasPendingInterrupt();
+ if (reflowInput->WillReflowAgainForClearance() ||
+ haveInterrupt) {
+ // Make sure that when we reflow again we'll actually reflow all the abs
+ // pos frames that might conceivably depend on our size (or all of them,
+ // if we're dirty right now and interrupted; in that case we also need
+ // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
+ // better than that, because we don't really know what our size will be,
+ // and it might in fact not change on the followup reflow!
+ if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) {
+ absoluteContainer->MarkAllFramesDirty();
+ } else {
+ absoluteContainer->MarkSizeDependentFramesDirty();
+ }
+ } else {
+ LogicalSize containingBlockSize =
+ CalculateContainingBlockSizeForAbsolutes(parentWM, *reflowInput,
+ aMetrics.Size(parentWM));
+
+ // Mark frames that depend on changes we just made to this frame as dirty:
+ // Now we can assume that the padding edge hasn't moved.
+ // We need to reflow the absolutes if one of them depends on
+ // its placeholder position, or the containing block size in a
+ // direction in which the containing block size might have
+ // changed.
+
+ // XXX "width" and "height" in this block will become ISize and BSize
+ // when nsAbsoluteContainingBlock is logicalized
+ bool cbWidthChanged = aMetrics.Width() != oldSize.width;
+ bool isRoot = !GetContent()->GetParent();
+ // If isRoot and we have auto height, then we are the initial
+ // containing block and the containing block height is the
+ // viewport height, which can't change during incremental
+ // reflow.
+ bool cbHeightChanged =
+ !(isRoot && NS_UNCONSTRAINEDSIZE == reflowInput->ComputedHeight()) &&
+ aMetrics.Height() != oldSize.height;
+
+ nsRect containingBlock(nsPoint(0, 0),
+ containingBlockSize.GetPhysicalSize(parentWM));
+ AbsPosReflowFlags flags = AbsPosReflowFlags::eConstrainHeight;
+ if (cbWidthChanged) {
+ flags |= AbsPosReflowFlags::eCBWidthChanged;
+ }
+ if (cbHeightChanged) {
+ flags |= AbsPosReflowFlags::eCBHeightChanged;
+ }
+ // Setup the line cursor here to optimize line searching for
+ // calculating hypothetical position of absolutely-positioned
+ // frames. The line cursor is immediately cleared afterward to
+ // avoid affecting the display list generation.
+ AutoLineCursorSetup autoLineCursor(this);
+ absoluteContainer->Reflow(this, aPresContext, *reflowInput,
+ state.mReflowStatus,
+ containingBlock, flags,
+ &aMetrics.mOverflowAreas);
+ }
+ }
+
+ FinishAndStoreOverflow(&aMetrics);
+
+ // Clear the float manager pointer in the block reflow state so we
+ // don't waste time translating the coordinate system back on a dead
+ // float manager.
+ if (needFloatManager)
+ state.mFloatManager = nullptr;
+
+ aStatus = state.mReflowStatus;
+
+#ifdef DEBUG
+ // Between when we drain pushed floats and when we complete reflow,
+ // we're allowed to have multiple continuations of the same float on
+ // our floats list, since a first-in-flow might get pushed to a later
+ // continuation of its containing block. But it's not permitted
+ // outside that time.
+ nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
+
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
+ aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
+ aMetrics.ISize(parentWM), aMetrics.BSize(parentWM),
+ aMetrics.mCarriedOutBEndMargin.get());
+ if (HasOverflowAreas()) {
+ printf(" overflow-vis={%d,%d,%d,%d}",
+ aMetrics.VisualOverflow().x,
+ aMetrics.VisualOverflow().y,
+ aMetrics.VisualOverflow().width,
+ aMetrics.VisualOverflow().height);
+ printf(" overflow-scr={%d,%d,%d,%d}",
+ aMetrics.ScrollableOverflow().x,
+ aMetrics.ScrollableOverflow().y,
+ aMetrics.ScrollableOverflow().width,
+ aMetrics.ScrollableOverflow().height);
+ }
+ printf("\n");
+ }
+
+ if (gLameReflowMetrics) {
+ PRTime end = PR_Now();
+
+ int32_t ectc = nsLineBox::GetCtorCount();
+ int32_t numLines = mLines.size();
+ if (!numLines) numLines = 1;
+ PRTime delta, perLineDelta, lines;
+ lines = int64_t(numLines);
+ delta = end - start;
+ perLineDelta = delta / lines;
+
+ ListTag(stdout);
+ char buf[400];
+ SprintfLiteral(buf,
+ ": %" PRId64 " elapsed (%" PRId64 " per line) (%d lines; %d new lines)",
+ delta, perLineDelta, numLines, ectc - ctc);
+ printf("%s\n", buf);
+ }
+#endif
+
+ NS_FRAME_SET_TRUNCATION(aStatus, (*reflowInput), aMetrics);
+}
+
+bool
+nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine()
+{
+ LineIterator begin = LinesBegin();
+ LineIterator line = LinesEnd();
+
+ while (true) {
+ if (begin == line) {
+ return false;
+ }
+ --line;
+ if (line->BSize() != 0 || !line->CachedIsEmpty()) {
+ return false;
+ }
+ if (line->HasClearance()) {
+ return true;
+ }
+ }
+ // not reached
+}
+
+void
+nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
+ BlockReflowInput& aState,
+ ReflowOutput& aMetrics,
+ nscoord* aBEndEdgeOfChildren)
+{
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ const LogicalMargin& borderPadding = aState.BorderPadding();
+#ifdef NOISY_FINAL_SIZE
+ ListTag(stdout);
+ printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
+ aState.mBCoord, aState.mFlags.mIsBEndMarginRoot ? "yes" : "no",
+ aState.mPrevBEndMargin.get(),
+ borderPadding.BStart(wm), borderPadding.BEnd(wm));
+#endif
+
+ // Compute final inline size
+ LogicalSize finalSize(wm);
+ finalSize.ISize(wm) =
+ NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.IStart(wm),
+ aReflowInput.ComputedISize()),
+ borderPadding.IEnd(wm));
+
+ // Return block-end margin information
+ // rbs says he hit this assertion occasionally (see bug 86947), so
+ // just set the margin to zero and we'll figure out why later
+ //NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
+ // "someone else set the margin");
+ nscoord nonCarriedOutBDirMargin = 0;
+ if (!aState.mFlags.mIsBEndMarginRoot) {
+ // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
+ // line with clearance and a non-zero block-start margin and all
+ // subsequent lines are empty, then we do not allow our children's
+ // carried out block-end margin to be carried out of us and collapse
+ // with our own block-end margin.
+ if (CheckForCollapsedBEndMarginFromClearanceLine()) {
+ // Convert the children's carried out margin to something that
+ // we will include in our height
+ nonCarriedOutBDirMargin = aState.mPrevBEndMargin.get();
+ aState.mPrevBEndMargin.Zero();
+ }
+ aMetrics.mCarriedOutBEndMargin = aState.mPrevBEndMargin;
+ } else {
+ aMetrics.mCarriedOutBEndMargin.Zero();
+ }
+
+ nscoord blockEndEdgeOfChildren = aState.mBCoord + nonCarriedOutBDirMargin;
+ // Shrink wrap our height around our contents.
+ if (aState.mFlags.mIsBEndMarginRoot ||
+ NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) {
+ // When we are a block-end-margin root make sure that our last
+ // childs block-end margin is fully applied. We also do this when
+ // we have a computed height, since in that case the carried out
+ // margin is not going to be applied anywhere, so we should note it
+ // here to be included in the overflow area.
+ // Apply the margin only if there's space for it.
+ if (blockEndEdgeOfChildren < aState.mReflowInput.AvailableBSize())
+ {
+ // Truncate block-end margin if it doesn't fit to our available BSize.
+ blockEndEdgeOfChildren =
+ std::min(blockEndEdgeOfChildren + aState.mPrevBEndMargin.get(),
+ aState.mReflowInput.AvailableBSize());
+ }
+ }
+ if (aState.mFlags.mBlockNeedsFloatManager) {
+ // Include the float manager's state to properly account for the
+ // block-end margin of any floated elements; e.g., inside a table cell.
+ nscoord floatHeight =
+ aState.ClearFloats(blockEndEdgeOfChildren, StyleClear::Both,
+ nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS);
+ blockEndEdgeOfChildren = std::max(blockEndEdgeOfChildren, floatHeight);
+ }
+
+ if (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()
+ && (GetParent()->GetType() != nsGkAtoms::columnSetFrame ||
+ aReflowInput.mParentReflowInput->AvailableBSize() == NS_UNCONSTRAINEDSIZE)) {
+ ComputeFinalBSize(aReflowInput, &aState.mReflowStatus,
+ aState.mBCoord + nonCarriedOutBDirMargin,
+ borderPadding, finalSize, aState.mConsumedBSize);
+ if (!NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
+ // Use the current height; continuations will take up the rest.
+ // Do extend the height to at least consume the available
+ // height, otherwise our left/right borders (for example) won't
+ // extend all the way to the break.
+ finalSize.BSize(wm) = std::max(aReflowInput.AvailableBSize(),
+ aState.mBCoord + nonCarriedOutBDirMargin);
+ // ... but don't take up more block size than is available
+ nscoord effectiveComputedBSize =
+ GetEffectiveComputedBSize(aReflowInput, aState.GetConsumedBSize());
+ finalSize.BSize(wm) =
+ std::min(finalSize.BSize(wm),
+ borderPadding.BStart(wm) + effectiveComputedBSize);
+ // XXX It's pretty wrong that our bottom border still gets drawn on
+ // on its own on the last-in-flow, even if we ran out of height
+ // here. We need GetSkipSides to check whether we ran out of content
+ // height in the current frame, not whether it's last-in-flow.
+ }
+
+ // Don't carry out a block-end margin when our BSize is fixed.
+ aMetrics.mCarriedOutBEndMargin.Zero();
+ }
+ else if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
+ nscoord contentBSize = blockEndEdgeOfChildren - borderPadding.BStart(wm);
+ nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(contentBSize);
+ if (autoBSize != contentBSize) {
+ // Our min- or max-bsize value made our bsize change. Don't carry out
+ // our kids' block-end margins.
+ aMetrics.mCarriedOutBEndMargin.Zero();
+ }
+ autoBSize += borderPadding.BStart(wm) + borderPadding.BEnd(wm);
+ finalSize.BSize(wm) = autoBSize;
+ }
+ else {
+ NS_ASSERTION(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
+ "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
+ finalSize.BSize(wm) = std::max(aState.mBCoord,
+ aReflowInput.AvailableBSize());
+ if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
+ // This should never happen, but it does. See bug 414255
+ finalSize.BSize(wm) = aState.mBCoord;
+ }
+ }
+
+ if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
+ if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
+ // Overflow containers can only be overflow complete.
+ // Note that auto height overflow containers have no normal children
+ NS_ASSERTION(finalSize.BSize(wm) == 0,
+ "overflow containers must be zero-block-size");
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
+ }
+ } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
+ !NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus) &&
+ NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
+ // Currently only used for grid items, but could be used in other contexts.
+ // The FragStretchBSizeProperty is our expected non-fragmented block-size
+ // we should stretch to (for align-self:stretch etc). In some fragmentation
+ // cases though, the last fragment (this frame since we're complete), needs
+ // to have extra size applied because earlier fragments consumed too much of
+ // our computed size due to overflowing their containing block. (E.g. this
+ // ensures we fill the last row when a multi-row grid item is fragmented).
+ bool found;
+ nscoord bSize = Properties().Get(FragStretchBSizeProperty(), &found);
+ if (found) {
+ finalSize.BSize(wm) = std::max(bSize, finalSize.BSize(wm));
+ }
+ }
+
+ // Clamp the content size to fit within the margin-box clamp size, if any.
+ if (MOZ_UNLIKELY(aReflowInput.mFlags.mBClampMarginBoxMinSize) &&
+ NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
+ bool found;
+ nscoord cbSize = Properties().Get(BClampMarginBoxMinSizeProperty(), &found);
+ if (found) {
+ auto marginBoxBSize = finalSize.BSize(wm) +
+ aReflowInput.ComputedLogicalMargin().BStartEnd(wm);
+ auto overflow = marginBoxBSize - cbSize;
+ if (overflow > 0) {
+ auto contentBSize = finalSize.BSize(wm) - borderPadding.BStartEnd(wm);
+ auto newContentBSize = std::max(nscoord(0), contentBSize - overflow);
+ // XXXmats deal with percentages better somehow?
+ finalSize.BSize(wm) -= contentBSize - newContentBSize;
+ }
+ }
+ }
+
+ // Screen out negative block sizes --- can happen due to integer overflows :-(
+ finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
+ *aBEndEdgeOfChildren = blockEndEdgeOfChildren;
+
+ FrameProperties properties = Properties();
+ if (blockEndEdgeOfChildren != finalSize.BSize(wm) - borderPadding.BEnd(wm)) {
+ properties.Set(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren);
+ } else {
+ properties.Delete(BlockEndEdgeOfChildrenProperty());
+ }
+
+ aMetrics.SetSize(wm, finalSize);
+
+#ifdef DEBUG_blocks
+ if ((CRAZY_SIZE(aMetrics.Width()) || CRAZY_SIZE(aMetrics.Height())) &&
+ !GetParent()->IsCrazySizeAssertSuppressed()) {
+ ListTag(stdout);
+ printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
+ }
+#endif
+}
+
+static void
+ConsiderBlockEndEdgeOfChildren(const WritingMode aWritingMode,
+ nscoord aBEndEdgeOfChildren,
+ nsOverflowAreas& aOverflowAreas)
+{
+ // Factor in the block-end edge of the children. Child frames will be added
+ // to the overflow area as we iterate through the lines, but their margins
+ // won't, so we need to account for block-end margins here.
+ // REVIEW: For now, we do this for both visual and scrollable area,
+ // although when we make scrollable overflow area not be a subset of
+ // visual, we can change this.
+ // XXX Currently, overflow areas are stored as physical rects, so we have
+ // to handle writing modes explicitly here. If we change overflow rects
+ // to be stored logically, this can be simplified again.
+ if (aWritingMode.IsVertical()) {
+ if (aWritingMode.IsVerticalLR()) {
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = aOverflowAreas.Overflow(otype);
+ o.width = std::max(o.XMost(), aBEndEdgeOfChildren) - o.x;
+ }
+ } else {
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = aOverflowAreas.Overflow(otype);
+ nscoord xmost = o.XMost();
+ o.x = std::min(o.x, xmost - aBEndEdgeOfChildren);
+ o.width = xmost - o.x;
+ }
+ }
+ } else {
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = aOverflowAreas.Overflow(otype);
+ o.height = std::max(o.YMost(), aBEndEdgeOfChildren) - o.y;
+ }
+ }
+}
+
+void
+nsBlockFrame::ComputeOverflowAreas(const nsRect& aBounds,
+ const nsStyleDisplay* aDisplay,
+ nscoord aBEndEdgeOfChildren,
+ nsOverflowAreas& aOverflowAreas)
+{
+ // Compute the overflow areas of our children
+ // XXX_perf: This can be done incrementally. It is currently one of
+ // the things that makes incremental reflow O(N^2).
+ nsOverflowAreas areas(aBounds, aBounds);
+ if (!ShouldApplyOverflowClipping(this, aDisplay)) {
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line) {
+ areas.UnionWith(line->GetOverflowAreas());
+ }
+
+ // Factor an outside bullet in; normally the bullet will be factored into
+ // the line-box's overflow areas. However, if the line is a block
+ // line then it won't; if there are no lines, it won't. So just
+ // factor it in anyway (it can't hurt if it was already done).
+ // XXXldb Can we just fix GetOverflowArea instead?
+ nsIFrame* outsideBullet = GetOutsideBullet();
+ if (outsideBullet) {
+ areas.UnionAllWith(outsideBullet->GetRect());
+ }
+
+ ConsiderBlockEndEdgeOfChildren(GetWritingMode(),
+ aBEndEdgeOfChildren, areas);
+ }
+
+#ifdef NOISY_COMBINED_AREA
+ ListTag(stdout);
+ const nsRect& vis = areas.VisualOverflow();
+ printf(": VisualOverflowArea CA=%d,%d,%d,%d\n", vis.x, vis.y, vis.width, vis.height);
+ const nsRect& scr = areas.ScrollableOverflow();
+ printf(": ScrollableOverflowArea CA=%d,%d,%d,%d\n", scr.x, scr.y, scr.width, scr.height);
+#endif
+
+ aOverflowAreas = areas;
+}
+
+void
+nsBlockFrame::UnionChildOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ // We need to update the overflow areas of lines manually, as they
+ // get cached and re-used otherwise. Lines aren't exposed as normal
+ // frame children, so calling UnionChildOverflow alone will end up
+ // using the old cached values.
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line) {
+ nsRect bounds = line->GetPhysicalBounds();
+ nsOverflowAreas lineAreas(bounds, bounds);
+
+ int32_t n = line->GetChildCount();
+ for (nsIFrame* lineFrame = line->mFirstChild;
+ n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
+ ConsiderChildOverflow(lineAreas, lineFrame);
+ }
+
+ // Consider the overflow areas of the floats attached to the line as well
+ if (line->HasFloats()) {
+ for (nsFloatCache* fc = line->GetFirstFloat(); fc; fc = fc->Next()) {
+ ConsiderChildOverflow(lineAreas, fc->mFloat);
+ }
+ }
+
+ line->SetOverflowAreas(lineAreas);
+ aOverflowAreas.UnionWith(lineAreas);
+ }
+
+ // Union with child frames, skipping the principal and float lists
+ // since we already handled those using the line boxes.
+ nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas,
+ kPrincipalList | kFloatList);
+}
+
+bool
+nsBlockFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ bool found;
+ nscoord blockEndEdgeOfChildren =
+ Properties().Get(BlockEndEdgeOfChildrenProperty(), &found);
+ if (found) {
+ ConsiderBlockEndEdgeOfChildren(GetWritingMode(),
+ blockEndEdgeOfChildren, aOverflowAreas);
+ }
+
+ // Line cursor invariants depend on the overflow areas of the lines, so
+ // we must clear the line cursor since those areas may have changed.
+ ClearLineCursor();
+ return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
+}
+
+void
+nsBlockFrame::LazyMarkLinesDirty()
+{
+ if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES) {
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end; ++line) {
+ int32_t n = line->GetChildCount();
+ for (nsIFrame* lineFrame = line->mFirstChild;
+ n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
+ if (NS_SUBTREE_DIRTY(lineFrame)) {
+ // NOTE: MarkLineDirty does more than just marking the line dirty.
+ MarkLineDirty(line, &mLines);
+ break;
+ }
+ }
+ }
+ RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
+ }
+}
+
+void
+nsBlockFrame::MarkLineDirty(LineIterator aLine, const nsLineList* aLineList)
+{
+ // Mark aLine dirty
+ aLine->MarkDirty();
+ aLine->SetInvalidateTextRuns(true);
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
+ }
+#endif
+
+ // Mark previous line dirty if it's an inline line so that it can
+ // maybe pullup something from the line just affected.
+ // XXX We don't need to do this if aPrevLine ends in a break-after...
+ if (aLine != aLineList->front() && aLine->IsInline() &&
+ aLine.prev()->IsInline()) {
+ aLine.prev()->MarkDirty();
+ aLine.prev()->SetInvalidateTextRuns(true);
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": mark prev-line %p dirty\n",
+ static_cast<void*>(aLine.prev().get()));
+ }
+#endif
+ }
+}
+
+/**
+ * Test whether lines are certain to be aligned left so that we can make
+ * resizing optimizations
+ */
+static inline bool
+IsAlignedLeft(uint8_t aAlignment,
+ uint8_t aDirection,
+ uint8_t aUnicodeBidi,
+ nsIFrame* aFrame)
+{
+ return aFrame->IsSVGText() ||
+ NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
+ (((NS_STYLE_TEXT_ALIGN_START == aAlignment &&
+ NS_STYLE_DIRECTION_LTR == aDirection) ||
+ (NS_STYLE_TEXT_ALIGN_END == aAlignment &&
+ NS_STYLE_DIRECTION_RTL == aDirection)) &&
+ !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
+}
+
+void
+nsBlockFrame::PrepareResizeReflow(BlockReflowInput& aState)
+{
+ // See if we can try and avoid marking all the lines as dirty
+ bool tryAndSkipLines =
+ // The left content-edge must be a constant distance from the left
+ // border-edge.
+ !StylePadding()->mPadding.GetLeft().HasPercent();
+
+#ifdef DEBUG
+ if (gDisableResizeOpt) {
+ tryAndSkipLines = false;
+ }
+ if (gNoisyReflow) {
+ if (!tryAndSkipLines) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": marking all lines dirty: availISize=%d\n",
+ aState.mReflowInput.AvailableISize());
+ }
+ }
+#endif
+
+ if (tryAndSkipLines) {
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ nscoord newAvailISize =
+ aState.mReflowInput.ComputedLogicalBorderPadding().IStart(wm) +
+ aState.mReflowInput.ComputedISize();
+ NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowInput.ComputedLogicalBorderPadding().IStart(wm) &&
+ NS_UNCONSTRAINEDSIZE != aState.mReflowInput.ComputedISize(),
+ "math on NS_UNCONSTRAINEDSIZE");
+
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": trying to avoid marking all lines dirty\n");
+ }
+#endif
+
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line)
+ {
+ // We let child blocks make their own decisions the same
+ // way we are here.
+ bool isLastLine = line == mLines.back() && !GetNextInFlow();
+ if (line->IsBlock() ||
+ line->HasFloats() ||
+ (!isLastLine && !line->HasBreakAfter()) ||
+ ((isLastLine || !line->IsLineWrapped())) ||
+ line->ResizeReflowOptimizationDisabled() ||
+ line->IsImpactedByFloat() ||
+ (line->IEnd() > newAvailISize)) {
+ line->MarkDirty();
+ }
+
+#ifdef REALLY_NOISY_REFLOW
+ if (!line->IsBlock()) {
+ printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
+ line.get(), line->IsImpactedByFloat() ? "" : "not ");
+ }
+#endif
+#ifdef DEBUG
+ if (gNoisyReflow && !line->IsDirty()) {
+ IndentBy(stdout, gNoiseIndent + 1);
+ printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%s/%s xmost=%d\n",
+ static_cast<void*>(line.get()),
+ static_cast<void*>((line.next() != LinesEnd() ? line.next().get() : nullptr)),
+ line->IsBlock() ? "block" : "inline",
+ line->HasBreakAfter() ? "has-break-after " : "",
+ line->HasFloats() ? "has-floats " : "",
+ line->IsImpactedByFloat() ? "impacted " : "",
+ line->BreakTypeToString(line->GetBreakTypeBefore()),
+ line->BreakTypeToString(line->GetBreakTypeAfter()),
+ line->IEnd());
+ }
+#endif
+ }
+ }
+ else {
+ // Mark everything dirty
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line)
+ {
+ line->MarkDirty();
+ }
+ }
+}
+
+//----------------------------------------
+
+/**
+ * Propagate reflow "damage" from from earlier lines to the current
+ * line. The reflow damage comes from the following sources:
+ * 1. The regions of float damage remembered during reflow.
+ * 2. The combination of nonzero |aDeltaBCoord| and any impact by a
+ * float, either the previous reflow or now.
+ *
+ * When entering this function, |aLine| is still at its old position and
+ * |aDeltaBCoord| indicates how much it will later be slid (assuming it
+ * doesn't get marked dirty and reflowed entirely).
+ */
+void
+nsBlockFrame::PropagateFloatDamage(BlockReflowInput& aState,
+ nsLineBox* aLine,
+ nscoord aDeltaBCoord)
+{
+ nsFloatManager *floatManager = aState.mReflowInput.mFloatManager;
+ NS_ASSERTION((aState.mReflowInput.mParentReflowInput &&
+ aState.mReflowInput.mParentReflowInput->mFloatManager == floatManager) ||
+ aState.mReflowInput.mBlockDelta == 0, "Bad block delta passed in");
+
+ // Check to see if there are any floats; if there aren't, there can't
+ // be any float damage
+ if (!floatManager->HasAnyFloats())
+ return;
+
+ // Check the damage region recorded in the float damage.
+ if (floatManager->HasFloatDamage()) {
+ // Need to check mBounds *and* mCombinedArea to find intersections
+ // with aLine's floats
+ nscoord lineBCoordBefore = aLine->BStart() + aDeltaBCoord;
+ nscoord lineBCoordAfter = lineBCoordBefore + aLine->BSize();
+ // Scrollable overflow should be sufficient for things that affect
+ // layout.
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ nsSize containerSize = aState.ContainerSize();
+ LogicalRect overflow = aLine->GetOverflowArea(eScrollableOverflow, wm,
+ containerSize);
+ nscoord lineBCoordCombinedBefore = overflow.BStart(wm) + aDeltaBCoord;
+ nscoord lineBCoordCombinedAfter = lineBCoordCombinedBefore +
+ overflow.BSize(wm);
+
+ bool isDirty = floatManager->IntersectsDamage(lineBCoordBefore,
+ lineBCoordAfter) ||
+ floatManager->IntersectsDamage(lineBCoordCombinedBefore,
+ lineBCoordCombinedAfter);
+ if (isDirty) {
+ aLine->MarkDirty();
+ return;
+ }
+ }
+
+ // Check if the line is moving relative to the float manager
+ if (aDeltaBCoord + aState.mReflowInput.mBlockDelta != 0) {
+ if (aLine->IsBlock()) {
+ // Unconditionally reflow sliding blocks; we only really need to reflow
+ // if there's a float impacting this block, but the current float manager
+ // makes it difficult to check that. Therefore, we let the child block
+ // decide what it needs to reflow.
+ aLine->MarkDirty();
+ } else {
+ bool wasImpactedByFloat = aLine->IsImpactedByFloat();
+ nsFlowAreaRect floatAvailableSpace =
+ aState.GetFloatAvailableSpaceForBSize(aLine->BStart() + aDeltaBCoord,
+ aLine->BSize(),
+ nullptr);
+
+#ifdef REALLY_NOISY_REFLOW
+ printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
+ this, wasImpactedByFloat, floatAvailableSpace.mHasFloats);
+#endif
+
+ // Mark the line dirty if it was or is affected by a float
+ // We actually only really need to reflow if the amount of impact
+ // changes, but that's not straightforward to check
+ if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) {
+ aLine->MarkDirty();
+ }
+ }
+ }
+}
+
+static bool LineHasClear(nsLineBox* aLine) {
+ return aLine->IsBlock()
+ ? (aLine->GetBreakTypeBefore() != StyleClear::None ||
+ (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) ||
+ !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
+ : aLine->HasFloatBreakAfter();
+}
+
+
+/**
+ * Reparent a whole list of floats from aOldParent to this block. The
+ * floats might be taken from aOldParent's overflow list. They will be
+ * removed from the list. They end up appended to our mFloats list.
+ */
+void
+nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
+ bool aReparentSiblings) {
+ nsFrameList list;
+ aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
+ if (list.NotEmpty()) {
+ for (nsIFrame* f : list) {
+ MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
+ "CollectFloats should've removed that bit");
+ ReparentFrame(f, aOldParent, this);
+ }
+ mFloats.AppendFrames(nullptr, list);
+ }
+}
+
+static void DumpLine(const BlockReflowInput& aState, nsLineBox* aLine,
+ nscoord aDeltaBCoord, int32_t aDeltaIndent) {
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyReflow) {
+ nsRect ovis(aLine->GetVisualOverflowArea());
+ nsRect oscr(aLine->GetScrollableOverflowArea());
+ nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
+ printf("line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
+ static_cast<void*>(aLine), aState.mBCoord,
+ aLine->IsDirty() ? "yes" : "no",
+ aLine->IStart(), aLine->BStart(),
+ aLine->ISize(), aLine->BSize(),
+ ovis.x, ovis.y, ovis.width, ovis.height,
+ oscr.x, oscr.y, oscr.width, oscr.height,
+ aDeltaBCoord, aState.mPrevBEndMargin.get(), aLine->GetChildCount());
+ }
+#endif
+}
+
+void
+nsBlockFrame::ReflowDirtyLines(BlockReflowInput& aState)
+{
+ bool keepGoing = true;
+ bool repositionViews = false; // should we really need this?
+ bool foundAnyClears = aState.mFloatBreakType != StyleClear::None;
+ bool willReflowAgain = false;
+
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent);
+ ListTag(stdout);
+ printf(": reflowing dirty lines");
+ printf(" computedISize=%d\n", aState.mReflowInput.ComputedISize());
+ }
+ AutoNoisyIndenter indent(gNoisyReflow);
+#endif
+
+ bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) ||
+ (aState.mReflowInput.IsBResize() &&
+ (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE));
+
+ // Reflow our last line if our availableBSize has increased
+ // so that we (and our last child) pull up content as necessary
+ if (aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE
+ && GetNextInFlow() && aState.mReflowInput.AvailableBSize() >
+ GetLogicalSize().BSize(aState.mReflowInput.GetWritingMode())) {
+ LineIterator lastLine = LinesEnd();
+ if (lastLine != LinesBegin()) {
+ --lastLine;
+ lastLine->MarkDirty();
+ }
+ }
+ // the amount by which we will slide the current line if it is not
+ // dirty
+ nscoord deltaBCoord = 0;
+
+ // whether we did NOT reflow the previous line and thus we need to
+ // recompute the carried out margin before the line if we want to
+ // reflow it or if its previous margin is dirty
+ bool needToRecoverState = false;
+ // Float continuations were reflowed in ReflowPushedFloats
+ bool reflowedFloat = mFloats.NotEmpty() &&
+ (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
+ bool lastLineMovedUp = false;
+ // We save up information about BR-clearance here
+ StyleClear inlineFloatBreakType = aState.mFloatBreakType;
+
+ LineIterator line = LinesBegin(), line_end = LinesEnd();
+
+ // Reflow the lines that are already ours
+ for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
+ DumpLine(aState, line, deltaBCoord, 0);
+#ifdef DEBUG
+ AutoNoisyIndenter indent2(gNoisyReflow);
+#endif
+
+ if (selfDirty)
+ line->MarkDirty();
+
+ // This really sucks, but we have to look inside any blocks that have clear
+ // elements inside them.
+ // XXX what can we do smarter here?
+ if (!line->IsDirty() && line->IsBlock() &&
+ (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
+ line->MarkDirty();
+ }
+
+ nsIFrame *replacedBlock = nullptr;
+ if (line->IsBlock() &&
+ !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
+ replacedBlock = line->mFirstChild;
+ }
+
+ // We have to reflow the line if it's a block whose clearance
+ // might have changed, so detect that.
+ if (!line->IsDirty() &&
+ (line->GetBreakTypeBefore() != StyleClear::None ||
+ replacedBlock)) {
+ nscoord curBCoord = aState.mBCoord;
+ // See where we would be after applying any clearance due to
+ // BRs.
+ if (inlineFloatBreakType != StyleClear::None) {
+ curBCoord = aState.ClearFloats(curBCoord, inlineFloatBreakType);
+ }
+
+ nscoord newBCoord =
+ aState.ClearFloats(curBCoord, line->GetBreakTypeBefore(), replacedBlock);
+
+ if (line->HasClearance()) {
+ // Reflow the line if it might not have clearance anymore.
+ if (newBCoord == curBCoord
+ // aState.mBCoord is the clearance point which should be the
+ // block-start border-edge of the block frame. If sliding the
+ // block by deltaBCoord isn't going to put it in the predicted
+ // position, then we'd better reflow the line.
+ || newBCoord != line->BStart() + deltaBCoord) {
+ line->MarkDirty();
+ }
+ } else {
+ // Reflow the line if the line might have clearance now.
+ if (curBCoord != newBCoord) {
+ line->MarkDirty();
+ }
+ }
+ }
+
+ // We might have to reflow a line that is after a clearing BR.
+ if (inlineFloatBreakType != StyleClear::None) {
+ aState.mBCoord = aState.ClearFloats(aState.mBCoord, inlineFloatBreakType);
+ if (aState.mBCoord != line->BStart() + deltaBCoord) {
+ // SlideLine is not going to put the line where the clearance
+ // put it. Reflow the line to be sure.
+ line->MarkDirty();
+ }
+ inlineFloatBreakType = StyleClear::None;
+ }
+
+ bool previousMarginWasDirty = line->IsPreviousMarginDirty();
+ if (previousMarginWasDirty) {
+ // If the previous margin is dirty, reflow the current line
+ line->MarkDirty();
+ line->ClearPreviousMarginDirty();
+ } else if (line->BEnd() + deltaBCoord > aState.mBEndEdge) {
+ // Lines that aren't dirty but get slid past our height constraint must
+ // be reflowed.
+ line->MarkDirty();
+ }
+
+ // If we have a constrained height (i.e., breaking columns/pages),
+ // and the distance to the bottom might have changed, then we need
+ // to reflow any line that might have floats in it, both because the
+ // breakpoints within those floats may have changed and because we
+ // might have to push/pull the floats in their entirety.
+ // FIXME: What about a deltaBCoord or block-size change that forces us to
+ // push lines? Why does that work?
+ if (!line->IsDirty() &&
+ aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
+ (deltaBCoord != 0 || aState.mReflowInput.IsBResize() ||
+ aState.mReflowInput.mFlags.mMustReflowPlaceholders) &&
+ (line->IsBlock() || line->HasFloats() || line->HadFloatPushed())) {
+ line->MarkDirty();
+ }
+
+ if (!line->IsDirty()) {
+ // See if there's any reflow damage that requires that we mark the
+ // line dirty.
+ PropagateFloatDamage(aState, line, deltaBCoord);
+ }
+
+ // If the container size has changed, reset mContainerSize. If the
+ // line's writing mode is not ltr, or if the line is not left-aligned, also
+ // mark the line dirty.
+ if (aState.ContainerSize() != line->mContainerSize) {
+ line->mContainerSize = aState.ContainerSize();
+
+ bool isLastLine = line == mLines.back() &&
+ !GetNextInFlow() &&
+ NS_STYLE_TEXT_ALIGN_AUTO == StyleText()->mTextAlignLast;
+ uint8_t align = isLastLine ?
+ StyleText()->mTextAlign : StyleText()->mTextAlignLast;
+
+ if (line->mWritingMode.IsVertical() ||
+ !line->mWritingMode.IsBidiLTR() ||
+ !IsAlignedLeft(align,
+ aState.mReflowInput.mStyleVisibility->mDirection,
+ StyleTextReset()->mUnicodeBidi, this)) {
+ line->MarkDirty();
+ }
+ }
+
+ if (needToRecoverState && line->IsDirty()) {
+ // We need to reconstruct the block-end margin only if we didn't
+ // reflow the previous line and we do need to reflow (or repair
+ // the block-start position of) the next line.
+ aState.ReconstructMarginBefore(line);
+ }
+
+ bool reflowedPrevLine = !needToRecoverState;
+ if (needToRecoverState) {
+ needToRecoverState = false;
+
+ // Update aState.mPrevChild as if we had reflowed all of the frames in
+ // this line.
+ if (line->IsDirty()) {
+ NS_ASSERTION(line->mFirstChild->GetPrevSibling() ==
+ line.prev()->LastChild(), "unexpected line frames");
+ aState.mPrevChild = line->mFirstChild->GetPrevSibling();
+ }
+ }
+
+ // Now repair the line and update |aState.mBCoord| by calling
+ // |ReflowLine| or |SlideLine|.
+ // If we're going to reflow everything again, then no need to reflow
+ // the dirty line ... unless the line has floats, in which case we'd
+ // better reflow it now to refresh its float cache, which may contain
+ // dangling frame pointers! Ugh! This reflow of the line may be
+ // incorrect because we skipped reflowing previous lines (e.g., floats
+ // may be placed incorrectly), but that's OK because we'll mark the
+ // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
+ if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
+ lastLineMovedUp = true;
+
+ bool maybeReflowingForFirstTime =
+ line->IStart() == 0 && line->BStart() == 0 &&
+ line->ISize() == 0 && line->BSize() == 0;
+
+ // Compute the dirty lines "before" BEnd, after factoring in
+ // the running deltaBCoord value - the running value is implicit in
+ // aState.mBCoord.
+ nscoord oldB = line->BStart();
+ nscoord oldBMost = line->BEnd();
+
+ NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
+ "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this");
+
+ // Reflow the dirty line. If it's an incremental reflow, then force
+ // it to invalidate the dirty area if necessary
+ ReflowLine(aState, line, &keepGoing);
+
+ if (aState.mReflowInput.WillReflowAgainForClearance()) {
+ line->MarkDirty();
+ willReflowAgain = true;
+ // Note that once we've entered this state, every line that gets here
+ // (e.g. because it has floats) gets marked dirty and reflowed again.
+ // in the next pass. This is important, see above.
+ }
+
+ if (line->HasFloats()) {
+ reflowedFloat = true;
+ }
+
+ if (!keepGoing) {
+ DumpLine(aState, line, deltaBCoord, -1);
+ if (0 == line->GetChildCount()) {
+ DeleteLine(aState, line, line_end);
+ }
+ break;
+ }
+
+ // Test to see whether the margin that should be carried out
+ // to the next line (NL) might have changed. In ReflowBlockFrame
+ // we call nextLine->MarkPreviousMarginDirty if the block's
+ // actual carried-out block-end margin changed. So here we only
+ // need to worry about the following effects:
+ // 1) the line was just created, and it might now be blocking
+ // a carried-out block-end margin from previous lines that
+ // used to reach NL from reaching NL
+ // 2) the line used to be empty, and is now not empty,
+ // thus blocking a carried-out block-end margin from previous lines
+ // that used to reach NL from reaching NL
+ // 3) the line wasn't empty, but now is, so a carried-out
+ // block-end margin from previous lines that didn't used to reach NL
+ // now does
+ // 4) the line might have changed in a way that affects NL's
+ // ShouldApplyBStartMargin decision. The three things that matter
+ // are the line's emptiness, its adjacency to the block-start edge of the
+ // block, and whether it has clearance (the latter only matters if the
+ // block was and is adjacent to the block-start and empty).
+ //
+ // If the line is empty now, we can't reliably tell if the line was empty
+ // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
+ // This means the checks in 4) are redundant; if the line is empty now
+ // we don't need to check 4), but if the line is not empty now and we're sure
+ // it wasn't empty before, any adjacency and clearance changes are irrelevant
+ // to the result of nextLine->ShouldApplyBStartMargin.
+ if (line.next() != LinesEnd()) {
+ bool maybeWasEmpty = oldB == line.next()->BStart();
+ bool isEmpty = line->CachedIsEmpty();
+ if (maybeReflowingForFirstTime /*1*/ ||
+ (isEmpty || maybeWasEmpty) /*2/3/4*/) {
+ line.next()->MarkPreviousMarginDirty();
+ // since it's marked dirty, nobody will care about |deltaBCoord|
+ }
+ }
+
+ // If the line was just reflowed for the first time, then its
+ // old mBounds cannot be trusted so this deltaBCoord computation is
+ // bogus. But that's OK because we just did
+ // MarkPreviousMarginDirty on the next line which will force it
+ // to be reflowed, so this computation of deltaBCoord will not be
+ // used.
+ deltaBCoord = line->BEnd() - oldBMost;
+
+ // Now do an interrupt check. We want to do this only in the case when we
+ // actually reflow the line, so that if we get back in here we'll get
+ // further on the reflow before interrupting.
+ aState.mPresContext->CheckForInterrupt(this);
+ } else {
+ aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
+ // Nop except for blocks (we don't create overflow container
+ // continuations for any inlines atm), so only checking mFirstChild
+ // is enough
+
+ lastLineMovedUp = deltaBCoord < 0;
+
+ if (deltaBCoord != 0)
+ SlideLine(aState, line, deltaBCoord);
+ else
+ repositionViews = true;
+
+ NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
+ "Possibly stale float cache here!");
+ if (willReflowAgain && line->IsBlock()) {
+ // If we're going to reflow everything again, and this line is a block,
+ // then there is no need to recover float state. The line may contain
+ // other lines with floats, but in that case RecoverStateFrom would only
+ // add floats to the float manager. We don't need to do that because
+ // everything's going to get reflowed again "for real". Calling
+ // RecoverStateFrom in this situation could be lethal because the
+ // block's descendant lines may have float caches containing dangling
+ // frame pointers. Ugh!
+ // If this line is inline, then we need to recover its state now
+ // to make sure that we don't forget to move its floats by deltaBCoord.
+ } else {
+ // XXX EVIL O(N^2) EVIL
+ aState.RecoverStateFrom(line, deltaBCoord);
+ }
+
+ // Keep mBCoord up to date in case we're propagating reflow damage
+ // and also because our final height may depend on it. If the
+ // line is inlines, then only update mBCoord if the line is not
+ // empty, because that's what PlaceLine does. (Empty blocks may
+ // want to update mBCoord, e.g. if they have clearance.)
+ if (line->IsBlock() || !line->CachedIsEmpty()) {
+ aState.mBCoord = line->BEnd();
+ }
+
+ needToRecoverState = true;
+
+ if (reflowedPrevLine && !line->IsBlock() &&
+ aState.mPresContext->HasPendingInterrupt()) {
+ // Need to make sure to pull overflows from any prev-in-flows
+ for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
+ inlineKid = inlineKid->PrincipalChildList().FirstChild()) {
+ inlineKid->PullOverflowsFromPrevInFlow();
+ }
+ }
+ }
+
+ // Record if we need to clear floats before reflowing the next
+ // line. Note that inlineFloatBreakType will be handled and
+ // cleared before the next line is processed, so there is no
+ // need to combine break types here.
+ if (line->HasFloatBreakAfter()) {
+ inlineFloatBreakType = line->GetBreakTypeAfter();
+ }
+
+ if (LineHasClear(line.get())) {
+ foundAnyClears = true;
+ }
+
+ DumpLine(aState, line, deltaBCoord, -1);
+
+ if (aState.mPresContext->HasPendingInterrupt()) {
+ willReflowAgain = true;
+ // Another option here might be to leave |line| clean if
+ // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
+ // that case the line really did reflow as it should have. Not sure
+ // whether that would be safe, so doing this for now instead. Also not
+ // sure whether we really want to mark all lines dirty after an
+ // interrupt, but until we get better at propagating float damage we
+ // really do need to do it this way; see comments inside MarkLineDirty.
+ MarkLineDirtyForInterrupt(line);
+ }
+ }
+
+ // Handle BR-clearance from the last line of the block
+ if (inlineFloatBreakType != StyleClear::None) {
+ aState.mBCoord = aState.ClearFloats(aState.mBCoord, inlineFloatBreakType);
+ }
+
+ if (needToRecoverState) {
+ // Is this expensive?
+ aState.ReconstructMarginBefore(line);
+
+ // Update aState.mPrevChild as if we had reflowed all of the frames in
+ // the last line.
+ NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
+ line.prev()->LastChild(), "unexpected line frames");
+ aState.mPrevChild =
+ line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling();
+ }
+
+ // Should we really have to do this?
+ if (repositionViews)
+ nsContainerFrame::PlaceFrameView(this);
+
+ // We can skip trying to pull up the next line if our height is constrained
+ // (so we can report being incomplete) and there is no next in flow or we
+ // were told not to or we know it will be futile, i.e.,
+ // -- the next in flow is not changing
+ // -- and we cannot have added more space for its first line to be
+ // pulled up into,
+ // -- it's an incremental reflow of a descendant
+ // -- and we didn't reflow any floats (so the available space
+ // didn't change)
+ // -- my chain of next-in-flows either has no first line, or its first
+ // line isn't dirty.
+ bool heightConstrained =
+ aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE;
+ bool skipPull = willReflowAgain && heightConstrained;
+ if (!skipPull && heightConstrained && aState.mNextInFlow &&
+ (aState.mReflowInput.mFlags.mNextInFlowUntouched &&
+ !lastLineMovedUp &&
+ !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
+ !reflowedFloat)) {
+ // We'll place lineIter at the last line of this block, so that
+ // nsBlockInFlowLineIterator::Next() will take us to the first
+ // line of my next-in-flow-chain. (But first, check that I
+ // have any lines -- if I don't, just bail out of this
+ // optimization.)
+ LineIterator lineIter = this->LinesEnd();
+ if (lineIter != this->LinesBegin()) {
+ lineIter--; // I have lines; step back from dummy iterator to last line.
+ nsBlockInFlowLineIterator bifLineIter(this, lineIter);
+
+ // Check for next-in-flow-chain's first line.
+ // (First, see if there is such a line, and second, see if it's clean)
+ if (!bifLineIter.Next() ||
+ !bifLineIter.GetLine()->IsDirty()) {
+ skipPull=true;
+ }
+ }
+ }
+
+ if (skipPull && aState.mNextInFlow) {
+ NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
+ if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
+ else
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+ }
+
+ if (!skipPull && aState.mNextInFlow) {
+ // Pull data from a next-in-flow if there's still room for more
+ // content here.
+ while (keepGoing && aState.mNextInFlow) {
+ // Grab first line from our next-in-flow
+ nsBlockFrame* nextInFlow = aState.mNextInFlow;
+ nsLineBox* pulledLine;
+ nsFrameList pulledFrames;
+ if (!nextInFlow->mLines.empty()) {
+ RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames,
+ &pulledLine, &pulledFrames);
+ } else {
+ // Grab an overflow line if there are any
+ FrameLines* overflowLines = nextInFlow->GetOverflowLines();
+ if (!overflowLines) {
+ aState.mNextInFlow =
+ static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
+ continue;
+ }
+ bool last =
+ RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
+ &pulledLine, &pulledFrames);
+ if (last) {
+ nextInFlow->DestroyOverflowLines();
+ }
+ }
+
+ if (pulledFrames.IsEmpty()) {
+ // The line is empty. Try the next one.
+ NS_ASSERTION(pulledLine->GetChildCount() == 0 &&
+ !pulledLine->mFirstChild, "bad empty line");
+ nextInFlow->FreeLineBox(pulledLine);
+ continue;
+ }
+
+ if (pulledLine == nextInFlow->GetLineCursor()) {
+ nextInFlow->ClearLineCursor();
+ }
+ ReparentFrames(pulledFrames, nextInFlow, this);
+
+ NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
+ "Unexpected last frame");
+ NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here");
+ NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
+ "Incorrect aState.mPrevChild before inserting line at end");
+
+ // Shift pulledLine's frames into our mFrames list.
+ mFrames.AppendFrames(nullptr, pulledFrames);
+
+ // Add line to our line list, and set its last child as our new prev-child
+ line = mLines.before_insert(LinesEnd(), pulledLine);
+ aState.mPrevChild = mFrames.LastChild();
+
+ // Reparent floats whose placeholders are in the line.
+ ReparentFloats(pulledLine->mFirstChild, nextInFlow, true);
+
+ DumpLine(aState, pulledLine, deltaBCoord, 0);
+#ifdef DEBUG
+ AutoNoisyIndenter indent2(gNoisyReflow);
+#endif
+
+ if (aState.mPresContext->HasPendingInterrupt()) {
+ MarkLineDirtyForInterrupt(line);
+ } else {
+ // Now reflow it and any lines that it makes during it's reflow
+ // (we have to loop here because reflowing the line may cause a new
+ // line to be created; see SplitLine's callers for examples of
+ // when this happens).
+ while (line != LinesEnd()) {
+ ReflowLine(aState, line, &keepGoing);
+
+ if (aState.mReflowInput.WillReflowAgainForClearance()) {
+ line->MarkDirty();
+ keepGoing = false;
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+ break;
+ }
+
+ DumpLine(aState, line, deltaBCoord, -1);
+ if (!keepGoing) {
+ if (0 == line->GetChildCount()) {
+ DeleteLine(aState, line, line_end);
+ }
+ break;
+ }
+
+ if (LineHasClear(line.get())) {
+ foundAnyClears = true;
+ }
+
+ if (aState.mPresContext->CheckForInterrupt(this)) {
+ MarkLineDirtyForInterrupt(line);
+ break;
+ }
+
+ // If this is an inline frame then its time to stop
+ ++line;
+ aState.AdvanceToNextLine();
+ }
+ }
+ }
+
+ if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
+ aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ } //XXXfr shouldn't set this flag when nextinflow has no lines
+ }
+
+ // Handle an odd-ball case: a list-item with no lines
+ if (HasOutsideBullet() && mLines.empty()) {
+ ReflowOutput metrics(aState.mReflowInput);
+ nsIFrame* bullet = GetOutsideBullet();
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ ReflowBullet(bullet, aState, metrics,
+ aState.mReflowInput.ComputedPhysicalBorderPadding().top);
+ NS_ASSERTION(!BulletIsEmpty() || metrics.BSize(wm) == 0,
+ "empty bullet took up space");
+
+ if (!BulletIsEmpty()) {
+ // There are no lines so we have to fake up some y motion so that
+ // we end up with *some* height.
+
+ if (metrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
+ nscoord ascent;
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ if (nsLayoutUtils::GetFirstLineBaseline(wm, bullet, &ascent)) {
+ metrics.SetBlockStartAscent(ascent);
+ } else {
+ metrics.SetBlockStartAscent(metrics.BSize(wm));
+ }
+ }
+
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
+
+ nscoord minAscent =
+ nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight,
+ wm.IsLineInverted());
+ nscoord minDescent = aState.mMinLineHeight - minAscent;
+
+ aState.mBCoord += std::max(minAscent, metrics.BlockStartAscent()) +
+ std::max(minDescent, metrics.BSize(wm) -
+ metrics.BlockStartAscent());
+
+ nscoord offset = minAscent - metrics.BlockStartAscent();
+ if (offset > 0) {
+ bullet->SetRect(bullet->GetRect() + nsPoint(0, offset));
+ }
+ }
+ }
+
+ if (foundAnyClears) {
+ AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
+ } else {
+ RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
+ }
+
+#ifdef DEBUG
+ VerifyLines(true);
+ VerifyOverflowSituation();
+ if (gNoisyReflow) {
+ IndentBy(stdout, gNoiseIndent - 1);
+ ListTag(stdout);
+ printf(": done reflowing dirty lines (status=%x)\n",
+ aState.mReflowStatus);
+ }
+#endif
+}
+
+void
+nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine)
+{
+ aLine->MarkDirty();
+
+ // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
+ // marked the lines that need to be marked dirty based on our
+ // vertical resize stuff. So we'll definitely reflow all those kids;
+ // the only question is how they should behave.
+ if (GetStateBits() & NS_FRAME_IS_DIRTY) {
+ // Mark all our child frames dirty so we make sure to reflow them
+ // later.
+ int32_t n = aLine->GetChildCount();
+ for (nsIFrame* f = aLine->mFirstChild; n > 0;
+ f = f->GetNextSibling(), --n) {
+ f->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+ // And mark all the floats whose reflows we might be skipping dirty too.
+ if (aLine->HasFloats()) {
+ for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) {
+ fc->mFloat->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+ }
+ } else {
+ // Dirty all the descendant lines of block kids to handle float damage,
+ // since our nsFloatManager will go away by the next time we're reflowing.
+ // XXXbz Can we do something more like what PropagateFloatDamage does?
+ // Would need to sort out the exact business with mBlockDelta for that....
+ // This marks way too much dirty. If we ever make this better, revisit
+ // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
+ nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
+ if (bf) {
+ MarkAllDescendantLinesDirty(bf);
+ }
+ }
+}
+
+void
+nsBlockFrame::DeleteLine(BlockReflowInput& aState,
+ nsLineList::iterator aLine,
+ nsLineList::iterator aLineEnd)
+{
+ NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
+ if (0 == aLine->GetChildCount()) {
+ NS_ASSERTION(aState.mCurrentLine == aLine,
+ "using function more generally than designed, "
+ "but perhaps OK now");
+ nsLineBox* line = aLine;
+ aLine = mLines.erase(aLine);
+ FreeLineBox(line);
+ // Mark the previous margin of the next line dirty since we need to
+ // recompute its top position.
+ if (aLine != aLineEnd)
+ aLine->MarkPreviousMarginDirty();
+ }
+}
+
+/**
+ * Reflow a line. The line will either contain a single block frame
+ * or contain 1 or more inline frames. aKeepReflowGoing indicates
+ * whether or not the caller should continue to reflow more lines.
+ */
+void
+nsBlockFrame::ReflowLine(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepReflowGoing)
+{
+ MOZ_ASSERT(aLine->GetChildCount(), "reflowing empty line");
+
+ // Setup the line-layout for the new line
+ aState.mCurrentLine = aLine;
+ aLine->ClearDirty();
+ aLine->InvalidateCachedIsEmpty();
+ aLine->ClearHadFloatPushed();
+
+ // Now that we know what kind of line we have, reflow it
+ if (aLine->IsBlock()) {
+ ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
+ } else {
+ aLine->SetLineWrapped(false);
+ ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
+ }
+}
+
+nsIFrame*
+nsBlockFrame::PullFrame(BlockReflowInput& aState,
+ LineIterator aLine)
+{
+ // First check our remaining lines.
+ if (LinesEnd() != aLine.next()) {
+ return PullFrameFrom(aLine, this, aLine.next());
+ }
+
+ NS_ASSERTION(!GetOverflowLines(),
+ "Our overflow lines should have been removed at the start of reflow");
+
+ // Try each next-in-flow.
+ nsBlockFrame* nextInFlow = aState.mNextInFlow;
+ while (nextInFlow) {
+ if (nextInFlow->mLines.empty()) {
+ nextInFlow->DrainSelfOverflowList();
+ }
+ if (!nextInFlow->mLines.empty()) {
+ return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin());
+ }
+ nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
+ aState.mNextInFlow = nextInFlow;
+ }
+
+ return nullptr;
+}
+
+nsIFrame*
+nsBlockFrame::PullFrameFrom(nsLineBox* aLine,
+ nsBlockFrame* aFromContainer,
+ nsLineList::iterator aFromLine)
+{
+ nsLineBox* fromLine = aFromLine;
+ MOZ_ASSERT(fromLine, "bad line to pull from");
+ MOZ_ASSERT(fromLine->GetChildCount(), "empty line");
+ MOZ_ASSERT(aLine->GetChildCount(), "empty line");
+
+ NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
+ "Disagreement about whether it's a block or not");
+
+ if (fromLine->IsBlock()) {
+ // If our line is not empty and the child in aFromLine is a block
+ // then we cannot pull up the frame into this line. In this case
+ // we stop pulling.
+ return nullptr;
+ }
+ // Take frame from fromLine
+ nsIFrame* frame = fromLine->mFirstChild;
+ nsIFrame* newFirstChild = frame->GetNextSibling();
+
+ if (aFromContainer != this) {
+ // The frame is being pulled from a next-in-flow; therefore we
+ // need to add it to our sibling list.
+ MOZ_ASSERT(aLine == mLines.back());
+ MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
+ "should only pull from first line");
+ aFromContainer->mFrames.RemoveFrame(frame);
+
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented.
+ ReparentFrame(frame, aFromContainer, this);
+ mFrames.AppendFrame(nullptr, frame);
+
+ // The frame might have (or contain) floats that need to be brought
+ // over too. (pass 'false' since there are no siblings to check)
+ ReparentFloats(frame, aFromContainer, false);
+ } else {
+ MOZ_ASSERT(aLine == aFromLine.prev());
+ }
+
+ aLine->NoteFrameAdded(frame);
+ fromLine->NoteFrameRemoved(frame);
+
+ if (fromLine->GetChildCount() > 0) {
+ // Mark line dirty now that we pulled a child
+ fromLine->MarkDirty();
+ fromLine->mFirstChild = newFirstChild;
+ } else {
+ // Free up the fromLine now that it's empty.
+ // Its bounds might need to be redrawn, though.
+ if (aFromLine.next() != aFromContainer->mLines.end()) {
+ aFromLine.next()->MarkPreviousMarginDirty();
+ }
+ aFromContainer->mLines.erase(aFromLine);
+ // aFromLine is now invalid
+ aFromContainer->FreeLineBox(fromLine);
+ }
+
+#ifdef DEBUG
+ VerifyLines(true);
+ VerifyOverflowSituation();
+#endif
+
+ return frame;
+}
+
+void
+nsBlockFrame::SlideLine(BlockReflowInput& aState,
+ nsLineBox* aLine, nscoord aDeltaBCoord)
+{
+ NS_PRECONDITION(aDeltaBCoord != 0, "why slide a line nowhere?");
+
+ // Adjust line state
+ aLine->SlideBy(aDeltaBCoord, aState.ContainerSize());
+
+ // Adjust the frames in the line
+ MoveChildFramesOfLine(aLine, aDeltaBCoord);
+}
+
+void
+nsBlockFrame::UpdateLineContainerSize(nsLineBox* aLine,
+ const nsSize& aNewContainerSize)
+{
+ if (aNewContainerSize == aLine->mContainerSize) {
+ return;
+ }
+
+ // Adjust line state
+ nsSize sizeDelta = aLine->UpdateContainerSize(aNewContainerSize);
+
+ // Changing container width only matters if writing mode is vertical-rl
+ if (GetWritingMode().IsVerticalRL()) {
+ MoveChildFramesOfLine(aLine, sizeDelta.width);
+ }
+}
+
+void
+nsBlockFrame::MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord)
+{
+ // Adjust the frames in the line
+ nsIFrame* kid = aLine->mFirstChild;
+ if (!kid) {
+ return;
+ }
+
+ WritingMode wm = GetWritingMode();
+ LogicalPoint translation(wm, 0, aDeltaBCoord);
+
+ if (aLine->IsBlock()) {
+ if (aDeltaBCoord) {
+ kid->MovePositionBy(wm, translation);
+ }
+
+ // Make sure the frame's view and any child views are updated
+ nsContainerFrame::PlaceFrameView(kid);
+ }
+ else {
+ // Adjust the block-dir coordinate of the frames in the line.
+ // Note: we need to re-position views even if aDeltaBCoord is 0, because
+ // one of our parent frames may have moved and so the view's position
+ // relative to its parent may have changed.
+ int32_t n = aLine->GetChildCount();
+ while (--n >= 0) {
+ if (aDeltaBCoord) {
+ kid->MovePositionBy(wm, translation);
+ }
+ // Make sure the frame's view and any child views are updated
+ nsContainerFrame::PlaceFrameView(kid);
+ kid = kid->GetNextSibling();
+ }
+ }
+}
+
+nsresult
+nsBlockFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID,
+ aAttribute, aModType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (nsGkAtoms::value == aAttribute) {
+ const nsStyleDisplay* styleDisplay = StyleDisplay();
+ if (mozilla::StyleDisplay::ListItem == styleDisplay->mDisplay) {
+ // Search for the closest ancestor that's a block frame. We
+ // make the assumption that all related list items share a
+ // common block/grid/flex ancestor.
+ // XXXldb I think that's a bad assumption.
+ nsContainerFrame* ancestor = GetParent();
+ for (; ancestor; ancestor = ancestor->GetParent()) {
+ auto frameType = ancestor->GetType();
+ if (frameType == nsGkAtoms::blockFrame ||
+ frameType == nsGkAtoms::flexContainerFrame ||
+ frameType == nsGkAtoms::gridContainerFrame) {
+ break;
+ }
+ }
+ // Tell the ancestor to renumber list items within itself.
+ if (ancestor) {
+ // XXX Not sure if this is necessary anymore
+ if (ancestor->RenumberList()) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(ancestor, nsIPresShell::eStyleChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+static inline bool
+IsNonAutoNonZeroBSize(const nsStyleCoord& aCoord)
+{
+ nsStyleUnit unit = aCoord.GetUnit();
+ if (unit == eStyleUnit_Auto ||
+ // The enumerated values were originally aimed at inline-size
+ // (or width, as it was before logicalization). For now, let them
+ // return false here, so we treat them like 'auto' pending a
+ // real implementation. (See bug 1126420.)
+ //
+ // FIXME (bug 567039, bug 527285)
+ // This isn't correct for the 'fill' value, which should more
+ // likely (but not necessarily, depending on the available space)
+ // be returning true.
+ unit == eStyleUnit_Enumerated) {
+ return false;
+ }
+ if (aCoord.IsCoordPercentCalcUnit()) {
+ // If we evaluate the length/percent/calc at a percentage basis of
+ // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
+ // length, percent, or combination thereof. Test > 0 so we clamp
+ // negative calc() results to 0.
+ return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
+ nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
+ }
+ MOZ_ASSERT(false, "unexpected unit for height or min-height");
+ return true;
+}
+
+/* virtual */ bool
+nsBlockFrame::IsSelfEmpty()
+{
+ // Blocks which are margin-roots (including inline-blocks) cannot be treated
+ // as empty for margin-collapsing and other purposes. They're more like
+ // replaced elements.
+ if (GetStateBits() & NS_BLOCK_MARGIN_ROOT) {
+ return false;
+ }
+
+ WritingMode wm = GetWritingMode();
+ const nsStylePosition* position = StylePosition();
+
+ if (IsNonAutoNonZeroBSize(position->MinBSize(wm)) ||
+ IsNonAutoNonZeroBSize(position->BSize(wm))) {
+ return false;
+ }
+
+ const nsStyleBorder* border = StyleBorder();
+ const nsStylePadding* padding = StylePadding();
+
+ if (border->GetComputedBorderWidth(wm.PhysicalSide(eLogicalSideBStart)) != 0 ||
+ border->GetComputedBorderWidth(wm.PhysicalSide(eLogicalSideBEnd)) != 0 ||
+ !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBStart(wm)) ||
+ !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBEnd(wm))) {
+ return false;
+ }
+
+ if (HasOutsideBullet() && !BulletIsEmpty()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+nsBlockFrame::CachedIsEmpty()
+{
+ if (!IsSelfEmpty()) {
+ return false;
+ }
+
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line)
+ {
+ if (!line->CachedIsEmpty())
+ return false;
+ }
+
+ return true;
+}
+
+bool
+nsBlockFrame::IsEmpty()
+{
+ if (!IsSelfEmpty()) {
+ return false;
+ }
+
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line)
+ {
+ if (!line->IsEmpty())
+ return false;
+ }
+
+ return true;
+}
+
+bool
+nsBlockFrame::ShouldApplyBStartMargin(BlockReflowInput& aState,
+ nsLineBox* aLine,
+ nsIFrame* aChildFrame)
+{
+ if (aState.mFlags.mShouldApplyBStartMargin) {
+ // Apply short-circuit check to avoid searching the line list
+ return true;
+ }
+
+ if (!aState.IsAdjacentWithTop() ||
+ aChildFrame->StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone) {
+ // If we aren't at the start block-coordinate then something of non-zero
+ // height must have been placed. Therefore the childs block-start margin
+ // applies.
+ aState.mFlags.mShouldApplyBStartMargin = true;
+ return true;
+ }
+
+ // Determine if this line is "essentially" the first line
+ LineIterator line = LinesBegin();
+ if (aState.mFlags.mHasLineAdjacentToTop) {
+ line = aState.mLineAdjacentToTop;
+ }
+ while (line != aLine) {
+ if (!line->CachedIsEmpty() || line->HasClearance()) {
+ // A line which precedes aLine is non-empty, or has clearance,
+ // so therefore the block-start margin applies.
+ aState.mFlags.mShouldApplyBStartMargin = true;
+ return true;
+ }
+ // No need to apply the block-start margin if the line has floats. We
+ // should collapse anyway (bug 44419)
+ ++line;
+ aState.mFlags.mHasLineAdjacentToTop = true;
+ aState.mLineAdjacentToTop = line;
+ }
+
+ // The line being reflowed is "essentially" the first line in the
+ // block. Therefore its block-start margin will be collapsed by the
+ // generational collapsing logic with its parent (us).
+ return false;
+}
+
+void
+nsBlockFrame::ReflowBlockFrame(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepReflowGoing)
+{
+ NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
+
+ nsIFrame* frame = aLine->mFirstChild;
+ if (!frame) {
+ NS_ASSERTION(false, "program error - unexpected empty line");
+ return;
+ }
+
+ // Prepare the block reflow engine
+ nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
+
+ StyleClear breakType = frame->StyleDisplay()->
+ PhysicalBreakType(aState.mReflowInput.GetWritingMode());
+ if (StyleClear::None != aState.mFloatBreakType) {
+ breakType = nsLayoutUtils::CombineBreakType(breakType,
+ aState.mFloatBreakType);
+ aState.mFloatBreakType = StyleClear::None;
+ }
+
+ // Clear past floats before the block if the clear style is not none
+ aLine->SetBreakTypeBefore(breakType);
+
+ // See if we should apply the block-start margin. If the block frame being
+ // reflowed is a continuation (non-null prev-in-flow) then we don't
+ // apply its block-start margin because it's not significant unless it has
+ // 'box-decoration-break:clone'. Otherwise, dig deeper.
+ bool applyBStartMargin = (frame->StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone ||
+ !frame->GetPrevInFlow()) &&
+ ShouldApplyBStartMargin(aState, aLine, frame);
+ if (applyBStartMargin) {
+ // The HasClearance setting is only valid if ShouldApplyBStartMargin
+ // returned false (in which case the block-start margin-root set our
+ // clearance flag). Otherwise clear it now. We'll set it later on
+ // ourselves if necessary.
+ aLine->ClearHasClearance();
+ }
+ bool treatWithClearance = aLine->HasClearance();
+
+ bool mightClearFloats = breakType != StyleClear::None;
+ nsIFrame *replacedBlock = nullptr;
+ if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
+ mightClearFloats = true;
+ replacedBlock = frame;
+ }
+
+ // If our block-start margin was counted as part of some parent's block-start
+ // margin collapse, and we are being speculatively reflowed assuming this
+ // frame DID NOT need clearance, then we need to check that
+ // assumption.
+ if (!treatWithClearance && !applyBStartMargin && mightClearFloats &&
+ aState.mReflowInput.mDiscoveredClearance) {
+ nscoord curBCoord = aState.mBCoord + aState.mPrevBEndMargin.get();
+ nscoord clearBCoord = aState.ClearFloats(curBCoord, breakType, replacedBlock);
+ if (clearBCoord != curBCoord) {
+ // Only record the first frame that requires clearance
+ if (!*aState.mReflowInput.mDiscoveredClearance) {
+ *aState.mReflowInput.mDiscoveredClearance = frame;
+ }
+ aState.mPrevChild = frame;
+ // Exactly what we do now is flexible since we'll definitely be
+ // reflowed.
+ return;
+ }
+ }
+ if (treatWithClearance) {
+ applyBStartMargin = true;
+ }
+
+ nsIFrame* clearanceFrame = nullptr;
+ nscoord startingBCoord = aState.mBCoord;
+ nsCollapsingMargin incomingMargin = aState.mPrevBEndMargin;
+ nscoord clearance;
+ // Save the original position of the frame so that we can reposition
+ // its view as needed.
+ nsPoint originalPosition = frame->GetPosition();
+ while (true) {
+ clearance = 0;
+ nscoord bStartMargin = 0;
+ bool mayNeedRetry = false;
+ bool clearedFloats = false;
+ if (applyBStartMargin) {
+ // Precompute the blocks block-start margin value so that we can get the
+ // correct available space (there might be a float that's
+ // already been placed below the aState.mPrevBEndMargin
+
+ // Setup a reflowInput to get the style computed block-start margin
+ // value. We'll use a reason of `resize' so that we don't fudge
+ // any incremental reflow state.
+
+ // The availSpace here is irrelevant to our needs - all we want
+ // out if this setup is the block-start margin value which doesn't depend
+ // on the childs available space.
+ // XXX building a complete ReflowInput just to get the block-start
+ // margin seems like a waste. And we do this for almost every block!
+ WritingMode wm = frame->GetWritingMode();
+ LogicalSize availSpace = aState.ContentSize(wm);
+ ReflowInput reflowInput(aState.mPresContext, aState.mReflowInput,
+ frame, availSpace);
+
+ if (treatWithClearance) {
+ aState.mBCoord += aState.mPrevBEndMargin.get();
+ aState.mPrevBEndMargin.Zero();
+ }
+
+ // Now compute the collapsed margin-block-start value into
+ // aState.mPrevBEndMargin, assuming that all child margins
+ // collapse down to clearanceFrame.
+ brc.ComputeCollapsedBStartMargin(reflowInput,
+ &aState.mPrevBEndMargin,
+ clearanceFrame,
+ &mayNeedRetry);
+
+ // XXX optimization; we could check the collapsing children to see if they are sure
+ // to require clearance, and so avoid retrying them
+
+ if (clearanceFrame) {
+ // Don't allow retries on the second pass. The clearance decisions for the
+ // blocks whose block-start margins collapse with ours are now fixed.
+ mayNeedRetry = false;
+ }
+
+ if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
+ // We don't know if we need clearance and this is the first,
+ // optimistic pass. So determine whether *this block* needs
+ // clearance. Note that we do not allow the decision for whether
+ // this block has clearance to change on the second pass; that
+ // decision is only allowed to be made under the optimistic
+ // first pass.
+ nscoord curBCoord = aState.mBCoord + aState.mPrevBEndMargin.get();
+ nscoord clearBCoord = aState.ClearFloats(curBCoord, breakType, replacedBlock);
+ if (clearBCoord != curBCoord) {
+ // Looks like we need clearance and we didn't know about it already. So
+ // recompute collapsed margin
+ treatWithClearance = true;
+ // Remember this decision, needed for incremental reflow
+ aLine->SetHasClearance();
+
+ // Apply incoming margins
+ aState.mBCoord += aState.mPrevBEndMargin.get();
+ aState.mPrevBEndMargin.Zero();
+
+ // Compute the collapsed margin again, ignoring the incoming margin this time
+ mayNeedRetry = false;
+ brc.ComputeCollapsedBStartMargin(reflowInput,
+ &aState.mPrevBEndMargin,
+ clearanceFrame,
+ &mayNeedRetry);
+ }
+ }
+
+ // Temporarily advance the running Y value so that the
+ // GetAvailableSpace method will return the right available
+ // space. This undone as soon as the horizontal margins are
+ // computed.
+ bStartMargin = aState.mPrevBEndMargin.get();
+
+ if (treatWithClearance) {
+ nscoord currentBCoord = aState.mBCoord;
+ // advance mBCoord to the clear position.
+ aState.mBCoord = aState.ClearFloats(aState.mBCoord, breakType,
+ replacedBlock);
+
+ clearedFloats = aState.mBCoord != currentBCoord;
+
+ // Compute clearance. It's the amount we need to add to the block-start
+ // border-edge of the frame, after applying collapsed margins
+ // from the frame and its children, to get it to line up with
+ // the block-end of the floats. The former is
+ // currentBCoord + bStartMargin, the latter is the current
+ // aState.mBCoord.
+ // Note that negative clearance is possible
+ clearance = aState.mBCoord - (currentBCoord + bStartMargin);
+
+ // Add clearance to our block-start margin while we compute available
+ // space for the frame
+ bStartMargin += clearance;
+
+ // Note that aState.mBCoord should stay where it is: at the block-start
+ // border-edge of the frame
+ } else {
+ // Advance aState.mBCoord to the block-start border-edge of the frame.
+ aState.mBCoord += bStartMargin;
+ }
+ }
+
+ aLine->SetLineIsImpactedByFloat(false);
+
+ // Here aState.mBCoord is the block-start border-edge of the block.
+ // Compute the available space for the block
+ nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ LogicalRect availSpace(wm);
+ aState.ComputeBlockAvailSpace(frame, floatAvailableSpace,
+ replacedBlock != nullptr, availSpace);
+
+ // The check for
+ // (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
+ // is to some degree out of paranoia: if we reliably eat up block-start
+ // margins at the top of the page as we ought to, it wouldn't be
+ // needed.
+ if ((!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats) &&
+ availSpace.BSize(wm) < 0) {
+ // We know already that this child block won't fit on this
+ // page/column due to the block-start margin or the clearance. So we
+ // need to get out of here now. (If we don't, most blocks will handle
+ // things fine, and report break-before, but zero-height blocks
+ // won't, and will thus make their parent overly-large and force
+ // *it* to be pushed in its entirety.)
+ // Doing this means that we also don't need to worry about the
+ // |availSpace.BSize(wm) += bStartMargin| below interacting with
+ // pushed floats (which force nscoord_MAX clearance) to cause a
+ // constrained block size to turn into an unconstrained one.
+ aState.mBCoord = startingBCoord;
+ aState.mPrevBEndMargin = incomingMargin;
+ *aKeepReflowGoing = false;
+ if (ShouldAvoidBreakInside(aState.mReflowInput)) {
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ } else {
+ PushLines(aState, aLine.prev());
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+ }
+ return;
+ }
+
+ // Now put the block-dir coordinate back to the start of the
+ // block-start-margin + clearance.
+ aState.mBCoord -= bStartMargin;
+ availSpace.BStart(wm) -= bStartMargin;
+ if (NS_UNCONSTRAINEDSIZE != availSpace.BSize(wm)) {
+ availSpace.BSize(wm) += bStartMargin;
+ }
+
+ // construct the html reflow state for the block. ReflowBlock
+ // will initialize it.
+ Maybe<ReflowInput> blockHtmlRI;
+ blockHtmlRI.emplace(
+ aState.mPresContext, aState.mReflowInput, frame,
+ availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
+
+ nsFloatManager::SavedState floatManagerState;
+ nsReflowStatus frameReflowStatus;
+ do {
+ if (floatAvailableSpace.mHasFloats) {
+ // Set if floatAvailableSpace.mHasFloats is true for any
+ // iteration of the loop.
+ aLine->SetLineIsImpactedByFloat(true);
+ }
+
+ // We might need to store into mDiscoveredClearance later if it's
+ // currently null; we want to overwrite any writes that
+ // brc.ReflowBlock() below does, so we need to remember now
+ // whether it's empty.
+ const bool shouldStoreClearance =
+ aState.mReflowInput.mDiscoveredClearance &&
+ !*aState.mReflowInput.mDiscoveredClearance;
+
+ // Reflow the block into the available space
+ if (mayNeedRetry || replacedBlock) {
+ aState.mFloatManager->PushState(&floatManagerState);
+ }
+
+ if (mayNeedRetry) {
+ blockHtmlRI->mDiscoveredClearance = &clearanceFrame;
+ } else if (!applyBStartMargin) {
+ blockHtmlRI->mDiscoveredClearance =
+ aState.mReflowInput.mDiscoveredClearance;
+ }
+
+ frameReflowStatus = NS_FRAME_COMPLETE;
+ brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin,
+ clearance, aState.IsAdjacentWithTop(),
+ aLine.get(), *blockHtmlRI, frameReflowStatus, aState);
+
+ // Now the block has a height. Using that height, get the
+ // available space again and call ComputeBlockAvailSpace again.
+ // If ComputeBlockAvailSpace gives a different result, we need to
+ // reflow again.
+ if (!replacedBlock) {
+ break;
+ }
+
+ LogicalRect oldFloatAvailableSpaceRect(floatAvailableSpace.mRect);
+ floatAvailableSpace = aState.GetFloatAvailableSpaceForBSize(
+ aState.mBCoord + bStartMargin,
+ brc.GetMetrics().BSize(wm),
+ &floatManagerState);
+ NS_ASSERTION(floatAvailableSpace.mRect.BStart(wm) ==
+ oldFloatAvailableSpaceRect.BStart(wm),
+ "yikes");
+ // Restore the height to the position of the next band.
+ floatAvailableSpace.mRect.BSize(wm) =
+ oldFloatAvailableSpaceRect.BSize(wm);
+ // Determine whether the available space shrunk on either side,
+ // because (the first time round) we now know the block's height,
+ // and it may intersect additional floats, or (on later
+ // iterations) because narrowing the width relative to the
+ // previous time may cause the block to become taller. Note that
+ // since we're reflowing the block, narrowing the width might also
+ // make it shorter, so we must pass aCanGrow as true.
+ if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect,
+ floatAvailableSpace.mRect, true)) {
+ // The size and position we chose before are fine (i.e., they
+ // don't cause intersecting with floats that requires a change
+ // in size or position), so we're done.
+ break;
+ }
+
+ bool advanced = false;
+ if (!aState.ReplacedBlockFitsInAvailSpace(replacedBlock,
+ floatAvailableSpace)) {
+ // Advance to the next band.
+ nscoord newBCoord = aState.mBCoord;
+ if (aState.AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
+ advanced = true;
+ }
+ // ClearFloats might be able to advance us further once we're there.
+ aState.mBCoord =
+ aState.ClearFloats(newBCoord, StyleClear::None, replacedBlock);
+ // Start over with a new available space rect at the new height.
+ floatAvailableSpace =
+ aState.GetFloatAvailableSpaceWithState(aState.mBCoord,
+ &floatManagerState);
+ }
+
+ LogicalRect oldAvailSpace(availSpace);
+ aState.ComputeBlockAvailSpace(frame, floatAvailableSpace,
+ replacedBlock != nullptr, availSpace);
+
+ if (!advanced && availSpace.IsEqualEdges(oldAvailSpace)) {
+ break;
+ }
+
+ // We need another reflow.
+ aState.mFloatManager->PopState(&floatManagerState);
+
+ if (!treatWithClearance && !applyBStartMargin &&
+ aState.mReflowInput.mDiscoveredClearance) {
+ // We set shouldStoreClearance above to record only the first
+ // frame that requires clearance.
+ if (shouldStoreClearance) {
+ *aState.mReflowInput.mDiscoveredClearance = frame;
+ }
+ aState.mPrevChild = frame;
+ // Exactly what we do now is flexible since we'll definitely be
+ // reflowed.
+ return;
+ }
+
+ if (advanced) {
+ // We're pushing down the border-box, so we don't apply margin anymore.
+ // This should never cause us to move up since the call to
+ // GetFloatAvailableSpaceForBSize above included the margin.
+ applyBStartMargin = false;
+ bStartMargin = 0;
+ treatWithClearance = true; // avoid hitting test above
+ clearance = 0;
+ }
+
+ blockHtmlRI.reset();
+ blockHtmlRI.emplace(
+ aState.mPresContext, aState.mReflowInput, frame,
+ availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
+ } while (true);
+
+ if (mayNeedRetry && clearanceFrame) {
+ aState.mFloatManager->PopState(&floatManagerState);
+ aState.mBCoord = startingBCoord;
+ aState.mPrevBEndMargin = incomingMargin;
+ continue;
+ }
+
+ aState.mPrevChild = frame;
+
+ if (blockHtmlRI->WillReflowAgainForClearance()) {
+ // If an ancestor of ours is going to reflow for clearance, we
+ // need to avoid calling PlaceBlock, because it unsets dirty bits
+ // on the child block (both itself, and through its call to
+ // nsFrame::DidReflow), and those dirty bits imply dirtiness for
+ // all of the child block, including the lines it didn't reflow.
+ NS_ASSERTION(originalPosition == frame->GetPosition(),
+ "we need to call PositionChildViews");
+ return;
+ }
+
+#if defined(REFLOW_STATUS_COVERAGE)
+ RecordReflowStatus(true, frameReflowStatus);
+#endif
+
+ if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
+ // None of the child block fits.
+ *aKeepReflowGoing = false;
+ if (ShouldAvoidBreakInside(aState.mReflowInput)) {
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ } else {
+ PushLines(aState, aLine.prev());
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+ }
+ }
+ else {
+ // Note: line-break-after a block is a nop
+
+ // Try to place the child block.
+ // Don't force the block to fit if we have positive clearance, because
+ // pushing it to the next page would give it more room.
+ // Don't force the block to fit if it's impacted by a float. If it is,
+ // then pushing it to the next page would give it more room. Note that
+ // isImpacted doesn't include impact from the block's own floats.
+ bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
+ !floatAvailableSpace.mHasFloats;
+ nsCollapsingMargin collapsedBEndMargin;
+ nsOverflowAreas overflowAreas;
+ *aKeepReflowGoing = brc.PlaceBlock(*blockHtmlRI, forceFit, aLine.get(),
+ collapsedBEndMargin,
+ overflowAreas,
+ frameReflowStatus);
+ if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) &&
+ ShouldAvoidBreakInside(aState.mReflowInput)) {
+ *aKeepReflowGoing = false;
+ }
+
+ if (aLine->SetCarriedOutBEndMargin(collapsedBEndMargin)) {
+ LineIterator nextLine = aLine;
+ ++nextLine;
+ if (nextLine != LinesEnd()) {
+ nextLine->MarkPreviousMarginDirty();
+ }
+ }
+
+ aLine->SetOverflowAreas(overflowAreas);
+ if (*aKeepReflowGoing) {
+ // Some of the child block fit
+
+ // Advance to new Y position
+ nscoord newBCoord = aLine->BEnd();
+ aState.mBCoord = newBCoord;
+
+
+ // Continue the block frame now if it didn't completely fit in
+ // the available space.
+ if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
+ bool madeContinuation =
+ CreateContinuationFor(aState, nullptr, frame);
+
+ nsIFrame* nextFrame = frame->GetNextInFlow();
+ NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
+
+ if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
+ // If nextFrame used to be an overflow container, make it a normal block
+ if (!madeContinuation &&
+ (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
+ nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame);
+ nsContainerFrame* parent = nextFrame->GetParent();
+ nsresult rv = parent->StealFrame(nextFrame);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ if (parent != this)
+ ReparentFrame(nextFrame, parent, this);
+ mFrames.InsertFrame(nullptr, frame, nextFrame);
+ madeContinuation = true; // needs to be added to mLines
+ nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+
+ // Push continuation to a new line, but only if we actually made one.
+ if (madeContinuation) {
+ nsLineBox* line = NewLineBox(nextFrame, true);
+ mLines.after_insert(aLine, line);
+ }
+
+ PushLines(aState, aLine);
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+
+ // If we need to reflow the continuation of the block child,
+ // then we'd better reflow our continuation
+ if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
+ aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ // We also need to make that continuation's line dirty so
+ // it gets reflowed when we reflow our next in flow. The
+ // nif's line must always be either a line of the nif's
+ // parent block (only if we didn't make a continuation) or
+ // else one of our own overflow lines. In the latter case
+ // the line is already marked dirty, so just handle the
+ // first case.
+ if (!madeContinuation) {
+ nsBlockFrame* nifBlock =
+ nsLayoutUtils::GetAsBlock(nextFrame->GetParent());
+ NS_ASSERTION(nifBlock,
+ "A block's child's next in flow's parent must be a block!");
+ for (LineIterator line = nifBlock->LinesBegin(),
+ line_end = nifBlock->LinesEnd(); line != line_end; ++line) {
+ if (line->Contains(nextFrame)) {
+ line->MarkDirty();
+ break;
+ }
+ }
+ }
+ }
+ *aKeepReflowGoing = false;
+
+ // The block-end margin for a block is only applied on the last
+ // flow block. Since we just continued the child block frame,
+ // we know that line->mFirstChild is not the last flow block
+ // therefore zero out the running margin value.
+#ifdef NOISY_BLOCK_DIR_MARGINS
+ ListTag(stdout);
+ printf(": reflow incomplete, frame=");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" prevBEndMargin=%d, setting to zero\n",
+ aState.mPrevBEndMargin.get());
+#endif
+ aState.mPrevBEndMargin.Zero();
+ }
+ else { // frame is complete but its overflow is not complete
+ // Disconnect the next-in-flow and put it in our overflow tracker
+ if (!madeContinuation &&
+ !(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
+ // It already exists, but as a normal next-in-flow, so we need
+ // to dig it out of the child lists.
+ nsresult rv = nextFrame->GetParent()->StealFrame(nextFrame);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+ else if (madeContinuation) {
+ mFrames.RemoveFrame(nextFrame);
+ }
+
+ // Put it in our overflow list
+ aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
+ NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus);
+
+#ifdef NOISY_BLOCK_DIR_MARGINS
+ ListTag(stdout);
+ printf(": reflow complete but overflow incomplete for ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
+ aState.mPrevBEndMargin.get(), collapsedBEndMargin.get());
+#endif
+ aState.mPrevBEndMargin = collapsedBEndMargin;
+ }
+ }
+ else { // frame is fully complete
+#ifdef NOISY_BLOCK_DIR_MARGINS
+ ListTag(stdout);
+ printf(": reflow complete for ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
+ aState.mPrevBEndMargin.get(), collapsedBEndMargin.get());
+#endif
+ aState.mPrevBEndMargin = collapsedBEndMargin;
+ }
+#ifdef NOISY_BLOCK_DIR_MARGINS
+ ListTag(stdout);
+ printf(": frame=");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
+ brc.GetCarriedOutBEndMargin().get(), collapsedBEndMargin.get(),
+ aState.mPrevBEndMargin.get());
+#endif
+ } else {
+ if ((aLine == mLines.front() && !GetPrevInFlow()) ||
+ ShouldAvoidBreakInside(aState.mReflowInput)) {
+ // If it's our very first line *or* we're not at the top of the page
+ // and we have page-break-inside:avoid, then we need to be pushed to
+ // our parent's next-in-flow.
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ } else {
+ // Push the line that didn't fit and any lines that follow it
+ // to our next-in-flow.
+ PushLines(aState, aLine.prev());
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+ }
+ }
+ }
+ break; // out of the reflow retry loop
+ }
+
+ // Now that we've got its final position all figured out, position any child
+ // views it may have. Note that the case when frame has a view got handled
+ // by FinishReflowChild, but that function didn't have the coordinates needed
+ // to correctly decide whether to reposition child views.
+ if (originalPosition != frame->GetPosition() && !frame->HasView()) {
+ nsContainerFrame::PositionChildViews(frame);
+ }
+
+#ifdef DEBUG
+ VerifyLines(true);
+#endif
+}
+
+void
+nsBlockFrame::ReflowInlineFrames(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepReflowGoing)
+{
+ *aKeepReflowGoing = true;
+
+ aLine->SetLineIsImpactedByFloat(false);
+
+ // Setup initial coordinate system for reflowing the inline frames
+ // into. Apply a previous block frame's block-end margin first.
+ if (ShouldApplyBStartMargin(aState, aLine, aLine->mFirstChild)) {
+ aState.mBCoord += aState.mPrevBEndMargin.get();
+ }
+ nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
+
+ LineReflowStatus lineReflowStatus;
+ do {
+ nscoord availableSpaceBSize = 0;
+ aState.mLineBSize.reset();
+ do {
+ bool allowPullUp = true;
+ nsIFrame* forceBreakInFrame = nullptr;
+ int32_t forceBreakOffset = -1;
+ gfxBreakPriority forceBreakPriority = gfxBreakPriority::eNoBreak;
+ do {
+ nsFloatManager::SavedState floatManagerState;
+ aState.mReflowInput.mFloatManager->PushState(&floatManagerState);
+
+ // Once upon a time we allocated the first 30 nsLineLayout objects
+ // on the stack, and then we switched to the heap. At that time
+ // these objects were large (1100 bytes on a 32 bit system).
+ // Then the nsLineLayout object was shrunk to 156 bytes by
+ // removing some internal buffers. Given that it is so much
+ // smaller, the complexity of 2 different ways of allocating
+ // no longer makes sense. Now we always allocate on the stack.
+ nsLineLayout lineLayout(aState.mPresContext,
+ aState.mReflowInput.mFloatManager,
+ &aState.mReflowInput, &aLine, nullptr);
+ lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
+ if (forceBreakInFrame) {
+ lineLayout.ForceBreakAtPosition(forceBreakInFrame, forceBreakOffset);
+ }
+ DoReflowInlineFrames(aState, lineLayout, aLine,
+ floatAvailableSpace, availableSpaceBSize,
+ &floatManagerState, aKeepReflowGoing,
+ &lineReflowStatus, allowPullUp);
+ lineLayout.EndLineReflow();
+
+ if (LineReflowStatus::RedoNoPull == lineReflowStatus ||
+ LineReflowStatus::RedoMoreFloats == lineReflowStatus ||
+ LineReflowStatus::RedoNextBand == lineReflowStatus) {
+ if (lineLayout.NeedsBackup()) {
+ NS_ASSERTION(!forceBreakInFrame, "Backing up twice; this should never be necessary");
+ // If there is no saved break position, then this will set
+ // set forceBreakInFrame to null and we won't back up, which is
+ // correct.
+ forceBreakInFrame =
+ lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset, &forceBreakPriority);
+ } else {
+ forceBreakInFrame = nullptr;
+ }
+ // restore the float manager state
+ aState.mReflowInput.mFloatManager->PopState(&floatManagerState);
+ // Clear out float lists
+ aState.mCurrentLineFloats.DeleteAll();
+ aState.mBelowCurrentLineFloats.DeleteAll();
+ }
+
+ // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
+ allowPullUp = false;
+ } while (LineReflowStatus::RedoNoPull == lineReflowStatus);
+ } while (LineReflowStatus::RedoMoreFloats == lineReflowStatus);
+ } while (LineReflowStatus::RedoNextBand == lineReflowStatus);
+}
+
+void
+nsBlockFrame::PushTruncatedLine(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepReflowGoing)
+{
+ PushLines(aState, aLine.prev());
+ *aKeepReflowGoing = false;
+ NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
+}
+
+void
+nsBlockFrame::DoReflowInlineFrames(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsFlowAreaRect& aFloatAvailableSpace,
+ nscoord& aAvailableSpaceBSize,
+ nsFloatManager::SavedState*
+ aFloatStateBeforeLine,
+ bool* aKeepReflowGoing,
+ LineReflowStatus* aLineReflowStatus,
+ bool aAllowPullUp)
+{
+ // Forget all of the floats on the line
+ aLine->FreeFloats(aState.mFloatCacheFreeList);
+ aState.mFloatOverflowAreas.Clear();
+
+ // We need to set this flag on the line if any of our reflow passes
+ // are impacted by floats.
+ if (aFloatAvailableSpace.mHasFloats)
+ aLine->SetLineIsImpactedByFloat(true);
+#ifdef REALLY_NOISY_REFLOW
+ printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
+ this, aFloatAvailableSpace.mHasFloats);
+#endif
+
+ WritingMode outerWM = aState.mReflowInput.GetWritingMode();
+ WritingMode lineWM = GetWritingMode(aLine->mFirstChild);
+ LogicalRect lineRect =
+ aFloatAvailableSpace.mRect.ConvertTo(lineWM, outerWM,
+ aState.ContainerSize());
+
+ nscoord iStart = lineRect.IStart(lineWM);
+ nscoord availISize = lineRect.ISize(lineWM);
+ nscoord availBSize;
+ if (aState.mFlags.mHasUnconstrainedBSize) {
+ availBSize = NS_UNCONSTRAINEDSIZE;
+ }
+ else {
+ /* XXX get the height right! */
+ availBSize = lineRect.BSize(lineWM);
+ }
+
+ // Make sure to enable resize optimization before we call BeginLineReflow
+ // because it might get disabled there
+ aLine->EnableResizeReflowOptimization();
+
+ aLineLayout.BeginLineReflow(iStart, aState.mBCoord,
+ availISize, availBSize,
+ aFloatAvailableSpace.mHasFloats,
+ false, /*XXX isTopOfPage*/
+ lineWM, aState.mContainerSize);
+
+ aState.mFlags.mIsLineLayoutEmpty = false;
+
+ // XXX Unfortunately we need to know this before reflowing the first
+ // inline frame in the line. FIX ME.
+ if ((0 == aLineLayout.GetLineNumber()) &&
+ (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
+ (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
+ aLineLayout.SetFirstLetterStyleOK(true);
+ }
+ NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
+ GetPrevContinuation()),
+ "first letter child bit should only be on first continuation");
+
+ // Reflow the frames that are already on the line first
+ LineReflowStatus lineReflowStatus = LineReflowStatus::OK;
+ int32_t i;
+ nsIFrame* frame = aLine->mFirstChild;
+
+ if (aFloatAvailableSpace.mHasFloats) {
+ // There is a soft break opportunity at the start of the line, because
+ // we can always move this line down below float(s).
+ if (aLineLayout.NotifyOptionalBreakPosition(
+ frame, 0, true, gfxBreakPriority::eNormalBreak)) {
+ lineReflowStatus = LineReflowStatus::RedoNextBand;
+ }
+ }
+
+ // need to repeatedly call GetChildCount here, because the child
+ // count can change during the loop!
+ for (i = 0; LineReflowStatus::OK == lineReflowStatus && i < aLine->GetChildCount();
+ i++, frame = frame->GetNextSibling()) {
+ ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus);
+ if (LineReflowStatus::OK != lineReflowStatus) {
+ // It is possible that one or more of next lines are empty
+ // (because of DeleteNextInFlowChild). If so, delete them now
+ // in case we are finished.
+ ++aLine;
+ while ((aLine != LinesEnd()) && (0 == aLine->GetChildCount())) {
+ // XXX Is this still necessary now that DeleteNextInFlowChild
+ // uses DoRemoveFrame?
+ nsLineBox *toremove = aLine;
+ aLine = mLines.erase(aLine);
+ NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line");
+ FreeLineBox(toremove);
+ }
+ --aLine;
+
+ NS_ASSERTION(lineReflowStatus != LineReflowStatus::Truncated,
+ "ReflowInlineFrame should never determine that a line "
+ "needs to go to the next page/column");
+ }
+ }
+
+ // Don't pull up new frames into lines with continuation placeholders
+ if (aAllowPullUp) {
+ // Pull frames and reflow them until we can't
+ while (LineReflowStatus::OK == lineReflowStatus) {
+ frame = PullFrame(aState, aLine);
+ if (!frame) {
+ break;
+ }
+
+ while (LineReflowStatus::OK == lineReflowStatus) {
+ int32_t oldCount = aLine->GetChildCount();
+ ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus);
+ if (aLine->GetChildCount() != oldCount) {
+ // We just created a continuation for aFrame AND its going
+ // to end up on this line (e.g. :first-letter
+ // situation). Therefore we have to loop here before trying
+ // to pull another frame.
+ frame = frame->GetNextSibling();
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ aState.mFlags.mIsLineLayoutEmpty = aLineLayout.LineIsEmpty();
+
+ // We only need to backup if the line isn't going to be reflowed again anyway
+ bool needsBackup = aLineLayout.NeedsBackup() &&
+ (lineReflowStatus == LineReflowStatus::Stop ||
+ lineReflowStatus == LineReflowStatus::OK);
+ if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
+ NS_WARNING("We shouldn't be backing up more than once! "
+ "Someone must have set a break opportunity beyond the available width, "
+ "even though there were better break opportunities before it");
+ needsBackup = false;
+ }
+ if (needsBackup) {
+ // We need to try backing up to before a text run
+ // XXX It's possible, in fact not unusual, for the break opportunity to already
+ // be the end of the line. We should detect that and optimize to not
+ // re-do the line.
+ if (aLineLayout.HasOptionalBreakPosition()) {
+ // We can back up!
+ lineReflowStatus = LineReflowStatus::RedoNoPull;
+ }
+ } else {
+ // In case we reflow this line again, remember that we don't
+ // need to force any breaking
+ aLineLayout.ClearOptionalBreakPosition();
+ }
+
+ if (LineReflowStatus::RedoNextBand == lineReflowStatus) {
+ // This happens only when we have a line that is impacted by
+ // floats and the first element in the line doesn't fit with
+ // the floats.
+ //
+ // What we do is to advance past the first float we find and
+ // then reflow the line all over again.
+ NS_ASSERTION(NS_UNCONSTRAINEDSIZE !=
+ aFloatAvailableSpace.mRect.BSize(outerWM),
+ "unconstrained block size on totally empty line");
+
+ // See the analogous code for blocks in BlockReflowInput::ClearFloats.
+ if (aFloatAvailableSpace.mRect.BSize(outerWM) > 0) {
+ NS_ASSERTION(aFloatAvailableSpace.mHasFloats,
+ "redo line on totally empty line with non-empty band...");
+ // We should never hit this case if we've placed floats on the
+ // line; if we have, then the GetFloatAvailableSpace call is wrong
+ // and needs to happen after the caller pops the space manager
+ // state.
+ aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
+ aState.mBCoord += aFloatAvailableSpace.mRect.BSize(outerWM);
+ aFloatAvailableSpace = aState.GetFloatAvailableSpace();
+ } else {
+ NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowInput.AvailableBSize(),
+ "We shouldn't be running out of height here");
+ if (NS_UNCONSTRAINEDSIZE == aState.mReflowInput.AvailableBSize()) {
+ // just move it down a bit to try to get out of this mess
+ aState.mBCoord += 1;
+ // We should never hit this case if we've placed floats on the
+ // line; if we have, then the GetFloatAvailableSpace call is wrong
+ // and needs to happen after the caller pops the space manager
+ // state.
+ aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
+ aFloatAvailableSpace = aState.GetFloatAvailableSpace();
+ } else {
+ // There's nowhere to retry placing the line, so we want to push
+ // it to the next page/column where its contents can fit not
+ // next to a float.
+ lineReflowStatus = LineReflowStatus::Truncated;
+ PushTruncatedLine(aState, aLine, aKeepReflowGoing);
+ }
+ }
+
+ // XXX: a small optimization can be done here when paginating:
+ // if the new Y coordinate is past the end of the block then
+ // push the line and return now instead of later on after we are
+ // past the float.
+ }
+ else if (LineReflowStatus::Truncated != lineReflowStatus &&
+ LineReflowStatus::RedoNoPull != lineReflowStatus) {
+ // If we are propagating out a break-before status then there is
+ // no point in placing the line.
+ if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
+ if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
+ aFloatAvailableSpace.mRect, aAvailableSpaceBSize,
+ aKeepReflowGoing)) {
+ lineReflowStatus = LineReflowStatus::RedoMoreFloats;
+ // PlaceLine already called GetAvailableSpaceForBSize for us.
+ }
+ }
+ }
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ printf("Line reflow status = %s\n", LineReflowStatusToString(lineReflowStatus));
+ }
+#endif
+
+ if (aLineLayout.GetDirtyNextLine()) {
+ // aLine may have been pushed to the overflow lines.
+ FrameLines* overflowLines = GetOverflowLines();
+ // We can't just compare iterators front() to aLine here, since they may be in
+ // different lists.
+ bool pushedToOverflowLines = overflowLines &&
+ overflowLines->mLines.front() == aLine.get();
+ if (pushedToOverflowLines) {
+ // aLine is stale, it's associated with the main line list but it should
+ // be associated with the overflow line list now
+ aLine = overflowLines->mLines.begin();
+ }
+ nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
+ if (iter.Next() && iter.GetLine()->IsInline()) {
+ iter.GetLine()->MarkDirty();
+ if (iter.GetContainer() != this) {
+ aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+ }
+ }
+
+ *aLineReflowStatus = lineReflowStatus;
+}
+
+/**
+ * Reflow an inline frame. The reflow status is mapped from the frames
+ * reflow status to the lines reflow status (not to our reflow status).
+ * The line reflow status is simple: true means keep placing frames
+ * on the line; false means don't (the line is done). If the line
+ * has some sort of breaking affect then aLine's break-type will be set
+ * to something other than StyleClear::None.
+ */
+void
+nsBlockFrame::ReflowInlineFrame(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsIFrame* aFrame,
+ LineReflowStatus* aLineReflowStatus)
+{
+ if (!aFrame) { // XXX change to MOZ_ASSERT(aFrame)
+ NS_ERROR("why call me?");
+ return;
+ }
+
+ *aLineReflowStatus = LineReflowStatus::OK;
+
+#ifdef NOISY_FIRST_LETTER
+ ListTag(stdout);
+ printf(": reflowing ");
+ nsFrame::ListTag(stdout, aFrame);
+ printf(" reflowingFirstLetter=%s\n",
+ aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
+#endif
+
+ // Reflow the inline frame
+ nsReflowStatus frameReflowStatus;
+ bool pushedFrame;
+ aLineLayout.ReflowFrame(aFrame, frameReflowStatus, nullptr, pushedFrame);
+
+ if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
+ aLineLayout.SetDirtyNextLine();
+ }
+
+#ifdef REALLY_NOISY_REFLOW
+ nsFrame::ListTag(stdout, aFrame);
+ printf(": status=%x\n", frameReflowStatus);
+#endif
+
+#if defined(REFLOW_STATUS_COVERAGE)
+ RecordReflowStatus(false, frameReflowStatus);
+#endif
+
+ // Send post-reflow notification
+ aState.mPrevChild = aFrame;
+
+ /* XXX
+ This is where we need to add logic to handle some odd behavior.
+ For one thing, we should usually place at least one thing next
+ to a left float, even when that float takes up all the width on a line.
+ see bug 22496
+ */
+
+ // Process the child frames reflow status. There are 5 cases:
+ // complete, not-complete, break-before, break-after-complete,
+ // break-after-not-complete. There are two situations: we are a
+ // block or we are an inline. This makes a total of 10 cases
+ // (fortunately, there is some overlap).
+ aLine->SetBreakTypeAfter(StyleClear::None);
+ if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
+ StyleClear::None != aState.mFloatBreakType) {
+ // Always abort the line reflow (because a line break is the
+ // minimal amount of break we do).
+ *aLineReflowStatus = LineReflowStatus::Stop;
+
+ // XXX what should aLine's break-type be set to in all these cases?
+ StyleClear breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
+ MOZ_ASSERT(StyleClear::None != breakType ||
+ StyleClear::None != aState.mFloatBreakType, "bad break type");
+
+ if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
+ // Break-before cases.
+ if (aFrame == aLine->mFirstChild) {
+ // If we break before the first frame on the line then we must
+ // be trying to place content where there's no room (e.g. on a
+ // line with wide floats). Inform the caller to reflow the
+ // line after skipping past a float.
+ *aLineReflowStatus = LineReflowStatus::RedoNextBand;
+ }
+ else {
+ // It's not the first child on this line so go ahead and split
+ // the line. We will see the frame again on the next-line.
+ SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
+
+ // If we're splitting the line because the frame didn't fit and it
+ // was pushed, then mark the line as having word wrapped. We need to
+ // know that if we're shrink wrapping our width
+ if (pushedFrame) {
+ aLine->SetLineWrapped(true);
+ }
+ }
+ }
+ else {
+ // If a float split and its prev-in-flow was followed by a <BR>, then combine
+ // the <BR>'s break type with the inline's break type (the inline will be the very
+ // next frame after the split float).
+ if (StyleClear::None != aState.mFloatBreakType) {
+ breakType = nsLayoutUtils::CombineBreakType(breakType,
+ aState.mFloatBreakType);
+ aState.mFloatBreakType = StyleClear::None;
+ }
+ // Break-after cases
+ if (breakType == StyleClear::Line) {
+ if (!aLineLayout.GetLineEndsInBR()) {
+ breakType = StyleClear::None;
+ }
+ }
+ aLine->SetBreakTypeAfter(breakType);
+ if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) {
+ // Split line, but after the frame just reflowed
+ SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
+
+ if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
+ !aLineLayout.GetLineEndsInBR()) {
+ aLineLayout.SetDirtyNextLine();
+ }
+ }
+ }
+ }
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
+ // Create a continuation for the incomplete frame. Note that the
+ // frame may already have a continuation.
+ CreateContinuationFor(aState, aLine, aFrame);
+
+ // Remember that the line has wrapped
+ if (!aLineLayout.GetLineEndsInBR()) {
+ aLine->SetLineWrapped(true);
+ }
+
+ // If we just ended a first-letter frame or reflowed a placeholder then
+ // don't split the line and don't stop the line reflow...
+ // But if we are going to stop anyways we'd better split the line.
+ if ((!(frameReflowStatus & NS_INLINE_BREAK_FIRST_LETTER_COMPLETE) &&
+ nsGkAtoms::placeholderFrame != aFrame->GetType()) ||
+ *aLineReflowStatus == LineReflowStatus::Stop) {
+ // Split line after the current frame
+ *aLineReflowStatus = LineReflowStatus::Stop;
+ SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
+ }
+ }
+}
+
+bool
+nsBlockFrame::CreateContinuationFor(BlockReflowInput& aState,
+ nsLineBox* aLine,
+ nsIFrame* aFrame)
+{
+ nsIFrame* newFrame = nullptr;
+
+ if (!aFrame->GetNextInFlow()) {
+ newFrame = aState.mPresContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(aState.mPresContext, aFrame, this);
+
+ mFrames.InsertFrame(nullptr, aFrame, newFrame);
+
+ if (aLine) {
+ aLine->NoteFrameAdded(newFrame);
+ }
+ }
+#ifdef DEBUG
+ VerifyLines(false);
+#endif
+ return !!newFrame;
+}
+
+nsresult
+nsBlockFrame::SplitFloat(BlockReflowInput& aState,
+ nsIFrame* aFloat,
+ nsReflowStatus aFloatStatus)
+{
+ MOZ_ASSERT(!NS_FRAME_IS_FULLY_COMPLETE(aFloatStatus),
+ "why split the frame if it's fully complete?");
+ MOZ_ASSERT(aState.mBlock == this);
+
+ nsIFrame* nextInFlow = aFloat->GetNextInFlow();
+ if (nextInFlow) {
+ nsContainerFrame *oldParent = nextInFlow->GetParent();
+ DebugOnly<nsresult> rv = oldParent->StealFrame(nextInFlow);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
+ if (oldParent != this) {
+ ReparentFrame(nextInFlow, oldParent, this);
+ }
+ if (!NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus)) {
+ nextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+ } else {
+ nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(aState.mPresContext, aFloat, this);
+ }
+ if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus)) {
+ nextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+
+ StyleFloat floatStyle =
+ aFloat->StyleDisplay()->PhysicalFloats(aState.mReflowInput.GetWritingMode());
+ if (floatStyle == StyleFloat::Left) {
+ aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
+ } else {
+ MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float side!");
+ aState.mFloatManager->SetSplitRightFloatAcrossBreak();
+ }
+
+ aState.AppendPushedFloatChain(nextInFlow);
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
+ return NS_OK;
+}
+
+static nsFloatCache*
+GetLastFloat(nsLineBox* aLine)
+{
+ nsFloatCache* fc = aLine->GetFirstFloat();
+ while (fc && fc->Next()) {
+ fc = fc->Next();
+ }
+ return fc;
+}
+
+static bool
+CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
+{
+ if (!aFC)
+ return true;
+ NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
+ "float in a line should never be a continuation");
+ NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
+ "float in a line should never be a pushed float");
+ nsIFrame* ph = aBlock->PresContext()->FrameManager()->
+ GetPlaceholderFrameFor(aFC->mFloat->FirstInFlow());
+ for (nsIFrame* f = ph; f; f = f->GetParent()) {
+ if (f->GetParent() == aBlock)
+ return aLine->Contains(f);
+ }
+ NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
+ return true;
+}
+
+void
+nsBlockFrame::SplitLine(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsIFrame* aFrame,
+ LineReflowStatus* aLineReflowStatus)
+{
+ MOZ_ASSERT(aLine->IsInline(), "illegal SplitLine on block line");
+
+ int32_t pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
+ MOZ_ASSERT(pushCount >= 0, "bad push count");
+
+#ifdef DEBUG
+ if (gNoisyReflow) {
+ nsFrame::IndentBy(stdout, gNoiseIndent);
+ printf("split line: from line=%p pushCount=%d aFrame=",
+ static_cast<void*>(aLine.get()), pushCount);
+ if (aFrame) {
+ nsFrame::ListTag(stdout, aFrame);
+ }
+ else {
+ printf("(null)");
+ }
+ printf("\n");
+ if (gReallyNoisyReflow) {
+ aLine->List(stdout, gNoiseIndent+1);
+ }
+ }
+#endif
+
+ if (0 != pushCount) {
+ MOZ_ASSERT(aLine->GetChildCount() > pushCount, "bad push");
+ MOZ_ASSERT(nullptr != aFrame, "whoops");
+#ifdef DEBUG
+ {
+ nsIFrame *f = aFrame;
+ int32_t count = pushCount;
+ while (f && count > 0) {
+ f = f->GetNextSibling();
+ --count;
+ }
+ NS_ASSERTION(count == 0, "Not enough frames to push");
+ }
+#endif
+
+ // Put frames being split out into their own line
+ nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
+ mLines.after_insert(aLine, newLine);
+#ifdef DEBUG
+ if (gReallyNoisyReflow) {
+ newLine->List(stdout, gNoiseIndent+1);
+ }
+#endif
+
+ // Let line layout know that some frames are no longer part of its
+ // state.
+ aLineLayout.SplitLineTo(aLine->GetChildCount());
+
+ // If floats have been placed whose placeholders have been pushed to the new
+ // line, we need to reflow the old line again. We don't want to look at the
+ // frames in the new line, because as a large paragraph is laid out the
+ // we'd get O(N^2) performance. So instead we just check that the last
+ // float and the last below-current-line float are still in aLine.
+ if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
+ !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
+ *aLineReflowStatus = LineReflowStatus::RedoNoPull;
+ }
+
+#ifdef DEBUG
+ VerifyLines(true);
+#endif
+ }
+}
+
+bool
+nsBlockFrame::IsLastLine(BlockReflowInput& aState,
+ LineIterator aLine)
+{
+ while (++aLine != LinesEnd()) {
+ // There is another line
+ if (0 != aLine->GetChildCount()) {
+ // If the next line is a block line then this line is the last in a
+ // group of inline lines.
+ return aLine->IsBlock();
+ }
+ // The next line is empty, try the next one
+ }
+
+ // XXX Not sure about this part
+ // Try our next-in-flows lines to answer the question
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
+ while (nullptr != nextInFlow) {
+ for (LineIterator line = nextInFlow->LinesBegin(),
+ line_end = nextInFlow->LinesEnd();
+ line != line_end;
+ ++line)
+ {
+ if (0 != line->GetChildCount())
+ return line->IsBlock();
+ }
+ nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
+ }
+
+ // This is the last line - so don't allow justification
+ return true;
+}
+
+bool
+nsBlockFrame::PlaceLine(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsFloatManager::SavedState *aFloatStateBeforeLine,
+ LogicalRect& aFloatAvailableSpace,
+ nscoord& aAvailableSpaceBSize,
+ bool* aKeepReflowGoing)
+{
+ // Trim extra white-space from the line before placing the frames
+ aLineLayout.TrimTrailingWhiteSpace();
+
+ // Vertically align the frames on this line.
+ //
+ // According to the CSS2 spec, section 12.6.1, the "marker" box
+ // participates in the height calculation of the list-item box's
+ // first line box.
+ //
+ // There are exactly two places a bullet can be placed: near the
+ // first or second line. It's only placed on the second line in a
+ // rare case: when the first line is empty.
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ bool addedBullet = false;
+ if (HasOutsideBullet() &&
+ ((aLine == mLines.front() &&
+ (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) ||
+ (mLines.front() != mLines.back() &&
+ 0 == mLines.front()->BSize() &&
+ aLine == mLines.begin().next()))) {
+ ReflowOutput metrics(aState.mReflowInput);
+ nsIFrame* bullet = GetOutsideBullet();
+ ReflowBullet(bullet, aState, metrics, aState.mBCoord);
+ NS_ASSERTION(!BulletIsEmpty() || metrics.BSize(wm) == 0,
+ "empty bullet took up space");
+ aLineLayout.AddBulletFrame(bullet, metrics);
+ addedBullet = true;
+ }
+ aLineLayout.VerticalAlignLine();
+
+ // We want to consider the floats in the current line when determining
+ // whether the float available space is shrunk. If mLineBSize doesn't
+ // exist, we are in the first pass trying to place the line. Calling
+ // GetFloatAvailableSpace() like we did in BlockReflowInput::AddFloat()
+ // for UpdateBand().
+
+ // floatAvailableSpaceWithOldLineBSize is the float available space with
+ // the old BSize, but including the floats that were added in this line.
+ LogicalRect floatAvailableSpaceWithOldLineBSize =
+ aState.mLineBSize.isNothing()
+ ? aState.GetFloatAvailableSpace(aLine->BStart()).mRect
+ : aState.GetFloatAvailableSpaceForBSize(aLine->BStart(),
+ aState.mLineBSize.value(),
+ nullptr).mRect;
+
+ // As we redo for floats, we can't reduce the amount of BSize we're
+ // checking.
+ aAvailableSpaceBSize = std::max(aAvailableSpaceBSize, aLine->BSize());
+ LogicalRect floatAvailableSpaceWithLineBSize =
+ aState.GetFloatAvailableSpaceForBSize(aLine->BStart(),
+ aAvailableSpaceBSize,
+ nullptr).mRect;
+
+ // If the available space between the floats is smaller now that we
+ // know the BSize, return false (and cause another pass with
+ // LineReflowStatus::RedoMoreFloats). We ensure aAvailableSpaceBSize
+ // never decreases, which means that we can't reduce the set of floats
+ // we intersect, which means that the available space cannot grow.
+ if (AvailableSpaceShrunk(wm, floatAvailableSpaceWithOldLineBSize,
+ floatAvailableSpaceWithLineBSize, false)) {
+ // Prepare data for redoing the line.
+ aState.mLineBSize = Some(aLine->BSize());
+
+ // Since we want to redo the line, we update aFloatAvailableSpace by
+ // using the aFloatStateBeforeLine, which is the float manager's state
+ // before the line is placed.
+ LogicalRect oldFloatAvailableSpace(aFloatAvailableSpace);
+ aFloatAvailableSpace =
+ aState.GetFloatAvailableSpaceForBSize(aLine->BStart(),
+ aAvailableSpaceBSize,
+ aFloatStateBeforeLine).mRect;
+ NS_ASSERTION(aFloatAvailableSpace.BStart(wm) ==
+ oldFloatAvailableSpace.BStart(wm), "yikes");
+ // Restore the BSize to the position of the next band.
+ aFloatAvailableSpace.BSize(wm) = oldFloatAvailableSpace.BSize(wm);
+
+ // Enforce both IStart() and IEnd() never move outwards to prevent
+ // infinite grow-shrink loops.
+ const nscoord iStartDiff =
+ aFloatAvailableSpace.IStart(wm) - oldFloatAvailableSpace.IStart(wm);
+ const nscoord iEndDiff =
+ aFloatAvailableSpace.IEnd(wm) - oldFloatAvailableSpace.IEnd(wm);
+ if (iStartDiff < 0) {
+ aFloatAvailableSpace.IStart(wm) -= iStartDiff;
+ aFloatAvailableSpace.ISize(wm) += iStartDiff;
+ }
+ if (iEndDiff > 0) {
+ aFloatAvailableSpace.ISize(wm) -= iEndDiff;
+ }
+
+ return false;
+ }
+
+#ifdef DEBUG
+ if (!GetParent()->IsCrazySizeAssertSuppressed()) {
+ static nscoord lastHeight = 0;
+ if (CRAZY_SIZE(aLine->BStart())) {
+ lastHeight = aLine->BStart();
+ if (abs(aLine->BStart() - lastHeight) > CRAZY_COORD/10) {
+ nsFrame::ListTag(stdout);
+ printf(": line=%p y=%d line.bounds.height=%d\n",
+ static_cast<void*>(aLine.get()),
+ aLine->BStart(), aLine->BSize());
+ }
+ }
+ else {
+ lastHeight = 0;
+ }
+ }
+#endif
+
+ // Only block frames horizontally align their children because
+ // inline frames "shrink-wrap" around their children (therefore
+ // there is no extra horizontal space).
+ const nsStyleText* styleText = StyleText();
+
+ /**
+ * text-align-last defaults to the same value as text-align when
+ * text-align-last is set to auto (except when text-align is set to justify),
+ * so in that case we don't need to set isLastLine.
+ *
+ * In other words, isLastLine really means isLastLineAndWeCare.
+ */
+ bool isLastLine =
+ !IsSVGText() &&
+ ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
+ NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
+ (aLineLayout.GetLineEndsInBR() ||
+ IsLastLine(aState, aLine)));
+
+ aLineLayout.TextAlignLine(aLine, isLastLine);
+
+ // From here on, pfd->mBounds rectangles are incorrect because bidi
+ // might have moved frames around!
+ nsOverflowAreas overflowAreas;
+ aLineLayout.RelativePositionFrames(overflowAreas);
+ aLine->SetOverflowAreas(overflowAreas);
+ if (addedBullet) {
+ aLineLayout.RemoveBulletFrame(GetOutsideBullet());
+ }
+
+ // Inline lines do not have margins themselves; however they are
+ // impacted by prior block margins. If this line ends up having some
+ // height then we zero out the previous block-end margin value that was
+ // already applied to the line's starting Y coordinate. Otherwise we
+ // leave it be so that the previous blocks block-end margin can be
+ // collapsed with a block that follows.
+ nscoord newBCoord;
+
+ if (!aLine->CachedIsEmpty()) {
+ // This line has some height. Therefore the application of the
+ // previous-bottom-margin should stick.
+ aState.mPrevBEndMargin.Zero();
+ newBCoord = aLine->BEnd();
+ }
+ else {
+ // Don't let the previous-bottom-margin value affect the newBCoord
+ // coordinate (it was applied in ReflowInlineFrames speculatively)
+ // since the line is empty.
+ // We already called |ShouldApplyBStartMargin|, and if we applied it
+ // then mShouldApplyBStartMargin is set.
+ nscoord dy = aState.mFlags.mShouldApplyBStartMargin
+ ? -aState.mPrevBEndMargin.get() : 0;
+ newBCoord = aState.mBCoord + dy;
+ }
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aState.mReflowStatus) &&
+ ShouldAvoidBreakInside(aState.mReflowInput)) {
+ aLine->AppendFloats(aState.mCurrentLineFloats);
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return true;
+ }
+
+ // See if the line fit (our first line always does).
+ if (mLines.front() != aLine &&
+ newBCoord > aState.mBEndEdge &&
+ aState.mBEndEdge != NS_UNCONSTRAINEDSIZE) {
+ NS_ASSERTION(aState.mCurrentLine == aLine, "oops");
+ if (ShouldAvoidBreakInside(aState.mReflowInput)) {
+ // All our content doesn't fit, start on the next page.
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ } else {
+ // Push aLine and all of its children and anything else that
+ // follows to our next-in-flow.
+ PushTruncatedLine(aState, aLine, aKeepReflowGoing);
+ }
+ return true;
+ }
+
+ aState.mBCoord = newBCoord;
+
+ // Add the already placed current-line floats to the line
+ aLine->AppendFloats(aState.mCurrentLineFloats);
+
+ // Any below current line floats to place?
+ if (aState.mBelowCurrentLineFloats.NotEmpty()) {
+ // Reflow the below-current-line floats, which places on the line's
+ // float list.
+ aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats, aLine);
+ aLine->AppendFloats(aState.mBelowCurrentLineFloats);
+ }
+
+ // When a line has floats, factor them into the combined-area
+ // computations.
+ if (aLine->HasFloats()) {
+ // Combine the float combined area (stored in aState) and the
+ // value computed by the line layout code.
+ nsOverflowAreas lineOverflowAreas;
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect &o = lineOverflowAreas.Overflow(otype);
+ o = aLine->GetOverflowArea(otype);
+#ifdef NOISY_COMBINED_AREA
+ ListTag(stdout);
+ printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
+ otype,
+ o.x, o.y, o.width, o.height,
+ aState.mFloatOverflowAreas.Overflow(otype).x,
+ aState.mFloatOverflowAreas.Overflow(otype).y,
+ aState.mFloatOverflowAreas.Overflow(otype).width,
+ aState.mFloatOverflowAreas.Overflow(otype).height);
+#endif
+ o.UnionRect(aState.mFloatOverflowAreas.Overflow(otype), o);
+
+#ifdef NOISY_COMBINED_AREA
+ printf(" ==> final lineCA=%d,%d,%d,%d\n",
+ o.x, o.y, o.width, o.height);
+#endif
+ }
+ aLine->SetOverflowAreas(lineOverflowAreas);
+ }
+
+ // Apply break-after clearing if necessary
+ // This must stay in sync with |ReflowDirtyLines|.
+ if (aLine->HasFloatBreakAfter()) {
+ aState.mBCoord = aState.ClearFloats(aState.mBCoord, aLine->GetBreakTypeAfter());
+ }
+ return true;
+}
+
+void
+nsBlockFrame::PushLines(BlockReflowInput& aState,
+ nsLineList::iterator aLineBefore)
+{
+ // NOTE: aLineBefore is always a normal line, not an overflow line.
+ // The following expression will assert otherwise.
+ DebugOnly<bool> check = aLineBefore == mLines.begin();
+
+ nsLineList::iterator overBegin(aLineBefore.next());
+
+ // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
+ bool firstLine = overBegin == LinesBegin();
+
+ if (overBegin != LinesEnd()) {
+ // Remove floats in the lines from mFloats
+ nsFrameList floats;
+ CollectFloats(overBegin->mFirstChild, floats, true);
+
+ if (floats.NotEmpty()) {
+#ifdef DEBUG
+ for (nsIFrame* f : floats) {
+ MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
+ "CollectFloats should've removed that bit");
+ }
+#endif
+ // Push the floats onto the front of the overflow out-of-flows list
+ nsAutoOOFFrameList oofs(this);
+ oofs.mList.InsertFrames(nullptr, nullptr, floats);
+ }
+
+ // overflow lines can already exist in some cases, in particular,
+ // when shrinkwrapping and we discover that the shrinkwap causes
+ // the height of some child block to grow which creates additional
+ // overflowing content. In such cases we must prepend the new
+ // overflow to the existing overflow.
+ FrameLines* overflowLines = RemoveOverflowLines();
+ if (!overflowLines) {
+ // XXXldb use presshell arena!
+ overflowLines = new FrameLines();
+ }
+ if (overflowLines) {
+ nsIFrame* lineBeforeLastFrame;
+ if (firstLine) {
+ lineBeforeLastFrame = nullptr; // removes all frames
+ } else {
+ nsIFrame* f = overBegin->mFirstChild;
+ lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
+ NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
+ "unexpected line frames");
+ }
+ nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame);
+ overflowLines->mFrames.InsertFrames(nullptr, nullptr, pushedFrames);
+
+ overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
+ overBegin, LinesEnd());
+ NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
+ // this takes ownership but it won't delete it immediately so we
+ // can keep using it.
+ SetOverflowLines(overflowLines);
+
+ // Mark all the overflow lines dirty so that they get reflowed when
+ // they are pulled up by our next-in-flow.
+
+ // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
+ for (LineIterator line = overflowLines->mLines.begin(),
+ line_end = overflowLines->mLines.end();
+ line != line_end;
+ ++line)
+ {
+ line->MarkDirty();
+ line->MarkPreviousMarginDirty();
+ line->SetBoundsEmpty();
+ if (line->HasFloats()) {
+ line->FreeFloats(aState.mFloatCacheFreeList);
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ VerifyOverflowSituation();
+#endif
+}
+
+// The overflowLines property is stored as a pointer to a line list,
+// which must be deleted. However, the following functions all maintain
+// the invariant that the property is never set if the list is empty.
+
+bool
+nsBlockFrame::DrainOverflowLines()
+{
+#ifdef DEBUG
+ VerifyOverflowSituation();
+#endif
+
+ // Steal the prev-in-flow's overflow lines and prepend them.
+ bool didFindOverflow = false;
+ nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
+ if (prevBlock) {
+ prevBlock->ClearLineCursor();
+ FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
+ if (overflowLines) {
+ // Make all the frames on the overflow line list mine.
+ ReparentFrames(overflowLines->mFrames, prevBlock, this);
+
+ // Make the overflow out-of-flow frames mine too.
+ nsAutoOOFFrameList oofs(prevBlock);
+ if (oofs.mList.NotEmpty()) {
+ // In case we own a next-in-flow of any of the drained frames, then
+ // those are now not PUSHED_FLOATs anymore.
+ for (nsFrameList::Enumerator e(oofs.mList); !e.AtEnd(); e.Next()) {
+ nsIFrame* nif = e.get()->GetNextInFlow();
+ for (; nif && nif->GetParent() == this; nif = nif->GetNextInFlow()) {
+ MOZ_ASSERT(mFloats.ContainsFrame(nif));
+ nif->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
+ }
+ }
+ ReparentFrames(oofs.mList, prevBlock, this);
+ mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
+ }
+
+ if (!mLines.empty()) {
+ // Remember to recompute the margins on the first line. This will
+ // also recompute the correct deltaBCoord if necessary.
+ mLines.front()->MarkPreviousMarginDirty();
+ }
+ // The overflow lines have already been marked dirty and their previous
+ // margins marked dirty also.
+
+ // Prepend the overflow frames/lines to our principal list.
+ mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
+ mLines.splice(mLines.begin(), overflowLines->mLines);
+ NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
+ delete overflowLines;
+ didFindOverflow = true;
+ }
+ }
+
+ // Now append our own overflow lines.
+ return DrainSelfOverflowList() || didFindOverflow;
+}
+
+bool
+nsBlockFrame::DrainSelfOverflowList()
+{
+ UniquePtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
+ if (!ourOverflowLines) {
+ return false;
+ }
+
+ // No need to reparent frames in our own overflow lines/oofs, because they're
+ // already ours. But we should put overflow floats back in mFloats.
+ // (explicit scope to remove the OOF list before VerifyOverflowSituation)
+ {
+ nsAutoOOFFrameList oofs(this);
+ if (oofs.mList.NotEmpty()) {
+#ifdef DEBUG
+ for (nsIFrame* f : oofs.mList) {
+ MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
+ "CollectFloats should've removed that bit");
+ }
+#endif
+ // The overflow floats go after our regular floats.
+ mFloats.AppendFrames(nullptr, oofs.mList);
+ }
+ }
+ if (!ourOverflowLines->mLines.empty()) {
+ mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
+ mLines.splice(mLines.end(), ourOverflowLines->mLines);
+ }
+
+#ifdef DEBUG
+ VerifyOverflowSituation();
+#endif
+ return true;
+}
+
+/**
+ * Pushed floats are floats whose placeholders are in a previous
+ * continuation. They might themselves be next-continuations of a float
+ * that partially fit in an earlier continuation, or they might be the
+ * first continuation of a float that couldn't be placed at all.
+ *
+ * Pushed floats live permanently at the beginning of a block's float
+ * list, where they must live *before* any floats whose placeholders are
+ * in that block.
+ *
+ * Temporarily, during reflow, they also live on the pushed floats list,
+ * which only holds them between (a) when one continuation pushes them to
+ * its pushed floats list because they don't fit and (b) when the next
+ * continuation pulls them onto the beginning of its float list.
+ *
+ * DrainPushedFloats sets up pushed floats the way we need them at the
+ * start of reflow; they are then reflowed by ReflowPushedFloats (which
+ * might push some of them on). Floats with placeholders in this block
+ * are reflowed by (BlockReflowInput/nsLineLayout)::AddFloat, which
+ * also maintains these invariants.
+ *
+ * DrainSelfPushedFloats moves any pushed floats from this block's own
+ * PushedFloats list back into mFloats. DrainPushedFloats additionally
+ * moves frames from its prev-in-flow's PushedFloats list into mFloats.
+ */
+void
+nsBlockFrame::DrainSelfPushedFloats()
+{
+ // If we're getting reflowed multiple times without our
+ // next-continuation being reflowed, we might need to pull back floats
+ // that we just put in the list to be pushed to our next-in-flow.
+ // We don't want to pull back any next-in-flows of floats on our own
+ // float list, and we only need to pull back first-in-flows whose
+ // placeholders were in earlier blocks (since first-in-flows whose
+ // placeholders are in this block will get pulled appropriately by
+ // AddFloat, and will then be more likely to be in the correct order).
+ // FIXME: What if there's a continuation in our pushed floats list
+ // whose prev-in-flow is in a previous continuation of this block
+ // rather than this block? Might we need to pull it back so we don't
+ // report ourselves complete?
+ // FIXME: Maybe we should just pull all of them back?
+ nsPresContext* presContext = PresContext();
+ nsFrameList* ourPushedFloats = GetPushedFloats();
+ if (ourPushedFloats) {
+ // When we pull back floats, we want to put them with the pushed
+ // floats, which must live at the start of our float list, but we
+ // want them at the end of those pushed floats.
+ // FIXME: This isn't quite right! What if they're all pushed floats?
+ nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */
+ for (nsIFrame* f = mFloats.FirstChild();
+ f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
+ f = f->GetNextSibling()) {
+ insertionPrevSibling = f;
+ }
+
+ for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
+ next = f->GetPrevSibling();
+
+ if (f->GetPrevContinuation()) {
+ // FIXME
+ } else {
+ nsPlaceholderFrame *placeholder =
+ presContext->FrameManager()->GetPlaceholderFrameFor(f);
+ nsIFrame *floatOriginalParent = presContext->PresShell()->
+ FrameConstructor()->GetFloatContainingBlock(placeholder);
+ if (floatOriginalParent != this) {
+ // This is a first continuation that was pushed from one of our
+ // previous continuations. Take it out of the pushed floats
+ // list and put it in our floats list, before any of our
+ // floats, but after other pushed floats.
+ ourPushedFloats->RemoveFrame(f);
+ mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
+ }
+ }
+ }
+
+ if (ourPushedFloats->IsEmpty()) {
+ RemovePushedFloats()->Delete(presContext->PresShell());
+ }
+ }
+}
+
+void
+nsBlockFrame::DrainPushedFloats()
+{
+ DrainSelfPushedFloats();
+
+ // After our prev-in-flow has completed reflow, it may have a pushed
+ // floats list, containing floats that we need to own. Take these.
+ nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
+ if (prevBlock) {
+ AutoFrameListPtr list(PresContext(), prevBlock->RemovePushedFloats());
+ if (list && list->NotEmpty()) {
+ mFloats.InsertFrames(this, nullptr, *list);
+ }
+ }
+}
+
+nsBlockFrame::FrameLines*
+nsBlockFrame::GetOverflowLines() const
+{
+ if (!HasOverflowLines()) {
+ return nullptr;
+ }
+ FrameLines* prop = Properties().Get(OverflowLinesProperty());
+ NS_ASSERTION(prop && !prop->mLines.empty() &&
+ prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
+ prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
+ "value should always be stored and non-empty when state set");
+ return prop;
+}
+
+nsBlockFrame::FrameLines*
+nsBlockFrame::RemoveOverflowLines()
+{
+ if (!HasOverflowLines()) {
+ return nullptr;
+ }
+ FrameLines* prop = Properties().Remove(OverflowLinesProperty());
+ NS_ASSERTION(prop && !prop->mLines.empty() &&
+ prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
+ prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
+ "value should always be stored and non-empty when state set");
+ RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
+ return prop;
+}
+
+void
+nsBlockFrame::DestroyOverflowLines()
+{
+ NS_ASSERTION(HasOverflowLines(), "huh?");
+ FrameLines* prop = Properties().Remove(OverflowLinesProperty());
+ NS_ASSERTION(prop && prop->mLines.empty(),
+ "value should always be stored but empty when destroying");
+ RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
+ delete prop;
+}
+
+// This takes ownership of aOverflowLines.
+// XXX We should allocate overflowLines from presShell arena!
+void
+nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines)
+{
+ NS_ASSERTION(aOverflowLines, "null lines");
+ NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
+ NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
+ aOverflowLines->mFrames.FirstChild(),
+ "invalid overflow lines / frames");
+ NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
+ "Overwriting existing overflow lines");
+
+ FrameProperties props = Properties();
+ // Verify that we won't overwrite an existing overflow list
+ NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list");
+ props.Set(OverflowLinesProperty(), aOverflowLines);
+ AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
+}
+
+nsFrameList*
+nsBlockFrame::GetOverflowOutOfFlows() const
+{
+ if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
+ return nullptr;
+ }
+ nsFrameList* result =
+ GetPropTableFrames(OverflowOutOfFlowsProperty());
+ NS_ASSERTION(result, "value should always be non-empty when state set");
+ return result;
+}
+
+// This takes ownership of the frames
+void
+nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList,
+ nsFrameList* aPropValue)
+{
+ NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
+ !!aPropValue, "state does not match value");
+
+ if (aList.IsEmpty()) {
+ if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
+ return;
+ }
+ nsFrameList* list = RemovePropTableFrames(OverflowOutOfFlowsProperty());
+ NS_ASSERTION(aPropValue == list, "prop value mismatch");
+ list->Clear();
+ list->Delete(PresContext()->PresShell());
+ RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
+ }
+ else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
+ NS_ASSERTION(aPropValue == GetPropTableFrames(OverflowOutOfFlowsProperty()),
+ "prop value mismatch");
+ *aPropValue = aList;
+ }
+ else {
+ SetPropTableFrames(new (PresContext()->PresShell()) nsFrameList(aList),
+ OverflowOutOfFlowsProperty());
+ AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
+ }
+}
+
+nsBulletFrame*
+nsBlockFrame::GetInsideBullet() const
+{
+ if (!HasInsideBullet()) {
+ return nullptr;
+ }
+ NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
+ nsBulletFrame* frame = Properties().Get(InsideBulletProperty());
+ NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame,
+ "bogus inside bullet frame");
+ return frame;
+}
+
+nsBulletFrame*
+nsBlockFrame::GetOutsideBullet() const
+{
+ nsFrameList* list = GetOutsideBulletList();
+ return list ? static_cast<nsBulletFrame*>(list->FirstChild())
+ : nullptr;
+}
+
+nsFrameList*
+nsBlockFrame::GetOutsideBulletList() const
+{
+ if (!HasOutsideBullet()) {
+ return nullptr;
+ }
+ NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
+ nsFrameList* list =
+ Properties().Get(OutsideBulletProperty());
+ NS_ASSERTION(list && list->GetLength() == 1 &&
+ list->FirstChild()->GetType() == nsGkAtoms::bulletFrame,
+ "bogus outside bullet list");
+ return list;
+}
+
+nsFrameList*
+nsBlockFrame::GetPushedFloats() const
+{
+ if (!HasPushedFloats()) {
+ return nullptr;
+ }
+ nsFrameList* result =
+ Properties().Get(PushedFloatProperty());
+ NS_ASSERTION(result, "value should always be non-empty when state set");
+ return result;
+}
+
+nsFrameList*
+nsBlockFrame::EnsurePushedFloats()
+{
+ nsFrameList *result = GetPushedFloats();
+ if (result)
+ return result;
+
+ result = new (PresContext()->PresShell()) nsFrameList;
+ Properties().Set(PushedFloatProperty(), result);
+ AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
+
+ return result;
+}
+
+nsFrameList*
+nsBlockFrame::RemovePushedFloats()
+{
+ if (!HasPushedFloats()) {
+ return nullptr;
+ }
+ nsFrameList *result = Properties().Remove(PushedFloatProperty());
+ RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
+ NS_ASSERTION(result, "value should always be non-empty when state set");
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Frame list manipulation routines
+
+void
+nsBlockFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ if (aFrameList.IsEmpty()) {
+ return;
+ }
+ if (aListID != kPrincipalList) {
+ if (kFloatList == aListID) {
+ DrainSelfPushedFloats(); // ensure the last frame is in mFloats
+ mFloats.AppendFrames(nullptr, aFrameList);
+ return;
+ }
+ MOZ_ASSERT(kNoReflowPrincipalList == aListID, "unexpected child list");
+ }
+
+ // Find the proper last-child for where the append should go
+ nsIFrame* lastKid = mFrames.LastChild();
+ NS_ASSERTION((mLines.empty() ? nullptr : mLines.back()->LastChild()) ==
+ lastKid, "out-of-sync mLines / mFrames");
+
+#ifdef NOISY_REFLOW_REASON
+ ListTag(stdout);
+ printf(": append ");
+ nsFrame::ListTag(stdout, aFrameList);
+ if (lastKid) {
+ printf(" after ");
+ nsFrame::ListTag(stdout, lastKid);
+ }
+ printf("\n");
+#endif
+
+ AddFrames(aFrameList, lastKid);
+ if (aListID != kNoReflowPrincipalList) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eTreeChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
+ }
+}
+
+void
+nsBlockFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
+ "inserting after sibling frame with different parent");
+
+ if (aListID != kPrincipalList) {
+ if (kFloatList == aListID) {
+ DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats
+ mFloats.InsertFrames(this, aPrevFrame, aFrameList);
+ return;
+ }
+ MOZ_ASSERT(kNoReflowPrincipalList == aListID, "unexpected child list");
+ }
+
+#ifdef NOISY_REFLOW_REASON
+ ListTag(stdout);
+ printf(": insert ");
+ nsFrame::ListTag(stdout, aFrameList);
+ if (aPrevFrame) {
+ printf(" after ");
+ nsFrame::ListTag(stdout, aPrevFrame);
+ }
+ printf("\n");
+#endif
+
+ AddFrames(aFrameList, aPrevFrame);
+ if (aListID != kNoReflowPrincipalList) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eTreeChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
+ }
+}
+
+void
+nsBlockFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+#ifdef NOISY_REFLOW_REASON
+ ListTag(stdout);
+ printf(": remove ");
+ nsFrame::ListTag(stdout, aOldFrame);
+ printf("\n");
+#endif
+
+ if (aListID == kPrincipalList) {
+ bool hasFloats = BlockHasAnyFloats(aOldFrame);
+ DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
+ if (hasFloats) {
+ MarkSameFloatManagerLinesDirty(this);
+ }
+ }
+ else if (kFloatList == aListID) {
+ // Make sure to mark affected lines dirty for the float frame
+ // we are removing; this way is a bit messy, but so is the rest of the code.
+ // See bug 390762.
+ NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
+ "RemoveFrame should not be called on pushed floats.");
+ for (nsIFrame* f = aOldFrame;
+ f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
+ f = f->GetNextContinuation()) {
+ MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent()));
+ }
+ DoRemoveOutOfFlowFrame(aOldFrame);
+ }
+ else if (kNoReflowPrincipalList == aListID) {
+ // Skip the call to |FrameNeedsReflow| below by returning now.
+ DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
+ return;
+ }
+ else {
+ MOZ_CRASH("unexpected child list");
+ }
+
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eTreeChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
+}
+
+static bool
+ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
+{
+ nsIAtom* type = aLastFrame->GetType();
+ if (type == nsGkAtoms::brFrame) {
+ return true;
+ }
+ // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
+ if (type == nsGkAtoms::textFrame &&
+ !(aLastFrame->GetStateBits() & TEXT_OFFSETS_NEED_FIXING)) {
+ return aLastFrame->HasSignificantTerminalNewline();
+ }
+ return false;
+}
+
+void
+nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
+{
+ // Clear our line cursor, since our lines may change.
+ ClearLineCursor();
+
+ if (aFrameList.IsEmpty()) {
+ return;
+ }
+
+ // If we're inserting at the beginning of our list and we have an
+ // inside bullet, insert after that bullet.
+ if (!aPrevSibling && HasInsideBullet()) {
+ aPrevSibling = GetInsideBullet();
+ }
+
+ // Attempt to find the line that contains the previous sibling
+ nsLineList* lineList = &mLines;
+ nsFrameList* frames = &mFrames;
+ nsLineList::iterator prevSibLine = lineList->end();
+ int32_t prevSiblingIndex = -1;
+ if (aPrevSibling) {
+ // XXX_perf This is technically O(N^2) in some cases, but by using
+ // RFind instead of Find, we make it O(N) in the most common case,
+ // which is appending content.
+
+ // Find the line that contains the previous sibling
+ if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
+ prevSibLine, mFrames.LastChild(),
+ &prevSiblingIndex)) {
+ // Not in mLines - try overflow lines.
+ FrameLines* overflowLines = GetOverflowLines();
+ bool found = false;
+ if (overflowLines) {
+ prevSibLine = overflowLines->mLines.end();
+ prevSiblingIndex = -1;
+ found = nsLineBox::RFindLineContaining(aPrevSibling,
+ overflowLines->mLines.begin(),
+ prevSibLine,
+ overflowLines->mFrames.LastChild(),
+ &prevSiblingIndex);
+ }
+ if (MOZ_LIKELY(found)) {
+ lineList = &overflowLines->mLines;
+ frames = &overflowLines->mFrames;
+ } else {
+ // Note: defensive code! RFindLineContaining must not return
+ // false in this case, so if it does...
+ MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
+ aPrevSibling = nullptr;
+ prevSibLine = lineList->end();
+ }
+ }
+ }
+
+ // Find the frame following aPrevSibling so that we can join up the
+ // two lists of frames.
+ if (aPrevSibling) {
+ // Split line containing aPrevSibling in two if the insertion
+ // point is somewhere in the middle of the line.
+ int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
+ if (rem) {
+ // Split the line in two where the frame(s) are being inserted.
+ nsLineBox* line = NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
+ lineList->after_insert(prevSibLine, line);
+ // Mark prevSibLine dirty and as needing textrun invalidation, since
+ // we may be breaking up text in the line. Its previous line may also
+ // need to be invalidated because it may be able to pull some text up.
+ MarkLineDirty(prevSibLine, lineList);
+ // The new line will also need its textruns recomputed because of the
+ // frame changes.
+ line->MarkDirty();
+ line->SetInvalidateTextRuns(true);
+ }
+ }
+ else if (! lineList->empty()) {
+ lineList->front()->MarkDirty();
+ lineList->front()->SetInvalidateTextRuns(true);
+ }
+ const nsFrameList::Slice& newFrames =
+ frames->InsertFrames(nullptr, aPrevSibling, aFrameList);
+
+ // Walk through the new frames being added and update the line data
+ // structures to fit.
+ for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
+ nsIFrame* newFrame = e.get();
+ NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
+ "Unexpected aPrevSibling");
+ NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
+ (!newFrame->IsAbsolutelyPositioned() &&
+ !newFrame->IsFloating()),
+ "Placeholders should not float or be positioned");
+
+ bool isBlock = newFrame->IsBlockOutside();
+
+ // If the frame is a block frame, or if there is no previous line or if the
+ // previous line is a block line we need to make a new line. We also make
+ // a new line, as an optimization, in the two cases we know we'll need it:
+ // if the previous line ended with a <br>, or if it has significant whitespace
+ // and ended in a newline.
+ if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
+ (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
+ // Create a new line for the frame and add its line to the line
+ // list.
+ nsLineBox* line = NewLineBox(newFrame, isBlock);
+ if (prevSibLine != lineList->end()) {
+ // Append new line after prevSibLine
+ lineList->after_insert(prevSibLine, line);
+ ++prevSibLine;
+ }
+ else {
+ // New line is going before the other lines
+ lineList->push_front(line);
+ prevSibLine = lineList->begin();
+ }
+ }
+ else {
+ prevSibLine->NoteFrameAdded(newFrame);
+ // We're adding inline content to prevSibLine, so we need to mark it
+ // dirty, ensure its textruns are recomputed, and possibly do the same
+ // to its previous line since that line may be able to pull content up.
+ MarkLineDirty(prevSibLine, lineList);
+ }
+
+ aPrevSibling = newFrame;
+ }
+
+#ifdef DEBUG
+ MOZ_ASSERT(aFrameList.IsEmpty());
+ VerifyLines(true);
+#endif
+}
+
+void
+nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat)
+{
+ // Find which line contains the float, so we can update
+ // the float cache.
+ LineIterator line = LinesBegin(), line_end = LinesEnd();
+ for ( ; line != line_end; ++line) {
+ if (line->IsInline() && line->RemoveFloat(aFloat)) {
+ break;
+ }
+ }
+}
+
+void
+nsBlockFrame::RemoveFloat(nsIFrame* aFloat)
+{
+#ifdef DEBUG
+ // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
+ // frame list properties.
+ if (!mFloats.ContainsFrame(aFloat)) {
+ MOZ_ASSERT((GetOverflowOutOfFlows() &&
+ GetOverflowOutOfFlows()->ContainsFrame(aFloat)) ||
+ (GetPushedFloats() &&
+ GetPushedFloats()->ContainsFrame(aFloat)),
+ "aFloat is not our child or on an unexpected frame list");
+ }
+#endif
+
+ if (mFloats.StartRemoveFrame(aFloat)) {
+ return;
+ }
+
+ nsFrameList* list = GetPushedFloats();
+ if (list && list->ContinueRemoveFrame(aFloat)) {
+#if 0
+ // XXXmats not yet - need to investigate BlockReflowInput::mPushedFloats
+ // first so we don't leave it pointing to a deleted list.
+ if (list->IsEmpty()) {
+ delete RemovePushedFloats();
+ }
+#endif
+ return;
+ }
+
+ {
+ nsAutoOOFFrameList oofs(this);
+ if (oofs.mList.ContinueRemoveFrame(aFloat)) {
+ return;
+ }
+ }
+}
+
+void
+nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
+{
+ // The containing block is always the parent of aFrame.
+ nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
+
+ // Remove aFrame from the appropriate list.
+ if (aFrame->IsAbsolutelyPositioned()) {
+ // This also deletes the next-in-flows
+ block->GetAbsoluteContainingBlock()->RemoveFrame(block,
+ kAbsoluteList,
+ aFrame);
+ }
+ else {
+ // First remove aFrame's next-in-flows.
+ nsIFrame* nif = aFrame->GetNextInFlow();
+ if (nif) {
+ nif->GetParent()->DeleteNextInFlowChild(nif, false);
+ }
+ // Now remove aFrame from its child list and Destroy it.
+ block->RemoveFloatFromFloatCache(aFrame);
+ block->RemoveFloat(aFrame);
+ aFrame->Destroy();
+ }
+}
+
+/**
+ * This helps us iterate over the list of all normal + overflow lines
+ */
+void
+nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
+ nsLineList::iterator* aStartIterator,
+ nsLineList::iterator* aEndIterator,
+ bool* aInOverflowLines,
+ FrameLines** aOverflowLines)
+{
+ if (*aIterator == *aEndIterator) {
+ if (!*aInOverflowLines) {
+ // Try the overflow lines
+ *aInOverflowLines = true;
+ FrameLines* lines = GetOverflowLines();
+ if (lines) {
+ *aStartIterator = lines->mLines.begin();
+ *aIterator = *aStartIterator;
+ *aEndIterator = lines->mLines.end();
+ *aOverflowLines = lines;
+ }
+ }
+ }
+}
+
+nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
+ LineIterator aLine)
+ : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines)
+{
+ // This will assert if aLine isn't in mLines of aFrame:
+ DebugOnly<bool> check = aLine == mFrame->LinesBegin();
+}
+
+nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
+ LineIterator aLine, bool aInOverflow)
+ : mFrame(aFrame), mLine(aLine),
+ mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines
+ : &aFrame->mLines)
+{
+}
+
+nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
+ bool* aFoundValidLine)
+ : mFrame(aFrame), mLineList(&aFrame->mLines)
+{
+ mLine = aFrame->LinesBegin();
+ *aFoundValidLine = FindValidLine();
+}
+
+static nsIFrame*
+FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame)
+{
+ NS_ASSERTION(aFrame, "must have frame");
+ nsIFrame* child;
+ while (true) {
+ nsIFrame* block = aFrame;
+ do {
+ child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
+ if (child)
+ break;
+ block = block->GetNextContinuation();
+ } while (block);
+ if (!child)
+ return nullptr;
+ if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
+ break;
+ aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child);
+ }
+
+ return child;
+}
+
+nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
+ nsIFrame* aFindFrame, bool* aFoundValidLine)
+ : mFrame(aFrame), mLineList(&aFrame->mLines)
+{
+ *aFoundValidLine = false;
+
+ nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
+ if (!child)
+ return;
+
+ LineIterator line_end = aFrame->LinesEnd();
+ // Try to use the cursor if it exists, otherwise fall back to the first line
+ if (nsLineBox* const cursor = aFrame->GetLineCursor()) {
+ mLine = line_end;
+ // Perform a simultaneous forward and reverse search starting from the
+ // line cursor.
+ nsBlockFrame::LineIterator line = aFrame->LinesBeginFrom(cursor);
+ nsBlockFrame::ReverseLineIterator rline = aFrame->LinesRBeginFrom(cursor);
+ nsBlockFrame::ReverseLineIterator rline_end = aFrame->LinesREnd();
+ // rline is positioned on the line containing 'cursor', so it's not
+ // rline_end. So we can safely increment it (i.e. move it to one line
+ // earlier) to start searching there.
+ ++rline;
+ while (line != line_end || rline != rline_end) {
+ if (line != line_end) {
+ if (line->Contains(child)) {
+ mLine = line;
+ break;
+ }
+ ++line;
+ }
+ if (rline != rline_end) {
+ if (rline->Contains(child)) {
+ mLine = rline;
+ break;
+ }
+ ++rline;
+ }
+ }
+ if (mLine != line_end) {
+ *aFoundValidLine = true;
+ if (mLine != cursor) {
+ aFrame->Properties().Set(nsBlockFrame::LineCursorProperty(), mLine);
+ }
+ return;
+ }
+ } else {
+ for (mLine = aFrame->LinesBegin(); mLine != line_end; ++mLine) {
+ if (mLine->Contains(child)) {
+ *aFoundValidLine = true;
+ return;
+ }
+ }
+ }
+ // Didn't find the line
+ MOZ_ASSERT(mLine == line_end, "mLine should be line_end at this point");
+
+ // If we reach here, it means that we have not been able to find the
+ // desired frame in our in-flow lines. So we should start looking at
+ // our overflow lines. In order to do that, we set mLine to the end
+ // iterator so that FindValidLine starts to look at overflow lines,
+ // if any.
+
+ if (!FindValidLine())
+ return;
+
+ do {
+ if (mLine->Contains(child)) {
+ *aFoundValidLine = true;
+ return;
+ }
+ } while (Next());
+}
+
+nsBlockFrame::LineIterator
+nsBlockInFlowLineIterator::End()
+{
+ return mLineList->end();
+}
+
+bool
+nsBlockInFlowLineIterator::IsLastLineInList()
+{
+ LineIterator end = End();
+ return mLine != end && mLine.next() == end;
+}
+
+bool
+nsBlockInFlowLineIterator::Next()
+{
+ ++mLine;
+ return FindValidLine();
+}
+
+bool
+nsBlockInFlowLineIterator::Prev()
+{
+ LineIterator begin = mLineList->begin();
+ if (mLine != begin) {
+ --mLine;
+ return true;
+ }
+ bool currentlyInOverflowLines = GetInOverflow();
+ while (true) {
+ if (currentlyInOverflowLines) {
+ mLineList = &mFrame->mLines;
+ mLine = mLineList->end();
+ if (mLine != mLineList->begin()) {
+ --mLine;
+ return true;
+ }
+ } else {
+ mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
+ if (!mFrame)
+ return false;
+ nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
+ if (overflowLines) {
+ mLineList = &overflowLines->mLines;
+ mLine = mLineList->end();
+ NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?");
+ --mLine;
+ return true;
+ }
+ }
+ currentlyInOverflowLines = !currentlyInOverflowLines;
+ }
+}
+
+bool
+nsBlockInFlowLineIterator::FindValidLine()
+{
+ LineIterator end = mLineList->end();
+ if (mLine != end)
+ return true;
+ bool currentlyInOverflowLines = GetInOverflow();
+ while (true) {
+ if (currentlyInOverflowLines) {
+ mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
+ if (!mFrame)
+ return false;
+ mLineList = &mFrame->mLines;
+ mLine = mLineList->begin();
+ if (mLine != mLineList->end())
+ return true;
+ } else {
+ nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
+ if (overflowLines) {
+ mLineList = &overflowLines->mLines;
+ mLine = mLineList->begin();
+ NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?");
+ return true;
+ }
+ }
+ currentlyInOverflowLines = !currentlyInOverflowLines;
+ }
+}
+
+static void RemoveBlockChild(nsIFrame* aFrame,
+ bool aRemoveOnlyFluidContinuations)
+{
+ if (!aFrame) {
+ return;
+ }
+ nsBlockFrame* nextBlock = nsLayoutUtils::GetAsBlock(aFrame->GetParent());
+ NS_ASSERTION(nextBlock,
+ "Our child's continuation's parent is not a block?");
+ nextBlock->DoRemoveFrame(aFrame,
+ (aRemoveOnlyFluidContinuations ? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS));
+}
+
+// This function removes aDeletedFrame and all its continuations. It
+// is optimized for deleting a whole series of frames. The easy
+// implementation would invoke itself recursively on
+// aDeletedFrame->GetNextContinuation, then locate the line containing
+// aDeletedFrame and remove aDeletedFrame from that line. But here we
+// start by locating aDeletedFrame and then scanning from that point
+// on looking for continuations.
+void
+nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags)
+{
+ // Clear our line cursor, since our lines may change.
+ ClearLineCursor();
+
+ if (aDeletedFrame->GetStateBits() &
+ (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ if (!aDeletedFrame->GetPrevInFlow()) {
+ NS_ASSERTION(aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "Expected out-of-flow frame");
+ DoRemoveOutOfFlowFrame(aDeletedFrame);
+ }
+ else {
+ nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame,
+ (aFlags & FRAMES_ARE_EMPTY) != 0);
+ }
+ return;
+ }
+
+ // Find the line that contains deletedFrame
+ nsLineList::iterator line_start = mLines.begin(),
+ line_end = mLines.end();
+ nsLineList::iterator line = line_start;
+ FrameLines* overflowLines = nullptr;
+ bool searchingOverflowList = false;
+ // Make sure we look in the overflow lines even if the normal line
+ // list is empty
+ TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
+ &overflowLines);
+ while (line != line_end) {
+ if (line->Contains(aDeletedFrame)) {
+ break;
+ }
+ ++line;
+ TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
+ &overflowLines);
+ }
+
+ if (line == line_end) {
+ NS_ERROR("can't find deleted frame in lines");
+ return;
+ }
+
+ if (!(aFlags & FRAMES_ARE_EMPTY)) {
+ if (line != line_start) {
+ line.prev()->MarkDirty();
+ line.prev()->SetInvalidateTextRuns(true);
+ }
+ else if (searchingOverflowList && !mLines.empty()) {
+ mLines.back()->MarkDirty();
+ mLines.back()->SetInvalidateTextRuns(true);
+ }
+ }
+
+ while (line != line_end && aDeletedFrame) {
+ NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
+ NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
+
+ if (!(aFlags & FRAMES_ARE_EMPTY)) {
+ line->MarkDirty();
+ line->SetInvalidateTextRuns(true);
+ }
+
+ // If the frame being deleted is the last one on the line then
+ // optimize away the line->Contains(next-in-flow) call below.
+ bool isLastFrameOnLine = 1 == line->GetChildCount();
+ if (!isLastFrameOnLine) {
+ LineIterator next = line.next();
+ nsIFrame* lastFrame = next != line_end ?
+ next->mFirstChild->GetPrevSibling() :
+ (searchingOverflowList ? overflowLines->mFrames.LastChild() :
+ mFrames.LastChild());
+ NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
+ "unexpected line frames");
+ isLastFrameOnLine = lastFrame == aDeletedFrame;
+ }
+
+ // Remove aDeletedFrame from the line
+ if (line->mFirstChild == aDeletedFrame) {
+ // We should be setting this to null if aDeletedFrame
+ // is the only frame on the line. HOWEVER in that case
+ // we will be removing the line anyway, see below.
+ line->mFirstChild = aDeletedFrame->GetNextSibling();
+ }
+
+ // Hmm, this won't do anything if we're removing a frame in the first
+ // overflow line... Hopefully doesn't matter
+ --line;
+ if (line != line_end && !line->IsBlock()) {
+ // Since we just removed a frame that follows some inline
+ // frames, we need to reflow the previous line.
+ line->MarkDirty();
+ }
+ ++line;
+
+ // Take aDeletedFrame out of the sibling list. Note that
+ // prevSibling will only be nullptr when we are deleting the very
+ // first frame in the main or overflow list.
+ if (searchingOverflowList) {
+ overflowLines->mFrames.RemoveFrame(aDeletedFrame);
+ } else {
+ mFrames.RemoveFrame(aDeletedFrame);
+ }
+
+ // Update the child count of the line to be accurate
+ line->NoteFrameRemoved(aDeletedFrame);
+
+ // Destroy frame; capture its next continuation first in case we need
+ // to destroy that too.
+ nsIFrame* deletedNextContinuation = (aFlags & REMOVE_FIXED_CONTINUATIONS) ?
+ aDeletedFrame->GetNextContinuation() : aDeletedFrame->GetNextInFlow();
+#ifdef NOISY_REMOVE_FRAME
+ printf("DoRemoveFrame: %s line=%p frame=",
+ searchingOverflowList?"overflow":"normal", line.get());
+ nsFrame::ListTag(stdout, aDeletedFrame);
+ printf(" prevSibling=%p deletedNextContinuation=%p\n",
+ aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
+#endif
+
+ // If next-in-flow is an overflow container, must remove it first.
+ if (deletedNextContinuation &&
+ deletedNextContinuation->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
+ deletedNextContinuation->GetParent()->
+ DeleteNextInFlowChild(deletedNextContinuation, false);
+ deletedNextContinuation = nullptr;
+ }
+
+ aDeletedFrame->Destroy();
+ aDeletedFrame = deletedNextContinuation;
+
+ bool haveAdvancedToNextLine = false;
+ // If line is empty, remove it now.
+ if (0 == line->GetChildCount()) {
+#ifdef NOISY_REMOVE_FRAME
+ printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
+ searchingOverflowList?"overflow":"normal", line.get());
+#endif
+ nsLineBox *cur = line;
+ if (!searchingOverflowList) {
+ line = mLines.erase(line);
+ // Invalidate the space taken up by the line.
+ // XXX We need to do this if we're removing a frame as a result of
+ // a call to RemoveFrame(), but we may not need to do this in all
+ // cases...
+#ifdef NOISY_BLOCK_INVALIDATE
+ nsRect visOverflow(cur->GetVisualOverflowArea());
+ printf("%p invalidate 10 (%d, %d, %d, %d)\n",
+ this, visOverflow.x, visOverflow.y,
+ visOverflow.width, visOverflow.height);
+#endif
+ } else {
+ line = overflowLines->mLines.erase(line);
+ if (overflowLines->mLines.empty()) {
+ DestroyOverflowLines();
+ overflowLines = nullptr;
+ // We just invalidated our iterators. Since we were in
+ // the overflow lines list, which is now empty, set them
+ // so we're at the end of the regular line list.
+ line_start = mLines.begin();
+ line_end = mLines.end();
+ line = line_end;
+ }
+ }
+ FreeLineBox(cur);
+
+ // If we're removing a line, ReflowDirtyLines isn't going to
+ // know that it needs to slide lines unless something is marked
+ // dirty. So mark the previous margin of the next line dirty if
+ // there is one.
+ if (line != line_end) {
+ line->MarkPreviousMarginDirty();
+ }
+ haveAdvancedToNextLine = true;
+ } else {
+ // Make the line that just lost a frame dirty, and advance to
+ // the next line.
+ if (!deletedNextContinuation || isLastFrameOnLine ||
+ !line->Contains(deletedNextContinuation)) {
+ line->MarkDirty();
+ ++line;
+ haveAdvancedToNextLine = true;
+ }
+ }
+
+ if (deletedNextContinuation) {
+ // See if we should keep looking in the current flow's line list.
+ if (deletedNextContinuation->GetParent() != this) {
+ // The deceased frames continuation is not a child of the
+ // current block. So break out of the loop so that we advance
+ // to the next parent.
+ //
+ // If we have a continuation in a different block then all bets are
+ // off regarding whether we are deleting frames without actual content,
+ // so don't propagate FRAMES_ARE_EMPTY any further.
+ aFlags &= ~FRAMES_ARE_EMPTY;
+ break;
+ }
+
+ // If we advanced to the next line then check if we should switch to the
+ // overflow line list.
+ if (haveAdvancedToNextLine) {
+ if (line != line_end && !searchingOverflowList &&
+ !line->Contains(deletedNextContinuation)) {
+ // We have advanced to the next *normal* line but the next-in-flow
+ // is not there - force a switch to the overflow line list.
+ line = line_end;
+ }
+
+ TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
+ &overflowLines);
+#ifdef NOISY_REMOVE_FRAME
+ printf("DoRemoveFrame: now on %s line=%p\n",
+ searchingOverflowList?"overflow":"normal", line.get());
+#endif
+ }
+ }
+ }
+
+ if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
+ line.next()->MarkDirty();
+ line.next()->SetInvalidateTextRuns(true);
+ }
+
+#ifdef DEBUG
+ VerifyLines(true);
+ VerifyOverflowSituation();
+#endif
+
+ // Advance to next flow block if the frame has more continuations
+ RemoveBlockChild(aDeletedFrame, !(aFlags & REMOVE_FIXED_CONTINUATIONS));
+}
+
+static bool
+FindBlockLineFor(nsIFrame* aChild,
+ nsLineList::iterator aBegin,
+ nsLineList::iterator aEnd,
+ nsLineList::iterator* aResult)
+{
+ MOZ_ASSERT(aChild->IsBlockOutside());
+ for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
+ MOZ_ASSERT(line->GetChildCount() > 0);
+ if (line->IsBlock() && line->mFirstChild == aChild) {
+ MOZ_ASSERT(line->GetChildCount() == 1);
+ *aResult = line;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+FindInlineLineFor(nsIFrame* aChild,
+ const nsFrameList& aFrameList,
+ nsLineList::iterator aBegin,
+ nsLineList::iterator aEnd,
+ nsLineList::iterator* aResult)
+{
+ MOZ_ASSERT(!aChild->IsBlockOutside());
+ for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
+ MOZ_ASSERT(line->GetChildCount() > 0);
+ if (!line->IsBlock()) {
+ // Optimize by comparing the line's last child first.
+ nsLineList::iterator next = line.next();
+ if (aChild == (next == aEnd ? aFrameList.LastChild()
+ : next->mFirstChild->GetPrevSibling()) ||
+ line->Contains(aChild)) {
+ *aResult = line;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool
+FindLineFor(nsIFrame* aChild,
+ const nsFrameList& aFrameList,
+ nsLineList::iterator aBegin,
+ nsLineList::iterator aEnd,
+ nsLineList::iterator* aResult)
+{
+ return aChild->IsBlockOutside() ?
+ FindBlockLineFor(aChild, aBegin, aEnd, aResult) :
+ FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult);
+}
+
+nsresult
+nsBlockFrame::StealFrame(nsIFrame* aChild)
+{
+ MOZ_ASSERT(aChild->GetParent() == this);
+
+ if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
+ aChild->IsFloating()) {
+ RemoveFloat(aChild);
+ return NS_OK;
+ }
+
+ if (MaybeStealOverflowContainerFrame(aChild)) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
+
+ nsLineList::iterator line;
+ if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) {
+ RemoveFrameFromLine(aChild, line, mFrames, mLines);
+ } else {
+ FrameLines* overflowLines = GetOverflowLines();
+ DebugOnly<bool> found;
+ found = FindLineFor(aChild, overflowLines->mFrames,
+ overflowLines->mLines.begin(),
+ overflowLines->mLines.end(), &line);
+ MOZ_ASSERT(found);
+ RemoveFrameFromLine(aChild, line, overflowLines->mFrames,
+ overflowLines->mLines);
+ if (overflowLines->mLines.empty()) {
+ DestroyOverflowLines();
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine,
+ nsFrameList& aFrameList, nsLineList& aLineList)
+{
+ aFrameList.RemoveFrame(aChild);
+ if (aChild == aLine->mFirstChild) {
+ aLine->mFirstChild = aChild->GetNextSibling();
+ }
+ aLine->NoteFrameRemoved(aChild);
+ if (aLine->GetChildCount() > 0) {
+ aLine->MarkDirty();
+ } else {
+ // The line became empty - destroy it.
+ nsLineBox* lineBox = aLine;
+ aLine = aLineList.erase(aLine);
+ if (aLine != aLineList.end()) {
+ aLine->MarkPreviousMarginDirty();
+ }
+ FreeLineBox(lineBox);
+ }
+}
+
+void
+nsBlockFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
+ bool aDeletingEmptyFrames)
+{
+ NS_PRECONDITION(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
+
+ if (aNextInFlow->GetStateBits() &
+ (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ nsContainerFrame::DeleteNextInFlowChild(aNextInFlow, aDeletingEmptyFrames);
+ }
+ else {
+#ifdef DEBUG
+ if (aDeletingEmptyFrames) {
+ nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
+ }
+#endif
+ DoRemoveFrame(aNextInFlow,
+ aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
+ }
+}
+
+const nsStyleText*
+nsBlockFrame::StyleTextForLineLayout()
+{
+ // Return the pointer to an unmodified style text
+ return StyleText();
+}
+
+////////////////////////////////////////////////////////////////////////
+// Float support
+
+LogicalRect
+nsBlockFrame::AdjustFloatAvailableSpace(BlockReflowInput& aState,
+ const LogicalRect& aFloatAvailableSpace,
+ nsIFrame* aFloatFrame)
+{
+ // Compute the available inline size. By default, assume the inline
+ // size of the containing block.
+ nscoord availISize;
+ const nsStyleDisplay* floatDisplay = aFloatFrame->StyleDisplay();
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+
+ if (mozilla::StyleDisplay::Table != floatDisplay->mDisplay ||
+ eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
+ availISize = aState.ContentISize();
+ }
+ else {
+ // This quirk matches the one in BlockReflowInput::FlowAndPlaceFloat
+ // give tables only the available space
+ // if they can shrink we may not be constrained to place
+ // them in the next line
+ availISize = aFloatAvailableSpace.ISize(wm);
+ }
+
+ nscoord availBSize = NS_UNCONSTRAINEDSIZE == aState.ContentBSize()
+ ? NS_UNCONSTRAINEDSIZE
+ : std::max(0, aState.ContentBEnd() - aState.mBCoord);
+
+ if (availBSize != NS_UNCONSTRAINEDSIZE &&
+ !aState.mFlags.mFloatFragmentsInsideColumnEnabled &&
+ nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame)) {
+ // Tell the float it has unrestricted block-size, so it won't break.
+ // If the float doesn't actually fit in the column it will fail to be
+ // placed, and either move to the block-start of the next column or just
+ // overflow.
+ availBSize = NS_UNCONSTRAINEDSIZE;
+ }
+
+ return LogicalRect(wm, aState.ContentIStart(), aState.ContentBStart(),
+ availISize, availBSize);
+}
+
+nscoord
+nsBlockFrame::ComputeFloatISize(BlockReflowInput& aState,
+ const LogicalRect& aFloatAvailableSpace,
+ nsIFrame* aFloat)
+{
+ NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "aFloat must be an out-of-flow frame");
+ // Reflow the float.
+ LogicalRect availSpace = AdjustFloatAvailableSpace(aState,
+ aFloatAvailableSpace,
+ aFloat);
+
+ WritingMode blockWM = aState.mReflowInput.GetWritingMode();
+ WritingMode floatWM = aFloat->GetWritingMode();
+ ReflowInput
+ floatRS(aState.mPresContext, aState.mReflowInput, aFloat,
+ availSpace.Size(blockWM).ConvertTo(floatWM, blockWM));
+
+ return floatRS.ComputedSizeWithMarginBorderPadding(blockWM).ISize(blockWM);
+}
+
+void
+nsBlockFrame::ReflowFloat(BlockReflowInput& aState,
+ const LogicalRect& aAdjustedAvailableSpace,
+ nsIFrame* aFloat,
+ LogicalMargin& aFloatMargin,
+ LogicalMargin& aFloatOffsets,
+ bool aFloatPushedDown,
+ nsReflowStatus& aReflowStatus)
+{
+ NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "aFloat must be an out-of-flow frame");
+ // Reflow the float.
+ aReflowStatus = NS_FRAME_COMPLETE;
+
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+#ifdef NOISY_FLOAT
+ printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
+ aFloat, this,
+ aAdjustedAvailableSpace.IStart(wm), aAdjustedAvailableSpace.BStart(wm),
+ aAdjustedAvailableSpace.ISize(wm), aAdjustedAvailableSpace.BSize(wm)
+ );
+#endif
+
+ ReflowInput
+ floatRS(aState.mPresContext, aState.mReflowInput, aFloat,
+ aAdjustedAvailableSpace.Size(wm).ConvertTo(aFloat->GetWritingMode(),
+ wm));
+
+ // Normally the mIsTopOfPage state is copied from the parent reflow
+ // state. However, when reflowing a float, if we've placed other
+ // floats that force this float *down* or *narrower*, we should unset
+ // the mIsTopOfPage state.
+ // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
+ // variable below, which has the exact same effect. Perhaps it should
+ // be merged into that, except that the test for narrowing here is not
+ // about adjacency with the top, so it seems misleading.
+ if (floatRS.mFlags.mIsTopOfPage &&
+ (aFloatPushedDown ||
+ aAdjustedAvailableSpace.ISize(wm) != aState.ContentISize())) {
+ floatRS.mFlags.mIsTopOfPage = false;
+ }
+
+ // Setup a block reflow context to reflow the float.
+ nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
+
+ // Reflow the float
+ bool isAdjacentWithTop = aState.IsAdjacentWithTop();
+
+ nsIFrame* clearanceFrame = nullptr;
+ do {
+ nsCollapsingMargin margin;
+ bool mayNeedRetry = false;
+ floatRS.mDiscoveredClearance = nullptr;
+ // Only first in flow gets a block-start margin.
+ if (!aFloat->GetPrevInFlow()) {
+ brc.ComputeCollapsedBStartMargin(floatRS, &margin,
+ clearanceFrame,
+ &mayNeedRetry);
+
+ if (mayNeedRetry && !clearanceFrame) {
+ floatRS.mDiscoveredClearance = &clearanceFrame;
+ // We don't need to push the float manager state because the the block has its own
+ // float manager that will be destroyed and recreated
+ }
+ }
+
+ brc.ReflowBlock(aAdjustedAvailableSpace, true, margin,
+ 0, isAdjacentWithTop,
+ nullptr, floatRS,
+ aReflowStatus, aState);
+ } while (clearanceFrame);
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aReflowStatus) &&
+ ShouldAvoidBreakInside(floatRS)) {
+ aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ } else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
+ (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.BSize(wm))) {
+ // An incomplete reflow status means we should split the float
+ // if the height is constrained (bug 145305).
+ aReflowStatus = NS_FRAME_COMPLETE;
+ }
+
+ if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
+ aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+
+ if (aFloat->GetType() == nsGkAtoms::letterFrame) {
+ // We never split floating first letters; an incomplete state for
+ // such frames simply means that there is more content to be
+ // reflowed on the line.
+ if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus))
+ aReflowStatus = NS_FRAME_COMPLETE;
+ }
+
+ // Capture the margin and offsets information for the caller
+ aFloatMargin =
+ // float margins don't collapse
+ floatRS.ComputedLogicalMargin().ConvertTo(wm, floatRS.GetWritingMode());
+ aFloatOffsets =
+ floatRS.ComputedLogicalOffsets().ConvertTo(wm, floatRS.GetWritingMode());
+
+ const ReflowOutput& metrics = brc.GetMetrics();
+
+ // Set the rect, make sure the view is properly sized and positioned,
+ // and tell the frame we're done reflowing it
+ // XXXldb This seems like the wrong place to be doing this -- shouldn't
+ // we be doing this in BlockReflowInput::FlowAndPlaceFloat after
+ // we've positioned the float, and shouldn't we be doing the equivalent
+ // of |PlaceFrameView| here?
+ WritingMode metricsWM = metrics.GetWritingMode();
+ aFloat->SetSize(metricsWM, metrics.Size(metricsWM));
+ if (aFloat->HasView()) {
+ nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, aFloat,
+ aFloat->GetView(),
+ metrics.VisualOverflow(),
+ NS_FRAME_NO_MOVE_VIEW);
+ }
+ // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
+ aFloat->DidReflow(aState.mPresContext, &floatRS,
+ nsDidReflowStatus::FINISHED);
+
+#ifdef NOISY_FLOAT
+ printf("end ReflowFloat %p, sized to %d,%d\n",
+ aFloat, metrics.Width(), metrics.Height());
+#endif
+}
+
+StyleClear
+nsBlockFrame::FindTrailingClear()
+{
+ // find the break type of the last line
+ for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
+ nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
+ LineIterator endLine = block->LinesEnd();
+ if (endLine != block->LinesBegin()) {
+ --endLine;
+ return endLine->GetBreakTypeAfter();
+ }
+ }
+ return StyleClear::None;
+}
+
+void
+nsBlockFrame::ReflowPushedFloats(BlockReflowInput& aState,
+ nsOverflowAreas& aOverflowAreas,
+ nsReflowStatus& aStatus)
+{
+ // Pushed floats live at the start of our float list; see comment
+ // above nsBlockFrame::DrainPushedFloats.
+ nsIFrame* f = mFloats.FirstChild();
+ nsIFrame* prev = nullptr;
+ while (f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)) {
+ MOZ_ASSERT(prev == f->GetPrevSibling());
+ // When we push a first-continuation float in a non-initial reflow,
+ // it's possible that we end up with two continuations with the same
+ // parent. This happens if, on the previous reflow of the block or
+ // a previous reflow of the line containing the block, the float was
+ // split between continuations A and B of the parent, but on the
+ // current reflow, none of the float can fit in A.
+ //
+ // When this happens, we might even have the two continuations
+ // out-of-order due to the management of the pushed floats. In
+ // particular, if the float's placeholder was in a pushed line that
+ // we reflowed before it was pushed, and we split the float during
+ // that reflow, we might have the continuation of the float before
+ // the float itself. (In the general case, however, it's correct
+ // for floats in the pushed floats list to come before floats
+ // anchored in pushed lines; however, in this case it's wrong. We
+ // should probably find a way to fix it somehow, since it leads to
+ // incorrect layout in some cases.)
+ //
+ // When we have these out-of-order continuations, we might hit the
+ // next-continuation before the previous-continuation. When that
+ // happens, just push it. When we reflow the next continuation,
+ // we'll either pull all of its content back and destroy it (by
+ // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
+ // pull it out of its current position and push it again (and
+ // potentially repeat this cycle for the next continuation, although
+ // hopefully then they'll be in the right order).
+ //
+ // We should also need this code for the in-order case if the first
+ // continuation of a float gets moved across more than one
+ // continuation of the containing block. In this case we'd manage
+ // to push the second continuation without this check, but not the
+ // third and later.
+ nsIFrame *prevContinuation = f->GetPrevContinuation();
+ if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
+ mFloats.RemoveFrame(f);
+ aState.AppendPushedFloatChain(f);
+ f = !prev ? mFloats.FirstChild() : prev->GetNextSibling();
+ continue;
+ }
+
+ // Always call FlowAndPlaceFloat; we might need to place this float
+ // if didn't belong to this block the last time it was reflowed.
+ aState.FlowAndPlaceFloat(f);
+ ConsiderChildOverflow(aOverflowAreas, f);
+
+ nsIFrame* next = !prev ? mFloats.FirstChild() : prev->GetNextSibling();
+ if (next == f) {
+ // We didn't push |f| so its next-sibling is next.
+ next = f->GetNextSibling();
+ prev = f;
+ } // else: we did push |f| so |prev|'s new next-sibling is next.
+ f = next;
+ }
+
+ // If there are continued floats, then we may need to continue BR clearance
+ if (0 != aState.ClearFloats(0, StyleClear::Both)) {
+ nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
+ if (prevBlock) {
+ aState.mFloatBreakType = prevBlock->FindTrailingClear();
+ }
+ }
+}
+
+void
+nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager, WritingMode aWM,
+ const nsSize& aContainerSize)
+{
+ // Recover our own floats
+ nsIFrame* stop = nullptr; // Stop before we reach pushed floats that
+ // belong to our next-in-flow
+ for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) {
+ LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize);
+ aFloatManager.AddFloat(f, region, aWM, aContainerSize);
+ if (!stop && f->GetNextInFlow())
+ stop = f->GetNextInFlow();
+ }
+
+ // Recurse into our overflow container children
+ for (nsIFrame* oc = GetChildList(kOverflowContainersList).FirstChild();
+ oc; oc = oc->GetNextSibling()) {
+ RecoverFloatsFor(oc, aFloatManager, aWM, aContainerSize);
+ }
+
+ // Recurse into our normal children
+ for (nsBlockFrame::LineIterator line = LinesBegin(); line != LinesEnd(); ++line) {
+ if (line->IsBlock()) {
+ RecoverFloatsFor(line->mFirstChild, aFloatManager, aWM, aContainerSize);
+ }
+ }
+}
+
+void
+nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
+ nsFloatManager& aFloatManager,
+ WritingMode aWM,
+ const nsSize& aContainerSize)
+{
+ NS_PRECONDITION(aFrame, "null frame");
+ // Only blocks have floats
+ nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
+ // Don't recover any state inside a block that has its own space manager
+ // (we don't currently have any blocks like this, though, thanks to our
+ // use of extra frames for 'overflow')
+ if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
+ // If the element is relatively positioned, then adjust x and y
+ // accordingly so that we consider relatively positioned frames
+ // at their original position.
+
+ LogicalRect rect(aWM, block->GetNormalRect(), aContainerSize);
+ nscoord lineLeft = rect.LineLeft(aWM, aContainerSize);
+ nscoord blockStart = rect.BStart(aWM);
+ aFloatManager.Translate(lineLeft, blockStart);
+ block->RecoverFloats(aFloatManager, aWM, aContainerSize);
+ aFloatManager.Translate(-lineLeft, -blockStart);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+// Painting, event handling
+
+#ifdef DEBUG
+static void ComputeVisualOverflowArea(nsLineList& aLines,
+ nscoord aWidth, nscoord aHeight,
+ nsRect& aResult)
+{
+ nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
+ for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
+ line != line_end;
+ ++line) {
+ // Compute min and max x/y values for the reflowed frame's
+ // combined areas
+ nsRect visOverflow(line->GetVisualOverflowArea());
+ nscoord x = visOverflow.x;
+ nscoord y = visOverflow.y;
+ nscoord xmost = x + visOverflow.width;
+ nscoord ymost = y + visOverflow.height;
+ if (x < xa) {
+ xa = x;
+ }
+ if (xmost > xb) {
+ xb = xmost;
+ }
+ if (y < ya) {
+ ya = y;
+ }
+ if (ymost > yb) {
+ yb = ymost;
+ }
+ }
+
+ aResult.x = xa;
+ aResult.y = ya;
+ aResult.width = xb - xa;
+ aResult.height = yb - ya;
+}
+#endif
+
+bool
+nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
+{
+ if (mContent->IsAnyOfHTMLElements(nsGkAtoms::html, nsGkAtoms::body))
+ return true;
+
+ nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
+ bool visible;
+ nsresult rv = aSelection->ContainsNode(node, true, &visible);
+ return NS_SUCCEEDED(rv) && visible;
+}
+
+#ifdef DEBUG
+static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) {
+ if (nsBlockFrame::gNoisyDamageRepair) {
+ nsFrame::IndentBy(stdout, aDepth+1);
+ nsRect lineArea = aLine->GetVisualOverflowArea();
+ printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
+ aDrawn ? "draw" : "skip",
+ static_cast<void*>(aLine),
+ aLine->IStart(), aLine->BStart(),
+ aLine->ISize(), aLine->BSize(),
+ lineArea.x, lineArea.y,
+ lineArea.width, lineArea.height);
+ }
+}
+#endif
+
+static void
+DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
+ const nsRect& aDirtyRect, nsBlockFrame::LineIterator& aLine,
+ int32_t aDepth, int32_t& aDrawnLines, const nsDisplayListSet& aLists,
+ nsBlockFrame* aFrame, TextOverflow* aTextOverflow) {
+ // If the line's combined area (which includes child frames that
+ // stick outside of the line's bounding box or our bounding box)
+ // intersects the dirty rect then paint the line.
+ bool intersect = aLineArea.Intersects(aDirtyRect);
+#ifdef DEBUG
+ if (nsBlockFrame::gLamePaintMetrics) {
+ aDrawnLines++;
+ }
+ DebugOutputDrawLine(aDepth, aLine.get(), intersect);
+#endif
+ // The line might contain a placeholder for a visible out-of-flow, in which
+ // case we need to descend into it. If there is such a placeholder, we will
+ // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
+ // In particular, we really want to check ShouldDescendIntoFrame()
+ // on all the frames on the line, but that might be expensive. So
+ // we approximate it by checking it on aFrame; if it's true for any
+ // frame in the line, it's also true for aFrame.
+ bool lineInline = aLine->IsInline();
+ bool lineMayHaveTextOverflow = aTextOverflow && lineInline;
+ if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
+ !lineMayHaveTextOverflow)
+ return;
+
+ // Collect our line's display items in a temporary nsDisplayListCollection,
+ // so that we can apply any "text-overflow" clipping to the entire collection
+ // without affecting previous lines.
+ nsDisplayListCollection collection;
+
+ // Block-level child backgrounds go on the blockBorderBackgrounds list ...
+ // Inline-level child backgrounds go on the regular child content list.
+ nsDisplayListSet childLists(collection,
+ lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
+
+ uint32_t flags = lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0;
+
+ nsIFrame* kid = aLine->mFirstChild;
+ int32_t n = aLine->GetChildCount();
+ while (--n >= 0) {
+ aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect,
+ childLists, flags);
+ kid = kid->GetNextSibling();
+ }
+
+ if (lineMayHaveTextOverflow) {
+ aTextOverflow->ProcessLine(collection, aLine.get());
+ }
+
+ collection.MoveTo(aLists);
+}
+
+void
+nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ int32_t drawnLines; // Will only be used if set (gLamePaintMetrics).
+ int32_t depth = 0;
+#ifdef DEBUG
+ if (gNoisyDamageRepair) {
+ depth = GetDepth();
+ nsRect ca;
+ ::ComputeVisualOverflowArea(mLines, mRect.width, mRect.height, ca);
+ nsFrame::IndentBy(stdout, depth);
+ ListTag(stdout);
+ printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
+ mRect.x, mRect.y, mRect.width, mRect.height,
+ aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
+ ca.x, ca.y, ca.width, ca.height);
+ }
+ PRTime start = 0; // Initialize these variables to silence the compiler.
+ if (gLamePaintMetrics) {
+ start = PR_Now();
+ drawnLines = 0;
+ }
+#endif
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ if (GetPrevInFlow()) {
+ DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
+ for (nsIFrame* f : mFloats) {
+ if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
+ BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
+ }
+ }
+
+ aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
+
+ // Prepare for text-overflow processing.
+ UniquePtr<TextOverflow> textOverflow(
+ TextOverflow::WillProcessLines(aBuilder, this));
+
+ // We'll collect our lines' display items here, & then append this to aLists.
+ nsDisplayListCollection linesDisplayListCollection;
+
+ // Don't use the line cursor if we might have a descendant placeholder ...
+ // it might skip lines that contain placeholders but don't themselves
+ // intersect with the dirty area.
+ // In particular, we really want to check ShouldDescendIntoFrame()
+ // on all our child frames, but that might be expensive. So we
+ // approximate it by checking it on |this|; if it's true for any
+ // frame in our child list, it's also true for |this|.
+ nsLineBox* cursor = aBuilder->ShouldDescendIntoFrame(this) ?
+ nullptr : GetFirstLineContaining(aDirtyRect.y);
+ LineIterator line_end = LinesEnd();
+
+ if (cursor) {
+ for (LineIterator line = mLines.begin(cursor);
+ line != line_end;
+ ++line) {
+ nsRect lineArea = line->GetVisualOverflowArea();
+ if (!lineArea.IsEmpty()) {
+ // Because we have a cursor, the combinedArea.ys are non-decreasing.
+ // Once we've passed aDirtyRect.YMost(), we can never see it again.
+ if (lineArea.y >= aDirtyRect.YMost()) {
+ break;
+ }
+ DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
+ linesDisplayListCollection, this, textOverflow.get());
+ }
+ }
+ } else {
+ bool nonDecreasingYs = true;
+ int32_t lineCount = 0;
+ nscoord lastY = INT32_MIN;
+ nscoord lastYMost = INT32_MIN;
+ for (LineIterator line = LinesBegin();
+ line != line_end;
+ ++line) {
+ nsRect lineArea = line->GetVisualOverflowArea();
+ DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
+ linesDisplayListCollection, this, textOverflow.get());
+ if (!lineArea.IsEmpty()) {
+ if (lineArea.y < lastY
+ || lineArea.YMost() < lastYMost) {
+ nonDecreasingYs = false;
+ }
+ lastY = lineArea.y;
+ lastYMost = lineArea.YMost();
+ }
+ lineCount++;
+ }
+
+ if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
+ SetupLineCursor();
+ }
+ }
+
+ // Pick up the resulting text-overflow markers. We append them to
+ // PositionedDescendants just before we append the lines' display items,
+ // so that our text-overflow markers will appear on top of this block's
+ // normal content but below any of its its' positioned children.
+ if (textOverflow) {
+ aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers());
+ }
+ linesDisplayListCollection.MoveTo(aLists);
+
+ if (HasOutsideBullet()) {
+ // Display outside bullets manually
+ nsIFrame* bullet = GetOutsideBullet();
+ BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists);
+ }
+
+#ifdef DEBUG
+ if (gLamePaintMetrics) {
+ PRTime end = PR_Now();
+
+ int32_t numLines = mLines.size();
+ if (!numLines) numLines = 1;
+ PRTime lines, deltaPerLine, delta;
+ lines = int64_t(numLines);
+ delta = end - start;
+ deltaPerLine = delta / lines;
+
+ ListTag(stdout);
+ char buf[400];
+ SprintfLiteral(buf,
+ ": %" PRId64 " elapsed (%" PRId64 " per line) lines=%d drawn=%d skip=%d",
+ delta, deltaPerLine,
+ numLines, drawnLines, numLines - drawnLines);
+ printf("%s\n", buf);
+ }
+#endif
+}
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsBlockFrame::AccessibleType()
+{
+ if (IsTableCaption()) {
+ return GetRect().IsEmpty() ? a11y::eNoType : a11y::eHTMLCaptionType;
+ }
+
+ // block frame may be for <hr>
+ if (mContent->IsHTMLElement(nsGkAtoms::hr)) {
+ return a11y::eHTMLHRType;
+ }
+
+ if (!HasBullet() || !PresContext()) {
+ //XXXsmaug What if we're in the shadow dom?
+ if (!mContent->GetParent()) {
+ // Don't create accessible objects for the root content node, they are redundant with
+ // the nsDocAccessible object created with the document node
+ return a11y::eNoType;
+ }
+
+ nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
+ do_QueryInterface(mContent->GetComposedDoc());
+ if (htmlDoc) {
+ nsCOMPtr<nsIDOMHTMLElement> body;
+ htmlDoc->GetBody(getter_AddRefs(body));
+ if (SameCOMIdentity(body, mContent)) {
+ // Don't create accessible objects for the body, they are redundant with
+ // the nsDocAccessible object created with the document node
+ return a11y::eNoType;
+ }
+ }
+
+ // Not a bullet, treat as normal HTML container
+ return a11y::eHyperTextType;
+ }
+
+ // Create special list bullet accessible
+ return a11y::eHTMLLiType;
+}
+#endif
+
+void nsBlockFrame::ClearLineCursor()
+{
+ if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
+ return;
+ }
+
+ Properties().Delete(LineCursorProperty());
+ RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
+}
+
+void nsBlockFrame::SetupLineCursor()
+{
+ if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
+ || mLines.empty()) {
+ return;
+ }
+
+ Properties().Set(LineCursorProperty(), mLines.front());
+ AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
+}
+
+nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
+{
+ if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
+ return nullptr;
+ }
+
+ FrameProperties props = Properties();
+
+ nsLineBox* property = props.Get(LineCursorProperty());
+ LineIterator cursor = mLines.begin(property);
+ nsRect cursorArea = cursor->GetVisualOverflowArea();
+
+ while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
+ && cursor != mLines.front()) {
+ cursor = cursor.prev();
+ cursorArea = cursor->GetVisualOverflowArea();
+ }
+ while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
+ && cursor != mLines.back()) {
+ cursor = cursor.next();
+ cursorArea = cursor->GetVisualOverflowArea();
+ }
+
+ if (cursor.get() != property) {
+ props.Set(LineCursorProperty(), cursor.get());
+ }
+
+ return cursor.get();
+}
+
+/* virtual */ void
+nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
+{
+ // See if the child is absolutely positioned
+ if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
+ aChild->IsAbsolutelyPositioned()) {
+ // do nothing
+ } else if (aChild == GetOutsideBullet()) {
+ // The bullet lives in the first line, unless the first line has
+ // height 0 and there is a second line, in which case it lives
+ // in the second line.
+ LineIterator bulletLine = LinesBegin();
+ if (bulletLine != LinesEnd() && bulletLine->BSize() == 0 &&
+ bulletLine != mLines.back()) {
+ bulletLine = bulletLine.next();
+ }
+
+ if (bulletLine != LinesEnd()) {
+ MarkLineDirty(bulletLine, &mLines);
+ }
+ // otherwise we have an empty line list, and ReflowDirtyLines
+ // will handle reflowing the bullet.
+ } else {
+ // Note that we should go through our children to mark lines dirty
+ // before the next reflow. Doing it now could make things O(N^2)
+ // since finding the right line is O(N).
+ // We don't need to worry about marking lines on the overflow list
+ // as dirty; we're guaranteed to reflow them if we take them off the
+ // overflow list.
+ // However, we might have gotten a float, in which case we need to
+ // reflow the line containing its placeholder. So find the
+ // ancestor-or-self of the placeholder that's a child of the block,
+ // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
+ // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
+ // We need to take some care to handle the case where a float is in
+ // a different continuation than its placeholder, including marking
+ // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
+ if (!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
+ } else {
+ NS_ASSERTION(aChild->IsFloating(), "should be a float");
+ nsIFrame *thisFC = FirstContinuation();
+ nsIFrame *placeholderPath =
+ PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild);
+ // SVG code sometimes sends FrameNeedsReflow notifications during
+ // frame destruction, leading to null placeholders, but we're safe
+ // ignoring those.
+ if (placeholderPath) {
+ for (;;) {
+ nsIFrame *parent = placeholderPath->GetParent();
+ if (parent->GetContent() == mContent &&
+ parent->FirstContinuation() == thisFC) {
+ parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
+ break;
+ }
+ placeholderPath = parent;
+ }
+ placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ }
+ }
+
+ nsContainerFrame::ChildIsDirty(aChild);
+}
+
+void
+nsBlockFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ if (aPrevInFlow) {
+ // Copy over the inherited block frame bits from the prev-in-flow.
+ RemoveStateBits(NS_BLOCK_FLAGS_MASK);
+ AddStateBits(aPrevInFlow->GetStateBits() &
+ (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
+ }
+
+ nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ if (!aPrevInFlow ||
+ aPrevInFlow->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION) {
+ AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
+ }
+
+ // If a box has a different block flow direction than its containing block:
+ // ...
+ // If the box is a block container, then it establishes a new block
+ // formatting context.
+ // (http://dev.w3.org/csswg/css-writing-modes/#block-flow)
+ // If the box has contain: paint (or contain: strict), then it should also
+ // establish a formatting context.
+ if ((GetParent() && StyleVisibility()->mWritingMode !=
+ GetParent()->StyleVisibility()->mWritingMode) ||
+ StyleDisplay()->IsContainPaint()) {
+ AddStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
+ }
+
+ if ((GetStateBits() &
+ (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) ==
+ (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) {
+ AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
+ }
+}
+
+void
+nsBlockFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ if (kFloatList == aListID) {
+ mFloats.SetFrames(aChildList);
+ } else if (kPrincipalList == aListID) {
+ NS_ASSERTION((GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET |
+ NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0,
+ "how can we have a bullet already?");
+
+#ifdef DEBUG
+ // The only times a block that is an anonymous box is allowed to have a
+ // first-letter frame are when it's the block inside a non-anonymous cell,
+ // the block inside a fieldset, button or column set, or a scrolled content
+ // block, except for <select>. Note that this means that blocks which are
+ // the anonymous block in {ib} splits do NOT get first-letter frames.
+ // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
+ // of the block.
+ nsIAtom *pseudo = StyleContext()->GetPseudo();
+ bool haveFirstLetterStyle =
+ (!pseudo ||
+ (pseudo == nsCSSAnonBoxes::cellContent &&
+ GetParent()->StyleContext()->GetPseudo() == nullptr) ||
+ pseudo == nsCSSAnonBoxes::fieldsetContent ||
+ pseudo == nsCSSAnonBoxes::buttonContent ||
+ pseudo == nsCSSAnonBoxes::columnContent ||
+ (pseudo == nsCSSAnonBoxes::scrolledContent &&
+ GetParent()->GetType() != nsGkAtoms::listControlFrame) ||
+ pseudo == nsCSSAnonBoxes::mozSVGText) &&
+ GetType() != nsGkAtoms::comboboxControlFrame &&
+ !IsFrameOfType(eMathML) &&
+ RefPtr<nsStyleContext>(GetFirstLetterStyle(PresContext())) != nullptr;
+ NS_ASSERTION(haveFirstLetterStyle ==
+ ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
+ "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
+#endif
+
+ AddFrames(aChildList, nullptr);
+
+ // Create a list bullet if this is a list-item. Note that this is
+ // done here so that RenumberLists will work (it needs the bullets
+ // to store the bullet numbers). Also note that due to various
+ // wrapper frames (scrollframes, columns) we want to use the
+ // outermost (primary, ideally, but it's not set yet when we get
+ // here) frame of our content for the display check. On the other
+ // hand, we look at ourselves for the GetPrevInFlow() check, since
+ // for a columnset we don't want a bullet per column. Note that
+ // the outermost frame for the content is the primary frame in
+ // most cases; the ones when it's not (like tables) can't be
+ // StyleDisplay::ListItem).
+ nsIFrame* possibleListItem = this;
+ while (1) {
+ nsIFrame* parent = possibleListItem->GetParent();
+ if (parent->GetContent() != GetContent()) {
+ break;
+ }
+ possibleListItem = parent;
+ }
+ if (mozilla::StyleDisplay::ListItem ==
+ possibleListItem->StyleDisplay()->mDisplay &&
+ !GetPrevInFlow()) {
+ // Resolve style for the bullet frame
+ const nsStyleList* styleList = StyleList();
+ CounterStyle* style = styleList->GetCounterStyle();
+
+ CreateBulletFrameForListItem(
+ style->IsBullet(),
+ styleList->mListStylePosition == NS_STYLE_LIST_STYLE_POSITION_INSIDE);
+ }
+ } else {
+ nsContainerFrame::SetInitialChildList(aListID, aChildList);
+ }
+}
+
+void
+nsBlockFrame::CreateBulletFrameForListItem(bool aCreateBulletList,
+ bool aListStylePositionInside)
+{
+ nsIPresShell* shell = PresContext()->PresShell();
+
+ CSSPseudoElementType pseudoType = aCreateBulletList ?
+ CSSPseudoElementType::mozListBullet :
+ CSSPseudoElementType::mozListNumber;
+
+ nsStyleContext* parentStyle =
+ CorrectStyleParentFrame(this,
+ nsCSSPseudoElements::GetPseudoAtom(pseudoType))->
+ StyleContext();
+
+ RefPtr<nsStyleContext> kidSC = shell->StyleSet()->
+ ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
+ parentStyle, nullptr);
+
+ // Create bullet frame
+ nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
+ bullet->Init(mContent, this, nullptr);
+
+ // If the list bullet frame should be positioned inside then add
+ // it to the flow now.
+ if (aListStylePositionInside) {
+ nsFrameList bulletList(bullet, bullet);
+ AddFrames(bulletList, nullptr);
+ Properties().Set(InsideBulletProperty(), bullet);
+ AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
+ } else {
+ nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
+ Properties().Set(OutsideBulletProperty(), bulletList);
+ AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
+ }
+}
+
+bool
+nsBlockFrame::BulletIsEmpty() const
+{
+ NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
+ mozilla::StyleDisplay::ListItem && HasOutsideBullet(),
+ "should only care when we have an outside bullet");
+ const nsStyleList* list = StyleList();
+ return list->GetCounterStyle()->IsNone() &&
+ !list->GetListStyleImage();
+}
+
+void
+nsBlockFrame::GetSpokenBulletText(nsAString& aText) const
+{
+ const nsStyleList* myList = StyleList();
+ if (myList->GetListStyleImage()) {
+ aText.Assign(kDiscCharacter);
+ aText.Append(' ');
+ } else {
+ nsBulletFrame* bullet = GetBullet();
+ if (bullet) {
+ bullet->GetSpokenText(aText);
+ } else {
+ aText.Truncate();
+ }
+ }
+}
+
+bool
+nsBlockFrame::RenumberChildFrames(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting)
+{
+ // Examine each line in the block
+ bool foundValidLine;
+ nsBlockInFlowLineIterator bifLineIter(this, &foundValidLine);
+ if (!foundValidLine) {
+ return false;
+ }
+
+ bool renumberedABullet = false;
+ do {
+ nsLineList::iterator line = bifLineIter.GetLine();
+ nsIFrame* kid = line->mFirstChild;
+ int32_t n = line->GetChildCount();
+ while (--n >= 0) {
+ bool kidRenumberedABullet =
+ kid->RenumberFrameAndDescendants(aOrdinal, aDepth, aIncrement, aForCounting);
+ if (!aForCounting && kidRenumberedABullet) {
+ line->MarkDirty();
+ renumberedABullet = true;
+ }
+ kid = kid->GetNextSibling();
+ }
+ } while (bifLineIter.Next());
+
+ // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
+ // the bullet and the caller of RenumberLists. But the caller itself
+ // has to be responsible for setting the bit itself, since that caller
+ // might be making a FrameNeedsReflow call, which requires that the
+ // bit not be set yet.
+ if (renumberedABullet && aDepth != 0) {
+ AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+
+ return renumberedABullet;
+}
+
+void
+nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
+ BlockReflowInput& aState,
+ ReflowOutput& aMetrics,
+ nscoord aLineTop)
+{
+ const ReflowInput &rs = aState.mReflowInput;
+
+ // Reflow the bullet now
+ WritingMode bulletWM = aBulletFrame->GetWritingMode();
+ LogicalSize availSize(bulletWM);
+ // Make up an inline-size since it doesn't really matter (XXX).
+ availSize.ISize(bulletWM) = aState.ContentISize();
+ availSize.BSize(bulletWM) = NS_UNCONSTRAINEDSIZE;
+
+ // Get the reason right.
+ // XXXwaterson Should this look just like the logic in
+ // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
+ ReflowInput reflowInput(aState.mPresContext, rs,
+ aBulletFrame, availSize);
+ nsReflowStatus status;
+ aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowInput, status);
+
+ // Get the float available space using our saved state from before we
+ // started reflowing the block, so that we ignore any floats inside
+ // the block.
+ // FIXME: aLineTop isn't actually set correctly by some callers, since
+ // they reposition the line.
+ LogicalRect floatAvailSpace =
+ aState.GetFloatAvailableSpaceWithState(aLineTop,
+ &aState.mFloatManagerStateBefore)
+ .mRect;
+ // FIXME (bug 25888): need to check the entire region that the first
+ // line overlaps, not just the top pixel.
+
+ // Place the bullet now. We want to place the bullet relative to the
+ // border-box of the associated block (using the right/left margin of
+ // the bullet frame as separation). However, if a line box would be
+ // displaced by floats that are *outside* the associated block, we
+ // want to displace it by the same amount. That is, we act as though
+ // the edge of the floats is the content-edge of the block, and place
+ // the bullet at a position offset from there by the block's padding,
+ // the block's border, and the bullet frame's margin.
+
+ // IStart from floatAvailSpace gives us the content/float start edge
+ // in the current writing mode. Then we subtract out the start
+ // border/padding and the bullet's width and margin to offset the position.
+ WritingMode wm = rs.GetWritingMode();
+ // Get the bullet's margin, converted to our writing mode so that we can
+ // combine it with other logical values here.
+ LogicalMargin bulletMargin =
+ reflowInput.ComputedLogicalMargin().ConvertTo(wm, bulletWM);
+ nscoord iStart = floatAvailSpace.IStart(wm) -
+ rs.ComputedLogicalBorderPadding().IStart(wm) -
+ bulletMargin.IEnd(wm) -
+ aMetrics.ISize(wm);
+
+ // Approximate the bullets position; vertical alignment will provide
+ // the final vertical location. We pass our writing-mode here, because
+ // it may be different from the bullet frame's mode.
+ nscoord bStart = floatAvailSpace.BStart(wm);
+ aBulletFrame->SetRect(wm, LogicalRect(wm, iStart, bStart,
+ aMetrics.ISize(wm),
+ aMetrics.BSize(wm)),
+ aState.ContainerSize());
+ aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowInput,
+ nsDidReflowStatus::FINISHED);
+}
+
+// This is used to scan frames for any float placeholders, add their
+// floats to the list represented by aList, and remove the
+// floats from whatever list they might be in. We don't search descendants
+// that are float containing blocks. Floats that or not children of 'this'
+// are ignored (they are not added to aList).
+void
+nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
+ bool aCollectSiblings)
+{
+ while (aFrame) {
+ // Don't descend into float containing blocks.
+ if (!aFrame->IsFloatContainingBlock()) {
+ nsIFrame *outOfFlowFrame =
+ aFrame->GetType() == nsGkAtoms::placeholderFrame ?
+ nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nullptr;
+ while (outOfFlowFrame && outOfFlowFrame->GetParent() == this) {
+ RemoveFloat(outOfFlowFrame);
+ // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
+ // the PushedFloats list.
+ outOfFlowFrame->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
+ aList.AppendFrame(nullptr, outOfFlowFrame);
+ outOfFlowFrame = outOfFlowFrame->GetNextInFlow();
+ // FIXME: By not pulling floats whose parent is one of our
+ // later siblings, are we risking the pushed floats getting
+ // out-of-order?
+ // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
+ }
+
+ DoCollectFloats(aFrame->PrincipalChildList().FirstChild(), aList, true);
+ DoCollectFloats(aFrame->GetChildList(kOverflowList).FirstChild(), aList, true);
+ }
+ if (!aCollectSiblings)
+ break;
+ aFrame = aFrame->GetNextSibling();
+ }
+}
+
+void
+nsBlockFrame::CheckFloats(BlockReflowInput& aState)
+{
+#ifdef DEBUG
+ // If any line is still dirty, that must mean we're going to reflow this
+ // block again soon (e.g. because we bailed out after noticing that
+ // clearance was imposed), so don't worry if the floats are out of sync.
+ bool anyLineDirty = false;
+
+ // Check that the float list is what we would have built
+ AutoTArray<nsIFrame*, 8> lineFloats;
+ for (LineIterator line = LinesBegin(), line_end = LinesEnd();
+ line != line_end; ++line) {
+ if (line->HasFloats()) {
+ nsFloatCache* fc = line->GetFirstFloat();
+ while (fc) {
+ lineFloats.AppendElement(fc->mFloat);
+ fc = fc->Next();
+ }
+ }
+ if (line->IsDirty()) {
+ anyLineDirty = true;
+ }
+ }
+
+ AutoTArray<nsIFrame*, 8> storedFloats;
+ bool equal = true;
+ uint32_t i = 0;
+ for (nsIFrame* f : mFloats) {
+ if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
+ continue;
+ storedFloats.AppendElement(f);
+ if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
+ equal = false;
+ }
+ ++i;
+ }
+
+ if ((!equal || lineFloats.Length() != storedFloats.Length()) && !anyLineDirty) {
+ NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
+#if defined(DEBUG_roc)
+ nsFrame::RootFrameList(PresContext(), stdout, 0);
+ for (i = 0; i < lineFloats.Length(); ++i) {
+ printf("Line float: %p\n", lineFloats.ElementAt(i));
+ }
+ for (i = 0; i < storedFloats.Length(); ++i) {
+ printf("Stored float: %p\n", storedFloats.ElementAt(i));
+ }
+#endif
+ }
+#endif
+
+ const nsFrameList* oofs = GetOverflowOutOfFlows();
+ if (oofs && oofs->NotEmpty()) {
+ // Floats that were pushed should be removed from our float
+ // manager. Otherwise the float manager's YMost or XMost might
+ // be larger than necessary, causing this block to get an
+ // incorrect desired height (or width). Some of these floats
+ // may not actually have been added to the float manager because
+ // they weren't reflowed before being pushed; that's OK,
+ // RemoveRegions will ignore them. It is safe to do this here
+ // because we know from here on the float manager will only be
+ // used for its XMost and YMost, not to place new floats and
+ // lines.
+ aState.mFloatManager->RemoveTrailingRegions(oofs->FirstChild());
+ }
+}
+
+void
+nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot, bool* aBEndMarginRoot)
+{
+ if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT)) {
+ nsIFrame* parent = GetParent();
+ if (!parent || parent->IsFloatContainingBlock()) {
+ *aBStartMarginRoot = false;
+ *aBEndMarginRoot = false;
+ return;
+ }
+ if (parent->GetType() == nsGkAtoms::columnSetFrame) {
+ *aBStartMarginRoot = GetPrevInFlow() == nullptr;
+ *aBEndMarginRoot = GetNextInFlow() == nullptr;
+ return;
+ }
+ }
+
+ *aBStartMarginRoot = true;
+ *aBEndMarginRoot = true;
+}
+
+/* static */
+bool
+nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock)
+{
+ NS_PRECONDITION(aBlock, "Must have a frame");
+ NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block");
+
+ nsIFrame* parent = aBlock->GetParent();
+ return (aBlock->GetStateBits() & NS_BLOCK_FLOAT_MGR) ||
+ (parent && !parent->IsFloatContainingBlock());
+}
+
+/* static */
+bool
+nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame)
+{
+ return aFrame->IsFrameOfType(nsIFrame::eBlockFrame) &&
+ !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
+ !(aFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR);
+}
+
+// Note that this width can vary based on the vertical position.
+// However, the cases where it varies are the cases where the width fits
+// in the available space given, which means that variation shouldn't
+// matter.
+/* static */
+nsBlockFrame::ReplacedElementISizeToClear
+nsBlockFrame::ISizeToClearPastFloats(const BlockReflowInput& aState,
+ const LogicalRect& aFloatAvailableSpace,
+ nsIFrame* aFrame)
+{
+ nscoord inlineStartOffset, inlineEndOffset;
+ WritingMode wm = aState.mReflowInput.GetWritingMode();
+ SizeComputationInput offsetState(aFrame, aState.mReflowInput.mRenderingContext,
+ wm, aState.mContentArea.ISize(wm));
+
+ ReplacedElementISizeToClear result;
+ aState.ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace,
+ inlineStartOffset,
+ inlineEndOffset);
+ nscoord availISize = aState.mContentArea.ISize(wm) -
+ inlineStartOffset - inlineEndOffset;
+
+ // We actually don't want the min width here; see bug 427782; we only
+ // want to displace if the width won't compute to a value small enough
+ // to fit.
+ // All we really need here is the result of ComputeSize, and we
+ // could *almost* get that from an SizeComputationInput, except for the
+ // last argument.
+ WritingMode frWM = aFrame->GetWritingMode();
+ LogicalSize availSpace = LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE).
+ ConvertTo(frWM, wm);
+ ReflowInput reflowInput(aState.mPresContext, aState.mReflowInput,
+ aFrame, availSpace);
+ result.borderBoxISize =
+ reflowInput.ComputedSizeWithBorderPadding().ConvertTo(wm, frWM).ISize(wm);
+ // Use the margins from offsetState rather than reflowInput so that
+ // they aren't reduced by ignoring margins in overconstrained cases.
+ LogicalMargin computedMargin =
+ offsetState.ComputedLogicalMargin().ConvertTo(wm, frWM);
+ result.marginIStart = computedMargin.IStart(wm);
+ return result;
+}
+
+/* static */
+nsBlockFrame*
+nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate)
+{
+ nsBlockFrame* block = nullptr;
+ while(aCandidate) {
+ block = nsLayoutUtils::GetAsBlock(aCandidate);
+ if (block) {
+ // yay, candidate is a block!
+ return block;
+ }
+ // Not a block. Check its parent next.
+ aCandidate = aCandidate->GetParent();
+ }
+ NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
+ return nullptr;
+}
+
+void
+nsBlockFrame::ComputeFinalBSize(const ReflowInput& aReflowInput,
+ nsReflowStatus* aStatus,
+ nscoord aContentBSize,
+ const LogicalMargin& aBorderPadding,
+ LogicalSize& aFinalSize,
+ nscoord aConsumed)
+{
+ WritingMode wm = aReflowInput.GetWritingMode();
+ // Figure out how much of the computed height should be
+ // applied to this frame.
+ nscoord computedBSizeLeftOver = GetEffectiveComputedBSize(aReflowInput,
+ aConsumed);
+ NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
+ && computedBSizeLeftOver ),
+ "overflow container must not have computedBSizeLeftOver");
+
+ aFinalSize.BSize(wm) =
+ NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding.BStart(wm),
+ computedBSizeLeftOver),
+ aBorderPadding.BEnd(wm));
+
+ if (NS_FRAME_IS_NOT_COMPLETE(*aStatus) &&
+ aFinalSize.BSize(wm) < aReflowInput.AvailableBSize()) {
+ // We fit in the available space - change status to OVERFLOW_INCOMPLETE.
+ // XXXmats why didn't Reflow report OVERFLOW_INCOMPLETE in the first place?
+ // XXXmats and why exclude the case when our size == AvailableBSize?
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus);
+ }
+
+ if (NS_FRAME_IS_COMPLETE(*aStatus)) {
+ if (computedBSizeLeftOver > 0 &&
+ NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize() &&
+ aFinalSize.BSize(wm) > aReflowInput.AvailableBSize()) {
+ if (ShouldAvoidBreakInside(aReflowInput)) {
+ *aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return;
+ }
+ // We don't fit and we consumed some of the computed height,
+ // so we should consume all the available height and then
+ // break. If our bottom border/padding straddles the break
+ // point, then this will increase our height and push the
+ // border/padding to the next page/column.
+ aFinalSize.BSize(wm) = std::max(aReflowInput.AvailableBSize(),
+ aContentBSize);
+ NS_FRAME_SET_INCOMPLETE(*aStatus);
+ if (!GetNextInFlow())
+ *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+ }
+}
+
+nsresult
+nsBlockFrame::ResolveBidi()
+{
+ NS_ASSERTION(!GetPrevInFlow(),
+ "ResolveBidi called on non-first continuation");
+
+ nsPresContext* presContext = PresContext();
+ if (!presContext->BidiEnabled()) {
+ return NS_OK;
+ }
+
+ return nsBidiPresUtils::Resolve(this);
+}
+
+#ifdef DEBUG
+void
+nsBlockFrame::VerifyLines(bool aFinalCheckOK)
+{
+ if (!gVerifyLines) {
+ return;
+ }
+ if (mLines.empty()) {
+ return;
+ }
+
+ nsLineBox* cursor = GetLineCursor();
+
+ // Add up the counts on each line. Also validate that IsFirstLine is
+ // set properly.
+ int32_t count = 0;
+ LineIterator line, line_end;
+ for (line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ++line) {
+ if (line == cursor) {
+ cursor = nullptr;
+ }
+ if (aFinalCheckOK) {
+ MOZ_ASSERT(line->GetChildCount(), "empty line");
+ if (line->IsBlock()) {
+ NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
+ }
+ }
+ count += line->GetChildCount();
+ }
+
+ // Then count the frames
+ int32_t frameCount = 0;
+ nsIFrame* frame = mLines.front()->mFirstChild;
+ while (frame) {
+ frameCount++;
+ frame = frame->GetNextSibling();
+ }
+ NS_ASSERTION(count == frameCount, "bad line list");
+
+ // Next: test that each line has right number of frames on it
+ for (line = LinesBegin(), line_end = LinesEnd();
+ line != line_end;
+ ) {
+ count = line->GetChildCount();
+ frame = line->mFirstChild;
+ while (--count >= 0) {
+ frame = frame->GetNextSibling();
+ }
+ ++line;
+ if ((line != line_end) && (0 != line->GetChildCount())) {
+ NS_ASSERTION(frame == line->mFirstChild, "bad line list");
+ }
+ }
+
+ if (cursor) {
+ FrameLines* overflowLines = GetOverflowLines();
+ if (overflowLines) {
+ LineIterator line = overflowLines->mLines.begin();
+ LineIterator line_end = overflowLines->mLines.end();
+ for (; line != line_end; ++line) {
+ if (line == cursor) {
+ cursor = nullptr;
+ break;
+ }
+ }
+ }
+ }
+ NS_ASSERTION(!cursor, "stale LineCursorProperty");
+}
+
+void
+nsBlockFrame::VerifyOverflowSituation()
+{
+ // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
+ nsFrameList* oofs = GetOverflowOutOfFlows() ;
+ if (oofs) {
+ for (nsFrameList::Enumerator e(*oofs); !e.AtEnd(); e.Next()) {
+ nsIFrame* nif = e.get()->GetNextInFlow();
+ MOZ_ASSERT(!nif || (!mFloats.ContainsFrame(nif) && !mFrames.ContainsFrame(nif)));
+ }
+ }
+
+ // Pushed floats must not have a next-in-flow in mFloats or mFrames.
+ oofs = GetPushedFloats();
+ if (oofs) {
+ for (nsFrameList::Enumerator e(*oofs); !e.AtEnd(); e.Next()) {
+ nsIFrame* nif = e.get()->GetNextInFlow();
+ MOZ_ASSERT(!nif || (!mFloats.ContainsFrame(nif) && !mFrames.ContainsFrame(nif)));
+ }
+ }
+
+ // A child float next-in-flow's parent must be |this| or a next-in-flow of |this|.
+ // Later next-in-flows must have the same or later parents.
+ nsIFrame::ChildListID childLists[] = { nsIFrame::kFloatList,
+ nsIFrame::kPushedFloatsList };
+ for (size_t i = 0; i < ArrayLength(childLists); ++i) {
+ nsFrameList children(GetChildList(childLists[i]));
+ for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+ nsIFrame* parent = this;
+ nsIFrame* nif = e.get()->GetNextInFlow();
+ for (; nif; nif = nif->GetNextInFlow()) {
+ bool found = false;
+ for (nsIFrame* p = parent; p; p = p->GetNextInFlow()) {
+ if (nif->GetParent() == p) {
+ parent = p;
+ found = true;
+ break;
+ }
+ }
+ MOZ_ASSERT(found, "next-in-flow is a child of parent earlier in the frame tree?");
+ }
+ }
+ }
+
+ nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow());
+ while (flow) {
+ FrameLines* overflowLines = flow->GetOverflowLines();
+ if (overflowLines) {
+ NS_ASSERTION(!overflowLines->mLines.empty(),
+ "should not be empty if present");
+ NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
+ "bad overflow lines");
+ NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
+ overflowLines->mFrames.FirstChild(),
+ "bad overflow frames / lines");
+ }
+ nsLineBox* cursor = flow->GetLineCursor();
+ if (cursor) {
+ LineIterator line = flow->LinesBegin();
+ LineIterator line_end = flow->LinesEnd();
+ for (; line != line_end && line != cursor; ++line)
+ ;
+ if (line == line_end && overflowLines) {
+ line = overflowLines->mLines.begin();
+ line_end = overflowLines->mLines.end();
+ for (; line != line_end && line != cursor; ++line)
+ ;
+ }
+ MOZ_ASSERT(line != line_end, "stale LineCursorProperty");
+ }
+ flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
+ }
+}
+
+int32_t
+nsBlockFrame::GetDepth() const
+{
+ int32_t depth = 0;
+ nsIFrame* parent = GetParent();
+ while (parent) {
+ parent = parent->GetParent();
+ depth++;
+ }
+ return depth;
+}
+
+already_AddRefed<nsStyleContext>
+nsBlockFrame::GetFirstLetterStyle(nsPresContext* aPresContext)
+{
+ return aPresContext->StyleSet()->
+ ProbePseudoElementStyle(mContent->AsElement(),
+ CSSPseudoElementType::firstLetter,
+ mStyleContext);
+}
+#endif
diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h
new file mode 100644
index 000000000..bb41a6e99
--- /dev/null
+++ b/layout/generic/nsBlockFrame.h
@@ -0,0 +1,1038 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object for CSS display:block, inline-block, and list-item
+ * boxes, also used for various anonymous boxes
+ */
+
+#ifndef nsBlockFrame_h___
+#define nsBlockFrame_h___
+
+#include "nsContainerFrame.h"
+#include "nsHTMLParts.h"
+#include "nsLineBox.h"
+#include "nsCSSPseudoElements.h"
+#include "nsFloatManager.h"
+
+enum class LineReflowStatus {
+ // The line was completely reflowed and fit in available width, and we should
+ // try to pull up content from the next line if possible.
+ OK,
+ // The line was completely reflowed and fit in available width, but we should
+ // not try to pull up content from the next line.
+ Stop,
+ // We need to reflow the line again at its current vertical position. The
+ // new reflow should not try to pull up any frames from the next line.
+ RedoNoPull,
+ // We need to reflow the line again using the floats from its height
+ // this reflow, since its height made it hit floats that were not
+ // adjacent to its top.
+ RedoMoreFloats,
+ // We need to reflow the line again at a lower vertical postion where there
+ // may be more horizontal space due to different float configuration.
+ RedoNextBand,
+ // The line did not fit in the available vertical space. Try pushing it to
+ // the next page or column if it's not the first line on the current page/column.
+ Truncated
+};
+
+class nsBlockInFlowLineIterator;
+class nsBulletFrame;
+namespace mozilla {
+class BlockReflowInput;
+} // namespace mozilla
+
+/**
+ * Some invariants:
+ * -- The overflow out-of-flows list contains the out-of-
+ * flow frames whose placeholders are in the overflow list.
+ * -- A given piece of content has at most one placeholder
+ * frame in a block's normal child list.
+ * -- While a block is being reflowed, and from then until
+ * its next-in-flow is reflowed it may have a
+ * PushedFloatProperty frame property that points to
+ * an nsFrameList. This list contains continuations for
+ * floats whose prev-in-flow is in the block's regular float
+ * list and first-in-flows of floats that did not fit, but
+ * whose placeholders are in the block or one of its
+ * prev-in-flows.
+ * -- In all these frame lists, if there are two frames for
+ * the same content appearing in the list, then the frames
+ * appear with the prev-in-flow before the next-in-flow.
+ * -- While reflowing a block, its overflow line list
+ * will usually be empty but in some cases will have lines
+ * (while we reflow the block at its shrink-wrap width).
+ * In this case any new overflowing content must be
+ * prepended to the overflow lines.
+ */
+
+/*
+ * Base class for block and inline frames.
+ * The block frame has an additional child list, kAbsoluteList, which
+ * contains the absolutely positioned frames.
+ */
+class nsBlockFrame : public nsContainerFrame
+{
+ using BlockReflowInput = mozilla::BlockReflowInput;
+
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsBlockFrame)
+ NS_DECL_FRAMEARENA_HELPERS
+
+ typedef nsLineList::iterator LineIterator;
+ typedef nsLineList::const_iterator ConstLineIterator;
+ typedef nsLineList::reverse_iterator ReverseLineIterator;
+ typedef nsLineList::const_reverse_iterator ConstReverseLineIterator;
+
+ LineIterator LinesBegin() { return mLines.begin(); }
+ LineIterator LinesEnd() { return mLines.end(); }
+ ConstLineIterator LinesBegin() const { return mLines.begin(); }
+ ConstLineIterator LinesEnd() const { return mLines.end(); }
+ ReverseLineIterator LinesRBegin() { return mLines.rbegin(); }
+ ReverseLineIterator LinesREnd() { return mLines.rend(); }
+ ConstReverseLineIterator LinesRBegin() const { return mLines.rbegin(); }
+ ConstReverseLineIterator LinesREnd() const { return mLines.rend(); }
+ LineIterator LinesBeginFrom(nsLineBox* aList) { return mLines.begin(aList); }
+ ReverseLineIterator LinesRBeginFrom(nsLineBox* aList) { return mLines.rbegin(aList); }
+
+ friend nsBlockFrame* NS_NewBlockFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+ // nsQueryFrame
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+ virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
+ virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+ bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+ nscoord* aBaseline) const override
+ {
+ nscoord lastBaseline;
+ if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) {
+ *aBaseline = BSize() - lastBaseline;
+ return true;
+ }
+ return false;
+ }
+ bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ nscoord* aBaseline) const override;
+ virtual nscoord GetCaretBaseline() const override;
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual nsSplittableType GetSplittableType() const override;
+ virtual bool IsFloatContainingBlock() const override;
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+ virtual nsIAtom* GetType() const override;
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eCanContainOverflowContainers |
+ nsIFrame::eBlockFrame));
+ }
+
+ virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
+ virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+#ifdef DEBUG
+ virtual nsFrameState GetDebugStateBits() const override;
+ const char* LineReflowStatusToString(LineReflowStatus aLineReflowStatus) const;
+#endif
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ // Line cursor methods to speed up line searching in which one query
+ // result is expected to be close to the next in general. This is
+ // mainly for searching line(s) containing a point. It is also used
+ // as a cache for local computation. Use AutoLineCursorSetup for the
+ // latter case so that it wouldn't interact unexpectedly with the
+ // former. The basic idea for the former is that we set the cursor
+ // property if the lines' overflowArea.VisualOverflow().ys and
+ // overflowArea.VisualOverflow().yMosts are non-decreasing
+ // (considering only non-empty overflowArea.VisualOverflow()s; empty
+ // overflowArea.VisualOverflow()s never participate in event handling
+ // or painting), and the block has sufficient number of lines. The
+ // cursor property points to a "recently used" line. If we get a
+ // series of requests that work on lines
+ // "near" the cursor, then we can find those nearby lines quickly by
+ // starting our search at the cursor.
+
+ // Clear out line cursor because we're disturbing the lines (i.e., Reflow)
+ void ClearLineCursor();
+ // Get the first line that might contain y-coord 'y', or nullptr if you must search
+ // all lines. If nonnull is returned then we guarantee that the lines'
+ // combinedArea.ys and combinedArea.yMosts are non-decreasing.
+ // The actual line returned might not contain 'y', but if not, it is guaranteed
+ // to be before any line which does contain 'y'.
+ nsLineBox* GetFirstLineContaining(nscoord y);
+ // Set the line cursor to our first line. Only call this if you
+ // guarantee that either the lines' combinedArea.ys and combinedArea.
+ // yMosts are non-decreasing, or the line cursor is cleared before
+ // building the display list of this frame.
+ void SetupLineCursor();
+
+ /**
+ * Helper RAII class for automatically set and clear line cursor for
+ * temporary use. If the frame already has line cursor, this would be
+ * a no-op.
+ */
+ class MOZ_STACK_CLASS AutoLineCursorSetup
+ {
+ public:
+ explicit AutoLineCursorSetup(nsBlockFrame* aFrame)
+ : mFrame(aFrame)
+ , mOrigCursor(aFrame->GetLineCursor())
+ {
+ if (!mOrigCursor) {
+ mFrame->SetupLineCursor();
+ }
+ }
+ ~AutoLineCursorSetup()
+ {
+ if (mOrigCursor) {
+ mFrame->Properties().Set(LineCursorProperty(), mOrigCursor);
+ } else {
+ mFrame->ClearLineCursor();
+ }
+ }
+
+ private:
+ nsBlockFrame* mFrame;
+ nsLineBox* mOrigCursor;
+ };
+
+ virtual void ChildIsDirty(nsIFrame* aChild) override;
+ virtual bool IsVisibleInSelection(nsISelection* aSelection) override;
+
+ virtual bool IsEmpty() override;
+ virtual bool CachedIsEmpty() override;
+ virtual bool IsSelfEmpty() override;
+
+ // Given that we have a bullet, does it actually draw something, i.e.,
+ // do we have either a 'list-style-type' or 'list-style-image' that is
+ // not 'none'?
+ bool BulletIsEmpty() const;
+
+ /**
+ * Return the bullet text equivalent.
+ */
+ void GetSpokenBulletText(nsAString& aText) const;
+
+ /**
+ * Return true if there's a bullet.
+ */
+ bool HasBullet() const {
+ return HasOutsideBullet() || HasInsideBullet();
+ }
+
+ /**
+ * @return true if this frame has an inside bullet frame.
+ */
+ bool HasInsideBullet() const {
+ return 0 != (mState & NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
+ }
+
+ /**
+ * @return true if this frame has an outside bullet frame.
+ */
+ bool HasOutsideBullet() const {
+ return 0 != (mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
+ }
+
+ /**
+ * @return the bullet frame or nullptr if we don't have one.
+ */
+ nsBulletFrame* GetBullet() const {
+ nsBulletFrame* outside = GetOutsideBullet();
+ return outside ? outside : GetInsideBullet();
+ }
+
+ virtual void MarkIntrinsicISizesDirty() override;
+private:
+ void CheckIntrinsicCacheAgainstShrinkWrapState();
+public:
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+
+ virtual nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
+
+ virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+ nscoord* aX,
+ nscoord* aXMost) override;
+
+ /**
+ * Compute the final block size of this frame.
+ *
+ * @param aReflowInput Data structure passed from parent during reflow.
+ * @param aReflowStatus A pointer to the reflow status for when we're finished
+ * doing reflow. this will get set appropriately if the block-size
+ * causes us to exceed the current available (page) block-size.
+ * @param aContentBSize The block-size of content, precomputed outside of this
+ * function. The final block-size that is used in aMetrics will be set
+ * to either this or the available block-size, whichever is larger, in
+ * the case where our available block-size is constrained, and we
+ * overflow that available block-size.
+ * @param aBorderPadding The margins representing the border padding for block
+ * frames. Can be 0.
+ * @param aFinalSize Out parameter for final block-size.
+ * @param aConsumed The block-size already consumed by our previous-in-flows.
+ */
+ void ComputeFinalBSize(const ReflowInput& aReflowInput,
+ nsReflowStatus* aStatus,
+ nscoord aContentBSize,
+ const mozilla::LogicalMargin& aBorderPadding,
+ mozilla::LogicalSize& aFinalSize,
+ nscoord aConsumed);
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+ /**
+ * Move any frames on our overflow list to the end of our principal list.
+ * @return true if there were any overflow frames
+ */
+ virtual bool DrainSelfOverflowList() override;
+
+ virtual nsresult StealFrame(nsIFrame* aChild) override;
+
+ virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
+ bool aDeletingEmptyFrames) override;
+
+ /**
+ * This is a special method that allows a child class of nsBlockFrame to
+ * return a special, customized nsStyleText object to the nsLineLayout
+ * constructor. It is used when the nsBlockFrame child needs to specify its
+ * custom rendering style.
+ */
+ virtual const nsStyleText* StyleTextForLineLayout();
+
+ /**
+ * Determines whether the collapsed margin carried out of the last
+ * line includes the margin-top of a line with clearance (in which
+ * case we must avoid collapsing that margin with our bottom margin)
+ */
+ bool CheckForCollapsedBEndMarginFromClearanceLine();
+
+ static nsresult GetCurrentLine(BlockReflowInput *aState, nsLineBox **aOutCurrentLine);
+
+ /**
+ * Determine if this block is a margin root at the top/bottom edges.
+ */
+ void IsMarginRoot(bool* aBStartMarginRoot, bool* aBEndMarginRoot);
+
+ static bool BlockNeedsFloatManager(nsIFrame* aBlock);
+
+ /**
+ * Returns whether aFrame is a block frame that will wrap its contents
+ * around floats intruding on it from the outside. (aFrame need not
+ * be a block frame, but if it's not, the result will be false.)
+ */
+ static bool BlockCanIntersectFloats(nsIFrame* aFrame);
+
+ /**
+ * Returns the inline size that needs to be cleared past floats for
+ * blocks that cannot intersect floats. aState must already have
+ * GetAvailableSpace called on it for the block-dir position that we
+ * care about (which need not be its current mBCoord)
+ */
+ struct ReplacedElementISizeToClear {
+ // Note that we care about the inline-start margin but can ignore
+ // the inline-end margin.
+ nscoord marginIStart, borderBoxISize;
+ };
+ static ReplacedElementISizeToClear
+ ISizeToClearPastFloats(const BlockReflowInput& aState,
+ const mozilla::LogicalRect& aFloatAvailableSpace,
+ nsIFrame* aFrame);
+
+ /**
+ * Creates a contination for aFloat and adds it to the list of overflow floats.
+ * Also updates aState.mReflowStatus to include the float's incompleteness.
+ * Must only be called while this block frame is in reflow.
+ * aFloatStatus must be the float's true, unmodified reflow status.
+ */
+ nsresult SplitFloat(BlockReflowInput& aState,
+ nsIFrame* aFloat,
+ nsReflowStatus aFloatStatus);
+
+ /**
+ * Walks up the frame tree, starting with aCandidate, and returns the first
+ * block frame that it encounters.
+ */
+ static nsBlockFrame* GetNearestAncestorBlock(nsIFrame* aCandidate);
+
+ struct FrameLines {
+ nsLineList mLines;
+ nsFrameList mFrames;
+ };
+
+protected:
+ explicit nsBlockFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+ , mMinWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
+ , mPrefWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
+ {
+#ifdef DEBUG
+ InitDebugFlags();
+#endif
+ }
+ virtual ~nsBlockFrame();
+
+#ifdef DEBUG
+ already_AddRefed<nsStyleContext> GetFirstLetterStyle(nsPresContext* aPresContext);
+#endif
+
+ NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(LineCursorProperty, nsLineBox)
+ bool HasLineCursor() { return GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR; }
+ nsLineBox* GetLineCursor() {
+ return HasLineCursor() ? Properties().Get(LineCursorProperty()) : nullptr;
+ }
+
+ nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) {
+ return NS_NewLineBox(PresContext()->PresShell(), aFrame, aIsBlock);
+ }
+ nsLineBox* NewLineBox(nsLineBox* aFromLine, nsIFrame* aFrame, int32_t aCount) {
+ return NS_NewLineBox(PresContext()->PresShell(), aFromLine, aFrame, aCount);
+ }
+ void FreeLineBox(nsLineBox* aLine) {
+ if (aLine == GetLineCursor()) {
+ ClearLineCursor();
+ }
+ aLine->Destroy(PresContext()->PresShell());
+ }
+ /**
+ * Helper method for StealFrame.
+ */
+ void RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine,
+ nsFrameList& aFrameList, nsLineList& aLineList);
+
+ void TryAllLines(nsLineList::iterator* aIterator,
+ nsLineList::iterator* aStartIterator,
+ nsLineList::iterator* aEndIterator,
+ bool* aInOverflowLines,
+ FrameLines** aOverflowLines);
+
+ /** move the frames contained by aLine by aDeltaBCoord
+ * if aLine is a block, its child floats are added to the state manager
+ */
+ void SlideLine(BlockReflowInput& aState,
+ nsLineBox* aLine, nscoord aDeltaBCoord);
+
+ void UpdateLineContainerSize(nsLineBox* aLine,
+ const nsSize& aNewContainerSize);
+
+ // helper for SlideLine and UpdateLineContainerSize
+ void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord);
+
+ void ComputeFinalSize(const ReflowInput& aReflowInput,
+ BlockReflowInput& aState,
+ ReflowOutput& aMetrics,
+ nscoord* aBottomEdgeOfChildren);
+
+ void ComputeOverflowAreas(const nsRect& aBounds,
+ const nsStyleDisplay* aDisplay,
+ nscoord aBottomEdgeOfChildren,
+ nsOverflowAreas& aOverflowAreas);
+
+ /**
+ * Add the frames in aFrameList to this block after aPrevSibling.
+ * This block thinks in terms of lines, but the frame construction code
+ * knows nothing about lines at all so we need to find the line that
+ * contains aPrevSibling and add aFrameList after aPrevSibling on that line.
+ * New lines are created as necessary to handle block data in aFrameList.
+ * This function will clear aFrameList.
+ */
+ void AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling);
+
+ /**
+ * Perform Bidi resolution on this frame
+ */
+ nsresult ResolveBidi();
+
+ /**
+ * Test whether the frame is a form control in a visual Bidi page.
+ * This is necessary for backwards-compatibility, because most visual
+ * pages use logical order for form controls so that they will
+ * display correctly on native widgets in OSs with Bidi support
+ * @param aPresContext the pres context
+ * @return whether the frame is a BIDI form control
+ */
+ bool IsVisualFormControl(nsPresContext* aPresContext);
+
+ /**
+ * Helper function to create bullet frame.
+ * @param aCreateBulletList true to create bullet list; otherwise number list.
+ * @param aListStylePositionInside true to put the list position inside;
+ * otherwise outside.
+ */
+ void CreateBulletFrameForListItem(bool aCreateBulletList,
+ bool aListStylePositionInside);
+
+public:
+ /**
+ * Does all the real work for removing aDeletedFrame
+ * -- finds the line containing aDeletedFrame
+ * -- removes all aDeletedFrame next-in-flows (or all continuations,
+ * if REMOVE_FIXED_CONTINUATIONS is given)
+ * -- marks lines dirty as needed
+ * -- marks textruns dirty (unless FRAMES_ARE_EMPTY is given, in which
+ * case textruns do not need to be dirtied)
+ * -- destroys all removed frames
+ */
+ enum {
+ REMOVE_FIXED_CONTINUATIONS = 0x02,
+ FRAMES_ARE_EMPTY = 0x04
+ };
+ void DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags);
+
+ void ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
+ bool aReparentSiblings);
+
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
+
+ virtual void UnionChildOverflow(nsOverflowAreas& aOverflowAreas) override;
+
+ /** Load all of aFrame's floats into the float manager iff aFrame is not a
+ * block formatting context. Handles all necessary float manager translations;
+ * assumes float manager is in aFrame's parent's coord system.
+ * Safe to call on non-blocks (does nothing).
+ */
+ static void RecoverFloatsFor(nsIFrame* aFrame,
+ nsFloatManager& aFloatManager,
+ mozilla::WritingMode aWM,
+ const nsSize& aContainerSize);
+
+ /**
+ * Determine if we have any pushed floats from a previous continuation.
+ *
+ * @returns true, if any of the floats at the beginning of our mFloats list
+ * have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise.
+ */
+ bool HasPushedFloatsFromPrevContinuation() const {
+ if (!mFloats.IsEmpty()) {
+ // If we have pushed floats, then they should be at the beginning of our
+ // float list.
+ if (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
+ return true;
+ }
+ }
+
+#ifdef DEBUG
+ // Double-check the above assertion that pushed floats should be at the
+ // beginning of our floats list.
+ for (nsFrameList::Enumerator e(mFloats); !e.AtEnd(); e.Next()) {
+ nsIFrame* f = e.get();
+ NS_ASSERTION(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
+ "pushed floats must be at the beginning of the float list");
+ }
+#endif
+ return false;
+ }
+
+ virtual bool RenumberChildFrames(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting) override;
+protected:
+
+ /** grab overflow lines from this block's prevInFlow, and make them
+ * part of this block's mLines list.
+ * @return true if any lines were drained.
+ */
+ bool DrainOverflowLines();
+
+ /**
+ * @return false iff this block does not have a float on any child list.
+ * This function is O(1).
+ */
+ bool MaybeHasFloats() const {
+ if (!mFloats.IsEmpty()) {
+ return true;
+ }
+ // XXX this could be replaced with HasPushedFloats() if we enforced
+ // removing the property when the frame list becomes empty.
+ nsFrameList* list = GetPushedFloats();
+ if (list && !list->IsEmpty()) {
+ return true;
+ }
+ // For the OverflowOutOfFlowsProperty I think we do enforce that, but it's
+ // a mix of out-of-flow frames, so that's why the method name has "Maybe".
+ return GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS;
+ }
+
+ /**
+ * Moves frames from our PushedFloats list back into our mFloats list.
+ */
+ void DrainSelfPushedFloats();
+
+ /**
+ * First calls DrainSelfPushedFloats() then grabs pushed floats from this
+ * block's prev-in-flow, and splice them into this block's mFloats list too.
+ */
+ void DrainPushedFloats();
+
+ /** Load all our floats into the float manager (without reflowing them).
+ * Assumes float manager is in our own coordinate system.
+ */
+ void RecoverFloats(nsFloatManager& aFloatManager,
+ mozilla::WritingMode aWM,
+ const nsSize& aContainerSize);
+
+ /** Reflow pushed floats
+ */
+ void ReflowPushedFloats(BlockReflowInput& aState,
+ nsOverflowAreas& aOverflowAreas,
+ nsReflowStatus& aStatus);
+
+ /** Find any trailing BR clear from the last line of the block (or its PIFs)
+ */
+ mozilla::StyleClear FindTrailingClear();
+
+ /**
+ * Remove a float from our float list.
+ */
+ void RemoveFloat(nsIFrame* aFloat);
+ /**
+ * Remove a float from the float cache for the line its placeholder is on.
+ */
+ void RemoveFloatFromFloatCache(nsIFrame* aFloat);
+
+ void CollectFloats(nsIFrame* aFrame, nsFrameList& aList,
+ bool aCollectFromSiblings) {
+ if (MaybeHasFloats()) {
+ DoCollectFloats(aFrame, aList, aCollectFromSiblings);
+ }
+ }
+ void DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
+ bool aCollectFromSiblings);
+
+ // Remove a float, abs, rel positioned frame from the appropriate block's list
+ static void DoRemoveOutOfFlowFrame(nsIFrame* aFrame);
+
+ /** set up the conditions necessary for an resize reflow
+ * the primary task is to mark the minimumly sufficient lines dirty.
+ */
+ void PrepareResizeReflow(BlockReflowInput& aState);
+
+ /** reflow all lines that have been marked dirty */
+ void ReflowDirtyLines(BlockReflowInput& aState);
+
+ /** Mark a given line dirty due to reflow being interrupted on or before it */
+ void MarkLineDirtyForInterrupt(nsLineBox* aLine);
+
+ //----------------------------------------
+ // Methods for line reflow
+ /**
+ * Reflow a line.
+ * @param aState the current reflow state
+ * @param aLine the line to reflow. can contain a single block frame
+ * or contain 1 or more inline frames.
+ * @param aKeepReflowGoing [OUT] indicates whether the caller should continue to reflow more lines
+ */
+ void ReflowLine(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepReflowGoing);
+
+ // Return false if it needs another reflow because of reduced space
+ // between floats that are next to it (but not next to its top), and
+ // return true otherwise.
+ bool PlaceLine(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsFloatManager::SavedState* aFloatStateBeforeLine,
+ mozilla::LogicalRect& aFloatAvailableSpace, //in-out
+ nscoord& aAvailableSpaceBSize, // in-out
+ bool* aKeepReflowGoing);
+
+ /**
+ * If NS_BLOCK_LOOK_FOR_DIRTY_FRAMES is set, call MarkLineDirty
+ * on any line with a child frame that is dirty.
+ */
+ void LazyMarkLinesDirty();
+
+ /**
+ * Mark |aLine| dirty, and, if necessary because of possible
+ * pull-up, mark the previous line dirty as well. Also invalidates textruns
+ * on those lines because the text in the lines might have changed due to
+ * addition/removal of frames.
+ * @param aLine the line to mark dirty
+ * @param aLineList the line list containing that line
+ */
+ void MarkLineDirty(LineIterator aLine, const nsLineList* aLineList);
+
+ // XXX where to go
+ bool IsLastLine(BlockReflowInput& aState,
+ LineIterator aLine);
+
+ void DeleteLine(BlockReflowInput& aState,
+ nsLineList::iterator aLine,
+ nsLineList::iterator aLineEnd);
+
+ //----------------------------------------
+ // Methods for individual frame reflow
+
+ bool ShouldApplyBStartMargin(BlockReflowInput& aState,
+ nsLineBox* aLine,
+ nsIFrame* aChildFrame);
+
+ void ReflowBlockFrame(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepGoing);
+
+ void ReflowInlineFrames(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepLineGoing);
+
+ void DoReflowInlineFrames(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsFlowAreaRect& aFloatAvailableSpace,
+ nscoord& aAvailableSpaceBSize,
+ nsFloatManager::SavedState* aFloatStateBeforeLine,
+ bool* aKeepReflowGoing,
+ LineReflowStatus* aLineReflowStatus,
+ bool aAllowPullUp);
+
+ void ReflowInlineFrame(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsIFrame* aFrame,
+ LineReflowStatus* aLineReflowStatus);
+
+ // Compute the available inline size for a float.
+ mozilla::LogicalRect AdjustFloatAvailableSpace(
+ BlockReflowInput& aState,
+ const mozilla::LogicalRect& aFloatAvailableSpace,
+ nsIFrame* aFloatFrame);
+ // Computes the border-box inline size of the float
+ nscoord ComputeFloatISize(BlockReflowInput& aState,
+ const mozilla::LogicalRect& aFloatAvailableSpace,
+ nsIFrame* aFloat);
+ // An incomplete aReflowStatus indicates the float should be split
+ // but only if the available height is constrained.
+ // aAdjustedAvailableSpace is the result of calling
+ // nsBlockFrame::AdjustFloatAvailableSpace.
+ void ReflowFloat(BlockReflowInput& aState,
+ const mozilla::LogicalRect& aAdjustedAvailableSpace,
+ nsIFrame* aFloat,
+ mozilla::LogicalMargin& aFloatMargin,
+ mozilla::LogicalMargin& aFloatOffsets,
+ // Whether the float's position
+ // (aAdjustedAvailableSpace) has been pushed down
+ // due to the presence of other floats.
+ bool aFloatPushedDown,
+ nsReflowStatus& aReflowStatus);
+
+ //----------------------------------------
+ // Methods for pushing/pulling lines/frames
+
+ /**
+ * Create a next-in-flow, if necessary, for aFrame. If a new frame is
+ * created, place it in aLine if aLine is not null.
+ * @param aState the block reflow state
+ * @param aLine where to put a new frame
+ * @param aFrame the frame
+ * @return true if a new frame was created, false if not
+ */
+ bool CreateContinuationFor(BlockReflowInput& aState,
+ nsLineBox* aLine,
+ nsIFrame* aFrame);
+
+ /**
+ * Push aLine (and any after it), since it cannot be placed on this
+ * page/column. Set aKeepReflowGoing to false and set
+ * flag aState.mReflowStatus as incomplete.
+ */
+ void PushTruncatedLine(BlockReflowInput& aState,
+ LineIterator aLine,
+ bool* aKeepReflowGoing);
+
+ void SplitLine(BlockReflowInput& aState,
+ nsLineLayout& aLineLayout,
+ LineIterator aLine,
+ nsIFrame* aFrame,
+ LineReflowStatus* aLineReflowStatus);
+
+ /**
+ * Pull a frame from the next available location (one of our lines or
+ * one of our next-in-flows lines).
+ * @return the pulled frame or nullptr
+ */
+ nsIFrame* PullFrame(BlockReflowInput& aState,
+ LineIterator aLine);
+
+ /**
+ * Try to pull a frame out of a line pointed at by aFromLine.
+ *
+ * Note: pulling a frame from a line that is a place-holder frame
+ * doesn't automatically remove the corresponding float from the
+ * line's float array. This happens indirectly: either the line gets
+ * emptied (and destroyed) or the line gets reflowed (because we mark
+ * it dirty) and the code at the top of ReflowLine empties the
+ * array. So eventually, it will be removed, just not right away.
+ *
+ * @return the pulled frame or nullptr
+ */
+ nsIFrame* PullFrameFrom(nsLineBox* aLine,
+ nsBlockFrame* aFromContainer,
+ nsLineList::iterator aFromLine);
+
+ /**
+ * Push the line after aLineBefore to the overflow line list.
+ * @param aLineBefore a line in 'mLines' (or LinesBegin() when
+ * pushing the first line)
+ */
+ void PushLines(BlockReflowInput& aState,
+ nsLineList::iterator aLineBefore);
+
+ void PropagateFloatDamage(BlockReflowInput& aState,
+ nsLineBox* aLine,
+ nscoord aDeltaBCoord);
+
+ void CheckFloats(BlockReflowInput& aState);
+
+ //----------------------------------------
+ // List handling kludge
+
+ void ReflowBullet(nsIFrame* aBulletFrame,
+ BlockReflowInput& aState,
+ ReflowOutput& aMetrics,
+ nscoord aLineTop);
+
+ //----------------------------------------
+
+ virtual nsILineIterator* GetLineIterator() override;
+
+public:
+ bool HasOverflowLines() const {
+ return 0 != (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES);
+ }
+ FrameLines* GetOverflowLines() const;
+protected:
+ FrameLines* RemoveOverflowLines();
+ void SetOverflowLines(FrameLines* aOverflowLines);
+ void DestroyOverflowLines();
+
+ /**
+ * This class is useful for efficiently modifying the out of flow
+ * overflow list. It gives the client direct writable access to
+ * the frame list temporarily but ensures that property is only
+ * written back if absolutely necessary.
+ */
+ struct nsAutoOOFFrameList {
+ nsFrameList mList;
+
+ explicit nsAutoOOFFrameList(nsBlockFrame* aBlock)
+ : mPropValue(aBlock->GetOverflowOutOfFlows())
+ , mBlock(aBlock) {
+ if (mPropValue) {
+ mList = *mPropValue;
+ }
+ }
+ ~nsAutoOOFFrameList() {
+ mBlock->SetOverflowOutOfFlows(mList, mPropValue);
+ }
+ protected:
+ nsFrameList* const mPropValue;
+ nsBlockFrame* const mBlock;
+ };
+ friend struct nsAutoOOFFrameList;
+
+ nsFrameList* GetOverflowOutOfFlows() const;
+ void SetOverflowOutOfFlows(const nsFrameList& aList, nsFrameList* aPropValue);
+
+ /**
+ * @return the inside bullet frame or nullptr if we don't have one.
+ */
+ nsBulletFrame* GetInsideBullet() const;
+
+ /**
+ * @return the outside bullet frame or nullptr if we don't have one.
+ */
+ nsBulletFrame* GetOutsideBullet() const;
+
+ /**
+ * @return the outside bullet frame list frame property.
+ */
+ nsFrameList* GetOutsideBulletList() const;
+
+ /**
+ * @return true if this frame has pushed floats.
+ */
+ bool HasPushedFloats() const {
+ return 0 != (GetStateBits() & NS_BLOCK_HAS_PUSHED_FLOATS);
+ }
+
+ // Get the pushed floats list, which is used for *temporary* storage
+ // of floats during reflow, between when we decide they don't fit in
+ // this block until our next continuation takes them.
+ nsFrameList* GetPushedFloats() const;
+ // Get the pushed floats list, or if there is not currently one,
+ // make a new empty one.
+ nsFrameList* EnsurePushedFloats();
+ // Remove and return the pushed floats list.
+ nsFrameList* RemovePushedFloats();
+
+#ifdef DEBUG
+ void VerifyLines(bool aFinalCheckOK);
+ void VerifyOverflowSituation();
+ int32_t GetDepth() const;
+#endif
+
+ nscoord mMinWidth, mPrefWidth;
+
+ nsLineList mLines;
+
+ // List of all floats in this block
+ // XXXmats blocks rarely have floats, make it a frame property
+ nsFrameList mFloats;
+
+ friend class mozilla::BlockReflowInput;
+ friend class nsBlockInFlowLineIterator;
+
+#ifdef DEBUG
+public:
+ static bool gLamePaintMetrics;
+ static bool gLameReflowMetrics;
+ static bool gNoisy;
+ static bool gNoisyDamageRepair;
+ static bool gNoisyIntrinsic;
+ static bool gNoisyReflow;
+ static bool gReallyNoisyReflow;
+ static bool gNoisyFloatManager;
+ static bool gVerifyLines;
+ static bool gDisableResizeOpt;
+
+ static int32_t gNoiseIndent;
+
+ static const char* kReflowCommandType[];
+
+protected:
+ static void InitDebugFlags();
+#endif
+};
+
+#ifdef DEBUG
+class AutoNoisyIndenter {
+public:
+ explicit AutoNoisyIndenter(bool aDoIndent) : mIndented(aDoIndent) {
+ if (mIndented) {
+ nsBlockFrame::gNoiseIndent++;
+ }
+ }
+ ~AutoNoisyIndenter() {
+ if (mIndented) {
+ nsBlockFrame::gNoiseIndent--;
+ }
+ }
+private:
+ bool mIndented;
+};
+#endif
+
+/**
+ * Iterates over all lines in the prev-in-flows/next-in-flows of this block.
+ */
+class nsBlockInFlowLineIterator {
+public:
+ typedef nsBlockFrame::LineIterator LineIterator;
+ /**
+ * Set up the iterator to point to aLine which must be a normal line
+ * in aFrame (not an overflow line).
+ */
+ nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine);
+ /**
+ * Set up the iterator to point to the first line found starting from
+ * aFrame. Sets aFoundValidLine to false if there is no such line.
+ * After aFoundValidLine has returned false, don't call any methods on this
+ * object again.
+ */
+ nsBlockInFlowLineIterator(nsBlockFrame* aFrame, bool* aFoundValidLine);
+ /**
+ * Set up the iterator to point to the line that contains aFindFrame (either
+ * directly or indirectly). If aFrame is out of flow, or contained in an
+ * out-of-flow, finds the line containing the out-of-flow's placeholder. If
+ * the frame is not found, sets aFoundValidLine to false. After
+ * aFoundValidLine has returned false, don't call any methods on this
+ * object again.
+ */
+ nsBlockInFlowLineIterator(nsBlockFrame* aFrame, nsIFrame* aFindFrame,
+ bool* aFoundValidLine);
+
+ LineIterator GetLine() { return mLine; }
+ bool IsLastLineInList();
+ nsBlockFrame* GetContainer() { return mFrame; }
+ bool GetInOverflow() { return mLineList != &mFrame->mLines; }
+
+ /**
+ * Returns the current line list we're iterating, null means
+ * we're iterating |mLines| of the container.
+ */
+ nsLineList* GetLineList() { return mLineList; }
+
+ /**
+ * Returns the end-iterator of whatever line list we're in.
+ */
+ LineIterator End();
+
+ /**
+ * Returns false if there are no more lines. After this has returned false,
+ * don't call any methods on this object again.
+ */
+ bool Next();
+ /**
+ * Returns false if there are no more lines. After this has returned false,
+ * don't call any methods on this object again.
+ */
+ bool Prev();
+
+private:
+ friend class nsBlockFrame;
+ friend class nsBidiPresUtils;
+ // XXX nsBlockFrame uses this internally in one place. Try to remove it.
+ // XXX uhm, and nsBidiPresUtils::Resolve too.
+ nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine, bool aInOverflow);
+
+ nsBlockFrame* mFrame;
+ LineIterator mLine;
+ nsLineList* mLineList; // the line list mLine is in
+
+ /**
+ * Moves iterator to next valid line reachable from the current block.
+ * Returns false if there are no valid lines.
+ */
+ bool FindValidLine();
+};
+
+#endif /* nsBlockFrame_h___ */
diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp
new file mode 100644
index 000000000..3be74c929
--- /dev/null
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* class that a parent frame uses to reflow a block frame */
+
+#include "nsBlockReflowContext.h"
+#include "BlockReflowInput.h"
+#include "nsFloatManager.h"
+#include "nsColumnSetFrame.h"
+#include "nsContainerFrame.h"
+#include "nsBlockFrame.h"
+#include "nsLineBox.h"
+#include "nsLayoutUtils.h"
+
+using namespace mozilla;
+
+#ifdef DEBUG
+#undef NOISY_MAX_ELEMENT_SIZE
+#undef REALLY_NOISY_MAX_ELEMENT_SIZE
+#undef NOISY_BLOCK_DIR_MARGINS
+#else
+#undef NOISY_MAX_ELEMENT_SIZE
+#undef REALLY_NOISY_MAX_ELEMENT_SIZE
+#undef NOISY_BLOCK_DIR_MARGINS
+#endif
+
+nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
+ const ReflowInput& aParentRS)
+ : mPresContext(aPresContext),
+ mOuterReflowInput(aParentRS),
+ mSpace(aParentRS.GetWritingMode()),
+ mMetrics(aParentRS)
+{
+}
+
+static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
+{
+ nsIAtom* type = aFrame->GetType();
+ if (type == nsGkAtoms::columnSetFrame) {
+ static_cast<nsColumnSetFrame*>(aFrame)->DrainOverflowColumns();
+ nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
+ if (child) {
+ return DescendIntoBlockLevelFrame(child);
+ }
+ }
+ return aFrame;
+}
+
+bool
+nsBlockReflowContext::ComputeCollapsedBStartMargin(const ReflowInput& aRI,
+ nsCollapsingMargin* aMargin,
+ nsIFrame* aClearanceFrame,
+ bool* aMayNeedRetry,
+ bool* aBlockIsEmpty)
+{
+ WritingMode wm = aRI.GetWritingMode();
+ WritingMode parentWM = mMetrics.GetWritingMode();
+
+ // Include block-start element of frame's margin
+ aMargin->Include(aRI.ComputedLogicalMargin().ConvertTo(parentWM, wm).BStart(parentWM));
+
+ // The inclusion of the block-end margin when empty is done by the caller
+ // since it doesn't need to be done by the top-level (non-recursive)
+ // caller.
+
+#ifdef NOISY_BLOCKDIR_MARGINS
+ nsFrame::ListTag(stdout, aRI.mFrame);
+ printf(": %d => %d\n", aRI.ComputedLogicalMargin().BStart(wm), aMargin->get());
+#endif
+
+ bool dirtiedLine = false;
+ bool setBlockIsEmpty = false;
+
+ // Calculate the frame's generational block-start-margin from its child
+ // blocks. Note that if the frame has a non-zero block-start-border or
+ // block-start-padding then this step is skipped because it will be a margin
+ // root. It is also skipped if the frame is a margin root for other
+ // reasons.
+ nsIFrame* frame = DescendIntoBlockLevelFrame(aRI.mFrame);
+ nsPresContext* prescontext = frame->PresContext();
+ nsBlockFrame* block = nullptr;
+ if (0 == aRI.ComputedLogicalBorderPadding().BStart(wm)) {
+ block = nsLayoutUtils::GetAsBlock(frame);
+ if (block) {
+ bool bStartMarginRoot, unused;
+ block->IsMarginRoot(&bStartMarginRoot, &unused);
+ if (bStartMarginRoot) {
+ block = nullptr;
+ }
+ }
+ }
+
+ // iterate not just through the lines of 'block' but also its
+ // overflow lines and the normal and overflow lines of its next in
+ // flows. Note that this will traverse some frames more than once:
+ // for example, if A contains B and A->nextinflow contains
+ // B->nextinflow, we'll traverse B->nextinflow twice. But this is
+ // OK because our traversal is idempotent.
+ for ( ;block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) {
+ for (int overflowLines = 0; overflowLines <= 1; ++overflowLines) {
+ nsBlockFrame::LineIterator line;
+ nsBlockFrame::LineIterator line_end;
+ bool anyLines = true;
+ if (overflowLines) {
+ nsBlockFrame::FrameLines* frames = block->GetOverflowLines();
+ nsLineList* lines = frames ? &frames->mLines : nullptr;
+ if (!lines) {
+ anyLines = false;
+ } else {
+ line = lines->begin();
+ line_end = lines->end();
+ }
+ } else {
+ line = block->LinesBegin();
+ line_end = block->LinesEnd();
+ }
+ for (; anyLines && line != line_end; ++line) {
+ if (!aClearanceFrame && line->HasClearance()) {
+ // If we don't have a clearance frame, then we're computing
+ // the collapsed margin in the first pass, assuming that all
+ // lines have no clearance. So clear their clearance flags.
+ line->ClearHasClearance();
+ line->MarkDirty();
+ dirtiedLine = true;
+ }
+
+ bool isEmpty;
+ if (line->IsInline()) {
+ isEmpty = line->IsEmpty();
+ } else {
+ nsIFrame* kid = line->mFirstChild;
+ if (kid == aClearanceFrame) {
+ line->SetHasClearance();
+ line->MarkDirty();
+ dirtiedLine = true;
+ if (!setBlockIsEmpty && aBlockIsEmpty) {
+ setBlockIsEmpty = true;
+ *aBlockIsEmpty = false;
+ }
+ goto done;
+ }
+ // Here is where we recur. Now that we have determined that a
+ // generational collapse is required we need to compute the
+ // child blocks margin and so in so that we can look into
+ // it. For its margins to be computed we need to have a reflow
+ // state for it.
+
+ // We may have to construct an extra reflow state here if
+ // we drilled down through a block wrapper. At the moment
+ // we can only drill down one level so we only have to support
+ // one extra reflow state.
+ const ReflowInput* outerReflowInput = &aRI;
+ if (frame != aRI.mFrame) {
+ NS_ASSERTION(frame->GetParent() == aRI.mFrame,
+ "Can only drill through one level of block wrapper");
+ LogicalSize availSpace = aRI.ComputedSize(frame->GetWritingMode());
+ outerReflowInput = new ReflowInput(prescontext,
+ aRI, frame, availSpace);
+ }
+ {
+ LogicalSize availSpace =
+ outerReflowInput->ComputedSize(kid->GetWritingMode());
+ ReflowInput innerReflowInput(prescontext,
+ *outerReflowInput, kid,
+ availSpace);
+ // Record that we're being optimistic by assuming the kid
+ // has no clearance
+ if (kid->StyleDisplay()->mBreakType != StyleClear::None ||
+ !nsBlockFrame::BlockCanIntersectFloats(kid)) {
+ *aMayNeedRetry = true;
+ }
+ if (ComputeCollapsedBStartMargin(innerReflowInput, aMargin,
+ aClearanceFrame, aMayNeedRetry,
+ &isEmpty)) {
+ line->MarkDirty();
+ dirtiedLine = true;
+ }
+ if (isEmpty) {
+ WritingMode innerWM = innerReflowInput.GetWritingMode();
+ LogicalMargin innerMargin =
+ innerReflowInput.ComputedLogicalMargin().ConvertTo(parentWM, innerWM);
+ aMargin->Include(innerMargin.BEnd(parentWM));
+ }
+ }
+ if (outerReflowInput != &aRI) {
+ delete const_cast<ReflowInput*>(outerReflowInput);
+ }
+ }
+ if (!isEmpty) {
+ if (!setBlockIsEmpty && aBlockIsEmpty) {
+ setBlockIsEmpty = true;
+ *aBlockIsEmpty = false;
+ }
+ goto done;
+ }
+ }
+ if (!setBlockIsEmpty && aBlockIsEmpty) {
+ // The first time we reach here is when this is the first block
+ // and we have processed all its normal lines.
+ setBlockIsEmpty = true;
+ // All lines are empty, or we wouldn't be here!
+ *aBlockIsEmpty = aRI.mFrame->IsSelfEmpty();
+ }
+ }
+ }
+ done:
+
+ if (!setBlockIsEmpty && aBlockIsEmpty) {
+ *aBlockIsEmpty = aRI.mFrame->IsEmpty();
+ }
+
+#ifdef NOISY_BLOCKDIR_MARGINS
+ nsFrame::ListTag(stdout, aRI.mFrame);
+ printf(": => %d\n", aMargin->get());
+#endif
+
+ return dirtiedLine;
+}
+
+void
+nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace,
+ bool aApplyBStartMargin,
+ nsCollapsingMargin& aPrevMargin,
+ nscoord aClearance,
+ bool aIsAdjacentWithBStart,
+ nsLineBox* aLine,
+ ReflowInput& aFrameRI,
+ nsReflowStatus& aFrameReflowStatus,
+ BlockReflowInput& aState)
+{
+ mFrame = aFrameRI.mFrame;
+ mWritingMode = aState.mReflowInput.GetWritingMode();
+ mContainerSize = aState.ContainerSize();
+ mSpace = aSpace;
+
+ if (!aIsAdjacentWithBStart) {
+ aFrameRI.mFlags.mIsTopOfPage = false; // make sure this is cleared
+ }
+
+ if (aApplyBStartMargin) {
+ mBStartMargin = aPrevMargin;
+
+#ifdef NOISY_BLOCKDIR_MARGINS
+ nsFrame::ListTag(stdout, mOuterReflowInput.mFrame);
+ printf(": reflowing ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" margin => %d, clearance => %d\n", mBStartMargin.get(), aClearance);
+#endif
+
+ // Adjust the available size if it's constrained so that the
+ // child frame doesn't think it can reflow into its margin area.
+ if (mWritingMode.IsOrthogonalTo(mFrame->GetWritingMode())) {
+ if (NS_UNCONSTRAINEDSIZE != aFrameRI.AvailableISize()) {
+ aFrameRI.AvailableISize() -= mBStartMargin.get() + aClearance;
+ }
+ } else {
+ if (NS_UNCONSTRAINEDSIZE != aFrameRI.AvailableBSize()) {
+ aFrameRI.AvailableBSize() -= mBStartMargin.get() + aClearance;
+ }
+ }
+ } else {
+ // nsBlockFrame::ReflowBlock might call us multiple times with
+ // *different* values of aApplyBStartMargin.
+ mBStartMargin.Zero();
+ }
+
+ nscoord tI = 0, tB = 0;
+ // The values of x and y do not matter for floats, so don't bother
+ // calculating them. Floats are guaranteed to have their own float
+ // manager, so tI and tB don't matter. mICoord and mBCoord don't
+ // matter becacuse they are only used in PlaceBlock, which is not used
+ // for floats.
+ if (aLine) {
+ // Compute inline/block coordinate where reflow will begin. Use the
+ // rules from 10.3.3 to determine what to apply. At this point in the
+ // reflow auto inline-start/end margins will have a zero value.
+
+ WritingMode frameWM = aFrameRI.GetWritingMode();
+ LogicalMargin usedMargin =
+ aFrameRI.ComputedLogicalMargin().ConvertTo(mWritingMode, frameWM);
+ mICoord = mSpace.IStart(mWritingMode) + usedMargin.IStart(mWritingMode);
+ mBCoord = mSpace.BStart(mWritingMode) + mBStartMargin.get() + aClearance;
+
+ LogicalRect space(mWritingMode, mICoord, mBCoord,
+ mSpace.ISize(mWritingMode) -
+ usedMargin.IStartEnd(mWritingMode),
+ mSpace.BSize(mWritingMode) -
+ usedMargin.BStartEnd(mWritingMode));
+ tI = space.LineLeft(mWritingMode, mContainerSize);
+ tB = mBCoord;
+
+ if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
+ aFrameRI.mBlockDelta =
+ mOuterReflowInput.mBlockDelta + mBCoord - aLine->BStart();
+ }
+
+#ifdef DEBUG
+ mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef);
+ mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef);
+#endif
+
+ mOuterReflowInput.mFloatManager->Translate(tI, tB);
+ mFrame->Reflow(mPresContext, mMetrics, aFrameRI, aFrameReflowStatus);
+ mOuterReflowInput.mFloatManager->Translate(-tI, -tB);
+
+#ifdef DEBUG
+ if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
+ if ((CRAZY_SIZE(mMetrics.ISize(mWritingMode)) ||
+ CRAZY_SIZE(mMetrics.BSize(mWritingMode))) &&
+ !mFrame->GetParent()->IsCrazySizeAssertSuppressed()) {
+ printf("nsBlockReflowContext: ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" metrics=%d,%d!\n",
+ mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode));
+ }
+ if ((mMetrics.ISize(mWritingMode) == nscoord(0xdeadbeef)) ||
+ (mMetrics.BSize(mWritingMode) == nscoord(0xdeadbeef))) {
+ printf("nsBlockReflowContext: ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" didn't set i/b %d,%d!\n",
+ mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode));
+ }
+ }
+#endif
+
+ if (!mFrame->HasOverflowAreas()) {
+ mMetrics.SetOverflowAreasToDesiredBounds();
+ }
+
+ if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) ||
+ (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ // If frame is complete and has a next-in-flow, we need to delete
+ // them now. Do not do this when a break-before is signaled because
+ // the frame is going to get reflowed again (and may end up wanting
+ // a next-in-flow where it ends up), unless it is an out of flow frame.
+ if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) {
+ nsIFrame* kidNextInFlow = mFrame->GetNextInFlow();
+ if (nullptr != kidNextInFlow) {
+ // Remove all of the childs next-in-flows. Make sure that we ask
+ // the right parent to do the removal (it's possible that the
+ // parent is not this because we are executing pullup code).
+ // Floats will eventually be removed via nsBlockFrame::RemoveFloat
+ // which detaches the placeholder from the float.
+ nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, mFrame);
+ kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to place the block frame within the available space. If
+ * it fits, apply inline-dir ("horizontal") positioning (CSS 10.3.3),
+ * collapse margins (CSS2 8.3.1). Also apply relative positioning.
+ */
+bool
+nsBlockReflowContext::PlaceBlock(const ReflowInput& aReflowInput,
+ bool aForceFit,
+ nsLineBox* aLine,
+ nsCollapsingMargin& aBEndMarginResult,
+ nsOverflowAreas& aOverflowAreas,
+ nsReflowStatus aReflowStatus)
+{
+ // Compute collapsed block-end margin value.
+ WritingMode wm = aReflowInput.GetWritingMode();
+ WritingMode parentWM = mMetrics.GetWritingMode();
+ if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
+ aBEndMarginResult = mMetrics.mCarriedOutBEndMargin;
+ aBEndMarginResult.Include(aReflowInput.ComputedLogicalMargin().
+ ConvertTo(parentWM, wm).BEnd(parentWM));
+ } else {
+ // The used block-end-margin is set to zero before a break.
+ aBEndMarginResult.Zero();
+ }
+
+ nscoord backupContainingBlockAdvance = 0;
+
+ // Check whether the block's block-end margin collapses with its block-start
+ // margin. See CSS 2.1 section 8.3.1; those rules seem to match
+ // nsBlockFrame::IsEmpty(). Any such block must have zero block-size so
+ // check that first. Note that a block can have clearance and still
+ // have adjoining block-start/end margins, because the clearance goes
+ // above the block-start margin.
+ // Mark the frame as non-dirty; it has been reflowed (or we wouldn't
+ // be here), and we don't want to assert in CachedIsEmpty()
+ mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY);
+ bool empty = 0 == mMetrics.BSize(parentWM) && aLine->CachedIsEmpty();
+ if (empty) {
+ // Collapse the block-end margin with the block-start margin that was
+ // already applied.
+ aBEndMarginResult.Include(mBStartMargin);
+
+#ifdef NOISY_BLOCKDIR_MARGINS
+ printf(" ");
+ nsFrame::ListTag(stdout, mOuterReflowInput.mFrame);
+ printf(": ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf(" -- collapsing block start & end margin together; BStart=%d spaceBStart=%d\n",
+ mBCoord, mSpace.BStart(mWritingMode));
+#endif
+ // Section 8.3.1 of CSS 2.1 says that blocks with adjoining
+ // "top/bottom" (i.e. block-start/end) margins whose top margin collapses
+ // with their parent's top margin should have their top border-edge at the
+ // top border-edge of their parent. We actually don't have to do
+ // anything special to make this happen. In that situation,
+ // nsBlockFrame::ShouldApplyBStartMargin will have returned false,
+ // and mBStartMargin and aClearance will have been zero in
+ // ReflowBlock.
+
+ // If we did apply our block-start margin, but now we're collapsing it
+ // into the block-end margin, we need to back up the containing
+ // block's bCoord-advance by our block-start margin so that it doesn't get
+ // counted twice. Note that here we're allowing the line's bounds
+ // to become different from the block's position; we do this
+ // because the containing block will place the next line at the
+ // line's BEnd, and it must place the next line at a different
+ // point from where this empty block will be.
+ backupContainingBlockAdvance = mBStartMargin.get();
+ }
+
+ // See if the frame fit. If it's the first frame or empty then it
+ // always fits. If the block-size is unconstrained then it always fits,
+ // even if there's some sort of integer overflow that makes bCoord +
+ // mMetrics.BSize() appear to go beyond the available block size.
+ if (!empty && !aForceFit &&
+ mSpace.BSize(mWritingMode) != NS_UNCONSTRAINEDSIZE) {
+ nscoord bEnd = mBCoord -
+ backupContainingBlockAdvance + mMetrics.BSize(mWritingMode);
+ if (bEnd > mSpace.BEnd(mWritingMode)) {
+ // didn't fit, we must acquit.
+ mFrame->DidReflow(mPresContext, &aReflowInput,
+ nsDidReflowStatus::FINISHED);
+ return false;
+ }
+ }
+
+ aLine->SetBounds(mWritingMode,
+ mICoord, mBCoord - backupContainingBlockAdvance,
+ mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode),
+ mContainerSize);
+
+ WritingMode frameWM = mFrame->GetWritingMode();
+ LogicalPoint logPos =
+ LogicalPoint(mWritingMode, mICoord, mBCoord).
+ ConvertTo(frameWM, mWritingMode,
+ mContainerSize - mMetrics.PhysicalSize());
+
+ // ApplyRelativePositioning in right-to-left writing modes needs to
+ // know the updated frame width
+ mFrame->SetSize(mWritingMode, mMetrics.Size(mWritingMode));
+ aReflowInput.ApplyRelativePositioning(&logPos, mContainerSize);
+
+ // Now place the frame and complete the reflow process
+ nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics,
+ &aReflowInput, frameWM, logPos,
+ mContainerSize, 0);
+
+ aOverflowAreas = mMetrics.mOverflowAreas + mFrame->GetPosition();
+
+ return true;
+}
diff --git a/layout/generic/nsBlockReflowContext.h b/layout/generic/nsBlockReflowContext.h
new file mode 100644
index 000000000..4d5541ff6
--- /dev/null
+++ b/layout/generic/nsBlockReflowContext.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* class that a parent frame uses to reflow a block frame */
+
+#ifndef nsBlockReflowContext_h___
+#define nsBlockReflowContext_h___
+
+#include "nsIFrame.h"
+#include "mozilla/ReflowOutput.h"
+
+class nsLineBox;
+class nsPresContext;
+namespace mozilla {
+class BlockReflowInput;
+} // namespace mozilla
+
+/**
+ * An encapsulation of the state and algorithm for reflowing block frames.
+ */
+class nsBlockReflowContext {
+ using BlockReflowInput = mozilla::BlockReflowInput;
+ using ReflowInput = mozilla::ReflowInput;
+ using ReflowOutput = mozilla::ReflowOutput;
+
+public:
+ nsBlockReflowContext(nsPresContext* aPresContext,
+ const ReflowInput& aParentRS);
+ ~nsBlockReflowContext() { }
+
+ void ReflowBlock(const mozilla::LogicalRect& aSpace,
+ bool aApplyBStartMargin,
+ nsCollapsingMargin& aPrevMargin,
+ nscoord aClearance,
+ bool aIsAdjacentWithBStart,
+ nsLineBox* aLine,
+ ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus,
+ BlockReflowInput& aState);
+
+ bool PlaceBlock(const ReflowInput& aReflowInput,
+ bool aForceFit,
+ nsLineBox* aLine,
+ nsCollapsingMargin& aBEndMarginResult /* out */,
+ nsOverflowAreas& aOverflowAreas,
+ nsReflowStatus aReflowStatus);
+
+ nsCollapsingMargin& GetCarriedOutBEndMargin() {
+ return mMetrics.mCarriedOutBEndMargin;
+ }
+
+ const ReflowOutput& GetMetrics() const {
+ return mMetrics;
+ }
+
+ /**
+ * Computes the collapsed block-start margin (in the context's parent's
+ * writing mode) for a block whose reflow state is in aRI.
+ * The computed margin is added into aMargin, whose writing mode is the
+ * parent's mode as found in mMetrics.GetWritingMode(); note this may not be
+ * the block's own writing mode as found in aRI.
+ * If aClearanceFrame is null then this is the first optimistic pass which
+ * shall assume that no frames have clearance, and we clear the HasClearance
+ * on all frames encountered.
+ * If non-null, this is the second pass and the caller has decided
+ * aClearanceFrame needs clearance (and we will therefore stop collapsing
+ * there); also, this function is responsible for marking it with
+ * SetHasClearance.
+ * If in the optimistic pass any frame is encountered that might possibly
+ * need clearance (i.e., if we really needed the optimism assumption) then
+ * we set aMayNeedRetry to true.
+ * We return true if we changed the clearance state of any line and marked it
+ * dirty.
+ */
+ bool ComputeCollapsedBStartMargin(const ReflowInput& aRI,
+ nsCollapsingMargin* aMargin,
+ nsIFrame* aClearanceFrame,
+ bool* aMayNeedRetry,
+ bool* aIsEmpty = nullptr);
+
+protected:
+ nsPresContext* mPresContext;
+ const ReflowInput& mOuterReflowInput;
+
+ nsIFrame* mFrame;
+ mozilla::LogicalRect mSpace;
+
+ nscoord mICoord, mBCoord;
+ nsSize mContainerSize;
+ mozilla::WritingMode mWritingMode;
+ ReflowOutput mMetrics;
+ nsCollapsingMargin mBStartMargin;
+};
+
+#endif /* nsBlockReflowContext_h___ */
diff --git a/layout/generic/nsBulletFrame.cpp b/layout/generic/nsBulletFrame.cpp
new file mode 100644
index 000000000..aa8794321
--- /dev/null
+++ b/layout/generic/nsBulletFrame.cpp
@@ -0,0 +1,1090 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for list-item bullets */
+
+#include "nsBulletFrame.h"
+
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Move.h"
+#include "nsCOMPtr.h"
+#include "nsFontMetrics.h"
+#include "nsGkAtoms.h"
+#include "nsGenericHTMLElement.h"
+#include "nsAttrValueInlines.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIDocument.h"
+#include "nsRenderingContext.h"
+#include "nsDisplayList.h"
+#include "nsCounterManager.h"
+#include "nsBidiUtils.h"
+#include "CounterStyleManager.h"
+
+#include "imgIContainer.h"
+#include "imgRequestProxy.h"
+#include "nsIURI.h"
+
+#include <algorithm>
+
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::image;
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
+
+#ifdef DEBUG
+NS_QUERYFRAME_HEAD(nsBulletFrame)
+ NS_QUERYFRAME_ENTRY(nsBulletFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
+#endif
+
+nsBulletFrame::~nsBulletFrame()
+{
+ NS_ASSERTION(!mBlockingOnload, "Still blocking onload in destructor?");
+}
+
+void
+nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ // Stop image loading first.
+ DeregisterAndCancelImageRequest();
+
+ if (mListener) {
+ mListener->SetFrame(nullptr);
+ }
+
+ // Let base class do the rest
+ nsFrame::DestroyFrom(aDestructRoot);
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsBulletFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult);
+}
+#endif
+
+nsIAtom*
+nsBulletFrame::GetType() const
+{
+ return nsGkAtoms::bulletFrame;
+}
+
+bool
+nsBulletFrame::IsEmpty()
+{
+ return IsSelfEmpty();
+}
+
+bool
+nsBulletFrame::IsSelfEmpty()
+{
+ return StyleList()->GetCounterStyle()->IsNone();
+}
+
+/* virtual */ void
+nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+ nsFrame::DidSetStyleContext(aOldStyleContext);
+
+ imgRequestProxy *newRequest = StyleList()->GetListStyleImage();
+
+ if (newRequest) {
+
+ if (!mListener) {
+ mListener = new nsBulletListener();
+ mListener->SetFrame(this);
+ }
+
+ bool needNewRequest = true;
+
+ if (mImageRequest) {
+ // Reload the image, maybe...
+ nsCOMPtr<nsIURI> oldURI;
+ mImageRequest->GetURI(getter_AddRefs(oldURI));
+ nsCOMPtr<nsIURI> newURI;
+ newRequest->GetURI(getter_AddRefs(newURI));
+ if (oldURI && newURI) {
+ bool same;
+ newURI->Equals(oldURI, &same);
+ if (same) {
+ needNewRequest = false;
+ }
+ }
+ }
+
+ if (needNewRequest) {
+ RefPtr<imgRequestProxy> newRequestClone;
+ newRequest->Clone(mListener, getter_AddRefs(newRequestClone));
+
+ // Deregister the old request. We wait until after Clone is done in case
+ // the old request and the new request are the same underlying image
+ // accessed via different URLs.
+ DeregisterAndCancelImageRequest();
+
+ // Register the new request.
+ mImageRequest = Move(newRequestClone);
+ RegisterImageRequest(/* aKnownToBeAnimated = */ false);
+ }
+ } else {
+ // No image request on the new style context.
+ DeregisterAndCancelImageRequest();
+ }
+
+#ifdef ACCESSIBILITY
+ // Update the list bullet accessible. If old style list isn't available then
+ // no need to update the accessible tree because it's not created yet.
+ if (aOldStyleContext) {
+ nsAccessibilityService* accService = nsIPresShell::AccService();
+ if (accService) {
+ const nsStyleList* oldStyleList = aOldStyleContext->PeekStyleList();
+ if (oldStyleList) {
+ bool hadBullet = oldStyleList->GetListStyleImage() ||
+ !oldStyleList->GetCounterStyle()->IsNone();
+
+ const nsStyleList* newStyleList = StyleList();
+ bool hasBullet = newStyleList->GetListStyleImage() ||
+ !newStyleList->GetCounterStyle()->IsNone();
+
+ if (hadBullet != hasBullet) {
+ accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
+ hasBullet);
+ }
+ }
+ }
+ }
+#endif
+}
+
+class nsDisplayBulletGeometry
+ : public nsDisplayItemGenericGeometry
+ , public nsImageGeometryMixin<nsDisplayBulletGeometry>
+{
+public:
+ nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
+ : nsDisplayItemGenericGeometry(aItem, aBuilder)
+ , nsImageGeometryMixin(aItem, aBuilder)
+ {
+ nsBulletFrame* f = static_cast<nsBulletFrame*>(aItem->Frame());
+ mOrdinal = f->GetOrdinal();
+ }
+
+ int32_t mOrdinal;
+};
+
+class nsDisplayBullet final : public nsDisplayItem {
+public:
+ nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ , mDisableSubpixelAA(false)
+ {
+ MOZ_COUNT_CTOR(nsDisplayBullet);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayBullet() {
+ MOZ_COUNT_DTOR(nsDisplayBullet);
+ }
+#endif
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override
+ {
+ *aSnap = false;
+ return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
+ }
+ virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+ HitTestState* aState,
+ nsTArray<nsIFrame*> *aOutFrames) override {
+ aOutFrames->AppendElement(mFrame);
+ }
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET)
+
+ virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
+ {
+ bool snap;
+ return GetBounds(aBuilder, &snap);
+ }
+
+ virtual void DisableComponentAlpha() override {
+ mDisableSubpixelAA = true;
+ }
+
+ virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
+ {
+ return new nsDisplayBulletGeometry(this, aBuilder);
+ }
+
+ virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+ const nsDisplayItemGeometry* aGeometry,
+ nsRegion *aInvalidRegion) override
+ {
+ const nsDisplayBulletGeometry* geometry = static_cast<const nsDisplayBulletGeometry*>(aGeometry);
+ nsBulletFrame* f = static_cast<nsBulletFrame*>(mFrame);
+
+ if (f->GetOrdinal() != geometry->mOrdinal) {
+ bool snap;
+ aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
+ return;
+ }
+
+ nsCOMPtr<imgIContainer> image = f->GetImage();
+ if (aBuilder->ShouldSyncDecodeImages() && image &&
+ geometry->ShouldInvalidateToSyncDecodeImages()) {
+ bool snap;
+ aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+ }
+
+ return nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
+ }
+
+protected:
+ bool mDisableSubpixelAA;
+};
+
+void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ uint32_t flags = imgIContainer::FLAG_NONE;
+ if (aBuilder->ShouldSyncDecodeImages()) {
+ flags |= imgIContainer::FLAG_SYNC_DECODE;
+ }
+
+ DrawResult result = static_cast<nsBulletFrame*>(mFrame)->
+ PaintBullet(*aCtx, ToReferenceFrame(), mVisibleRect, flags,
+ mDisableSubpixelAA);
+
+ nsDisplayBulletGeometry::UpdateDrawResult(this, result);
+}
+
+void
+nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
+
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayBullet(aBuilder, this));
+}
+
+DrawResult
+nsBulletFrame::PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
+ const nsRect& aDirtyRect, uint32_t aFlags,
+ bool aDisableSubpixelAA)
+{
+ const nsStyleList* myList = StyleList();
+ CounterStyle* listStyleType = myList->GetCounterStyle();
+ nsMargin padding = mPadding.GetPhysicalMargin(GetWritingMode());
+
+ if (myList->GetListStyleImage() && mImageRequest) {
+ uint32_t status;
+ mImageRequest->GetImageStatus(&status);
+ if (status & imgIRequest::STATUS_LOAD_COMPLETE &&
+ !(status & imgIRequest::STATUS_ERROR)) {
+ nsCOMPtr<imgIContainer> imageCon;
+ mImageRequest->GetImage(getter_AddRefs(imageCon));
+ if (imageCon) {
+ nsRect dest(padding.left, padding.top,
+ mRect.width - (padding.left + padding.right),
+ mRect.height - (padding.top + padding.bottom));
+ return
+ nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
+ PresContext(),
+ imageCon, nsLayoutUtils::GetSamplingFilterForFrame(this),
+ dest + aPt, aDirtyRect, nullptr, aFlags);
+ }
+ }
+ }
+
+ ColorPattern color(ToDeviceColor(
+ nsLayoutUtils::GetColor(this, eCSSProperty_color)));
+
+ DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
+ int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
+
+ switch (listStyleType->GetStyle()) {
+ case NS_STYLE_LIST_STYLE_NONE:
+ break;
+
+ case NS_STYLE_LIST_STYLE_DISC:
+ case NS_STYLE_LIST_STYLE_CIRCLE:
+ {
+ nsRect rect(padding.left + aPt.x,
+ padding.top + aPt.y,
+ mRect.width - (padding.left + padding.right),
+ mRect.height - (padding.top + padding.bottom));
+ Rect devPxRect = NSRectToRect(rect, appUnitsPerDevPixel);
+ RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
+ AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
+ RefPtr<Path> ellipse = builder->Finish();
+ if (listStyleType->GetStyle() == NS_STYLE_LIST_STYLE_DISC) {
+ drawTarget->Fill(ellipse, color);
+ } else {
+ drawTarget->Stroke(ellipse, color);
+ }
+ }
+ break;
+
+ case NS_STYLE_LIST_STYLE_SQUARE:
+ {
+ nsRect rect(aPt, mRect.Size());
+ rect.Deflate(padding);
+
+ // Snap the height and the width of the rectangle to device pixels,
+ // and then center the result within the original rectangle, so that
+ // all square bullets at the same font size have the same visual
+ // size (bug 376690).
+ // FIXME: We should really only do this if we're not transformed
+ // (like gfxContext::UserToDevicePixelSnapped does).
+ nsPresContext *pc = PresContext();
+ nsRect snapRect(rect.x, rect.y,
+ pc->RoundAppUnitsToNearestDevPixels(rect.width),
+ pc->RoundAppUnitsToNearestDevPixels(rect.height));
+ snapRect.MoveBy((rect.width - snapRect.width) / 2,
+ (rect.height - snapRect.height) / 2);
+ Rect devPxRect =
+ NSRectToSnappedRect(snapRect, appUnitsPerDevPixel, *drawTarget);
+ drawTarget->FillRect(devPxRect, color);
+ }
+ break;
+
+ case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+ case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+ {
+ nsRect rect(aPt, mRect.Size());
+ rect.Deflate(padding);
+
+ WritingMode wm = GetWritingMode();
+ bool isVertical = wm.IsVertical();
+ bool isClosed =
+ listStyleType->GetStyle() == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
+ bool isDown = (!isVertical && !isClosed) || (isVertical && isClosed);
+ nscoord diff = NSToCoordRound(0.1f * rect.height);
+ if (isDown) {
+ rect.y += diff * 2;
+ rect.height -= diff * 2;
+ } else {
+ rect.Deflate(diff, 0);
+ }
+ nsPresContext *pc = PresContext();
+ rect.x = pc->RoundAppUnitsToNearestDevPixels(rect.x);
+ rect.y = pc->RoundAppUnitsToNearestDevPixels(rect.y);
+
+ RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
+ if (isDown) {
+ // to bottom
+ builder->MoveTo(NSPointToPoint(rect.TopLeft(), appUnitsPerDevPixel));
+ builder->LineTo(NSPointToPoint(rect.TopRight(), appUnitsPerDevPixel));
+ builder->LineTo(NSPointToPoint((rect.BottomLeft() + rect.BottomRight()) / 2,
+ appUnitsPerDevPixel));
+ } else {
+ bool isLR = isVertical ? wm.IsVerticalLR() : wm.IsBidiLTR();
+ if (isLR) {
+ // to right
+ builder->MoveTo(NSPointToPoint(rect.TopLeft(), appUnitsPerDevPixel));
+ builder->LineTo(NSPointToPoint((rect.TopRight() + rect.BottomRight()) / 2,
+ appUnitsPerDevPixel));
+ builder->LineTo(NSPointToPoint(rect.BottomLeft(), appUnitsPerDevPixel));
+ } else {
+ // to left
+ builder->MoveTo(NSPointToPoint(rect.TopRight(), appUnitsPerDevPixel));
+ builder->LineTo(NSPointToPoint(rect.BottomRight(), appUnitsPerDevPixel));
+ builder->LineTo(NSPointToPoint((rect.TopLeft() + rect.BottomLeft()) / 2,
+ appUnitsPerDevPixel));
+ }
+ }
+ RefPtr<Path> path = builder->Finish();
+ drawTarget->Fill(path, color);
+ }
+ break;
+
+ default:
+ {
+ DrawTargetAutoDisableSubpixelAntialiasing
+ disable(aRenderingContext.GetDrawTarget(), aDisableSubpixelAA);
+
+ aRenderingContext.ThebesContext()->SetColor(
+ Color::FromABGR(nsLayoutUtils::GetColor(this, eCSSProperty_color)));
+
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
+ nsAutoString text;
+ GetListItemText(text);
+ WritingMode wm = GetWritingMode();
+ nscoord ascent = wm.IsLineInverted()
+ ? fm->MaxDescent() : fm->MaxAscent();
+ aPt.MoveBy(padding.left, padding.top);
+ gfxContext *ctx = aRenderingContext.ThebesContext();
+ if (wm.IsVertical()) {
+ if (wm.IsVerticalLR()) {
+ aPt.x = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineX(
+ this, ctx, aPt.x, ascent));
+ } else {
+ aPt.x = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineX(
+ this, ctx, aPt.x + mRect.width,
+ -ascent));
+ }
+ } else {
+ aPt.y = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineY(
+ this, ctx, aPt.y, ascent));
+ }
+ nsPresContext* presContext = PresContext();
+ if (!presContext->BidiEnabled() && HasRTLChars(text)) {
+ presContext->SetBidiEnabled();
+ }
+ nsLayoutUtils::DrawString(this, *fm, &aRenderingContext,
+ text.get(), text.Length(), aPt);
+ }
+ break;
+ }
+
+ return DrawResult::SUCCESS;
+}
+
+int32_t
+nsBulletFrame::SetListItemOrdinal(int32_t aNextOrdinal,
+ bool* aChanged,
+ int32_t aIncrement)
+{
+ MOZ_ASSERT(aIncrement == 1 || aIncrement == -1,
+ "We shouldn't have weird increments here");
+
+ // Assume that the ordinal comes from the caller
+ int32_t oldOrdinal = mOrdinal;
+ mOrdinal = aNextOrdinal;
+
+ // Try to get value directly from the list-item, if it specifies a
+ // value attribute. Note: we do this with our parent's content
+ // because our parent is the list-item.
+ nsIContent* parentContent = GetParent()->GetContent();
+ if (parentContent) {
+ nsGenericHTMLElement *hc =
+ nsGenericHTMLElement::FromContent(parentContent);
+ if (hc) {
+ const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::value);
+ if (attr && attr->Type() == nsAttrValue::eInteger) {
+ // Use ordinal specified by the value attribute
+ mOrdinal = attr->GetIntegerValue();
+ }
+ }
+ }
+
+ *aChanged = oldOrdinal != mOrdinal;
+
+ return nsCounterManager::IncrementCounter(mOrdinal, aIncrement);
+}
+
+void
+nsBulletFrame::GetListItemText(nsAString& aResult)
+{
+ CounterStyle* style = StyleList()->GetCounterStyle();
+ NS_ASSERTION(style->GetStyle() != NS_STYLE_LIST_STYLE_NONE &&
+ style->GetStyle() != NS_STYLE_LIST_STYLE_DISC &&
+ style->GetStyle() != NS_STYLE_LIST_STYLE_CIRCLE &&
+ style->GetStyle() != NS_STYLE_LIST_STYLE_SQUARE &&
+ style->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
+ style->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN,
+ "we should be using specialized code for these types");
+
+ bool isRTL;
+ nsAutoString counter, prefix, suffix;
+ style->GetPrefix(prefix);
+ style->GetSuffix(suffix);
+ style->GetCounterText(mOrdinal, GetWritingMode(), counter, isRTL);
+
+ aResult.Truncate();
+ aResult.Append(prefix);
+ if (GetWritingMode().IsBidiLTR() != isRTL) {
+ aResult.Append(counter);
+ } else {
+ // RLM = 0x200f, LRM = 0x200e
+ char16_t mark = isRTL ? 0x200f : 0x200e;
+ aResult.Append(mark);
+ aResult.Append(counter);
+ aResult.Append(mark);
+ }
+ aResult.Append(suffix);
+}
+
+#define MIN_BULLET_SIZE 1
+
+void
+nsBulletFrame::AppendSpacingToPadding(nsFontMetrics* aFontMetrics,
+ LogicalMargin* aPadding)
+{
+ aPadding->IEnd(GetWritingMode()) += aFontMetrics->EmHeight() / 2;
+}
+
+void
+nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
+ nsRenderingContext *aRenderingContext,
+ ReflowOutput& aMetrics,
+ float aFontSizeInflation,
+ LogicalMargin* aPadding)
+{
+ // Reset our padding. If we need it, we'll set it below.
+ WritingMode wm = GetWritingMode();
+ aPadding->SizeTo(wm, 0, 0, 0, 0);
+ LogicalSize finalSize(wm);
+
+ const nsStyleList* myList = StyleList();
+ nscoord ascent;
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation);
+
+ RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
+
+ if (myList->GetListStyleImage() && mImageRequest) {
+ uint32_t status;
+ mImageRequest->GetImageStatus(&status);
+ if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
+ !(status & imgIRequest::STATUS_ERROR)) {
+ // auto size the image
+ finalSize.ISize(wm) = mIntrinsicSize.ISize(wm);
+ aMetrics.SetBlockStartAscent(finalSize.BSize(wm) =
+ mIntrinsicSize.BSize(wm));
+ aMetrics.SetSize(wm, finalSize);
+
+ AppendSpacingToPadding(fm, aPadding);
+
+ AddStateBits(BULLET_FRAME_IMAGE_LOADING);
+
+ return;
+ }
+ }
+
+ // If we're getting our desired size and don't have an image, reset
+ // mIntrinsicSize to (0,0). Otherwise, if we used to have an image, it
+ // changed, and the new one is coming in, but we're reflowing before it's
+ // fully there, we'll end up with mIntrinsicSize not matching our size, but
+ // won't trigger a reflow in OnStartContainer (because mIntrinsicSize will
+ // match the image size).
+ mIntrinsicSize.SizeTo(wm, 0, 0);
+
+ nscoord bulletSize;
+
+ nsAutoString text;
+ switch (myList->GetCounterStyle()->GetStyle()) {
+ case NS_STYLE_LIST_STYLE_NONE:
+ finalSize.ISize(wm) = finalSize.BSize(wm) = 0;
+ aMetrics.SetBlockStartAscent(0);
+ break;
+
+ case NS_STYLE_LIST_STYLE_DISC:
+ case NS_STYLE_LIST_STYLE_CIRCLE:
+ case NS_STYLE_LIST_STYLE_SQUARE: {
+ ascent = fm->MaxAscent();
+ bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+ NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
+ aPadding->BEnd(wm) = NSToCoordRound(float(ascent) / 8.0f);
+ finalSize.ISize(wm) = finalSize.BSize(wm) = bulletSize;
+ aMetrics.SetBlockStartAscent(bulletSize + aPadding->BEnd(wm));
+ AppendSpacingToPadding(fm, aPadding);
+ break;
+ }
+
+ case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+ case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+ ascent = fm->EmAscent();
+ bulletSize = std::max(
+ nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+ NSToCoordRound(0.75f * ascent));
+ aPadding->BEnd(wm) = NSToCoordRound(0.125f * ascent);
+ finalSize.ISize(wm) = finalSize.BSize(wm) = bulletSize;
+ if (!wm.IsVertical()) {
+ aMetrics.SetBlockStartAscent(bulletSize + aPadding->BEnd(wm));
+ }
+ AppendSpacingToPadding(fm, aPadding);
+ break;
+
+ default:
+ GetListItemText(text);
+ finalSize.BSize(wm) = fm->MaxHeight();
+ finalSize.ISize(wm) =
+ nsLayoutUtils::AppUnitWidthOfStringBidi(text, this, *fm, *aRenderingContext);
+ aMetrics.SetBlockStartAscent(wm.IsLineInverted()
+ ? fm->MaxDescent() : fm->MaxAscent());
+ break;
+ }
+ aMetrics.SetSize(wm, finalSize);
+}
+
+void
+nsBulletFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+
+ float inflation = nsLayoutUtils::FontSizeInflationFor(this);
+ SetFontSizeInflation(inflation);
+
+ // Get the base size
+ GetDesiredSize(aPresContext, aReflowInput.mRenderingContext, aMetrics, inflation,
+ &mPadding);
+
+ // Add in the border and padding; split the top/bottom between the
+ // ascent and descent to make things look nice
+ WritingMode wm = aReflowInput.GetWritingMode();
+ const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding();
+ mPadding.BStart(wm) += NSToCoordRound(bp.BStart(wm) * inflation);
+ mPadding.IEnd(wm) += NSToCoordRound(bp.IEnd(wm) * inflation);
+ mPadding.BEnd(wm) += NSToCoordRound(bp.BEnd(wm) * inflation);
+ mPadding.IStart(wm) += NSToCoordRound(bp.IStart(wm) * inflation);
+
+ WritingMode lineWM = aMetrics.GetWritingMode();
+ LogicalMargin linePadding = mPadding.ConvertTo(lineWM, wm);
+ aMetrics.ISize(lineWM) += linePadding.IStartEnd(lineWM);
+ aMetrics.BSize(lineWM) += linePadding.BStartEnd(lineWM);
+ aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
+ linePadding.BStart(lineWM));
+
+ // XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
+ // overflow their font-boxes. It'll do for now; to fix it for real, we really
+ // should rewrite all the text-handling code here to use gfxTextRun (bug
+ // 397294).
+ aMetrics.SetOverflowAreasToDesiredBounds();
+
+ aStatus = NS_FRAME_COMPLETE;
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+/* virtual */ nscoord
+nsBulletFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ WritingMode wm = GetWritingMode();
+ ReflowOutput reflowOutput(wm);
+ DISPLAY_MIN_WIDTH(this, reflowOutput.ISize(wm));
+ LogicalMargin padding(wm);
+ GetDesiredSize(PresContext(), aRenderingContext, reflowOutput, 1.0f, &padding);
+ reflowOutput.ISize(wm) += padding.IStartEnd(wm);
+ return reflowOutput.ISize(wm);
+}
+
+/* virtual */ nscoord
+nsBulletFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ WritingMode wm = GetWritingMode();
+ ReflowOutput metrics(wm);
+ DISPLAY_PREF_WIDTH(this, metrics.ISize(wm));
+ LogicalMargin padding(wm);
+ GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f, &padding);
+ metrics.ISize(wm) += padding.IStartEnd(wm);
+ return metrics.ISize(wm);
+}
+
+// If a bullet has zero size and is "ignorable" from its styling, we behave
+// as if it doesn't exist, from a line-breaking/isize-computation perspective.
+// Otherwise, we use the default implementation, same as nsFrame.
+static inline bool
+IsIgnoreable(const nsIFrame* aFrame, nscoord aISize)
+{
+ if (aISize != nscoord(0)) {
+ return false;
+ }
+ auto listStyle = aFrame->StyleList();
+ return listStyle->GetCounterStyle()->IsNone() &&
+ !listStyle->GetListStyleImage();
+}
+
+/* virtual */ void
+nsBulletFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlineMinISizeData* aData)
+{
+ nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ this, nsLayoutUtils::MIN_ISIZE);
+ if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) {
+ aData->DefaultAddInlineMinISize(this, isize);
+ }
+}
+
+/* virtual */ void
+nsBulletFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlinePrefISizeData* aData)
+{
+ nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ this, nsLayoutUtils::PREF_ISIZE);
+ if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) {
+ aData->DefaultAddInlinePrefISize(isize);
+ }
+}
+
+NS_IMETHODIMP
+nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
+{
+ if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
+ nsCOMPtr<imgIContainer> image;
+ aRequest->GetImage(getter_AddRefs(image));
+ return OnSizeAvailable(aRequest, image);
+ }
+
+ if (aType == imgINotificationObserver::FRAME_UPDATE) {
+ // The image has changed.
+ // Invalidate the entire content area. Maybe it's not optimal but it's simple and
+ // always correct, and I'll be a stunned mullet if it ever matters for performance
+ InvalidateFrame();
+ }
+
+ if (aType == imgINotificationObserver::IS_ANIMATED) {
+ // Register the image request with the refresh driver now that we know it's
+ // animated.
+ if (aRequest == mImageRequest) {
+ RegisterImageRequest(/* aKnownToBeAnimated = */ true);
+ }
+ }
+
+ if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+ // Unconditionally start decoding for now.
+ // XXX(seth): We eventually want to decide whether to do this based on
+ // visibility. We should get that for free from bug 1091236.
+ nsCOMPtr<imgIContainer> container;
+ aRequest->GetImage(getter_AddRefs(container));
+ if (container) {
+ // Retrieve the intrinsic size of the image.
+ int32_t width = 0;
+ int32_t height = 0;
+ container->GetWidth(&width);
+ container->GetHeight(&height);
+
+ // Request a decode at that size.
+ container->RequestDecodeForSize(IntSize(width, height),
+ imgIContainer::DECODE_FLAGS_DEFAULT);
+ }
+
+ InvalidateFrame();
+ }
+
+ if (aType == imgINotificationObserver::DECODE_COMPLETE) {
+ if (nsIDocument* parent = GetOurCurrentDoc()) {
+ nsCOMPtr<imgIContainer> container;
+ aRequest->GetImage(getter_AddRefs(container));
+ if (container) {
+ container->PropagateUseCounters(parent);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBulletFrame::BlockOnload(imgIRequest* aRequest)
+{
+ if (aRequest != mImageRequest) {
+ return NS_OK;
+ }
+
+ NS_ASSERTION(!mBlockingOnload, "Double BlockOnload for an nsBulletFrame?");
+
+ nsIDocument* doc = GetOurCurrentDoc();
+ if (doc) {
+ mBlockingOnload = true;
+ doc->BlockOnload();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBulletFrame::UnblockOnload(imgIRequest* aRequest)
+{
+ if (aRequest != mImageRequest) {
+ return NS_OK;
+ }
+
+ NS_ASSERTION(!mBlockingOnload, "Double UnblockOnload for an nsBulletFrame?");
+
+ nsIDocument* doc = GetOurCurrentDoc();
+ if (doc) {
+ doc->UnblockOnload(false);
+ }
+ mBlockingOnload = false;
+
+ return NS_OK;
+}
+
+nsIDocument*
+nsBulletFrame::GetOurCurrentDoc() const
+{
+ nsIContent* parentContent = GetParent()->GetContent();
+ return parentContent ? parentContent->GetComposedDoc()
+ : nullptr;
+}
+
+nsresult
+nsBulletFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
+{
+ if (!aImage) return NS_ERROR_INVALID_ARG;
+ if (!aRequest) return NS_ERROR_INVALID_ARG;
+
+ uint32_t status;
+ aRequest->GetImageStatus(&status);
+ if (status & imgIRequest::STATUS_ERROR) {
+ return NS_OK;
+ }
+
+ nscoord w, h;
+ aImage->GetWidth(&w);
+ aImage->GetHeight(&h);
+
+ nsPresContext* presContext = PresContext();
+
+ LogicalSize newsize(GetWritingMode(),
+ nsSize(nsPresContext::CSSPixelsToAppUnits(w),
+ nsPresContext::CSSPixelsToAppUnits(h)));
+
+ if (mIntrinsicSize != newsize) {
+ mIntrinsicSize = newsize;
+
+ // Now that the size is available (or an error occurred), trigger
+ // a reflow of the bullet frame.
+ nsIPresShell *shell = presContext->GetPresShell();
+ if (shell) {
+ shell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+ }
+ }
+
+ // Handle animations
+ aImage->SetAnimationMode(presContext->ImageAnimationMode());
+ // Ensure the animation (if any) is started. Note: There is no
+ // corresponding call to Decrement for this. This Increment will be
+ // 'cleaned up' by the Request when it is destroyed, but only then.
+ aRequest->IncrementAnimationConsumers();
+
+ return NS_OK;
+}
+
+void
+nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
+{
+ if (!aPresContext)
+ return;
+
+ NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
+
+ nsIPresShell *shell = aPresContext->GetPresShell();
+
+ if (!shell)
+ return;
+
+ nsIDocument *doc = shell->GetDocument();
+ if (!doc)
+ return;
+
+ *aLoadGroup = doc->GetDocumentLoadGroup().take();
+}
+
+float
+nsBulletFrame::GetFontSizeInflation() const
+{
+ if (!HasFontSizeInflation()) {
+ return 1.0f;
+ }
+ return Properties().Get(FontSizeInflationProperty());
+}
+
+void
+nsBulletFrame::SetFontSizeInflation(float aInflation)
+{
+ if (aInflation == 1.0f) {
+ if (HasFontSizeInflation()) {
+ RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
+ Properties().Delete(FontSizeInflationProperty());
+ }
+ return;
+ }
+
+ AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
+ Properties().Set(FontSizeInflationProperty(), aInflation);
+}
+
+already_AddRefed<imgIContainer>
+nsBulletFrame::GetImage() const
+{
+ if (mImageRequest && StyleList()->GetListStyleImage()) {
+ nsCOMPtr<imgIContainer> imageCon;
+ mImageRequest->GetImage(getter_AddRefs(imageCon));
+ return imageCon.forget();
+ }
+
+ return nullptr;
+}
+
+nscoord
+nsBulletFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+ nscoord ascent = 0, baselinePadding;
+ if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
+ ascent = BSize(aWritingMode);
+ } else {
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
+ CounterStyle* listStyleType = StyleList()->GetCounterStyle();
+ switch (listStyleType->GetStyle()) {
+ case NS_STYLE_LIST_STYLE_NONE:
+ break;
+
+ case NS_STYLE_LIST_STYLE_DISC:
+ case NS_STYLE_LIST_STYLE_CIRCLE:
+ case NS_STYLE_LIST_STYLE_SQUARE:
+ ascent = fm->MaxAscent();
+ baselinePadding = NSToCoordRound(float(ascent) / 8.0f);
+ ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+ NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
+ ascent += baselinePadding;
+ break;
+
+ case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+ case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+ ascent = fm->EmAscent();
+ baselinePadding = NSToCoordRound(0.125f * ascent);
+ ascent = std::max(
+ nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+ NSToCoordRound(0.75f * ascent));
+ ascent += baselinePadding;
+ break;
+
+ default:
+ ascent = fm->MaxAscent();
+ break;
+ }
+ }
+ return ascent +
+ GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
+}
+
+void
+nsBulletFrame::GetSpokenText(nsAString& aText)
+{
+ CounterStyle* style = StyleList()->GetCounterStyle();
+ bool isBullet;
+ style->GetSpokenCounterText(mOrdinal, GetWritingMode(), aText, isBullet);
+ if (isBullet) {
+ if (!style->IsNone()) {
+ aText.Append(' ');
+ }
+ } else {
+ nsAutoString prefix, suffix;
+ style->GetPrefix(prefix);
+ style->GetSuffix(suffix);
+ aText = prefix + aText + suffix;
+ }
+}
+
+void
+nsBulletFrame::RegisterImageRequest(bool aKnownToBeAnimated)
+{
+ if (mImageRequest) {
+ // mRequestRegistered is a bitfield; unpack it temporarily so we can take
+ // the address.
+ bool isRequestRegistered = mRequestRegistered;
+
+ if (aKnownToBeAnimated) {
+ nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
+ &isRequestRegistered);
+ } else {
+ nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
+ mImageRequest,
+ &isRequestRegistered);
+ }
+
+ isRequestRegistered = mRequestRegistered;
+ }
+}
+
+
+void
+nsBulletFrame::DeregisterAndCancelImageRequest()
+{
+ if (mImageRequest) {
+ // mRequestRegistered is a bitfield; unpack it temporarily so we can take
+ // the address.
+ bool isRequestRegistered = mRequestRegistered;
+
+ // Deregister our image request from the refresh driver.
+ nsLayoutUtils::DeregisterImageRequest(PresContext(),
+ mImageRequest,
+ &isRequestRegistered);
+
+ isRequestRegistered = mRequestRegistered;
+
+ // Unblock onload if we blocked it.
+ if (mBlockingOnload) {
+ nsIDocument* doc = GetOurCurrentDoc();
+ if (doc) {
+ doc->UnblockOnload(false);
+ }
+ mBlockingOnload = false;
+ }
+
+ // Cancel the image request and forget about it.
+ mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
+ mImageRequest = nullptr;
+ }
+}
+
+
+
+
+
+
+NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
+
+nsBulletListener::nsBulletListener() :
+ mFrame(nullptr)
+{
+}
+
+nsBulletListener::~nsBulletListener()
+{
+}
+
+NS_IMETHODIMP
+nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
+{
+ if (!mFrame) {
+ return NS_ERROR_FAILURE;
+ }
+ return mFrame->Notify(aRequest, aType, aData);
+}
+
+NS_IMETHODIMP
+nsBulletListener::BlockOnload(imgIRequest* aRequest)
+{
+ if (!mFrame) {
+ return NS_ERROR_FAILURE;
+ }
+ return mFrame->BlockOnload(aRequest);
+}
+
+NS_IMETHODIMP
+nsBulletListener::UnblockOnload(imgIRequest* aRequest)
+{
+ if (!mFrame) {
+ return NS_ERROR_FAILURE;
+ }
+ return mFrame->UnblockOnload(aRequest);
+}
diff --git a/layout/generic/nsBulletFrame.h b/layout/generic/nsBulletFrame.h
new file mode 100644
index 000000000..e35ed0923
--- /dev/null
+++ b/layout/generic/nsBulletFrame.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for list-item bullets */
+
+#ifndef nsBulletFrame_h___
+#define nsBulletFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsFrame.h"
+
+#include "imgIContainer.h"
+#include "imgINotificationObserver.h"
+#include "imgIOnloadBlocker.h"
+
+class imgIContainer;
+class imgRequestProxy;
+
+class nsBulletFrame;
+
+class nsBulletListener final : public imgINotificationObserver,
+ public imgIOnloadBlocker
+{
+public:
+ nsBulletListener();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_IMGINOTIFICATIONOBSERVER
+ NS_DECL_IMGIONLOADBLOCKER
+
+ void SetFrame(nsBulletFrame *frame) { mFrame = frame; }
+
+private:
+ virtual ~nsBulletListener();
+
+ nsBulletFrame *mFrame;
+};
+
+/**
+ * A simple class that manages the layout and rendering of html bullets.
+ * This class also supports the CSS list-style properties.
+ */
+class nsBulletFrame final : public nsFrame {
+ typedef mozilla::image::DrawResult DrawResult;
+
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+#ifdef DEBUG
+ NS_DECL_QUERYFRAME_TARGET(nsBulletFrame)
+ NS_DECL_QUERYFRAME
+#endif
+
+ explicit nsBulletFrame(nsStyleContext* aContext)
+ : nsFrame(aContext)
+ , mPadding(GetWritingMode())
+ , mIntrinsicSize(GetWritingMode())
+ , mRequestRegistered(false)
+ , mBlockingOnload(false)
+ { }
+ virtual ~nsBulletFrame();
+
+ NS_IMETHOD Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData);
+ NS_IMETHOD BlockOnload(imgIRequest* aRequest);
+ NS_IMETHOD UnblockOnload(imgIRequest* aRequest);
+
+ // nsIFrame
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+ virtual nsIAtom* GetType() const override;
+ virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ void AddInlineMinISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlineMinISizeData* aData) override;
+ void AddInlinePrefISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlinePrefISizeData* aData) override;
+
+ // nsBulletFrame
+ int32_t SetListItemOrdinal(int32_t aNextOrdinal, bool* aChanged,
+ int32_t aIncrement);
+
+ /* get list item text, with prefix & suffix */
+ void GetListItemText(nsAString& aResult);
+
+ void GetSpokenText(nsAString& aText);
+
+ DrawResult PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
+ const nsRect& aDirtyRect, uint32_t aFlags,
+ bool aDisableSubpixelAA);
+
+ virtual bool IsEmpty() override;
+ virtual bool IsSelfEmpty() override;
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+
+ float GetFontSizeInflation() const;
+ bool HasFontSizeInflation() const {
+ return (GetStateBits() & BULLET_FRAME_HAS_FONT_INFLATION) != 0;
+ }
+ void SetFontSizeInflation(float aInflation);
+
+ int32_t GetOrdinal() { return mOrdinal; }
+
+ already_AddRefed<imgIContainer> GetImage() const;
+
+protected:
+ nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
+
+ void AppendSpacingToPadding(nsFontMetrics* aFontMetrics,
+ mozilla::LogicalMargin* aPadding);
+ void GetDesiredSize(nsPresContext* aPresContext,
+ nsRenderingContext *aRenderingContext,
+ ReflowOutput& aMetrics,
+ float aFontSizeInflation,
+ mozilla::LogicalMargin* aPadding);
+
+ void GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup);
+ nsIDocument* GetOurCurrentDoc() const;
+
+ mozilla::LogicalMargin mPadding;
+ RefPtr<imgRequestProxy> mImageRequest;
+ RefPtr<nsBulletListener> mListener;
+
+ mozilla::LogicalSize mIntrinsicSize;
+ int32_t mOrdinal;
+
+private:
+ void RegisterImageRequest(bool aKnownToBeAnimated);
+ void DeregisterAndCancelImageRequest();
+
+ // This is a boolean flag indicating whether or not the current image request
+ // has been registered with the refresh driver.
+ bool mRequestRegistered : 1;
+
+ // Whether we're currently blocking onload.
+ bool mBlockingOnload : 1;
+};
+
+#endif /* nsBulletFrame_h___ */
diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp
new file mode 100644
index 000000000..70a2117cf
--- /dev/null
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -0,0 +1,744 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object that goes directly inside the document's scrollbars */
+
+#include "nsCanvasFrame.h"
+
+#include "AccessibleCaretEventHub.h"
+#include "gfxUtils.h"
+#include "nsContainerFrame.h"
+#include "nsCSSRendering.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+#include "nsRenderingContext.h"
+#include "nsGkAtoms.h"
+#include "nsPresShell.h"
+#include "nsIPresShell.h"
+#include "nsDisplayList.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsFrameManager.h"
+#include "gfxPlatform.h"
+#include "nsPrintfCString.h"
+#include "mozilla/dom/AnonymousContent.h"
+// for focus
+#include "nsIScrollableFrame.h"
+#ifdef DEBUG_CANVAS_FOCUS
+#include "nsIDocShell.h"
+#endif
+
+//#define DEBUG_CANVAS_FOCUS
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layout;
+using namespace mozilla::gfx;
+
+nsCanvasFrame*
+NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsCanvasFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
+
+NS_QUERYFRAME_HEAD(nsCanvasFrame)
+ NS_QUERYFRAME_ENTRY(nsCanvasFrame)
+ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+void
+nsCanvasFrame::ShowCustomContentContainer()
+{
+ if (mCustomContentContainer) {
+ mCustomContentContainer->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
+ }
+}
+
+void
+nsCanvasFrame::HideCustomContentContainer()
+{
+ if (mCustomContentContainer) {
+ mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
+ NS_LITERAL_STRING("true"),
+ true);
+ }
+}
+
+nsresult
+nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+ if (!mContent) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
+ nsresult rv = NS_OK;
+
+ // Create the custom content container.
+ mCustomContentContainer = doc->CreateHTMLElement(nsGkAtoms::div);
+#ifdef DEBUG
+ // We restyle our mCustomContentContainer, even though it's root anonymous
+ // content. Normally that's not OK because the frame constructor doesn't know
+ // how to order the frame tree in such cases, but we make this work for this
+ // particular case, so it's OK.
+ mCustomContentContainer->SetProperty(nsGkAtoms::restylableAnonymousNode,
+ reinterpret_cast<void*>(true));
+#endif // DEBUG
+
+ aElements.AppendElement(mCustomContentContainer);
+
+ // XXX add :moz-native-anonymous or will that be automatically set?
+ rv = mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+ NS_LITERAL_STRING("moz-custom-content-container"),
+ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Append all existing AnonymousContent nodes stored at document level if any.
+ size_t len = doc->GetAnonymousContents().Length();
+ for (size_t i = 0; i < len; ++i) {
+ nsCOMPtr<Element> node = doc->GetAnonymousContents()[i]->GetContentNode();
+ mCustomContentContainer->AppendChildTo(node->AsContent(), true);
+ }
+
+ // Only create a frame for mCustomContentContainer if it has some children.
+ if (len == 0) {
+ HideCustomContentContainer();
+ }
+
+ RefPtr<AccessibleCaretEventHub> eventHub =
+ PresContext()->GetPresShell()->GetAccessibleCaretEventHub();
+ if (eventHub) {
+ // AccessibleCaret will insert anonymous caret elements.
+ eventHub->Init();
+ }
+
+ return NS_OK;
+}
+
+void
+nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter)
+{
+ aElements.AppendElement(mCustomContentContainer);
+}
+
+void
+nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ nsIScrollableFrame* sf =
+ PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
+ if (sf) {
+ sf->RemoveScrollPositionListener(this);
+ }
+
+ // Elements inserted in the custom content container have the same lifetime as
+ // the document, so before destroying the container, make sure to keep a clone
+ // of each of them at document level so they can be re-appended on reframe.
+ if (mCustomContentContainer) {
+ nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
+ ErrorResult rv;
+
+ nsTArray<RefPtr<mozilla::dom::AnonymousContent>>& docAnonContents =
+ doc->GetAnonymousContents();
+ for (size_t i = 0, len = docAnonContents.Length(); i < len; ++i) {
+ AnonymousContent* content = docAnonContents[i];
+ nsCOMPtr<nsINode> clonedElement = content->GetContentNode()->CloneNode(true, rv);
+ content->SetContentNode(clonedElement->AsElement());
+ }
+ }
+ nsContentUtils::DestroyAnonymousContent(&mCustomContentContainer);
+
+ nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+void
+nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY)
+{
+ if (mDoPaintFocus) {
+ mDoPaintFocus = false;
+ PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree();
+ }
+}
+
+NS_IMETHODIMP
+nsCanvasFrame::SetHasFocus(bool aHasFocus)
+{
+ if (mDoPaintFocus != aHasFocus) {
+ mDoPaintFocus = aHasFocus;
+ PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree();
+
+ if (!mAddedScrollPositionListener) {
+ nsIScrollableFrame* sf =
+ PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
+ if (sf) {
+ sf->AddScrollPositionListener(this);
+ mAddedScrollPositionListener = true;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+#ifdef DEBUG
+void
+nsCanvasFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ NS_ASSERTION(aListID != kPrincipalList ||
+ aChildList.IsEmpty() || aChildList.OnlyChild(),
+ "Primary child list can have at most one frame in it");
+ nsContainerFrame::SetInitialChildList(aListID, aChildList);
+}
+
+void
+nsCanvasFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
+ if (!mFrames.IsEmpty()) {
+ for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
+ // We only allow native anonymous child frames to be in principal child
+ // list in canvas frame.
+ MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
+ "invalid child list");
+ }
+ }
+ nsFrame::VerifyDirtyBitSet(aFrameList);
+ nsContainerFrame::AppendFrames(aListID, aFrameList);
+}
+
+void
+nsCanvasFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ // Because we only support a single child frame inserting is the same
+ // as appending
+ MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
+ AppendFrames(aListID, aFrameList);
+}
+
+void
+nsCanvasFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
+ nsContainerFrame::RemoveFrame(aListID, aOldFrame);
+}
+#endif
+
+nsRect nsCanvasFrame::CanvasArea() const
+{
+ // Not clear which overflow rect we want here, but it probably doesn't
+ // matter.
+ nsRect result(GetVisualOverflowRect());
+
+ nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
+ if (scrollableFrame) {
+ nsRect portRect = scrollableFrame->GetScrollPortRect();
+ result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size()));
+ }
+ return result;
+}
+
+void
+nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+ nsPoint offset = ToReferenceFrame();
+ nsRect bgClipRect = frame->CanvasArea() + offset;
+ if (NS_GET_A(mColor) > 0) {
+ DrawTarget* drawTarget = aCtx->GetDrawTarget();
+ int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+ Rect devPxRect =
+ NSRectToSnappedRect(bgClipRect, appUnitsPerDevPixel, *drawTarget);
+ drawTarget->FillRect(devPxRect, ColorPattern(ToDeviceColor(mColor)));
+ }
+}
+
+#ifdef MOZ_DUMP_PAINTING
+void
+nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
+{
+ aStream << " (rgba "
+ << (int)NS_GET_R(mColor) << ","
+ << (int)NS_GET_G(mColor) << ","
+ << (int)NS_GET_B(mColor) << ","
+ << (int)NS_GET_A(mColor) << ")";
+}
+#endif
+
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
+static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource)
+{
+ RefPtr<SourceSurface> source = aSource->Snapshot();
+ aDest->DrawSurface(source,
+ Rect(aRect.x, aRect.y, aRect.width, aRect.height),
+ Rect(0, 0, aRect.width, aRect.height));
+}
+#endif
+
+void
+nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+ nsPoint offset = ToReferenceFrame();
+ nsRect bgClipRect = frame->CanvasArea() + offset;
+
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
+ RefPtr<gfxContext> dest = aCtx->ThebesContext();
+ gfxRect destRect;
+ if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) &&
+ aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() &&
+ !dest->CurrentMatrix().HasNonIntegerTranslation()) {
+ // Snap image rectangle to nearest pixel boundaries. This is the right way
+ // to snap for this context, because we checked HasNonIntegerTranslation
+ // above.
+ destRect.Round();
+ RefPtr<DrawTarget> dt =
+ Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT());
+ DrawTarget* destDT = dest->GetDrawTarget();
+ if (dt) {
+ BlitSurface(destDT, destRect, dt);
+ return;
+ }
+
+ dt = destDT->CreateSimilarDrawTarget(IntSize::Ceil(destRect.width,
+ destRect.height),
+ SurfaceFormat::B8G8R8A8);
+ if (dt && dt->IsValid()) {
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(ctx); // already checked draw target above
+ ctx->SetMatrix(ctx->CurrentMatrix().Translate(-destRect.x, -destRect.y));
+ nsRenderingContext context(ctx);
+ PaintInternal(aBuilder, &context, bgClipRect, &bgClipRect);
+ BlitSurface(dest->GetDrawTarget(), destRect, dt);
+ frame->Properties().Set(nsIFrame::CachedBackgroundImageDT(),
+ dt.forget().take());
+ return;
+ }
+ }
+#endif
+ PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
+}
+
+bool
+nsDisplayCanvasBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
+ const nsRect& aClipRect,
+ gfxRect* aDestRect)
+{
+ if (!mBackgroundStyle)
+ return false;
+
+ if (mBackgroundStyle->mImage.mLayers.Length() != 1)
+ return false;
+
+
+ nsPresContext* presContext = mFrame->PresContext();
+ uint32_t flags = aBuilder->GetBackgroundPaintFlags();
+ nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
+ const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
+
+ if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED)
+ return false;
+
+ nsBackgroundLayerState state =
+ nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags,
+ borderArea, aClipRect, layer);
+
+
+ // We only care about images here, not gradients.
+ if (!mIsRasterImage)
+ return false;
+
+ int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+ *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
+
+ return true;
+}
+
+
+void
+nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+ nsPoint offset = ToReferenceFrame();
+ nsRect bgClipRect = frame->CanvasArea() + offset;
+
+ PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
+}
+
+/**
+ * A display item to paint the focus ring for the document.
+ *
+ * The only reason this can't use nsDisplayGeneric is overriding GetBounds.
+ */
+class nsDisplayCanvasFocus : public nsDisplayItem {
+public:
+ nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame *aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ {
+ MOZ_COUNT_CTOR(nsDisplayCanvasFocus);
+ }
+ virtual ~nsDisplayCanvasFocus() {
+ MOZ_COUNT_DTOR(nsDisplayCanvasFocus);
+ }
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override
+ {
+ *aSnap = false;
+ // This is an overestimate, but that's not a problem.
+ nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+ return frame->CanvasArea() + ToReferenceFrame();
+ }
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override
+ {
+ nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+ frame->PaintFocus(aCtx->GetDrawTarget(), ToReferenceFrame());
+ }
+
+ NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS)
+};
+
+void
+nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (GetPrevInFlow()) {
+ DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
+ }
+
+ // Force a background to be shown. We may have a background propagated to us,
+ // in which case StyleBackground wouldn't have the right background
+ // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us
+ // a background.
+ // We don't have any border or outline, and our background draws over
+ // the overflow area, so just add nsDisplayCanvasBackground instead of
+ // calling DisplayBorderBackgroundOutline.
+ if (IsVisibleForPainting(aBuilder)) {
+ nsStyleContext* bgSC;
+ const nsStyleBackground* bg = nullptr;
+ bool isThemed = IsThemed();
+ if (!isThemed && nsCSSRendering::FindBackground(this, &bgSC)) {
+ bg = bgSC->StyleBackground();
+ }
+ aLists.BorderBackground()->AppendNewToTop(
+ new (aBuilder) nsDisplayCanvasBackgroundColor(aBuilder, this));
+
+ if (isThemed) {
+ aLists.BorderBackground()->AppendNewToTop(
+ new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this));
+ return;
+ }
+
+ if (!bg) {
+ return;
+ }
+
+ const DisplayItemScrollClip* scrollClip =
+ aBuilder->ClipState().GetCurrentInnermostScrollClip();
+
+ bool needBlendContainer = false;
+
+ // Create separate items for each background layer.
+ const nsStyleImageLayers& layers = bg->mImage;
+ NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
+ if (layers.mLayers[i].mImage.IsEmpty()) {
+ continue;
+ }
+ if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
+ needBlendContainer = true;
+ }
+
+ nsDisplayList thisItemList;
+ nsDisplayCanvasBackgroundImage* bgItem =
+ new (aBuilder) nsDisplayCanvasBackgroundImage(aBuilder, this, i, bg);
+ if (bgItem->ShouldFixToViewport(aBuilder)) {
+ thisItemList.AppendNewToTop(
+ nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, this, bgItem, i));
+ } else {
+ thisItemList.AppendNewToTop(bgItem);
+ }
+
+ if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
+ thisItemList.AppendNewToTop(
+ new (aBuilder) nsDisplayBlendMode(aBuilder, this, &thisItemList,
+ layers.mLayers[i].mBlendMode,
+ scrollClip, i + 1));
+ }
+ aLists.BorderBackground()->AppendToTop(&thisItemList);
+ }
+
+ if (needBlendContainer) {
+ aLists.BorderBackground()->AppendNewToTop(
+ nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, this,
+ aLists.BorderBackground(),
+ scrollClip));
+ }
+ }
+
+ for (nsIFrame* kid : PrincipalChildList()) {
+ // Put our child into its own pseudo-stack.
+ BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
+ }
+
+#ifdef DEBUG_CANVAS_FOCUS
+ nsCOMPtr<nsIContent> focusContent;
+ aPresContext->EventStateManager()->
+ GetFocusedContent(getter_AddRefs(focusContent));
+
+ bool hasFocus = false;
+ nsCOMPtr<nsISupports> container;
+ aPresContext->GetContainer(getter_AddRefs(container));
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
+ if (docShell) {
+ docShell->GetHasFocus(&hasFocus);
+ printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this,
+ mRect.x, mRect.y, mRect.width, mRect.height,
+ aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
+ }
+ printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell.get(), hasFocus?"Y":"N",
+ focusContent.get(), mDoPaintFocus?"Y":"N");
+#endif
+
+ if (!mDoPaintFocus)
+ return;
+ // Only paint the focus if we're visible
+ if (!StyleVisibility()->IsVisible())
+ return;
+
+ aLists.Outlines()->AppendNewToTop(new (aBuilder)
+ nsDisplayCanvasFocus(aBuilder, this));
+}
+
+void
+nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt)
+{
+ nsRect focusRect(aPt, GetSize());
+
+ nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
+ if (scrollableFrame) {
+ nsRect portRect = scrollableFrame->GetScrollPortRect();
+ focusRect.width = portRect.width;
+ focusRect.height = portRect.height;
+ focusRect.MoveBy(scrollableFrame->GetScrollPosition());
+ }
+
+ // XXX use the root frame foreground color, but should we find BODY frame
+ // for HTML documents?
+ nsIFrame* root = mFrames.FirstChild();
+ const nsStyleColor* color = root ? root->StyleColor() : StyleColor();
+ if (!color) {
+ NS_ERROR("current color cannot be found");
+ return;
+ }
+
+ nsCSSRendering::PaintFocus(PresContext(), aDrawTarget,
+ focusRect, color->mColor);
+}
+
+/* virtual */ nscoord
+nsCanvasFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_MIN_WIDTH(this, result);
+ if (mFrames.IsEmpty())
+ result = 0;
+ else
+ result = mFrames.FirstChild()->GetMinISize(aRenderingContext);
+ return result;
+}
+
+/* virtual */ nscoord
+nsCanvasFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_PREF_WIDTH(this, result);
+ if (mFrames.IsEmpty())
+ result = 0;
+ else
+ result = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
+ return result;
+}
+
+void
+nsCanvasFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
+
+ // Initialize OUT parameter
+ aStatus = NS_FRAME_COMPLETE;
+
+ nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>
+ (GetPrevInFlow());
+ if (prevCanvasFrame) {
+ AutoFrameListPtr overflow(aPresContext,
+ prevCanvasFrame->StealOverflowFrames());
+ if (overflow) {
+ NS_ASSERTION(overflow->OnlyChild(),
+ "must have doc root as canvas frame's only child");
+ nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this);
+ // Prepend overflow to the our child list. There may already be
+ // children placeholders for fixed-pos elements, which don't get
+ // reflowed but must not be lost until the canvas frame is destroyed.
+ mFrames.InsertFrames(this, nullptr, *overflow);
+ }
+ }
+
+ // Set our size up front, since some parts of reflow depend on it
+ // being already set. Note that the computed height may be
+ // unconstrained; that's ok. Consumers should watch out for that.
+ SetSize(nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()));
+
+ // Reflow our one and only normal child frame. It's either the root
+ // element's frame or a placeholder for that frame, if the root element
+ // is abs-pos or fixed-pos. We may have additional children which
+ // are placeholders for continuations of fixed-pos content, but those
+ // don't need to be reflowed. The normal child is always comes before
+ // the fixed-pos placeholders, because we insert it at the start
+ // of the child list, above.
+ ReflowOutput kidDesiredSize(aReflowInput);
+ if (mFrames.IsEmpty()) {
+ // We have no child frame, so return an empty size
+ aDesiredSize.Width() = aDesiredSize.Height() = 0;
+ } else {
+ nsIFrame* kidFrame = mFrames.FirstChild();
+ bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0;
+
+ ReflowInput
+ kidReflowInput(aPresContext, aReflowInput, kidFrame,
+ aReflowInput.AvailableSize(kidFrame->GetWritingMode()));
+
+ if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
+ (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
+ // Tell our kid it's being block-dir resized too. Bit of a
+ // hack for framesets.
+ kidReflowInput.SetBResize(true);
+ }
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ WritingMode kidWM = kidReflowInput.GetWritingMode();
+ nsSize containerSize = aReflowInput.ComputedPhysicalSize();
+
+ LogicalMargin margin = kidReflowInput.ComputedLogicalMargin();
+ LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
+
+ kidReflowInput.ApplyRelativePositioning(&kidPt, containerSize);
+
+ // Reflow the frame
+ ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput,
+ kidWM, kidPt, containerSize, 0, aStatus);
+
+ // Complete the reflow and position and size the child frame
+ FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput,
+ kidWM, kidPt, containerSize, 0);
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
+ nsIFrame* nextFrame = kidFrame->GetNextInFlow();
+ NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
+ "If it's incomplete and has no nif yet, it must flag a nif reflow.");
+ if (!nextFrame) {
+ nextFrame = aPresContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(aPresContext, kidFrame, this);
+ SetOverflowFrames(nsFrameList(nextFrame, nextFrame));
+ // Root overflow containers will be normal children of
+ // the canvas frame, but that's ok because there
+ // aren't any other frames we need to isolate them from
+ // during reflow.
+ }
+ if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
+ nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+ }
+
+ // If the child frame was just inserted, then we're responsible for making sure
+ // it repaints
+ if (kidDirty) {
+ // But we have a new child, which will affect our background, so
+ // invalidate our whole rect.
+ // Note: Even though we request to be sized to our child's size, our
+ // scroll frame ensures that we are always the size of the viewport.
+ // Also note: GetPosition() on a CanvasFrame is always going to return
+ // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect()
+ // could also include overflow to our top and left (out of the viewport)
+ // which doesn't need to be painted.
+ nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame();
+ viewport->InvalidateFrame();
+ }
+
+ // Return our desired size. Normally it's what we're told, but
+ // sometimes we can be given an unconstrained height (when a window
+ // is sizing-to-content), and we should compute our desired height.
+ LogicalSize finalSize(wm);
+ finalSize.ISize(wm) = aReflowInput.ComputedISize();
+ if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
+ finalSize.BSize(wm) = kidFrame->GetLogicalSize(wm).BSize(wm) +
+ kidReflowInput.ComputedLogicalMargin().BStartEnd(wm);
+ } else {
+ finalSize.BSize(wm) = aReflowInput.ComputedBSize();
+ }
+
+ aDesiredSize.SetSize(wm, finalSize);
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ aDesiredSize.mOverflowAreas.UnionWith(
+ kidDesiredSize.mOverflowAreas + kidFrame->GetPosition());
+ }
+
+ if (prevCanvasFrame) {
+ ReflowOverflowContainerChildren(aPresContext, aReflowInput,
+ aDesiredSize.mOverflowAreas, 0,
+ aStatus);
+ }
+
+ FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
+
+ NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nsIAtom*
+nsCanvasFrame::GetType() const
+{
+ return nsGkAtoms::canvasFrame;
+}
+
+nsresult
+nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent,
+ nsIContent** aContent)
+{
+ NS_ENSURE_ARG_POINTER(aContent);
+ nsresult rv = nsFrame::GetContentForEvent(aEvent,
+ aContent);
+ if (NS_FAILED(rv) || !*aContent) {
+ nsIFrame* kid = mFrames.FirstChild();
+ if (kid) {
+ rv = kid->GetContentForEvent(aEvent,
+ aContent);
+ }
+ }
+
+ return rv;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsCanvasFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult);
+}
+#endif
diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h
new file mode 100644
index 000000000..236ffb9c7
--- /dev/null
+++ b/layout/generic/nsCanvasFrame.h
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object that goes directly inside the document's scrollbars */
+
+#ifndef nsCanvasFrame_h___
+#define nsCanvasFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "nsContainerFrame.h"
+#include "nsIScrollPositionListener.h"
+#include "nsDisplayList.h"
+#include "nsIAnonymousContentCreator.h"
+
+class nsPresContext;
+class nsRenderingContext;
+
+/**
+ * Root frame class.
+ *
+ * The root frame is the parent frame for the document element's frame.
+ * It only supports having a single child frame which must be an area
+ * frame.
+ * @note nsCanvasFrame keeps overflow container continuations of its child
+ * frame in the main child list.
+ */
+class nsCanvasFrame final : public nsContainerFrame,
+ public nsIScrollPositionListener,
+ public nsIAnonymousContentCreator
+{
+public:
+ explicit nsCanvasFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext),
+ mDoPaintFocus(false),
+ mAddedScrollPositionListener(false) {}
+
+ NS_DECL_QUERYFRAME_TARGET(nsCanvasFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ virtual mozilla::WritingMode GetWritingMode() const override
+ {
+ nsIContent* rootElem = GetContent();
+ if (rootElem) {
+ nsIFrame* rootElemFrame = rootElem->GetPrimaryFrame();
+ if (rootElemFrame) {
+ return rootElemFrame->GetWritingMode();
+ }
+ }
+ return nsIFrame::GetWritingMode();
+ }
+
+#ifdef DEBUG
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+#endif
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eCanContainOverflowContainers));
+ }
+
+ // nsIAnonymousContentCreator
+ virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
+ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter) override;
+
+ mozilla::dom::Element* GetCustomContentContainer() const
+ {
+ return mCustomContentContainer;
+ }
+
+ /**
+ * Unhide the CustomContentContainer. This call only has an effect if
+ * mCustomContentContainer is non-null.
+ */
+ void ShowCustomContentContainer();
+
+ /**
+ * Hide the CustomContentContainer. This call only has an effect if
+ * mCustomContentContainer is non-null.
+ */
+ void HideCustomContentContainer();
+
+ /** SetHasFocus tells the CanvasFrame to draw with focus ring
+ * @param aHasFocus true to show focus ring, false to hide it
+ */
+ NS_IMETHOD SetHasFocus(bool aHasFocus);
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ void PaintFocus(mozilla::gfx::DrawTarget* aRenderingContext, nsPoint aPt);
+
+ // nsIScrollPositionListener
+ virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) override;
+ virtual void ScrollPositionDidChange(nscoord aX, nscoord aY) override {}
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::canvasFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+ virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
+ nsIContent** aContent) override;
+
+ nsRect CanvasArea() const;
+
+protected:
+ // Data members
+ bool mDoPaintFocus;
+ bool mAddedScrollPositionListener;
+
+ nsCOMPtr<mozilla::dom::Element> mCustomContentContainer;
+};
+
+/**
+ * Override nsDisplayBackground methods so that we pass aBGClipRect to
+ * PaintBackground, covering the whole overflow area.
+ * We can also paint an "extra background color" behind the normal
+ * background.
+ */
+class nsDisplayCanvasBackgroundColor : public nsDisplaySolidColorBase {
+public:
+ nsDisplayCanvasBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame)
+ : nsDisplaySolidColorBase(aBuilder, aFrame, NS_RGBA(0,0,0,0))
+ {
+ }
+
+ virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
+ nsRegion* aVisibleRegion) override
+ {
+ return NS_GET_A(mColor) > 0;
+ }
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
+ {
+ nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+ *aSnap = true;
+ return frame->CanvasArea() + ToReferenceFrame();
+ }
+ virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+ HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override
+ {
+ // We need to override so we don't consider border-radius.
+ aOutFrames->AppendElement(mFrame);
+ }
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+
+ void SetExtraBackgroundColor(nscolor aColor)
+ {
+ mColor = aColor;
+ }
+
+ NS_DISPLAY_DECL_NAME("CanvasBackgroundColor", TYPE_CANVAS_BACKGROUND_COLOR)
+#ifdef MOZ_DUMP_PAINTING
+ virtual void WriteDebugInfo(std::stringstream& aStream) override;
+#endif
+};
+
+class nsDisplayCanvasBackgroundImage : public nsDisplayBackgroundImage {
+public:
+ nsDisplayCanvasBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+ uint32_t aLayer, const nsStyleBackground* aBg)
+ : nsDisplayBackgroundImage(aBuilder, aFrame, aLayer,
+ aFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(aFrame),
+ aBg)
+ {
+ if (ShouldFixToViewport(aBuilder)) {
+ mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(this);
+ }
+ }
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
+
+ virtual void NotifyRenderingChanged() override
+ {
+ mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT());
+ }
+
+ virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) override
+ {
+ // Put background-attachment:fixed canvas background images in their own
+ // compositing layer. Since we know their background painting area can't
+ // change (unless the viewport size itself changes), async scrolling
+ // will work well.
+ return ShouldTreatAsFixed() &&
+ !mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty();
+ }
+
+ // We still need to paint a background color as well as an image for this item,
+ // so we can't support this yet.
+ virtual bool SupportsOptimizingToImage() override { return false; }
+
+ bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
+ const nsRect& aClipRect,
+ gfxRect* aDestRect);
+
+
+ NS_DISPLAY_DECL_NAME("CanvasBackgroundImage", TYPE_CANVAS_BACKGROUND_IMAGE)
+};
+
+class nsDisplayCanvasThemedBackground : public nsDisplayThemedBackground {
+public:
+ nsDisplayCanvasThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+ : nsDisplayThemedBackground(aBuilder, aFrame,
+ aFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(aFrame))
+ {}
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
+
+ NS_DISPLAY_DECL_NAME("CanvasThemedBackground", TYPE_CANVAS_THEMED_BACKGROUND)
+};
+
+#endif /* nsCanvasFrame_h___ */
diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp
new file mode 100644
index 000000000..ad36ba1a8
--- /dev/null
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -0,0 +1,1169 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for css3 multi-column layout */
+
+#include "mozilla/Unused.h"
+#include "nsColumnSetFrame.h"
+#include "nsCSSRendering.h"
+#include "nsDisplayList.h"
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+/**
+ * Tracking issues:
+ *
+ * XXX cursor movement around the top and bottom of colums seems to make the editor
+ * lose the caret.
+ *
+ * XXX should we support CSS columns applied to table elements?
+ */
+nsContainerFrame*
+NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags)
+{
+ nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame(aContext);
+ it->AddStateBits(aStateFlags | NS_BLOCK_MARGIN_ROOT);
+ return it;
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsColumnSetFrame)
+
+nsColumnSetFrame::nsColumnSetFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext), mLastBalanceBSize(NS_INTRINSICSIZE),
+ mLastFrameStatus(NS_FRAME_COMPLETE)
+{
+}
+
+nsIAtom*
+nsColumnSetFrame::GetType() const
+{
+ return nsGkAtoms::columnSetFrame;
+}
+
+static void
+PaintColumnRule(nsIFrame* aFrame, nsRenderingContext* aCtx,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ static_cast<nsColumnSetFrame*>(aFrame)->PaintColumnRule(aCtx, aDirtyRect, aPt);
+}
+
+void
+nsColumnSetFrame::PaintColumnRule(nsRenderingContext* aCtx,
+ const nsRect& aDirtyRect,
+ const nsPoint& aPt)
+{
+ nsIFrame* child = mFrames.FirstChild();
+ if (!child)
+ return; // no columns
+
+ nsIFrame* nextSibling = child->GetNextSibling();
+ if (!nextSibling)
+ return; // 1 column only - this means no gap to draw on
+
+ WritingMode wm = GetWritingMode();
+ bool isVertical = wm.IsVertical();
+ bool isRTL = !wm.IsBidiLTR();
+ const nsStyleColumn* colStyle = StyleColumn();
+
+ uint8_t ruleStyle;
+ // Per spec, inset => ridge and outset => groove
+ if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_INSET)
+ ruleStyle = NS_STYLE_BORDER_STYLE_RIDGE;
+ else if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_OUTSET)
+ ruleStyle = NS_STYLE_BORDER_STYLE_GROOVE;
+ else
+ ruleStyle = colStyle->mColumnRuleStyle;
+
+ nsPresContext* presContext = PresContext();
+ nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
+ if (!ruleWidth)
+ return;
+
+ nscolor ruleColor =
+ GetVisitedDependentColor(eCSSProperty_column_rule_color);
+
+ // In order to re-use a large amount of code, we treat the column rule as a border.
+ // We create a new border style object and fill in all the details of the column rule as
+ // the left border. PaintBorder() does all the rendering for us, so we not
+ // only save an enormous amount of code but we'll support all the line styles that
+ // we support on borders!
+ nsStyleBorder border(presContext);
+ Sides skipSides;
+ if (isVertical) {
+ border.SetBorderWidth(NS_SIDE_TOP, ruleWidth);
+ border.SetBorderStyle(NS_SIDE_TOP, ruleStyle);
+ border.mBorderTopColor = StyleComplexColor::FromColor(ruleColor);
+ skipSides |= mozilla::eSideBitsLeftRight;
+ skipSides |= mozilla::eSideBitsBottom;
+ } else {
+ border.SetBorderWidth(NS_SIDE_LEFT, ruleWidth);
+ border.SetBorderStyle(NS_SIDE_LEFT, ruleStyle);
+ border.mBorderLeftColor = StyleComplexColor::FromColor(ruleColor);
+ skipSides |= mozilla::eSideBitsTopBottom;
+ skipSides |= mozilla::eSideBitsRight;
+ }
+
+ // Get our content rect as an absolute coordinate, not relative to
+ // our parent (which is what the X and Y normally is)
+ nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt;
+ nsSize ruleSize = isVertical ? nsSize(contentRect.width, ruleWidth)
+ : nsSize(ruleWidth, contentRect.height);
+
+ while (nextSibling) {
+ // The frame tree goes RTL in RTL.
+ // The |prevFrame| and |nextFrame| frames here are the visually preceding
+ // (left/above) and following (right/below) frames, not in logical writing-
+ // mode direction.
+ nsIFrame* prevFrame = isRTL ? nextSibling : child;
+ nsIFrame* nextFrame = isRTL ? child : nextSibling;
+
+ // Each child frame's position coordinates is actually relative to this
+ // nsColumnSetFrame.
+ // linePt will be at the top-left edge to paint the line.
+ nsPoint linePt;
+ if (isVertical) {
+ nscoord edgeOfPrev = prevFrame->GetRect().YMost() + aPt.y;
+ nscoord edgeOfNext = nextFrame->GetRect().Y() + aPt.y;
+ linePt = nsPoint(contentRect.x,
+ (edgeOfPrev + edgeOfNext - ruleSize.height) / 2);
+ } else {
+ nscoord edgeOfPrev = prevFrame->GetRect().XMost() + aPt.x;
+ nscoord edgeOfNext = nextFrame->GetRect().X() + aPt.x;
+ linePt = nsPoint((edgeOfPrev + edgeOfNext - ruleSize.width) / 2,
+ contentRect.y);
+ }
+
+ nsRect lineRect(linePt, ruleSize);
+
+ // Assert that we're not drawing a border-image here; if we were, we
+ // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns.
+ MOZ_ASSERT(border.mBorderImageSource.GetType() == eStyleImageType_Null);
+
+ Unused <<
+ nsCSSRendering::PaintBorderWithStyleBorder(presContext, *aCtx, this,
+ aDirtyRect, lineRect, border,
+ StyleContext(),
+ PaintBorderFlags::SYNC_DECODE_IMAGES,
+ skipSides);
+
+ child = nextSibling;
+ nextSibling = nextSibling->GetNextSibling();
+ }
+}
+
+static nscoord
+GetAvailableContentISize(const ReflowInput& aReflowInput)
+{
+ if (aReflowInput.AvailableISize() == NS_INTRINSICSIZE) {
+ return NS_INTRINSICSIZE;
+ }
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ nscoord borderPaddingISize =
+ aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
+ return std::max(0, aReflowInput.AvailableISize() - borderPaddingISize);
+}
+
+nscoord
+nsColumnSetFrame::GetAvailableContentBSize(const ReflowInput& aReflowInput)
+{
+ if (aReflowInput.AvailableBSize() == NS_INTRINSICSIZE) {
+ return NS_INTRINSICSIZE;
+ }
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalMargin bp = aReflowInput.ComputedLogicalBorderPadding();
+ bp.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
+ bp.BEnd(wm) = aReflowInput.ComputedLogicalBorderPadding().BEnd(wm);
+ return std::max(0, aReflowInput.AvailableBSize() - bp.BStartEnd(wm));
+}
+
+static nscoord
+GetColumnGap(nsColumnSetFrame* aFrame,
+ const nsStyleColumn* aColStyle)
+{
+ if (eStyleUnit_Normal == aColStyle->mColumnGap.GetUnit())
+ return aFrame->StyleFont()->mFont.size;
+ if (eStyleUnit_Coord == aColStyle->mColumnGap.GetUnit()) {
+ nscoord colGap = aColStyle->mColumnGap.GetCoordValue();
+ NS_ASSERTION(colGap >= 0, "negative column gap");
+ return colGap;
+ }
+
+ NS_NOTREACHED("Unknown gap type");
+ return 0;
+}
+
+nsColumnSetFrame::ReflowConfig
+nsColumnSetFrame::ChooseColumnStrategy(const ReflowInput& aReflowInput,
+ bool aForceAuto = false,
+ nscoord aFeasibleBSize = NS_INTRINSICSIZE,
+ nscoord aInfeasibleBSize = 0)
+{
+ nscoord knownFeasibleBSize = aFeasibleBSize;
+ nscoord knownInfeasibleBSize = aInfeasibleBSize;
+
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord availContentISize = GetAvailableContentISize(aReflowInput);
+ if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
+ availContentISize = aReflowInput.ComputedISize();
+ }
+
+ nscoord consumedBSize = GetConsumedBSize();
+
+ // The effective computed height is the height of the current continuation
+ // of the column set frame. This should be the same as the computed height
+ // if we have an unconstrained available height.
+ nscoord computedBSize = GetEffectiveComputedBSize(aReflowInput,
+ consumedBSize);
+ nscoord colBSize = GetAvailableContentBSize(aReflowInput);
+
+ if (aReflowInput.ComputedBSize() != NS_INTRINSICSIZE) {
+ colBSize = aReflowInput.ComputedBSize();
+ } else if (aReflowInput.ComputedMaxBSize() != NS_INTRINSICSIZE) {
+ colBSize = std::min(colBSize, aReflowInput.ComputedMaxBSize());
+ }
+
+ nscoord colGap = GetColumnGap(this, colStyle);
+ int32_t numColumns = colStyle->mColumnCount;
+
+ // If column-fill is set to 'balance', then we want to balance the columns.
+ const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE
+ && !aForceAuto;
+ if (isBalancing) {
+ const uint32_t MAX_NESTED_COLUMN_BALANCING = 2;
+ uint32_t cnt = 0;
+ for (const ReflowInput* rs = aReflowInput.mParentReflowInput;
+ rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->mParentReflowInput) {
+ if (rs->mFlags.mIsColumnBalancing) {
+ ++cnt;
+ }
+ }
+ if (cnt == MAX_NESTED_COLUMN_BALANCING) {
+ numColumns = 1;
+ }
+ }
+
+ nscoord colISize;
+ // In vertical writing-mode, "column-width" (inline size) will actually be
+ // physical height, but its CSS name is still column-width.
+ if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
+ colISize = colStyle->mColumnWidth.GetCoordValue();
+ NS_ASSERTION(colISize >= 0, "negative column width");
+ // Reduce column count if necessary to make columns fit in the
+ // available width. Compute max number of columns that fit in
+ // availContentISize, satisfying colGap*(maxColumns - 1) +
+ // colISize*maxColumns <= availContentISize
+ if (availContentISize != NS_INTRINSICSIZE && colGap + colISize > 0
+ && numColumns > 0) {
+ // This expression uses truncated rounding, which is what we
+ // want
+ int32_t maxColumns =
+ std::min(nscoord(nsStyleColumn::kMaxColumnCount),
+ (availContentISize + colGap) / (colGap + colISize));
+ numColumns = std::max(1, std::min(numColumns, maxColumns));
+ }
+ } else if (numColumns > 0 && availContentISize != NS_INTRINSICSIZE) {
+ nscoord iSizeMinusGaps = availContentISize - colGap * (numColumns - 1);
+ colISize = iSizeMinusGaps / numColumns;
+ } else {
+ colISize = NS_INTRINSICSIZE;
+ }
+ // Take care of the situation where there's only one column but it's
+ // still too wide
+ colISize = std::max(1, std::min(colISize, availContentISize));
+
+ nscoord expectedISizeLeftOver = 0;
+
+ if (colISize != NS_INTRINSICSIZE && availContentISize != NS_INTRINSICSIZE) {
+ // distribute leftover space
+
+ // First, determine how many columns will be showing if the column
+ // count is auto
+ if (numColumns <= 0) {
+ // choose so that colGap*(nominalColumnCount - 1) +
+ // colISize*nominalColumnCount is nearly availContentISize
+ // make sure to round down
+ if (colGap + colISize > 0) {
+ numColumns = (availContentISize + colGap) / (colGap + colISize);
+ // The number of columns should never exceed kMaxColumnCount.
+ numColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount),
+ numColumns);
+ }
+ if (numColumns <= 0) {
+ numColumns = 1;
+ }
+ }
+
+ // Compute extra space and divide it among the columns
+ nscoord extraSpace =
+ std::max(0, availContentISize - (colISize * numColumns +
+ colGap * (numColumns - 1)));
+ nscoord extraToColumns = extraSpace / numColumns;
+ colISize += extraToColumns;
+ expectedISizeLeftOver = extraSpace - (extraToColumns * numColumns);
+ }
+
+ if (isBalancing) {
+ if (numColumns <= 0) {
+ // Hmm, auto column count, column width or available width is unknown,
+ // and balancing is required. Let's just use one column then.
+ numColumns = 1;
+ }
+ colBSize = std::min(mLastBalanceBSize, colBSize);
+ } else {
+ // This is the case when the column-fill property is set to 'auto'.
+ // No balancing, so don't limit the column count
+ numColumns = INT32_MAX;
+
+ // XXX_jwir3: If a page's height is set to 0, we could continually
+ // create continuations, resulting in an infinite loop, since
+ // no progress is ever made. This is an issue with the spec
+ // (css3-multicol, css3-page, and css3-break) that is
+ // unresolved as of 27 Feb 2013. For the time being, we set this
+ // to have a minimum of 1 css px. Once a resolution is made
+ // on what minimum to have for a page height, we may need to
+ // change this value to match the appropriate spec(s).
+ colBSize = std::max(colBSize, nsPresContext::CSSPixelsToAppUnits(1));
+ }
+
+#ifdef DEBUG_roc
+ printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colISize=%d,"
+ " expectedISizeLeftOver=%d, colBSize=%d, colGap=%d\n",
+ numColumns, colISize, expectedISizeLeftOver, colBSize, colGap);
+#endif
+ ReflowConfig config = { numColumns, colISize, expectedISizeLeftOver, colGap,
+ colBSize, isBalancing, knownFeasibleBSize,
+ knownInfeasibleBSize, computedBSize, consumedBSize };
+ return config;
+}
+
+bool
+nsColumnSetFrame::ReflowColumns(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus,
+ ReflowConfig& aConfig,
+ bool aLastColumnUnbounded,
+ nsCollapsingMargin* aCarriedOutBEndMargin,
+ ColumnBalanceData& aColData)
+{
+ bool feasible = ReflowChildren(aDesiredSize, aReflowInput,
+ aReflowStatus, aConfig, aLastColumnUnbounded,
+ aCarriedOutBEndMargin, aColData);
+
+ if (aColData.mHasExcessBSize) {
+ aConfig = ChooseColumnStrategy(aReflowInput, true);
+
+ // We need to reflow our children again one last time, otherwise we might
+ // end up with a stale column height for some of our columns, since we
+ // bailed out of balancing.
+ feasible = ReflowChildren(aDesiredSize, aReflowInput, aReflowStatus,
+ aConfig, aLastColumnUnbounded,
+ aCarriedOutBEndMargin, aColData);
+ }
+
+ return feasible;
+}
+
+static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin,
+ WritingMode aWM, const nsSize& aContainerSize)
+{
+ if (aChild->GetLogicalPosition(aWM, aContainerSize) == aOrigin) {
+ return;
+ }
+
+ aChild->SetPosition(aWM, aOrigin, aContainerSize);
+ nsContainerFrame::PlaceFrameView(aChild);
+}
+
+nscoord
+nsColumnSetFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord iSize = 0;
+ DISPLAY_MIN_WIDTH(this, iSize);
+ if (mFrames.FirstChild()) {
+ iSize = mFrames.FirstChild()->GetMinISize(aRenderingContext);
+ }
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord colISize;
+ if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
+ colISize = colStyle->mColumnWidth.GetCoordValue();
+ // As available width reduces to zero, we reduce our number of columns
+ // to one, and don't enforce the column width, so just return the min
+ // of the child's min-width with any specified column width.
+ iSize = std::min(iSize, colISize);
+ } else {
+ NS_ASSERTION(colStyle->mColumnCount > 0,
+ "column-count and column-width can't both be auto");
+ // As available width reduces to zero, we still have mColumnCount columns,
+ // so multiply the child's min-width by the number of columns (n) and
+ // include n-1 column gaps.
+ colISize = iSize;
+ iSize *= colStyle->mColumnCount;
+ nscoord colGap = GetColumnGap(this, colStyle);
+ iSize += colGap * (colStyle->mColumnCount - 1);
+ // The multiplication above can make 'width' negative (integer overflow),
+ // so use std::max to protect against that.
+ iSize = std::max(iSize, colISize);
+ }
+ // XXX count forced column breaks here? Maybe we should return the child's
+ // min-width times the minimum number of columns.
+ return iSize;
+}
+
+nscoord
+nsColumnSetFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ // Our preferred width is our desired column width, if specified, otherwise
+ // the child's preferred width, times the number of columns, plus the width
+ // of any required column gaps
+ // XXX what about forced column breaks here?
+ nscoord result = 0;
+ DISPLAY_PREF_WIDTH(this, result);
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord colGap = GetColumnGap(this, colStyle);
+
+ nscoord colISize;
+ if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
+ colISize = colStyle->mColumnWidth.GetCoordValue();
+ } else if (mFrames.FirstChild()) {
+ colISize = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
+ } else {
+ colISize = 0;
+ }
+
+ int32_t numColumns = colStyle->mColumnCount;
+ if (numColumns <= 0) {
+ // if column-count is auto, assume one column
+ numColumns = 1;
+ }
+
+ nscoord iSize = colISize * numColumns + colGap * (numColumns - 1);
+ // The multiplication above can make 'iSize' negative (integer overflow),
+ // so use std::max to protect against that.
+ result = std::max(iSize, colISize);
+ return result;
+}
+
+bool
+nsColumnSetFrame::ReflowChildren(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ const ReflowConfig& aConfig,
+ bool aUnboundedLastColumn,
+ nsCollapsingMargin* aCarriedOutBEndMargin,
+ ColumnBalanceData& aColData)
+{
+ aColData.Reset();
+ bool allFit = true;
+ WritingMode wm = GetWritingMode();
+ bool isVertical = wm.IsVertical();
+ bool isRTL = !wm.IsBidiLTR();
+ bool shrinkingBSizeOnly = !NS_SUBTREE_DIRTY(this) &&
+ mLastBalanceBSize > aConfig.mColMaxBSize;
+
+#ifdef DEBUG_roc
+ printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n"
+ " mBalanceColCount=%d, mColISize=%d, mColGap=%d\n",
+ mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount,
+ aConfig.mColISize, aConfig.mColGap);
+#endif
+
+ DrainOverflowColumns();
+
+ const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize;
+
+ if (colBSizeChanged) {
+ mLastBalanceBSize = aConfig.mColMaxBSize;
+ // XXX Seems like this could fire if incremental reflow pushed the column set
+ // down so we reflow incrementally with a different available height.
+ // We need a way to do an incremental reflow and be sure availableHeight
+ // changes are taken account of! Right now I think block frames with absolute
+ // children might exit early.
+ //NS_ASSERTION(aKidReason != eReflowReason_Incremental,
+ // "incremental reflow should not have changed the balance height");
+ }
+
+ // get our border and padding
+ LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
+ borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
+
+ nsRect contentRect(0, 0, 0, 0);
+ nsOverflowAreas overflowRects;
+
+ nsIFrame* child = mFrames.FirstChild();
+ LogicalPoint childOrigin(wm, borderPadding.IStart(wm),
+ borderPadding.BStart(wm));
+ // In vertical-rl mode, columns will not be correctly placed if the
+ // reflowInput's ComputedWidth() is UNCONSTRAINED (in which case we'll get
+ // a containerSize.width of zero here). In that case, the column positions
+ // will be adjusted later, after our correct contentSize is known.
+ nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
+
+ // For RTL, since the columns might not fill the frame exactly, we
+ // need to account for the slop. Otherwise we'll waste time moving the
+ // columns by some tiny amount
+
+ // XXX when all of layout is converted to logical coordinates, we
+ // probably won't need to do this hack any more. For now, we
+ // confine it to the legacy horizontal-rl case
+ if (!isVertical && isRTL) {
+ nscoord availISize = aReflowInput.AvailableISize();
+ if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
+ availISize = aReflowInput.ComputedISize();
+ }
+ if (availISize != NS_INTRINSICSIZE) {
+ childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) -
+ availISize;
+#ifdef DEBUG_roc
+ printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm));
+#endif
+ }
+ }
+
+ int columnCount = 0;
+ int contentBEnd = 0;
+ bool reflowNext = false;
+
+ while (child) {
+ // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't
+ // skip if the next column is dirty, because the next column's first line(s)
+ // might be pullable back to this column. We can't skip if it's the last child
+ // because we need to obtain the bottom margin. We can't skip
+ // if this is the last column and we're supposed to assign unbounded
+ // height to it, because that could change the available height from
+ // the last time we reflowed it and we should try to pull all the
+ // content from its next sibling. (Note that it might be the last
+ // column, but not be the last child because the desired number of columns
+ // has changed.)
+ bool skipIncremental = !aReflowInput.ShouldReflowAllKids()
+ && !NS_SUBTREE_DIRTY(child)
+ && child->GetNextSibling()
+ && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1)
+ && !NS_SUBTREE_DIRTY(child->GetNextSibling());
+ // If we need to pull up content from the prev-in-flow then this is not just
+ // a height shrink. The prev in flow will have set the dirty bit.
+ // Check the overflow rect YMost instead of just the child's content height. The child
+ // may have overflowing content that cares about the available height boundary.
+ // (It may also have overflowing content that doesn't care about the available height
+ // boundary, but if so, too bad, this optimization is defeated.)
+ // We want scrollable overflow here since this is a calculation that
+ // affects layout.
+ bool skipResizeBSizeShrink = false;
+ if (shrinkingBSizeOnly) {
+ switch (wm.GetBlockDir()) {
+ case WritingMode::eBlockTB:
+ if (child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxBSize) {
+ skipResizeBSizeShrink = true;
+ }
+ break;
+ case WritingMode::eBlockLR:
+ if (child->GetScrollableOverflowRect().XMost() <= aConfig.mColMaxBSize) {
+ skipResizeBSizeShrink = true;
+ }
+ break;
+ case WritingMode::eBlockRL:
+ // XXX not sure how to handle this, so for now just don't attempt
+ // the optimization
+ break;
+ default:
+ NS_NOTREACHED("unknown block direction");
+ break;
+ }
+ }
+
+ nscoord childContentBEnd = 0;
+ if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) {
+ // This child does not need to be reflowed, but we may need to move it
+ MoveChildTo(child, childOrigin, wm, containerSize);
+
+ // If this is the last frame then make sure we get the right status
+ nsIFrame* kidNext = child->GetNextSibling();
+ if (kidNext) {
+ aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
+ ? NS_FRAME_OVERFLOW_INCOMPLETE
+ : NS_FRAME_NOT_COMPLETE;
+ } else {
+ aStatus = mLastFrameStatus;
+ }
+ childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
+#ifdef DEBUG_roc
+ printf("*** Skipping child #%d %p (incremental %d, resize block-size shrink %d): status = %d\n",
+ columnCount, (void*)child, skipIncremental, skipResizeBSizeShrink, aStatus);
+#endif
+ } else {
+ LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize);
+ if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) {
+ availSize.BSize(wm) = GetAvailableContentBSize(aReflowInput);
+ }
+
+ LogicalSize computedSize = aReflowInput.ComputedSize(wm);
+
+ if (reflowNext)
+ child->AddStateBits(NS_FRAME_IS_DIRTY);
+
+ LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm));
+ ReflowInput kidReflowInput(PresContext(), aReflowInput, child,
+ availSize, &kidCBSize);
+ kidReflowInput.mFlags.mIsTopOfPage = true;
+ kidReflowInput.mFlags.mTableIsSplittable = false;
+ kidReflowInput.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX;
+
+ // We need to reflow any float placeholders, even if our column height
+ // hasn't changed.
+ kidReflowInput.mFlags.mMustReflowPlaceholders = !colBSizeChanged;
+
+#ifdef DEBUG_roc
+ printf("*** Reflowing child #%d %p: availHeight=%d\n",
+ columnCount, (void*)child,availSize.BSize(wm));
+#endif
+
+ // Note if the column's next in flow is not being changed by this incremental reflow.
+ // This may allow the current column to avoid trying to pull lines from the next column.
+ if (child->GetNextSibling() &&
+ !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
+ !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) {
+ kidReflowInput.mFlags.mNextInFlowUntouched = true;
+ }
+
+ ReflowOutput kidDesiredSize(wm, aDesiredSize.mFlags);
+
+ // XXX it would be cool to consult the float manager for the
+ // previous block to figure out the region of floats from the
+ // previous column that extend into this column, and subtract
+ // that region from the new float manager. So you could stick a
+ // really big float in the first column and text in following
+ // columns would flow around it.
+
+ // Reflow the frame
+ LogicalPoint origin(wm,
+ childOrigin.I(wm) +
+ kidReflowInput.ComputedLogicalMargin().IStart(wm),
+ childOrigin.B(wm) +
+ kidReflowInput.ComputedLogicalMargin().BStart(wm));
+ ReflowChild(child, PresContext(), kidDesiredSize, kidReflowInput,
+ wm, origin, containerSize, 0, aStatus);
+
+ reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0;
+
+#ifdef DEBUG_roc
+ printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n",
+ columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(),
+ kidDesiredSize.mCarriedOutBEndMargin.get());
+#endif
+
+ NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
+
+ *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin;
+
+ FinishReflowChild(child, PresContext(), kidDesiredSize,
+ &kidReflowInput, wm, childOrigin, containerSize, 0);
+
+ childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
+ if (childContentBEnd > aConfig.mColMaxBSize) {
+ allFit = false;
+ }
+ if (childContentBEnd > availSize.BSize(wm)) {
+ aColData.mMaxOverflowingBSize = std::max(childContentBEnd,
+ aColData.mMaxOverflowingBSize);
+ }
+ }
+
+ contentRect.UnionRect(contentRect, child->GetRect());
+
+ ConsiderChildOverflow(overflowRects, child);
+ contentBEnd = std::max(contentBEnd, childContentBEnd);
+ aColData.mLastBSize = childContentBEnd;
+ aColData.mSumBSize += childContentBEnd;
+
+ // Build a continuation column if necessary
+ nsIFrame* kidNextInFlow = child->GetNextInFlow();
+
+ if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) {
+ NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted");
+ child = nullptr;
+ break;
+ } else {
+ ++columnCount;
+ // Make sure that the column has a next-in-flow. If not, we must
+ // create one to hold the overflowing stuff, even if we're just
+ // going to put it on our overflow list and let *our*
+ // next in flow handle it.
+ if (!kidNextInFlow) {
+ NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
+ "We have to create a continuation, but the block doesn't want us to reflow it?");
+
+ // We need to create a continuing column
+ kidNextInFlow = CreateNextInFlow(child);
+ }
+
+ // Make sure we reflow a next-in-flow when it switches between being
+ // normal or overflow container
+ if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
+ if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ reflowNext = true;
+ kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+ }
+ else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ reflowNext = true;
+ kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+
+ if ((contentBEnd > aReflowInput.ComputedMaxBSize() ||
+ contentBEnd > aReflowInput.ComputedBSize()) &&
+ aConfig.mBalanceColCount < INT32_MAX) {
+ // We overflowed vertically, but have not exceeded the number of
+ // columns. We're going to go into overflow columns now, so balancing
+ // no longer applies.
+ aColData.mHasExcessBSize = true;
+ }
+
+ if (columnCount >= aConfig.mBalanceColCount) {
+ // No more columns allowed here. Stop.
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY);
+ // Move any of our leftover columns to our overflow list. Our
+ // next-in-flow will eventually pick them up.
+ const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child);
+ if (continuationColumns.NotEmpty()) {
+ SetOverflowFrames(continuationColumns);
+ }
+ child = nullptr;
+ break;
+ }
+ }
+
+ if (PresContext()->HasPendingInterrupt()) {
+ // Stop the loop now while |child| still points to the frame that bailed
+ // out. We could keep going here and condition a bunch of the code in
+ // this loop on whether there's an interrupt, or even just keep going and
+ // trying to reflow the blocks (even though we know they'll interrupt
+ // right after their first line), but stopping now is conceptually the
+ // simplest (and probably fastest) thing.
+ break;
+ }
+
+ // Advance to the next column
+ child = child->GetNextSibling();
+
+ if (child) {
+ childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap;
+
+#ifdef DEBUG_roc
+ printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm));
+#endif
+ }
+ }
+
+ if (PresContext()->CheckForInterrupt(this) &&
+ (GetStateBits() & NS_FRAME_IS_DIRTY)) {
+ // Mark all our kids starting with |child| dirty
+
+ // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt,
+ // because we might have interrupted while reflowing |child|, and since
+ // we're about to add a dirty bit to |child| we need to make sure that
+ // |this| is scheduled to have dirty bits marked on it and its ancestors.
+ // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll
+ // bail out immediately, since it'll already have a dirty bit.
+ for (; child; child = child->GetNextSibling()) {
+ child->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+ }
+
+ aColData.mMaxBSize = contentBEnd;
+ LogicalSize contentSize = LogicalSize(wm, contentRect.Size());
+ contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd);
+ mLastFrameStatus = aStatus;
+
+ // Apply computed and min/max values
+ if (aConfig.mComputedBSize != NS_INTRINSICSIZE) {
+ if (aReflowInput.AvailableBSize() != NS_INTRINSICSIZE) {
+ contentSize.BSize(wm) = std::min(contentSize.BSize(wm),
+ aConfig.mComputedBSize);
+ } else {
+ contentSize.BSize(wm) = aConfig.mComputedBSize;
+ }
+ } else {
+ // We add the "consumed" block-size back in so that we're applying
+ // constraints to the correct bSize value, then subtract it again
+ // after we've finished with the min/max calculation. This prevents us from
+ // having a last continuation that is smaller than the min bSize. but which
+ // has prev-in-flows, trigger a larger bSize than actually required.
+ contentSize.BSize(wm) =
+ aReflowInput.ApplyMinMaxBSize(contentSize.BSize(wm),
+ aConfig.mConsumedBSize);
+ }
+ if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
+ contentSize.ISize(wm) = aReflowInput.ComputedISize();
+ } else {
+ contentSize.ISize(wm) =
+ aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm));
+ }
+
+ contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
+ contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
+ aDesiredSize.SetSize(wm, contentSize);
+ aDesiredSize.mOverflowAreas = overflowRects;
+ aDesiredSize.UnionOverflowAreasWithDesiredBounds();
+
+ // In vertical-rl mode, make a second pass if necessary to reposition the
+ // columns with the correct container width. (In other writing modes,
+ // correct containerSize was not required for column positioning so we don't
+ // need this fixup.)
+ if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) {
+ const nsSize finalContainerSize = aDesiredSize.PhysicalSize();
+ for (nsIFrame* child : mFrames) {
+ // Get the logical position as set previously using a provisional or
+ // dummy containerSize, and reset with the correct container size.
+ child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize),
+ finalContainerSize);
+ }
+ }
+
+#ifdef DEBUG_roc
+ printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
+ && !NS_FRAME_IS_TRUNCATED(aStatus));
+#endif
+ return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
+ && !NS_FRAME_IS_TRUNCATED(aStatus);
+}
+
+void
+nsColumnSetFrame::DrainOverflowColumns()
+{
+ // First grab the prev-in-flows overflows and reparent them to this
+ // frame.
+ nsPresContext* presContext = PresContext();
+ nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
+ if (prev) {
+ AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames());
+ if (overflows) {
+ nsContainerFrame::ReparentFrameViewList(*overflows, prev, this);
+
+ mFrames.InsertFrames(this, nullptr, *overflows);
+ }
+ }
+
+ // Now pull back our own overflows and append them to our children.
+ // We don't need to reparent them since we're already their parent.
+ AutoFrameListPtr overflows(presContext, StealOverflowFrames());
+ if (overflows) {
+ // We're already the parent for these frames, so no need to set
+ // their parent again.
+ mFrames.AppendFrames(nullptr, *overflows);
+ }
+}
+
+void
+nsColumnSetFrame::FindBestBalanceBSize(const ReflowInput& aReflowInput,
+ nsPresContext* aPresContext,
+ ReflowConfig& aConfig,
+ ColumnBalanceData& aColData,
+ ReflowOutput& aDesiredSize,
+ nsCollapsingMargin& aOutMargin,
+ bool& aUnboundedLastColumn,
+ bool& aRunWasFeasible,
+ nsReflowStatus& aStatus)
+{
+ bool feasible = aRunWasFeasible;
+
+ nsMargin bp = aReflowInput.ComputedPhysicalBorderPadding();
+ bp.ApplySkipSides(GetSkipSides());
+ bp.bottom = aReflowInput.ComputedPhysicalBorderPadding().bottom;
+
+ nscoord availableContentBSize =
+ GetAvailableContentBSize(aReflowInput);
+
+ // Termination of the algorithm below is guaranteed because
+ // aConfig.knownFeasibleBSize - aConfig.knownInfeasibleBSize decreases in every
+ // iteration.
+
+ // We set this flag when we detect that we may contain a frame
+ // that can break anywhere (thus foiling the linear decrease-by-one
+ // search)
+ bool maybeContinuousBreakingDetected = false;
+
+ while (!aPresContext->HasPendingInterrupt()) {
+ nscoord lastKnownFeasibleBSize = aConfig.mKnownFeasibleBSize;
+
+ // Record what we learned from the last reflow
+ if (feasible) {
+ // maxBSize is feasible. Also, mLastBalanceBSize is feasible.
+ aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
+ aColData.mMaxBSize);
+ aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
+ mLastBalanceBSize);
+
+ // Furthermore, no height less than the height of the last
+ // column can ever be feasible. (We might be able to reduce the
+ // height of a non-last column by moving content to a later column,
+ // but we can't do that with the last column.)
+ if (mFrames.GetLength() == aConfig.mBalanceColCount) {
+ aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
+ aColData.mLastBSize - 1);
+ }
+ } else {
+ aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
+ mLastBalanceBSize);
+ // If a column didn't fit in its available height, then its current
+ // height must be the minimum height for unbreakable content in
+ // the column, and therefore no smaller height can be feasible.
+ aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
+ aColData.mMaxOverflowingBSize - 1);
+
+ if (aUnboundedLastColumn) {
+ // The last column is unbounded, so all content got reflowed, so the
+ // mColMaxBSize is feasible.
+ aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
+ aColData.mMaxBSize);
+ }
+ }
+
+#ifdef DEBUG_roc
+ printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n",
+ aConfig.mKnownInfeasibleBSize, aConfig.mKnownFeasibleBSize);
+#endif
+
+
+ if (aConfig.mKnownInfeasibleBSize >= aConfig.mKnownFeasibleBSize - 1) {
+ // aConfig.mKnownFeasibleBSize is where we want to be
+ break;
+ }
+
+ if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
+ break;
+ }
+
+ if (lastKnownFeasibleBSize - aConfig.mKnownFeasibleBSize == 1) {
+ // We decreased the feasible height by one twip only. This could
+ // indicate that there is a continuously breakable child frame
+ // that we are crawling through.
+ maybeContinuousBreakingDetected = true;
+ }
+
+ nscoord nextGuess = (aConfig.mKnownFeasibleBSize + aConfig.mKnownInfeasibleBSize)/2;
+ // The constant of 600 twips is arbitrary. It's about two line-heights.
+ if (aConfig.mKnownFeasibleBSize - nextGuess < 600 &&
+ !maybeContinuousBreakingDetected) {
+ // We're close to our target, so just try shrinking just the
+ // minimum amount that will cause one of our columns to break
+ // differently.
+ nextGuess = aConfig.mKnownFeasibleBSize - 1;
+ } else if (aUnboundedLastColumn) {
+ // Make a guess by dividing that into N columns. Add some slop
+ // to try to make it on the feasible side. The constant of
+ // 600 twips is arbitrary. It's about two line-heights.
+ nextGuess = aColData.mSumBSize/aConfig.mBalanceColCount + 600;
+ // Sanitize it
+ nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleBSize + 1,
+ aConfig.mKnownFeasibleBSize - 1);
+ } else if (aConfig.mKnownFeasibleBSize == NS_INTRINSICSIZE) {
+ // This can happen when we had a next-in-flow so we didn't
+ // want to do an unbounded height measuring step. Let's just increase
+ // from the infeasible height by some reasonable amount.
+ nextGuess = aConfig.mKnownInfeasibleBSize*2 + 600;
+ }
+ // Don't bother guessing more than our height constraint.
+ nextGuess = std::min(availableContentBSize, nextGuess);
+
+#ifdef DEBUG_roc
+ printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess);
+#endif
+
+ aConfig.mColMaxBSize = nextGuess;
+
+ aUnboundedLastColumn = false;
+ AddStateBits(NS_FRAME_IS_DIRTY);
+ feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, aConfig, false,
+ &aOutMargin, aColData);
+
+ if (!aConfig.mIsBalancing) {
+ // Looks like we had excess height when balancing, so we gave up on
+ // trying to balance.
+ break;
+ }
+ }
+
+ if (aConfig.mIsBalancing && !feasible &&
+ !aPresContext->HasPendingInterrupt()) {
+ // We may need to reflow one more time at the feasible height to
+ // get a valid layout.
+ bool skip = false;
+ if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
+ aConfig.mColMaxBSize = availableContentBSize;
+ if (mLastBalanceBSize == availableContentBSize) {
+ skip = true;
+ }
+ } else {
+ aConfig.mColMaxBSize = aConfig.mKnownFeasibleBSize;
+ }
+ if (!skip) {
+ // If our height is unconstrained, make sure that the last column is
+ // allowed to have arbitrary height here, even though we were balancing.
+ // Otherwise we'd have to split, and it's not clear what we'd do with
+ // that.
+ AddStateBits(NS_FRAME_IS_DIRTY);
+ feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, aConfig,
+ availableContentBSize == NS_UNCONSTRAINEDSIZE,
+ &aOutMargin, aColData);
+ }
+ }
+
+ aRunWasFeasible = feasible;
+}
+
+void
+nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ // Don't support interruption in columns
+ nsPresContext::InterruptPreventer noInterrupts(aPresContext);
+
+ DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ // Initialize OUT parameter
+ aStatus = NS_FRAME_COMPLETE;
+
+ // Our children depend on our block-size if we have a fixed block-size.
+ if (aReflowInput.ComputedBSize() != NS_AUTOHEIGHT) {
+ AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ } else {
+ RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ }
+
+#ifdef DEBUG
+ nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList));
+ for (; !oc.AtEnd(); oc.Next()) {
+ MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get()));
+ }
+ nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList));
+ for (; !eoc.AtEnd(); eoc.Next()) {
+ MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get()));
+ }
+#endif
+
+ nsOverflowAreas ocBounds;
+ nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
+ if (GetPrevInFlow()) {
+ ReflowOverflowContainerChildren(aPresContext, aReflowInput, ocBounds, 0,
+ ocStatus);
+ }
+
+ //------------ Handle Incremental Reflow -----------------
+
+ // If inline size is unconstrained, set aForceAuto to true to allow
+ // the columns to expand in the inline direction. (This typically
+ // happens in orthogonal flows where the inline direction is the
+ // container's block direction).
+ ReflowConfig config =
+ ChooseColumnStrategy(aReflowInput,
+ aReflowInput.ComputedISize() == NS_UNCONSTRAINEDSIZE);
+
+ // If balancing, then we allow the last column to grow to unbounded
+ // height during the first reflow. This gives us a way to estimate
+ // what the average column height should be, because we can measure
+ // the heights of all the columns and sum them up. But don't do this
+ // if we have a next in flow because we don't want to suck all its
+ // content back here and then have to push it out again!
+ nsIFrame* nextInFlow = GetNextInFlow();
+ bool unboundedLastColumn = config.mIsBalancing && !nextInFlow;
+ nsCollapsingMargin carriedOutBottomMargin;
+ ColumnBalanceData colData;
+ colData.mHasExcessBSize = false;
+
+ bool feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, config,
+ unboundedLastColumn, &carriedOutBottomMargin,
+ colData);
+
+ // If we're not balancing, then we're already done, since we should have
+ // reflown all of our children, and there is no need for a binary search to
+ // determine proper column height.
+ if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) {
+ FindBestBalanceBSize(aReflowInput, aPresContext, config, colData,
+ aDesiredSize, carriedOutBottomMargin,
+ unboundedLastColumn, feasible, aStatus);
+ }
+
+ if (aPresContext->HasPendingInterrupt() &&
+ aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
+ // In this situation, we might be lying about our reflow status, because
+ // our last kid (the one that got interrupted) was incomplete. Fix that.
+ aStatus = NS_FRAME_COMPLETE;
+ }
+
+ NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
+ aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
+ "Column set should be complete if the available block-size is unconstrained");
+
+ // Merge overflow container bounds and status.
+ aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
+ NS_MergeReflowStatusInto(&aStatus, ocStatus);
+
+ FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus, false);
+
+ aDesiredSize.mCarriedOutBEndMargin = carriedOutBottomMargin;
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+void
+nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ if (IsVisibleForPainting(aBuilder)) {
+ aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+ nsDisplayGenericOverflow(aBuilder, this, ::PaintColumnRule, "ColumnRule",
+ nsDisplayItem::TYPE_COLUMN_RULE));
+ }
+
+ // Our children won't have backgrounds so it doesn't matter where we put them.
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, aLists);
+ }
+}
+
+#ifdef DEBUG
+void
+nsColumnSetFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ MOZ_ASSERT(aListID != kPrincipalList || aChildList.OnlyChild(),
+ "initial principal child list must have exactly one child");
+ nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList);
+}
+
+void
+nsColumnSetFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ MOZ_CRASH("unsupported operation");
+}
+
+void
+nsColumnSetFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ MOZ_CRASH("unsupported operation");
+}
+
+void
+nsColumnSetFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ MOZ_CRASH("unsupported operation");
+}
+#endif
diff --git a/layout/generic/nsColumnSetFrame.h b/layout/generic/nsColumnSetFrame.h
new file mode 100644
index 000000000..db44183d6
--- /dev/null
+++ b/layout/generic/nsColumnSetFrame.h
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsColumnSetFrame_h___
+#define nsColumnSetFrame_h___
+
+/* rendering object for css3 multi-column layout */
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+#include "nsIFrameInlines.h" // for methods used by IS_TRUE_OVERFLOW_CONTAINER
+
+/**
+ * nsColumnSetFrame implements CSS multi-column layout.
+ * @note nsColumnSetFrame keeps true overflow containers in the normal flow
+ * child lists (i.e. the principal and overflow lists).
+ */
+class nsColumnSetFrame final : public nsContainerFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsColumnSetFrame(nsStyleContext* aContext);
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+#ifdef DEBUG
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+#endif
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+
+ /**
+ * Retrieve the available height for content of this frame. The available content
+ * height is the available height for the frame, minus borders and padding.
+ */
+ virtual nscoord GetAvailableContentBSize(const ReflowInput& aReflowInput);
+
+ virtual nsContainerFrame* GetContentInsertionFrame() override {
+ nsIFrame* frame = PrincipalChildList().FirstChild();
+
+ // if no children return nullptr
+ if (!frame)
+ return nullptr;
+
+ return frame->GetContentInsertionFrame();
+ }
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eCanContainOverflowContainers));
+ }
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual void PaintColumnRule(nsRenderingContext* aCtx,
+ const nsRect& aDirtyRect,
+ const nsPoint& aPt);
+
+ /**
+ * Similar to nsBlockFrame::DrainOverflowLines. Locate any columns not
+ * handled by our prev-in-flow, and any columns sitting on our own
+ * overflow list, and put them in our primary child list for reflowing.
+ */
+ void DrainOverflowColumns();
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override {
+ return MakeFrameName(NS_LITERAL_STRING("ColumnSet"), aResult);
+ }
+#endif
+
+protected:
+ nscoord mLastBalanceBSize;
+ nsReflowStatus mLastFrameStatus;
+
+ /**
+ * These are the parameters that control the layout of columns.
+ */
+ struct ReflowConfig {
+ // The number of columns that we want to balance across. If we're not
+ // balancing, this will be set to INT32_MAX.
+ int32_t mBalanceColCount;
+
+ // The inline-size of each individual column.
+ nscoord mColISize;
+
+ // The amount of inline-size that is expected to be left over after all the
+ // columns and column gaps are laid out.
+ nscoord mExpectedISizeLeftOver;
+
+ // The width (inline-size) of each column gap.
+ nscoord mColGap;
+
+ // The maximum bSize of any individual column during a reflow iteration.
+ // This parameter is set during each iteration of the binary search for
+ // the best column block-size.
+ nscoord mColMaxBSize;
+
+ // A boolean controlling whether or not we are balancing. This should be
+ // equivalent to mBalanceColCount == INT32_MAX.
+ bool mIsBalancing;
+
+ // The last known column block-size that was 'feasible'. A column bSize is
+ // feasible if all child content fits within the specified bSize.
+ nscoord mKnownFeasibleBSize;
+
+ // The last known block-size that was 'infeasible'. A column bSize is
+ // infeasible if not all child content fits within the specified bSize.
+ nscoord mKnownInfeasibleBSize;
+
+ // block-size of the column set frame
+ nscoord mComputedBSize;
+
+ // The block-size "consumed" by previous-in-flows.
+ // The computed block-size should be equal to the block-size of the element
+ // (i.e. the computed block-size itself) plus the consumed block-size.
+ nscoord mConsumedBSize;
+ };
+
+ /**
+ * Some data that is better calculated during reflow
+ */
+ struct ColumnBalanceData {
+ // The maximum "content block-size" of any column
+ nscoord mMaxBSize;
+ // The sum of the "content block-size" for all columns
+ nscoord mSumBSize;
+ // The "content block-size" of the last column
+ nscoord mLastBSize;
+ // The maximum "content block-size" of all columns that overflowed
+ // their available block-size
+ nscoord mMaxOverflowingBSize;
+ // This flag determines whether the last reflow of children exceeded the
+ // computed block-size of the column set frame. If so, we set the bSize to
+ // this maximum allowable bSize, and continue reflow without balancing.
+ bool mHasExcessBSize;
+
+ void Reset() {
+ mMaxBSize = mSumBSize = mLastBSize = mMaxOverflowingBSize = 0;
+ mHasExcessBSize = false;
+ }
+ };
+
+ bool ReflowColumns(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus,
+ ReflowConfig& aConfig,
+ bool aLastColumnUnbounded,
+ nsCollapsingMargin* aCarriedOutBEndMargin,
+ ColumnBalanceData& aColData);
+
+ /**
+ * The basic reflow strategy is to call this function repeatedly to
+ * obtain specific parameters that determine the layout of the
+ * columns. This function will compute those parameters from the CSS
+ * style. This function will also be responsible for implementing
+ * the state machine that controls column balancing.
+ */
+ ReflowConfig ChooseColumnStrategy(const ReflowInput& aReflowInput,
+ bool aForceAuto, nscoord aFeasibleBSize,
+ nscoord aInfeasibleBSize);
+
+ /**
+ * Perform the binary search for the best balance height for this column set.
+ *
+ * @param aReflowInput The input parameters for the current reflow iteration.
+ * @param aPresContext The presentation context in which the current reflow
+ * iteration is occurring.
+ * @param aConfig The ReflowConfig object associated with this column set
+ * frame, generated by ChooseColumnStrategy().
+ * @param aColData A data structure used to keep track of data needed between
+ * successive iterations of the balancing process.
+ * @param aDesiredSize The final output size of the column set frame (output
+ * of reflow procedure).
+ * @param aOutMargin The bottom margin of the column set frame that may be
+ * carried out from reflow (and thus collapsed).
+ * @param aUnboundedLastColumn A boolean value indicating that the last column
+ * can be of any height. Used during the first iteration of the
+ * balancing procedure to measure the height of all content in
+ * descendant frames of the column set.
+ * @param aRunWasFeasible An input/output parameter indicating whether or not
+ * the last iteration of the balancing loop was a feasible height to
+ * fit all content from descendant frames.
+ * @param aStatus A final reflow status of the column set frame, passed in as
+ * an output parameter.
+ */
+ void FindBestBalanceBSize(const ReflowInput& aReflowInput,
+ nsPresContext* aPresContext,
+ ReflowConfig& aConfig,
+ ColumnBalanceData& aColData,
+ ReflowOutput& aDesiredSize,
+ nsCollapsingMargin& aOutMargin,
+ bool& aUnboundedLastColumn,
+ bool& aRunWasFeasible,
+ nsReflowStatus& aStatus);
+ /**
+ * Reflow column children. Returns true iff the content that was reflowed
+ * fit into the mColMaxBSize.
+ */
+ bool ReflowChildren(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ const ReflowConfig& aConfig,
+ bool aLastColumnUnbounded,
+ nsCollapsingMargin* aCarriedOutBEndMargin,
+ ColumnBalanceData& aColData);
+};
+
+#endif // nsColumnSetFrame_h___
diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp
new file mode 100644
index 000000000..813d19dfe
--- /dev/null
+++ b/layout/generic/nsContainerFrame.cpp
@@ -0,0 +1,2332 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class #1 for rendering objects that have child lists */
+
+#include "nsContainerFrame.h"
+
+#include "mozilla/dom/HTMLDetailsElement.h"
+#include "mozilla/dom/HTMLSummaryElement.h"
+#include "nsAbsoluteContainingBlock.h"
+#include "nsIDocument.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+#include "nsRect.h"
+#include "nsPoint.h"
+#include "nsStyleConsts.h"
+#include "nsView.h"
+#include "nsIPresShell.h"
+#include "nsCOMPtr.h"
+#include "nsGkAtoms.h"
+#include "nsViewManager.h"
+#include "nsIWidget.h"
+#include "nsCSSRendering.h"
+#include "nsError.h"
+#include "nsDisplayList.h"
+#include "nsIBaseWindow.h"
+#include "nsBoxLayoutState.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsBlockFrame.h"
+#include "nsPlaceholderFrame.h"
+#include "mozilla/AutoRestore.h"
+#include "nsIFrameInlines.h"
+#include "nsPrintfCString.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layout;
+
+nsContainerFrame::~nsContainerFrame()
+{
+}
+
+NS_QUERYFRAME_HEAD(nsContainerFrame)
+ NS_QUERYFRAME_ENTRY(nsContainerFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
+
+void
+nsContainerFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
+ if (aPrevInFlow) {
+ // Make sure we copy bits from our prev-in-flow that will affect
+ // us. A continuation for a container frame needs to know if it
+ // has a child with a view so that we'll properly reposition it.
+ if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)
+ AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
+ }
+}
+
+void
+nsContainerFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+#ifdef DEBUG
+ nsFrame::VerifyDirtyBitSet(aChildList);
+#endif
+ if (aListID == kPrincipalList) {
+ MOZ_ASSERT(mFrames.IsEmpty(),
+ "unexpected second call to SetInitialChildList");
+ mFrames.SetFrames(aChildList);
+ } else if (aListID == kBackdropList) {
+ MOZ_ASSERT(StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
+ "Only top layer frames should have backdrop");
+ MOZ_ASSERT(GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "Top layer frames should be out-of-flow");
+ MOZ_ASSERT(!Properties().Get(BackdropProperty()),
+ "We shouldn't have setup backdrop frame list before");
+#ifdef DEBUG
+ {
+ nsIFrame* placeholder = aChildList.FirstChild();
+ MOZ_ASSERT(aChildList.OnlyChild(), "Should have only one backdrop");
+ MOZ_ASSERT(placeholder->GetType() == nsGkAtoms::placeholderFrame,
+ "The frame to be stored should be a placeholder");
+ MOZ_ASSERT(static_cast<nsPlaceholderFrame*>(placeholder)->
+ GetOutOfFlowFrame()->GetType() == nsGkAtoms::backdropFrame,
+ "The placeholder should points to a backdrop frame");
+ }
+#endif
+ nsFrameList* list =
+ new (PresContext()->PresShell()) nsFrameList(aChildList);
+ Properties().Set(BackdropProperty(), list);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Unexpected child list");
+ }
+}
+
+void
+nsContainerFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
+ "unexpected child list");
+
+ if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
+ return;
+ }
+
+ DrainSelfOverflowList(); // ensure the last frame is in mFrames
+ mFrames.AppendFrames(this, aFrameList);
+
+ if (aListID != kNoReflowPrincipalList) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eTreeChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+}
+
+void
+nsContainerFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
+ "unexpected child list");
+ NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
+ "inserting after sibling frame with different parent");
+
+ if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
+ return;
+ }
+
+ DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
+ mFrames.InsertFrames(this, aPrevFrame, aFrameList);
+
+ if (aListID != kNoReflowPrincipalList) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eTreeChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+}
+
+void
+nsContainerFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
+ "unexpected child list");
+
+ // Loop and destroy aOldFrame and all of its continuations.
+ // Request a reflow on the parent frames involved unless we were explicitly
+ // told not to (kNoReflowPrincipalList).
+ bool generateReflowCommand = true;
+ if (kNoReflowPrincipalList == aListID) {
+ generateReflowCommand = false;
+ }
+ nsIPresShell* shell = PresContext()->PresShell();
+ nsContainerFrame* lastParent = nullptr;
+ while (aOldFrame) {
+ nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
+ nsContainerFrame* parent = aOldFrame->GetParent();
+ // Please note that 'parent' may not actually be where 'aOldFrame' lives.
+ // We really MUST use StealFrame() and nothing else here.
+ // @see nsInlineFrame::StealFrame for details.
+ parent->StealFrame(aOldFrame);
+ aOldFrame->Destroy();
+ aOldFrame = oldFrameNextContinuation;
+ if (parent != lastParent && generateReflowCommand) {
+ shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ lastParent = parent;
+ }
+ }
+}
+
+void
+nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot)
+{
+ if (IsAbsoluteContainer()) {
+ GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot);
+ MarkAsNotAbsoluteContainingBlock();
+ }
+}
+
+void
+nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
+ nsIPresShell* aPresShell,
+ FramePropertyTable* aPropTable,
+ FrameListPropertyDescriptor aProp)
+{
+ // Note that the last frame can be removed through another route and thus
+ // delete the property -- that's why we fetch the property again before
+ // removing each frame rather than fetching it once and iterating the list.
+ while (nsFrameList* frameList = aPropTable->Get(this, aProp)) {
+ nsIFrame* frame = frameList->RemoveFirstChild();
+ if (MOZ_LIKELY(frame)) {
+ frame->DestroyFrom(aDestructRoot);
+ } else {
+ aPropTable->Remove(this, aProp);
+ frameList->Delete(aPresShell);
+ return;
+ }
+ }
+}
+
+void
+nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ // Prevent event dispatch during destruction.
+ if (HasView()) {
+ GetView()->SetFrame(nullptr);
+ }
+
+ DestroyAbsoluteFrames(aDestructRoot);
+
+ // Destroy frames on the principal child list.
+ mFrames.DestroyFramesFrom(aDestructRoot);
+
+ // Destroy frames on the auxiliary frame lists and delete the lists.
+ nsPresContext* pc = PresContext();
+ nsIPresShell* shell = pc->PresShell();
+ FramePropertyTable* props = pc->PropertyTable();
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty());
+
+ MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) ||
+ !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) ||
+ props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())),
+ "this type of frame should't have overflow containers");
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ OverflowContainersProperty());
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ ExcessOverflowContainersProperty());
+
+ MOZ_ASSERT(!props->Get(this, BackdropProperty()) ||
+ StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
+ "only top layer frame may have backdrop");
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props, BackdropProperty());
+
+ nsSplittableFrame::DestroyFrom(aDestructRoot);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Child frame enumeration
+
+const nsFrameList&
+nsContainerFrame::GetChildList(ChildListID aListID) const
+{
+ // We only know about the principal child list, the overflow lists,
+ // and the backdrop list.
+ switch (aListID) {
+ case kPrincipalList:
+ return mFrames;
+ case kOverflowList: {
+ nsFrameList* list = GetOverflowFrames();
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ case kOverflowContainersList: {
+ nsFrameList* list = GetPropTableFrames(OverflowContainersProperty());
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ case kExcessOverflowContainersList: {
+ nsFrameList* list =
+ GetPropTableFrames(ExcessOverflowContainersProperty());
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ case kBackdropList: {
+ nsFrameList* list = GetPropTableFrames(BackdropProperty());
+ return list ? *list : nsFrameList::EmptyList();
+ }
+ default:
+ return nsSplittableFrame::GetChildList(aListID);
+ }
+}
+
+static void
+AppendIfNonempty(const nsIFrame* aFrame,
+ FramePropertyTable* aPropTable,
+ nsContainerFrame::FrameListPropertyDescriptor aProperty,
+ nsTArray<nsIFrame::ChildList>* aLists,
+ nsIFrame::ChildListID aListID)
+{
+ if (nsFrameList* list = aPropTable->Get(aFrame, aProperty)) {
+ list->AppendIfNonempty(aLists, aListID);
+ }
+}
+
+void
+nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const
+{
+ mFrames.AppendIfNonempty(aLists, kPrincipalList);
+ FramePropertyTable* propTable = PresContext()->PropertyTable();
+ ::AppendIfNonempty(this, propTable, OverflowProperty(),
+ aLists, kOverflowList);
+ if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
+ ::AppendIfNonempty(this, propTable, OverflowContainersProperty(),
+ aLists, kOverflowContainersList);
+ ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(),
+ aLists, kExcessOverflowContainersList);
+ }
+ // Bypass BackdropProperty hashtable lookup for any in-flow frames
+ // since frames in the top layer (only which can have backdrop) are
+ // definitely out-of-flow.
+ if (GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+ ::AppendIfNonempty(this, propTable, BackdropProperty(),
+ aLists, kBackdropList);
+ }
+ nsSplittableFrame::GetChildLists(aLists);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Painting/Events
+
+void
+nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
+}
+
+void
+nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists,
+ uint32_t aFlags)
+{
+ nsIFrame* kid = mFrames.FirstChild();
+ // Put each child's background directly onto the content list
+ nsDisplayListSet set(aLists, aLists.Content());
+ // The children should be in content order
+ while (kid) {
+ BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags);
+ kid = kid->GetNextSibling();
+ }
+}
+
+/* virtual */ void
+nsContainerFrame::ChildIsDirty(nsIFrame* aChild)
+{
+ NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty");
+
+ AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+}
+
+bool
+nsContainerFrame::IsLeaf() const
+{
+ return false;
+}
+
+nsIFrame::FrameSearchResult
+nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ // Don't allow the caret to stay in an empty (leaf) container frame.
+ return CONTINUE_EMPTY;
+}
+
+nsIFrame::FrameSearchResult
+nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ // Don't allow the caret to stay in an empty (leaf) container frame.
+ return CONTINUE_EMPTY;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Helper member functions
+
+static void
+ReparentFrameViewTo(nsIFrame* aFrame,
+ nsViewManager* aViewManager,
+ nsView* aNewParentView,
+ nsView* aOldParentView)
+{
+ if (aFrame->HasView()) {
+#ifdef MOZ_XUL
+ if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
+ // This view must be parented by the root view, don't reparent it.
+ return;
+ }
+#endif
+ nsView* view = aFrame->GetView();
+ // Verify that the current parent view is what we think it is
+ //nsView* parentView;
+ //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
+
+ aViewManager->RemoveChild(view);
+
+ // The view will remember the Z-order and other attributes that have been set on it.
+ nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
+ aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
+ } else if (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ // Iterate the child frames, and check each child frame to see if it has
+ // a view
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ ReparentFrameViewTo(childFrames.get(), aViewManager,
+ aNewParentView, aOldParentView);
+ }
+ }
+ }
+}
+
+void
+nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame,
+ bool aForce)
+{
+ if (aFrame->HasView()) {
+ return;
+ }
+
+ // If we don't yet have a view, see if we need a view
+ if (!aForce && !aFrame->NeedsView()) {
+ // don't need a view
+ return;
+ }
+
+ nsView* parentView = aFrame->GetParent()->GetClosestView();
+ NS_ASSERTION(parentView, "no parent with view");
+
+ nsViewManager* viewManager = parentView->GetViewManager();
+ NS_ASSERTION(viewManager, "null view manager");
+
+ // Create a view
+ nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView);
+
+ SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view);
+
+ nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame);
+ // we insert this view 'above' the insertBefore view, unless insertBefore is null,
+ // in which case we want to call with aAbove == false to insert at the beginning
+ // in document order
+ viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
+
+ // REVIEW: Don't create a widget for fixed-pos elements anymore.
+ // ComputeRepaintRegionForCopy will calculate the right area to repaint
+ // when we scroll.
+ // Reparent views on any child frames (or their descendants) to this
+ // view. We can just call ReparentFrameViewTo on this frame because
+ // we know this frame has no view, so it will crawl the children. Also,
+ // we know that any descendants with views must have 'parentView' as their
+ // parent view.
+ ReparentFrameViewTo(aFrame, viewManager, view, parentView);
+
+ // Remember our view
+ aFrame->SetView(view);
+
+ NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
+ ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p",
+ aFrame, view));
+}
+
+/**
+ * Position the view associated with |aKidFrame|, if there is one. A
+ * container frame should call this method after positioning a frame,
+ * but before |Reflow|.
+ */
+void
+nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame)
+{
+ nsIFrame* parentFrame = aKidFrame->GetParent();
+ if (!aKidFrame->HasView() || !parentFrame)
+ return;
+
+ nsView* view = aKidFrame->GetView();
+ nsViewManager* vm = view->GetViewManager();
+ nsPoint pt;
+ nsView* ancestorView = parentFrame->GetClosestView(&pt);
+
+ if (ancestorView != view->GetParent()) {
+ NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
+ "Allowed only one anonymous view between frames");
+ // parentFrame is responsible for positioning aKidFrame's view
+ // explicitly
+ return;
+ }
+
+ pt += aKidFrame->GetPosition();
+ vm->MoveViewTo(view, pt.x, pt.y);
+}
+
+nsresult
+nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
+ nsIFrame* aOldParentFrame,
+ nsIFrame* aNewParentFrame)
+{
+ NS_PRECONDITION(aChildFrame, "null child frame pointer");
+ NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
+ NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
+ NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
+
+ // See if either the old parent frame or the new parent frame have a view
+ while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
+ // Walk up both the old parent frame and the new parent frame nodes
+ // stopping when we either find a common parent or views for one
+ // or both of the frames.
+ //
+ // This works well in the common case where we push/pull and the old parent
+ // frame and the new parent frame are part of the same flow. They will
+ // typically be the same distance (height wise) from the
+ aOldParentFrame = aOldParentFrame->GetParent();
+ aNewParentFrame = aNewParentFrame->GetParent();
+
+ // We should never walk all the way to the root frame without finding
+ // a view
+ NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
+
+ // See if we reached a common ancestor
+ if (aOldParentFrame == aNewParentFrame) {
+ break;
+ }
+ }
+
+ // See if we found a common parent frame
+ if (aOldParentFrame == aNewParentFrame) {
+ // We found a common parent and there are no views between the old parent
+ // and the common parent or the new parent frame and the common parent.
+ // Because neither the old parent frame nor the new parent frame have views,
+ // then any child views don't need reparenting
+ return NS_OK;
+ }
+
+ // We found views for one or both of the ancestor frames before we
+ // found a common ancestor.
+ nsView* oldParentView = aOldParentFrame->GetClosestView();
+ nsView* newParentView = aNewParentFrame->GetClosestView();
+
+ // See if the old parent frame and the new parent frame are in the
+ // same view sub-hierarchy. If they are then we don't have to do
+ // anything
+ if (oldParentView != newParentView) {
+ // They're not so we need to reparent any child views
+ ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
+ oldParentView);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
+ nsIFrame* aOldParentFrame,
+ nsIFrame* aNewParentFrame)
+{
+ NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list");
+ NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
+ NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
+ NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
+
+ // See if either the old parent frame or the new parent frame have a view
+ while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
+ // Walk up both the old parent frame and the new parent frame nodes
+ // stopping when we either find a common parent or views for one
+ // or both of the frames.
+ //
+ // This works well in the common case where we push/pull and the old parent
+ // frame and the new parent frame are part of the same flow. They will
+ // typically be the same distance (height wise) from the
+ aOldParentFrame = aOldParentFrame->GetParent();
+ aNewParentFrame = aNewParentFrame->GetParent();
+
+ // We should never walk all the way to the root frame without finding
+ // a view
+ NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
+
+ // See if we reached a common ancestor
+ if (aOldParentFrame == aNewParentFrame) {
+ break;
+ }
+ }
+
+
+ // See if we found a common parent frame
+ if (aOldParentFrame == aNewParentFrame) {
+ // We found a common parent and there are no views between the old parent
+ // and the common parent or the new parent frame and the common parent.
+ // Because neither the old parent frame nor the new parent frame have views,
+ // then any child views don't need reparenting
+ return NS_OK;
+ }
+
+ // We found views for one or both of the ancestor frames before we
+ // found a common ancestor.
+ nsView* oldParentView = aOldParentFrame->GetClosestView();
+ nsView* newParentView = aNewParentFrame->GetClosestView();
+
+ // See if the old parent frame and the new parent frame are in the
+ // same view sub-hierarchy. If they are then we don't have to do
+ // anything
+ if (oldParentView != newParentView) {
+ nsViewManager* viewManager = oldParentView->GetViewManager();
+
+ // They're not so we need to reparent any child views
+ for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
+ ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView);
+ }
+ }
+
+ return NS_OK;
+}
+
+static nsIWidget*
+GetPresContextContainerWidget(nsPresContext* aPresContext)
+{
+ nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
+ if (!baseWindow)
+ return nullptr;
+
+ nsCOMPtr<nsIWidget> mainWidget;
+ baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+ return mainWidget;
+}
+
+static bool
+IsTopLevelWidget(nsIWidget* aWidget)
+{
+ nsWindowType windowType = aWidget->WindowType();
+ return windowType == eWindowType_toplevel ||
+ windowType == eWindowType_dialog ||
+ windowType == eWindowType_popup ||
+ windowType == eWindowType_sheet;
+}
+
+void
+nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsView* aView,
+ nsRenderingContext* aRC,
+ uint32_t aFlags)
+{
+#ifdef MOZ_XUL
+ if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
+ return;
+
+ nsCOMPtr<nsIWidget> windowWidget = GetPresContextContainerWidget(aPresContext);
+ if (!windowWidget || !IsTopLevelWidget(windowWidget))
+ return;
+
+ nsViewManager* vm = aView->GetViewManager();
+ nsView* rootView = vm->GetRootView();
+
+ if (aView != rootView)
+ return;
+
+ Element* rootElement = aPresContext->Document()->GetRootElement();
+ if (!rootElement || !rootElement->IsXULElement()) {
+ // Scrollframes use native widgets which don't work well with
+ // translucent windows, at least in Windows XP. So if the document
+ // has a root scrollrame it's useless to try to make it transparent,
+ // we'll just get something broken.
+ // nsCSSFrameConstructor::ConstructRootFrame constructs root
+ // scrollframes whenever the root element is not a XUL element, so
+ // we test for that here. We can't just call
+ // presShell->GetRootScrollFrame() since that might not have
+ // been constructed yet.
+ // We can change this to allow translucent toplevel HTML documents
+ // (e.g. to do something like Dashboard widgets), once we
+ // have broad support for translucent scrolled documents, but be
+ // careful because apparently some Firefox extensions expect
+ // openDialog("something.html") to produce an opaque window
+ // even if the HTML doesn't have a background-color set.
+ return;
+ }
+
+ nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
+ if (!rootFrame)
+ return;
+
+ if (aFlags & SET_ASYNC) {
+ aView->SetNeedsWindowPropertiesSync();
+ return;
+ }
+
+ RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
+ nsWeakFrame weak(rootFrame);
+
+ nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
+ int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow;
+ nsCOMPtr<nsIWidget> viewWidget = aView->GetWidget();
+ viewWidget->SetTransparencyMode(mode);
+ windowWidget->SetWindowShadowStyle(shadow);
+
+ if (!aRC)
+ return;
+
+ if (!weak.IsAlive()) {
+ return;
+ }
+
+ nsBoxLayoutState aState(aPresContext, aRC);
+ nsSize minSize = rootFrame->GetXULMinSize(aState);
+ nsSize maxSize = rootFrame->GetXULMaxSize(aState);
+
+ SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize);
+#endif
+}
+
+void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
+ nsIWidget* aWidget,
+ const nsSize& aMinSize,
+ const nsSize& aMaxSize)
+{
+ LayoutDeviceIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width),
+ aPresContext->AppUnitsToDevPixels(aMinSize.height));
+ LayoutDeviceIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE :
+ aPresContext->AppUnitsToDevPixels(aMaxSize.width),
+ aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE :
+ aPresContext->AppUnitsToDevPixels(aMaxSize.height));
+
+ // MinSize has a priority over MaxSize
+ if (devMinSize.width > devMaxSize.width)
+ devMaxSize.width = devMinSize.width;
+ if (devMinSize.height > devMaxSize.height)
+ devMaxSize.height = devMinSize.height;
+
+ widget::SizeConstraints constraints(devMinSize, devMaxSize);
+
+ // The sizes are in inner window sizes, so convert them into outer window sizes.
+ // Use a size of (200, 200) as only the difference between the inner and outer
+ // size is needed.
+ LayoutDeviceIntSize windowSize =
+ aWidget->ClientToWindowSize(LayoutDeviceIntSize(200, 200));
+ if (constraints.mMinSize.width)
+ constraints.mMinSize.width += windowSize.width - 200;
+ if (constraints.mMinSize.height)
+ constraints.mMinSize.height += windowSize.height - 200;
+ if (constraints.mMaxSize.width != NS_MAXSIZE)
+ constraints.mMaxSize.width += windowSize.width - 200;
+ if (constraints.mMaxSize.height != NS_MAXSIZE)
+ constraints.mMaxSize.height += windowSize.height - 200;
+
+ aWidget->SetSizeConstraints(constraints);
+}
+
+void
+nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsView* aView,
+ const nsRect& aVisualOverflowArea,
+ uint32_t aFlags)
+{
+ if (!aView) {
+ return;
+ }
+
+ // Make sure the view is sized and positioned correctly
+ if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
+ PositionFrameView(aFrame);
+ }
+
+ if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
+ nsViewManager* vm = aView->GetViewManager();
+
+ vm->ResizeView(aView, aVisualOverflowArea, true);
+ }
+}
+
+void
+nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsStyleContext* aStyleContext,
+ nsView* aView,
+ uint32_t aFlags)
+{
+ NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext,
+ "Wrong style context for frame?");
+
+ if (!aView) {
+ return;
+ }
+
+ nsViewManager* vm = aView->GetViewManager();
+
+ if (nullptr == aStyleContext) {
+ aStyleContext = aFrame->StyleContext();
+ }
+
+ // Make sure visibility is correct. This only affects nsSubdocumentFrame.
+ if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) &&
+ !aFrame->SupportsVisibilityHidden()) {
+ // See if the view should be hidden or visible
+ vm->SetViewVisibility(aView,
+ aStyleContext->StyleVisibility()->IsVisible()
+ ? nsViewVisibility_kShow : nsViewVisibility_kHide);
+ }
+
+ int32_t zIndex = 0;
+ bool autoZIndex = false;
+
+ if (aFrame->IsAbsPosContainingBlock()) {
+ // Make sure z-index is correct
+ const nsStylePosition* position = aStyleContext->StylePosition();
+
+ if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
+ zIndex = position->mZIndex.GetIntValue();
+ } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
+ autoZIndex = true;
+ }
+ } else {
+ autoZIndex = true;
+ }
+
+ vm->SetViewZIndex(aView, autoZIndex, zIndex);
+}
+
+static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
+{
+ if (aCoord.ConvertsToLength()) {
+ return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
+ }
+ return aIfNotCoord;
+}
+
+void
+nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext,
+ InlineIntrinsicISizeData *aData,
+ nsLayoutUtils::IntrinsicISizeType aType)
+{
+ if (GetPrevInFlow())
+ return; // Already added.
+
+ NS_PRECONDITION(aType == nsLayoutUtils::MIN_ISIZE ||
+ aType == nsLayoutUtils::PREF_ISIZE, "bad type");
+
+ WritingMode wm = GetWritingMode();
+ mozilla::css::Side startSide =
+ wm.PhysicalSideForInlineAxis(eLogicalEdgeStart);
+ mozilla::css::Side endSide =
+ wm.PhysicalSideForInlineAxis(eLogicalEdgeEnd);
+
+ const nsStylePadding *stylePadding = StylePadding();
+ const nsStyleBorder *styleBorder = StyleBorder();
+ const nsStyleMargin *styleMargin = StyleMargin();
+
+ // This goes at the beginning no matter how things are broken and how
+ // messy the bidi situations are, since per CSS2.1 section 8.6
+ // (implemented in bug 328168), the startSide border is always on the
+ // first line.
+ // This frame is a first-in-flow, but it might have a previous bidi
+ // continuation, in which case that continuation should handle the startSide
+ // border.
+ // For box-decoration-break:clone we setup clonePBM = startPBM + endPBM and
+ // add that to each line. For box-decoration-break:slice clonePBM is zero.
+ nscoord clonePBM = 0; // PBM = PaddingBorderMargin
+ const bool sliceBreak =
+ styleBorder->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
+ if (!GetPrevContinuation()) {
+ nscoord startPBM =
+ // clamp negative calc() to 0
+ std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) +
+ styleBorder->GetComputedBorderWidth(startSide) +
+ GetCoord(styleMargin->mMargin.Get(startSide), 0);
+ if (MOZ_LIKELY(sliceBreak)) {
+ aData->mCurrentLine += startPBM;
+ } else {
+ clonePBM = startPBM;
+ }
+ }
+
+ nscoord endPBM =
+ // clamp negative calc() to 0
+ std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
+ styleBorder->GetComputedBorderWidth(endSide) +
+ GetCoord(styleMargin->mMargin.Get(endSide), 0);
+ if (MOZ_UNLIKELY(!sliceBreak)) {
+ clonePBM += endPBM;
+ }
+
+ const nsLineList_iterator* savedLine = aData->mLine;
+ nsIFrame* const savedLineContainer = aData->LineContainer();
+
+ nsContainerFrame *lastInFlow;
+ for (nsContainerFrame *nif = this; nif;
+ nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
+ if (aData->mCurrentLine == 0) {
+ aData->mCurrentLine = clonePBM;
+ }
+ for (nsIFrame* kid : nif->mFrames) {
+ if (aType == nsLayoutUtils::MIN_ISIZE)
+ kid->AddInlineMinISize(aRenderingContext,
+ static_cast<InlineMinISizeData*>(aData));
+ else
+ kid->AddInlinePrefISize(aRenderingContext,
+ static_cast<InlinePrefISizeData*>(aData));
+ }
+
+ // After we advance to our next-in-flow, the stored line and line container
+ // may no longer be correct. Just forget them.
+ aData->mLine = nullptr;
+ aData->SetLineContainer(nullptr);
+
+ lastInFlow = nif;
+ }
+
+ aData->mLine = savedLine;
+ aData->SetLineContainer(savedLineContainer);
+
+ // This goes at the end no matter how things are broken and how
+ // messy the bidi situations are, since per CSS2.1 section 8.6
+ // (implemented in bug 328168), the endSide border is always on the
+ // last line.
+ // We reached the last-in-flow, but it might have a next bidi
+ // continuation, in which case that continuation should handle
+ // the endSide border.
+ if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) {
+ aData->mCurrentLine += endPBM;
+ }
+}
+
+/* virtual */
+LogicalSize
+nsContainerFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
+ nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
+ aBorder.ISize(aWM) - aPadding.ISize(aWM);
+ // replaced elements always shrink-wrap
+ if ((aFlags & ComputeSizeFlags::eShrinkWrap) || IsFrameOfType(eReplaced)) {
+ // don't bother setting it if the result won't be used
+ if (StylePosition()->ISize(aWM).GetUnit() == eStyleUnit_Auto) {
+ result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
+ }
+ } else {
+ result.ISize(aWM) = availBased;
+ }
+
+ if (IsTableCaption()) {
+ // If we're a container for font size inflation, then shrink
+ // wrapping inside of us should not apply font size inflation.
+ AutoMaybeDisableFontInflation an(this);
+
+ WritingMode tableWM = GetParent()->GetWritingMode();
+ uint8_t captionSide = StyleTableBorder()->mCaptionSide;
+
+ if (aWM.IsOrthogonalTo(tableWM)) {
+ if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+ captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
+ captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
+ captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
+ // For an orthogonal caption on a block-dir side of the table,
+ // shrink-wrap to min-isize.
+ result.ISize(aWM) = GetMinISize(aRenderingContext);
+ } else {
+ // An orthogonal caption on an inline-dir side of the table
+ // is constrained to the containing block.
+ nscoord pref = GetPrefISize(aRenderingContext);
+ if (pref > aCBSize.ISize(aWM)) {
+ pref = aCBSize.ISize(aWM);
+ }
+ if (pref < result.ISize(aWM)) {
+ result.ISize(aWM) = pref;
+ }
+ }
+ } else {
+ if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
+ captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
+ result.ISize(aWM) = GetMinISize(aRenderingContext);
+ } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+ captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+ // The outer frame constrains our available isize to the isize of
+ // the table. Grow if our min-isize is bigger than that, but not
+ // larger than the containing block isize. (It would really be nice
+ // to transmit that information another way, so we could grow up to
+ // the table's available isize, but that's harder.)
+ nscoord min = GetMinISize(aRenderingContext);
+ if (min > aCBSize.ISize(aWM)) {
+ min = aCBSize.ISize(aWM);
+ }
+ if (min > result.ISize(aWM)) {
+ result.ISize(aWM) = min;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+void
+nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ const WritingMode& aWM,
+ const LogicalPoint& aPos,
+ const nsSize& aContainerSize,
+ uint32_t aFlags,
+ nsReflowStatus& aStatus,
+ nsOverflowContinuationTracker* aTracker)
+{
+ NS_PRECONDITION(aReflowInput.mFrame == aKidFrame, "bad reflow state");
+ if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
+ NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
+ "ReflowChild with unconstrained container width!");
+ }
+
+ // Position the child frame and its view if requested.
+ if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
+ aKidFrame->SetPosition(aWM, aPos, aContainerSize);
+ }
+
+ if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
+ PositionFrameView(aKidFrame);
+ }
+
+ // Reflow the child frame
+ aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
+
+ // If the child frame is complete, delete any next-in-flows,
+ // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
+ if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
+ NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
+ !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
+ nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
+ if (kidNextInFlow) {
+ // Remove all of the childs next-in-flows. Make sure that we ask
+ // the right parent to do the removal (it's possible that the
+ // parent is not this because we are executing pullup code)
+ nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
+ kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
+ }
+ }
+}
+
+//XXX temporary: hold on to a copy of the old physical version of
+// ReflowChild so that we can convert callers incrementally.
+void
+nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nscoord aX,
+ nscoord aY,
+ uint32_t aFlags,
+ nsReflowStatus& aStatus,
+ nsOverflowContinuationTracker* aTracker)
+{
+ NS_PRECONDITION(aReflowInput.mFrame == aKidFrame, "bad reflow state");
+
+ // Position the child frame and its view if requested.
+ if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
+ aKidFrame->SetPosition(nsPoint(aX, aY));
+ }
+
+ if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
+ PositionFrameView(aKidFrame);
+ }
+
+ // Reflow the child frame
+ aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
+
+ // If the child frame is complete, delete any next-in-flows,
+ // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
+ if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
+ !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
+ nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
+ if (kidNextInFlow) {
+ // Remove all of the childs next-in-flows. Make sure that we ask
+ // the right parent to do the removal (it's possible that the
+ // parent is not this because we are executing pullup code)
+ nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
+ kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
+ }
+ }
+}
+
+
+/**
+ * Position the views of |aFrame|'s descendants. A container frame
+ * should call this method if it moves a frame after |Reflow|.
+ */
+void
+nsContainerFrame::PositionChildViews(nsIFrame* aFrame)
+{
+ if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
+ return;
+ }
+
+ // Recursively walk aFrame's child frames.
+ // Process the additional child lists, but skip the popup list as the
+ // view for popups is managed by the parent. Currently only nsMenuFrame
+ // and nsPopupSetFrame have a popupList and during layout will adjust the
+ // view manually to position the popup.
+ ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ if (lists.CurrentID() == kPopupList) {
+ continue;
+ }
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ // Position the frame's view (if it has one) otherwise recursively
+ // process its children
+ nsIFrame* childFrame = childFrames.get();
+ if (childFrame->HasView()) {
+ PositionFrameView(childFrame);
+ } else {
+ PositionChildViews(childFrame);
+ }
+ }
+ }
+}
+
+/**
+ * The second half of frame reflow. Does the following:
+ * - sets the frame's bounds
+ * - sizes and positions (if requested) the frame's view. If the frame's final
+ * position differs from the current position and the frame itself does not
+ * have a view, then any child frames with views are positioned so they stay
+ * in sync
+ * - sets the view's visibility, opacity, content transparency, and clip
+ * - invoked the DidReflow() function
+ *
+ * Flags:
+ * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
+ * case. Also implies NS_FRAME_NO_MOVE_VIEW
+ * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
+ * don't want to automatically sync the frame and view
+ * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
+ */
+void
+nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ const ReflowOutput& aDesiredSize,
+ const ReflowInput* aReflowInput,
+ const WritingMode& aWM,
+ const LogicalPoint& aPos,
+ const nsSize& aContainerSize,
+ uint32_t aFlags)
+{
+ if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
+ NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
+ "FinishReflowChild with unconstrained container width!");
+ }
+
+ nsPoint curOrigin = aKidFrame->GetPosition();
+ WritingMode outerWM = aDesiredSize.GetWritingMode();
+ LogicalSize convertedSize = aDesiredSize.Size(outerWM).ConvertTo(aWM,
+ outerWM);
+
+ if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
+ aKidFrame->SetRect(aWM, LogicalRect(aWM, aPos, convertedSize),
+ aContainerSize);
+ } else {
+ aKidFrame->SetSize(aWM, convertedSize);
+ }
+
+ if (aKidFrame->HasView()) {
+ nsView* view = aKidFrame->GetView();
+ // Make sure the frame's view is properly sized and positioned and has
+ // things like opacity correct
+ SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
+ aDesiredSize.VisualOverflow(), aFlags);
+ }
+
+ nsPoint newOrigin = aKidFrame->GetPosition();
+ if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && curOrigin != newOrigin) {
+ if (!aKidFrame->HasView()) {
+ // If the frame has moved, then we need to make sure any child views are
+ // correctly positioned
+ PositionChildViews(aKidFrame);
+ }
+ }
+
+ aKidFrame->DidReflow(aPresContext, aReflowInput, nsDidReflowStatus::FINISHED);
+}
+
+//XXX temporary: hold on to a copy of the old physical version of
+// FinishReflowChild so that we can convert callers incrementally.
+void
+nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ const ReflowOutput& aDesiredSize,
+ const ReflowInput* aReflowInput,
+ nscoord aX,
+ nscoord aY,
+ uint32_t aFlags)
+{
+ nsPoint curOrigin = aKidFrame->GetPosition();
+
+ if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
+ aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height()));
+ } else {
+ aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
+ }
+
+ if (aKidFrame->HasView()) {
+ nsView* view = aKidFrame->GetView();
+ // Make sure the frame's view is properly sized and positioned and has
+ // things like opacity correct
+ SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
+ aDesiredSize.VisualOverflow(), aFlags);
+ }
+
+ if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) &&
+ (curOrigin.x != aX || curOrigin.y != aY)) {
+ if (!aKidFrame->HasView()) {
+ // If the frame has moved, then we need to make sure any child views are
+ // correctly positioned
+ PositionChildViews(aKidFrame);
+ }
+ }
+
+ aKidFrame->DidReflow(aPresContext, aReflowInput, nsDidReflowStatus::FINISHED);
+}
+
+void
+nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsOverflowAreas& aOverflowRects,
+ uint32_t aFlags,
+ nsReflowStatus& aStatus,
+ ChildFrameMerger aMergeFunc)
+{
+ NS_PRECONDITION(aPresContext, "null pointer");
+
+ nsFrameList* overflowContainers = DrainExcessOverflowContainersList(aMergeFunc);
+ if (!overflowContainers) {
+ return; // nothing to reflow
+ }
+
+ nsOverflowContinuationTracker tracker(this, false, false);
+ bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids();
+
+ for (nsIFrame* frame : *overflowContainers) {
+ if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
+ // frame's prevInFlow has moved, skip reflowing this frame;
+ // it will get reflowed once it's been placed
+ continue;
+ }
+ // If the available vertical height has changed, we need to reflow
+ // even if the frame isn't dirty.
+ if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) {
+ // Get prev-in-flow
+ nsIFrame* prevInFlow = frame->GetPrevInFlow();
+ NS_ASSERTION(prevInFlow,
+ "overflow container frame must have a prev-in-flow");
+ NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
+ "overflow container frame must have overflow container bit set");
+ WritingMode wm = frame->GetWritingMode();
+ nsSize containerSize = aReflowInput.AvailableSize(wm).GetPhysicalSize(wm);
+ LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize);
+
+ // Initialize reflow params
+ LogicalSize availSpace(wm, prevRect.ISize(wm),
+ aReflowInput.AvailableSize(wm).BSize(wm));
+ ReflowOutput desiredSize(aReflowInput);
+ ReflowInput frameState(aPresContext, aReflowInput,
+ frame, availSpace);
+ nsReflowStatus frameStatus;
+
+ // Reflow
+ LogicalPoint pos(wm, prevRect.IStart(wm), 0);
+ ReflowChild(frame, aPresContext, desiredSize, frameState,
+ wm, pos, containerSize, aFlags, frameStatus, &tracker);
+ //XXXfr Do we need to override any shrinkwrap effects here?
+ // e.g. desiredSize.Width() = prevRect.width;
+ FinishReflowChild(frame, aPresContext, desiredSize, &frameState,
+ wm, pos, containerSize, aFlags);
+
+ // Handle continuations
+ if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) {
+ if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+ // Abspos frames can't cause their parent to be incomplete,
+ // only overflow incomplete.
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus);
+ }
+ else {
+ NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus),
+ "overflow container frames can't be incomplete, only overflow-incomplete");
+ }
+
+ // Acquire a next-in-flow, creating it if necessary
+ nsIFrame* nif = frame->GetNextInFlow();
+ if (!nif) {
+ NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW,
+ "Someone forgot a REFLOW_NEXTINFLOW flag");
+ nif = aPresContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(aPresContext, frame, this);
+ }
+ else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ // used to be a normal next-in-flow; steal it from the child list
+ nsresult rv = nif->GetParent()->StealFrame(nif);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+
+ tracker.Insert(nif, frameStatus);
+ }
+ NS_MergeReflowStatusInto(&aStatus, frameStatus);
+ // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(),
+ // but we have some unsplittable frames that, when taller than
+ // availableHeight will push zero-height content into a next-in-flow.
+ }
+ else {
+ tracker.Skip(frame, aStatus);
+ if (aReflowInput.mFloatManager) {
+ nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager,
+ aReflowInput.GetWritingMode(),
+ aReflowInput.ComputedPhysicalSize());
+ }
+ }
+ ConsiderChildOverflow(aOverflowRects, frame);
+ }
+}
+
+void
+nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty());
+ if (overflowconts) {
+ for (nsIFrame* frame : *overflowconts) {
+ BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists);
+ }
+ }
+}
+
+static bool
+TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable,
+ nsContainerFrame::FrameListPropertyDescriptor aProp,
+ nsIFrame* aChildToRemove)
+{
+ nsFrameList* list = aPropTable->Get(aFrame, aProp);
+ if (list && list->StartRemoveFrame(aChildToRemove)) {
+ // aChildToRemove *may* have been removed from this list.
+ if (list->IsEmpty()) {
+ aPropTable->Remove(aFrame, aProp);
+ list->Delete(aFrame->PresContext()->PresShell());
+ }
+ return true;
+ }
+ return false;
+}
+
+bool
+nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild)
+{
+ bool removed = false;
+ if (MOZ_UNLIKELY(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ FramePropertyTable* propTable = PresContext()->PropertyTable();
+ // Try removing from the overflow container list.
+ removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(),
+ aChild);
+ if (!removed) {
+ // It might be in the excess overflow container list.
+ removed = ::TryRemoveFrame(this, propTable,
+ ExcessOverflowContainersProperty(),
+ aChild);
+ }
+ }
+ return removed;
+}
+
+nsresult
+nsContainerFrame::StealFrame(nsIFrame* aChild)
+{
+#ifdef DEBUG
+ if (!mFrames.ContainsFrame(aChild)) {
+ nsFrameList* list = GetOverflowFrames();
+ if (!list || !list->ContainsFrame(aChild)) {
+ FramePropertyTable* propTable = PresContext()->PropertyTable();
+ list = propTable->Get(this, OverflowContainersProperty());
+ if (!list || !list->ContainsFrame(aChild)) {
+ list = propTable->Get(this, ExcessOverflowContainersProperty());
+ MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child"
+ " or on a frame list not supported by StealFrame");
+ }
+ }
+ }
+#endif
+
+ bool removed = MaybeStealOverflowContainerFrame(aChild);
+ if (!removed) {
+ // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers
+ // on the normal lists so we might get here also if the frame bit
+ // NS_FRAME_IS_OVERFLOW_CONTAINER is set.
+ removed = mFrames.StartRemoveFrame(aChild);
+ if (!removed) {
+ // We didn't find the child in our principal child list.
+ // Maybe it's on the overflow list?
+ nsFrameList* frameList = GetOverflowFrames();
+ if (frameList) {
+ removed = frameList->ContinueRemoveFrame(aChild);
+ if (frameList->IsEmpty()) {
+ DestroyOverflowList();
+ }
+ }
+ }
+ }
+
+ NS_POSTCONDITION(removed, "StealFrame: can't find aChild");
+ return removed ? NS_OK : NS_ERROR_UNEXPECTED;
+}
+
+nsFrameList
+nsContainerFrame::StealFramesAfter(nsIFrame* aChild)
+{
+ NS_ASSERTION(!aChild ||
+ !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
+ "StealFramesAfter doesn't handle overflow containers");
+ NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call");
+
+ if (!aChild) {
+ nsFrameList copy(mFrames);
+ mFrames.Clear();
+ return copy;
+ }
+
+ for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd();
+ iter.Next()) {
+ if (iter.PrevFrame() == aChild) {
+ return mFrames.ExtractTail(iter);
+ }
+ }
+
+ // We didn't find the child in the principal child list.
+ // Maybe it's on the overflow list?
+ nsFrameList* overflowFrames = GetOverflowFrames();
+ if (overflowFrames) {
+ for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd();
+ iter.Next()) {
+ if (iter.PrevFrame() == aChild) {
+ return overflowFrames->ExtractTail(iter);
+ }
+ }
+ }
+
+ NS_ERROR("StealFramesAfter: can't find aChild");
+ return nsFrameList::EmptyList();
+}
+
+/*
+ * Create a next-in-flow for aFrame. Will return the newly created
+ * frame <b>if and only if</b> a new frame is created; otherwise
+ * nullptr is returned.
+ */
+nsIFrame*
+nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame)
+{
+ NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame,
+ "you should have called nsBlockFrame::CreateContinuationFor instead");
+ NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");
+
+ nsPresContext* pc = PresContext();
+ nsIFrame* nextInFlow = aFrame->GetNextInFlow();
+ if (nullptr == nextInFlow) {
+ // Create a continuation frame for the child frame and insert it
+ // into our child list.
+ nextInFlow = pc->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(pc, aFrame, this);
+ mFrames.InsertFrame(nullptr, aFrame, nextInFlow);
+
+ NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
+ ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
+ aFrame, nextInFlow));
+
+ return nextInFlow;
+ }
+ return nullptr;
+}
+
+/**
+ * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
+ * pointers
+ */
+void
+nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
+ bool aDeletingEmptyFrames)
+{
+#ifdef DEBUG
+ nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
+#endif
+ NS_PRECONDITION(prevInFlow, "bad prev-in-flow");
+
+ // If the next-in-flow has a next-in-flow then delete it, too (and
+ // delete it first).
+ // Do this in a loop so we don't overflow the stack for frames
+ // with very many next-in-flows
+ nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
+ if (nextNextInFlow) {
+ AutoTArray<nsIFrame*, 8> frames;
+ for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
+ frames.AppendElement(f);
+ }
+ for (int32_t i = frames.Length() - 1; i >= 0; --i) {
+ nsIFrame* delFrame = frames.ElementAt(i);
+ delFrame->GetParent()->
+ DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames);
+ }
+ }
+
+ // Take the next-in-flow out of the parent's child list
+ DebugOnly<nsresult> rv = StealFrame(aNextInFlow);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
+
+#ifdef DEBUG
+ if (aDeletingEmptyFrames) {
+ nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
+ }
+#endif
+
+ // Delete the next-in-flow frame and its descendants. This will also
+ // remove it from its next-in-flow/prev-in-flow chain.
+ aNextInFlow->Destroy();
+
+ NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
+}
+
+/**
+ * Set the frames on the overflow list
+ */
+void
+nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames)
+{
+ NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
+
+ nsPresContext* pc = PresContext();
+ nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames);
+
+ pc->PropertyTable()->Set(this, OverflowProperty(), newList);
+}
+
+nsFrameList*
+nsContainerFrame::GetPropTableFrames(
+ FrameListPropertyDescriptor aProperty) const
+{
+ return PresContext()->PropertyTable()->Get(this, aProperty);
+}
+
+nsFrameList*
+nsContainerFrame::RemovePropTableFrames(FrameListPropertyDescriptor aProperty)
+{
+ return PresContext()->PropertyTable()->Remove(this, aProperty);
+}
+
+void
+nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList,
+ FrameListPropertyDescriptor aProperty)
+{
+ NS_PRECONDITION(aProperty && aFrameList, "null ptr");
+ NS_PRECONDITION(
+ (aProperty != nsContainerFrame::OverflowContainersProperty() &&
+ aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
+ IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
+ "this type of frame can't have overflow containers");
+ MOZ_ASSERT(!GetPropTableFrames(aProperty));
+ PresContext()->PropertyTable()->Set(this, aProperty, aFrameList);
+}
+
+/**
+ * Push aFromChild and its next siblings to the next-in-flow. Change the
+ * geometric parent of each frame that's pushed. If there is no next-in-flow
+ * the frames are placed on the overflow list (and the geometric parent is
+ * left unchanged).
+ *
+ * Updates the next-in-flow's child count. Does <b>not</b> update the
+ * pusher's child count.
+ *
+ * @param aFromChild the first child frame to push. It is disconnected from
+ * aPrevSibling
+ * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's
+ * an error to push a parent's first child frame
+ */
+void
+nsContainerFrame::PushChildren(nsIFrame* aFromChild,
+ nsIFrame* aPrevSibling)
+{
+ NS_PRECONDITION(aFromChild, "null pointer");
+ NS_PRECONDITION(aPrevSibling, "pushing first child");
+ NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
+
+ // Disconnect aFromChild from its previous sibling
+ nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling);
+
+ nsContainerFrame* nextInFlow =
+ static_cast<nsContainerFrame*>(GetNextInFlow());
+ if (nextInFlow) {
+ // XXX This is not a very good thing to do. If it gets removed
+ // then remove the copy of this routine that doesn't do this from
+ // nsInlineFrame.
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented.
+ for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) {
+ nsContainerFrame::ReparentFrameView(f, this, nextInFlow);
+ }
+ nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail);
+ }
+ else {
+ // Add the frames to our overflow list
+ SetOverflowFrames(tail);
+ }
+}
+
+/**
+ * Moves any frames on the overflow lists (the prev-in-flow's overflow list and
+ * the receiver's overflow list) to the child list.
+ *
+ * Updates this frame's child count and content mapping.
+ *
+ * @return true if any frames were moved and false otherwise
+ */
+bool
+nsContainerFrame::MoveOverflowToChildList()
+{
+ bool result = false;
+
+ // Check for an overflow list with our prev-in-flow
+ nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
+ if (nullptr != prevInFlow) {
+ AutoFrameListPtr prevOverflowFrames(PresContext(),
+ prevInFlow->StealOverflowFrames());
+ if (prevOverflowFrames) {
+ // Tables are special; they can have repeated header/footer
+ // frames on mFrames at this point.
+ NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
+ "bad overflow list");
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented.
+ nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames,
+ prevInFlow, this);
+ mFrames.AppendFrames(this, *prevOverflowFrames);
+ result = true;
+ }
+ }
+
+ // It's also possible that we have an overflow list for ourselves.
+ return DrainSelfOverflowList() || result;
+}
+
+bool
+nsContainerFrame::DrainSelfOverflowList()
+{
+ AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
+ if (overflowFrames) {
+ mFrames.AppendFrames(nullptr, *overflowFrames);
+ return true;
+ }
+ return false;
+}
+
+nsFrameList*
+nsContainerFrame::DrainExcessOverflowContainersList(ChildFrameMerger aMergeFunc)
+{
+ nsFrameList* overflowContainers =
+ GetPropTableFrames(OverflowContainersProperty());
+
+ NS_ASSERTION(!(overflowContainers && GetPrevInFlow()
+ && static_cast<nsContainerFrame*>(GetPrevInFlow())
+ ->GetPropTableFrames(ExcessOverflowContainersProperty())),
+ "conflicting overflow containers lists");
+
+ if (!overflowContainers) {
+ // Drain excess from previnflow
+ nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow();
+ if (prev) {
+ nsFrameList* excessFrames =
+ prev->RemovePropTableFrames(ExcessOverflowContainersProperty());
+ if (excessFrames) {
+ excessFrames->ApplySetParent(this);
+ nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this);
+ overflowContainers = excessFrames;
+ SetPropTableFrames(overflowContainers, OverflowContainersProperty());
+ }
+ }
+ }
+
+ // Our own excess overflow containers from a previous reflow can still be
+ // present if our next-in-flow hasn't been reflown yet. Move any children
+ // from it that don't have a continuation in this frame to the
+ // OverflowContainers list.
+ nsFrameList* selfExcessOCFrames =
+ RemovePropTableFrames(ExcessOverflowContainersProperty());
+ if (selfExcessOCFrames) {
+ nsFrameList toMove;
+ auto child = selfExcessOCFrames->FirstChild();
+ while (child) {
+ auto next = child->GetNextSibling();
+ MOZ_ASSERT(child->GetPrevInFlow(),
+ "ExcessOverflowContainers frames must be continuations");
+ if (child->GetPrevInFlow()->GetParent() != this) {
+ selfExcessOCFrames->RemoveFrame(child);
+ toMove.AppendFrame(nullptr, child);
+ }
+ child = next;
+ }
+ if (toMove.IsEmpty()) {
+ SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty());
+ } else if (overflowContainers) {
+ aMergeFunc(*overflowContainers, toMove, this);
+ if (selfExcessOCFrames->IsEmpty()) {
+ selfExcessOCFrames->Delete(PresContext()->PresShell());
+ } else {
+ SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty());
+ }
+ } else {
+ if (selfExcessOCFrames->IsEmpty()) {
+ *selfExcessOCFrames = toMove;
+ overflowContainers = selfExcessOCFrames;
+ } else {
+ SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty());
+ auto shell = PresContext()->PresShell();
+ overflowContainers = new (shell) nsFrameList(toMove);
+ }
+ SetPropTableFrames(overflowContainers, OverflowContainersProperty());
+ }
+ }
+
+ return overflowContainers;
+}
+
+nsIFrame*
+nsContainerFrame::GetNextInFlowChild(ContinuationTraversingState& aState,
+ bool* aIsInOverflow)
+{
+ nsContainerFrame*& nextInFlow = aState.mNextInFlow;
+ while (nextInFlow) {
+ // See if there is any frame in the container
+ nsIFrame* frame = nextInFlow->mFrames.FirstChild();
+ if (frame) {
+ if (aIsInOverflow) {
+ *aIsInOverflow = false;
+ }
+ return frame;
+ }
+ // No frames in the principal list, try its overflow list
+ nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
+ if (overflowFrames) {
+ if (aIsInOverflow) {
+ *aIsInOverflow = true;
+ }
+ return overflowFrames->FirstChild();
+ }
+ nextInFlow = static_cast<nsContainerFrame*>(nextInFlow->GetNextInFlow());
+ }
+ return nullptr;
+}
+
+nsIFrame*
+nsContainerFrame::PullNextInFlowChild(ContinuationTraversingState& aState)
+{
+ bool isInOverflow;
+ nsIFrame* frame = GetNextInFlowChild(aState, &isInOverflow);
+ if (frame) {
+ nsContainerFrame* nextInFlow = aState.mNextInFlow;
+ if (isInOverflow) {
+ nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
+ overflowFrames->RemoveFirstChild();
+ if (overflowFrames->IsEmpty()) {
+ nextInFlow->DestroyOverflowList();
+ }
+ } else {
+ nextInFlow->mFrames.RemoveFirstChild();
+ }
+
+ // Move the frame to the principal frame list of this container
+ mFrames.AppendFrame(this, frame);
+ // AppendFrame has reparented the frame, we need
+ // to reparent the frame view then.
+ nsContainerFrame::ReparentFrameView(frame, nextInFlow, this);
+ }
+ return frame;
+}
+
+bool
+nsContainerFrame::ResolvedOrientationIsVertical()
+{
+ StyleOrient orient = StyleDisplay()->mOrient;
+ switch (orient) {
+ case StyleOrient::Horizontal:
+ return false;
+ case StyleOrient::Vertical:
+ return true;
+ case StyleOrient::Inline:
+ return GetWritingMode().IsVertical();
+ case StyleOrient::Block:
+ return !GetWritingMode().IsVertical();
+ }
+ NS_NOTREACHED("unexpected -moz-orient value");
+ return false;
+}
+
+// static
+bool
+nsContainerFrame::FrameStartsCounterScope(nsIFrame* aFrame)
+{
+ nsIContent* content = aFrame->GetContent();
+ if (!content || !content->IsHTMLElement())
+ return false;
+
+ nsIAtom* localName = content->NodeInfo()->NameAtom();
+ return localName == nsGkAtoms::ol ||
+ localName == nsGkAtoms::ul ||
+ localName == nsGkAtoms::dir ||
+ localName == nsGkAtoms::menu;
+}
+
+bool
+nsContainerFrame::RenumberList()
+{
+ if (!FrameStartsCounterScope(this)) {
+ // If this frame doesn't start a counter scope then we don't need
+ // to renumber child list items.
+ return false;
+ }
+
+ MOZ_ASSERT(mContent->IsHTMLElement(),
+ "FrameStartsCounterScope should only return true for HTML elements");
+
+ // Setup initial list ordinal value
+ // XXX Map html's start property to counter-reset style
+ int32_t ordinal = 1;
+ int32_t increment;
+ if (mContent->IsHTMLElement(nsGkAtoms::ol) &&
+ mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) {
+ increment = -1;
+ } else {
+ increment = 1;
+ }
+
+ nsGenericHTMLElement* hc = nsGenericHTMLElement::FromContent(mContent);
+ // Must be non-null, since FrameStartsCounterScope only returns true
+ // for HTML elements.
+ MOZ_ASSERT(hc, "How is mContent not HTML?");
+ const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
+ nsContainerFrame* fif = static_cast<nsContainerFrame*>(FirstInFlow());
+ if (attr && attr->Type() == nsAttrValue::eInteger) {
+ ordinal = attr->GetIntegerValue();
+ } else if (increment < 0) {
+ // <ol reversed> case, or some other case with a negative increment: count
+ // up the child list
+ ordinal = 0;
+ fif->RenumberChildFrames(&ordinal, 0, -increment, true);
+ }
+
+ return fif->RenumberChildFrames(&ordinal, 0, increment, false);
+}
+
+// add in a sanity check for absurdly deep frame trees. See bug 42138
+// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
+#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
+
+bool
+nsContainerFrame::RenumberFrameAndDescendants(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting)
+{
+ NS_PRECONDITION(aOrdinal, "null params are immoral!");
+
+ // add in a sanity check for absurdly deep frame trees. See bug 42138
+ if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth) {
+ return false;
+ }
+ const nsStyleDisplay* display = StyleDisplay();
+
+ // drill down through any wrappers to the real frame
+ nsIFrame* kid = GetContentInsertionFrame();
+ if (!kid) {
+ return false;
+ }
+
+ // Do not renumber list for summary elements.
+ if (HTMLDetailsElement::IsDetailsEnabled()) {
+ HTMLSummaryElement* summary =
+ HTMLSummaryElement::FromContent(kid->GetContent());
+ if (summary && summary->IsMainSummary()) {
+ return false;
+ }
+ }
+
+ bool kidRenumberedABullet = false;
+
+ // If the frame is a list-item and the frame implements our
+ // block frame API then get its bullet and set the list item
+ // ordinal.
+ if (mozilla::StyleDisplay::ListItem == display->mDisplay) {
+ // Make certain that the frame is a block frame in case
+ // something foreign has crept in.
+ nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid);
+ if (listItem) {
+ nsBulletFrame* bullet = listItem->GetBullet();
+ if (bullet) {
+ if (!aForCounting) {
+ bool changed;
+ *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement);
+ if (changed) {
+ kidRenumberedABullet = true;
+
+ // The ordinal changed - mark the bullet frame, and any
+ // intermediate frames between it and the block (are there
+ // ever any?), dirty.
+ // The calling code will make the necessary FrameNeedsReflow
+ // call for the list ancestor.
+ bullet->AddStateBits(NS_FRAME_IS_DIRTY);
+ nsIFrame *f = bullet;
+ do {
+ nsIFrame *parent = f->GetParent();
+ parent->ChildIsDirty(f);
+ f = parent;
+ } while (f != listItem);
+ }
+ } else {
+ // We're only counting the number of children,
+ // not restyling them. Don't take |value|
+ // into account when incrementing the ordinal
+ // or dirty the bullet.
+ *aOrdinal += aIncrement;
+ }
+ }
+
+ // XXX temporary? if the list-item has child list-items they
+ // should be numbered too; especially since the list-item is
+ // itself (ASSUMED!) not to be a counter-resetter.
+ bool meToo = listItem->RenumberChildFrames(aOrdinal, aDepth + 1,
+ aIncrement, aForCounting);
+ if (meToo) {
+ kidRenumberedABullet = true;
+ }
+ }
+ } else if (display->mDisplay == mozilla::StyleDisplay::Block ||
+ display->mDisplay == mozilla::StyleDisplay::Flex ||
+ display->mDisplay == mozilla::StyleDisplay::Grid) {
+ if (FrameStartsCounterScope(kid)) {
+ // Don't bother recursing into a frame that is a new counter scope.
+ // Any list-items in there will be handled by it.
+ } else {
+ nsContainerFrame* container = do_QueryFrame(kid);
+ if (container) {
+ kidRenumberedABullet =
+ container->RenumberChildFrames(aOrdinal, aDepth + 1,
+ aIncrement, aForCounting);
+ }
+ }
+ }
+ return kidRenumberedABullet;
+}
+
+bool
+nsContainerFrame::RenumberChildFrames(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting)
+{
+ bool renumbered = false;
+ for (auto kid : mFrames) {
+ bool kidRenumbered =
+ kid->RenumberFrameAndDescendants(aOrdinal, aDepth, aIncrement, aForCounting);
+ if (!aForCounting && kidRenumbered) {
+ renumbered = true;
+ }
+ }
+
+ // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
+ // the bullet and the caller of RenumberLists. But the caller itself
+ // has to be responsible for setting the bit itself, since that caller
+ // might be making a FrameNeedsReflow call, which requires that the
+ // bit not be set yet.
+ if (renumbered && aDepth != 0) {
+ AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+
+ return renumbered;
+}
+
+uint16_t
+nsContainerFrame::CSSAlignmentForAbsPosChild(const ReflowInput& aChildRI,
+ LogicalAxis aLogicalAxis) const
+{
+ MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
+ "This method should only be called for abspos children");
+ NS_ERROR("Child classes that use css box alignment for abspos children "
+ "should provide their own implementation of this method!");
+
+ // In the unexpected/unlikely event that this implementation gets invoked,
+ // just use "start" alignment.
+ return NS_STYLE_ALIGN_START;
+}
+
+nsresult
+nsContainerFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
+ aAttribute, aModType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (nsGkAtoms::start == aAttribute ||
+ (nsGkAtoms::reversed == aAttribute &&
+ mContent->IsHTMLElement(nsGkAtoms::ol))) {
+
+ // XXX Not sure if this is necessary anymore
+ if (RenumberList()) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eStyleChange,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ }
+ return rv;
+}
+
+nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame,
+ bool aWalkOOFFrames,
+ bool aSkipOverflowContainerChildren)
+ : mOverflowContList(nullptr),
+ mPrevOverflowCont(nullptr),
+ mSentry(nullptr),
+ mParent(aFrame),
+ mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
+ mWalkOOFFrames(aWalkOOFFrames)
+{
+ NS_PRECONDITION(aFrame, "null frame pointer");
+ SetupOverflowContList();
+}
+
+void
+nsOverflowContinuationTracker::SetupOverflowContList()
+{
+ NS_PRECONDITION(mParent, "null frame pointer");
+ NS_PRECONDITION(!mOverflowContList, "already have list");
+ nsContainerFrame* nif =
+ static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
+ if (nif) {
+ mOverflowContList = nif->GetPropTableFrames(
+ nsContainerFrame::OverflowContainersProperty());
+ if (mOverflowContList) {
+ mParent = nif;
+ SetUpListWalker();
+ }
+ }
+ if (!mOverflowContList) {
+ mOverflowContList = mParent->GetPropTableFrames(
+ nsContainerFrame::ExcessOverflowContainersProperty());
+ if (mOverflowContList) {
+ SetUpListWalker();
+ }
+ }
+}
+
+/**
+ * Helper function to walk past overflow continuations whose prev-in-flow
+ * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
+ */
+void
+nsOverflowContinuationTracker::SetUpListWalker()
+{
+ NS_ASSERTION(!mSentry && !mPrevOverflowCont,
+ "forgot to reset mSentry or mPrevOverflowCont");
+ if (mOverflowContList) {
+ nsIFrame* cur = mOverflowContList->FirstChild();
+ if (mSkipOverflowContainerChildren) {
+ while (cur && (cur->GetPrevInFlow()->GetStateBits()
+ & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ mPrevOverflowCont = cur;
+ cur = cur->GetNextSibling();
+ }
+ while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
+ == mWalkOOFFrames)) {
+ mPrevOverflowCont = cur;
+ cur = cur->GetNextSibling();
+ }
+ }
+ if (cur) {
+ mSentry = cur->GetPrevInFlow();
+ }
+ }
+}
+
+/**
+ * Helper function to step forward through the overflow continuations list.
+ * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
+ * as appropriate. May only be called when we have already set up an
+ * mOverflowContList; mOverflowContList cannot be null.
+ */
+void
+nsOverflowContinuationTracker::StepForward()
+{
+ NS_PRECONDITION(mOverflowContList, "null list");
+
+ // Step forward
+ if (mPrevOverflowCont) {
+ mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
+ }
+ else {
+ mPrevOverflowCont = mOverflowContList->FirstChild();
+ }
+
+ // Skip over oof or non-oof frames as appropriate
+ if (mSkipOverflowContainerChildren) {
+ nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
+ while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
+ == mWalkOOFFrames)) {
+ mPrevOverflowCont = cur;
+ cur = cur->GetNextSibling();
+ }
+ }
+
+ // Set up the sentry
+ mSentry = (mPrevOverflowCont->GetNextSibling())
+ ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
+ : nullptr;
+}
+
+nsresult
+nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
+ nsReflowStatus& aReflowStatus)
+{
+ NS_PRECONDITION(aOverflowCont, "null frame pointer");
+ NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames ==
+ !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
+ "shouldn't insert frame that doesn't match walker type");
+ NS_PRECONDITION(aOverflowCont->GetPrevInFlow(),
+ "overflow containers must have a prev-in-flow");
+ nsresult rv = NS_OK;
+ bool reparented = false;
+ nsPresContext* presContext = aOverflowCont->PresContext();
+ bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
+
+ // If we have a list and aOverflowCont is already in it then don't try to
+ // add it again.
+ if (addToList && aOverflowCont->GetParent() == mParent &&
+ (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
+ mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
+ addToList = false;
+ mPrevOverflowCont = aOverflowCont->GetPrevSibling();
+ }
+
+ if (addToList) {
+ if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
+ // aOverflowCont is in some other overflow container list,
+ // steal it first
+ NS_ASSERTION(!(mOverflowContList &&
+ mOverflowContList->ContainsFrame(aOverflowCont)),
+ "overflow containers out of order");
+ rv = aOverflowCont->GetParent()->StealFrame(aOverflowCont);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+ if (!mOverflowContList) {
+ mOverflowContList = new (presContext->PresShell()) nsFrameList();
+ mParent->SetPropTableFrames(mOverflowContList,
+ nsContainerFrame::ExcessOverflowContainersProperty());
+ SetUpListWalker();
+ }
+ if (aOverflowCont->GetParent() != mParent) {
+ nsContainerFrame::ReparentFrameView(aOverflowCont,
+ aOverflowCont->GetParent(),
+ mParent);
+ reparented = true;
+ }
+
+ // If aOverflowCont has a prev/next-in-flow that might be in
+ // mOverflowContList we need to find it and insert after/before it to
+ // maintain the order amongst next-in-flows in this list.
+ nsIFrame* pif = aOverflowCont->GetPrevInFlow();
+ nsIFrame* nif = aOverflowCont->GetNextInFlow();
+ if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
+ (nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
+ for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) {
+ nsIFrame* f = e.get();
+ if (f == pif) {
+ mPrevOverflowCont = pif;
+ break;
+ }
+ if (f == nif) {
+ mPrevOverflowCont = f->GetPrevSibling();
+ break;
+ }
+ }
+ }
+
+ mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
+ aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+
+ // If we need to reflow it, mark it dirty
+ if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW)
+ aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY);
+
+ // It's in our list, just step forward
+ StepForward();
+ NS_ASSERTION(mPrevOverflowCont == aOverflowCont ||
+ (mSkipOverflowContainerChildren &&
+ (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) !=
+ (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)),
+ "OverflowContTracker in unexpected state");
+
+ if (addToList) {
+ // Convert all non-overflow-container continuations of aOverflowCont
+ // into overflow containers and move them to our overflow
+ // tracker. This preserves the invariant that the next-continuations
+ // of an overflow container are also overflow containers.
+ nsIFrame* f = aOverflowCont->GetNextContinuation();
+ if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
+ (!reparented && f->GetParent() == mParent) ||
+ (reparented && f->GetParent() != mParent))) {
+ if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ rv = f->GetParent()->StealFrame(f);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ Insert(f, aReflowStatus);
+ }
+ }
+ return rv;
+}
+
+void
+nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild)
+{
+ NS_PRECONDITION(aChild, "null ptr");
+ NS_PRECONDITION(aChild->GetNextInFlow(),
+ "supposed to call Finish *before* deleting next-in-flow!");
+ for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
+ // We'll update these in EndFinish after the next-in-flows are gone.
+ if (f == mPrevOverflowCont) {
+ mSentry = nullptr;
+ mPrevOverflowCont = nullptr;
+ break;
+ }
+ if (f == mSentry) {
+ mSentry = nullptr;
+ break;
+ }
+ }
+}
+
+void
+nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild)
+{
+ if (!mOverflowContList) {
+ return;
+ }
+ // Forget mOverflowContList if it was deleted.
+ nsPresContext* pc = aChild->PresContext();
+ FramePropertyTable* propTable = pc->PropertyTable();
+ nsFrameList* eoc = propTable->Get(
+ mParent, nsContainerFrame::ExcessOverflowContainersProperty());
+ if (eoc != mOverflowContList) {
+ nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent,
+ nsContainerFrame::OverflowContainersProperty()));
+ if (oc != mOverflowContList) {
+ // mOverflowContList was deleted
+ mPrevOverflowCont = nullptr;
+ mSentry = nullptr;
+ mParent = aChild->GetParent();
+ mOverflowContList = nullptr;
+ SetupOverflowContList();
+ return;
+ }
+ }
+ // The list survived, update mSentry if needed.
+ if (!mSentry) {
+ if (!mPrevOverflowCont) {
+ SetUpListWalker();
+ } else {
+ mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
+ // step backward to make StepForward() use our current mPrevOverflowCont
+ mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
+ StepForward();
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Debugging
+
+#ifdef DEBUG_FRAME_DUMP
+void
+nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+
+ // Output the children
+ bool outputOneList = false;
+ ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ if (outputOneList) {
+ str += aPrefix;
+ }
+ if (lists.CurrentID() != kPrincipalList) {
+ if (!outputOneList) {
+ str += "\n";
+ str += aPrefix;
+ }
+ str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()),
+ &GetChildList(lists.CurrentID()));
+ }
+ fprintf_stderr(out, "%s<\n", str.get());
+ str = "";
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* kid = childFrames.get();
+ // Verify the child frame's parent frame pointer is correct
+ NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer");
+
+ // Have the child frame list
+ nsCString pfx(aPrefix);
+ pfx += " ";
+ kid->List(out, pfx.get(), aFlags);
+ }
+ fprintf_stderr(out, "%s>\n", aPrefix);
+ outputOneList = true;
+ }
+
+ if (!outputOneList) {
+ fprintf_stderr(out, "%s<>\n", str.get());
+ }
+}
+#endif
diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h
new file mode 100644
index 000000000..a9f6d52df
--- /dev/null
+++ b/layout/generic/nsContainerFrame.h
@@ -0,0 +1,923 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class #1 for rendering objects that have child lists */
+
+#ifndef nsContainerFrame_h___
+#define nsContainerFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsSplittableFrame.h"
+#include "nsFrameList.h"
+#include "nsLayoutUtils.h"
+
+// Option flags for ReflowChild() and FinishReflowChild()
+// member functions
+#define NS_FRAME_NO_MOVE_VIEW 0x0001
+#define NS_FRAME_NO_MOVE_FRAME (0x0002 | NS_FRAME_NO_MOVE_VIEW)
+#define NS_FRAME_NO_SIZE_VIEW 0x0004
+#define NS_FRAME_NO_VISIBILITY 0x0008
+// Only applies to ReflowChild; if true, don't delete the next-in-flow, even
+// if the reflow is fully complete.
+#define NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD 0x0010
+
+class nsOverflowContinuationTracker;
+namespace mozilla {
+class FramePropertyTable;
+} // namespace mozilla
+
+// Some macros for container classes to do sanity checking on
+// width/height/x/y values computed during reflow.
+// NOTE: AppUnitsPerCSSPixel value hardwired here to remove the
+// dependency on nsDeviceContext.h. It doesn't matter if it's a
+// little off.
+#ifdef DEBUG
+// 10 million pixels, converted to app units. Note that this a bit larger
+// than 1/4 of nscoord_MAX. So, if any content gets to be this large, we're
+// definitely in danger of grazing up against nscoord_MAX; hence, it's CRAZY.
+#define CRAZY_COORD (10000000*60)
+#define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_COORD))
+#endif
+
+/**
+ * Implementation of a container frame.
+ */
+class nsContainerFrame : public nsSplittableFrame
+{
+public:
+ NS_DECL_ABSTRACT_FRAME(nsContainerFrame)
+ NS_DECL_QUERYFRAME_TARGET(nsContainerFrame)
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame overrides
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual nsContainerFrame* GetContentInsertionFrame() override
+ {
+ return this;
+ }
+
+ virtual const nsFrameList& GetChildList(ChildListID aList) const override;
+ virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual void ChildIsDirty(nsIFrame* aChild) override;
+
+ virtual bool IsLeaf() const override;
+ virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override;
+ virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) override;
+
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
+#endif
+
+ // nsContainerFrame methods
+
+ /**
+ * Called to set the initial list of frames. This happens after the frame
+ * has been initialized.
+ *
+ * This is only called once for a given child list, and won't be called
+ * at all for child lists with no initial list of frames.
+ *
+ * @param aListID the child list identifier.
+ * @param aChildList list of child frames. Each of the frames has its
+ * NS_FRAME_IS_DIRTY bit set. Must not be empty.
+ * This method cannot handle the child list returned by
+ * GetAbsoluteListID().
+ * @see #Init()
+ */
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList);
+
+ /**
+ * This method is responsible for appending frames to the frame
+ * list. The implementation should append the frames to the specified
+ * child list and then generate a reflow command.
+ *
+ * @param aListID the child list identifier.
+ * @param aFrameList list of child frames to append. Each of the frames has
+ * its NS_FRAME_IS_DIRTY bit set. Must not be empty.
+ */
+ virtual void AppendFrames(ChildListID aListID, nsFrameList& aFrameList);
+
+ /**
+ * This method is responsible for inserting frames into the frame
+ * list. The implementation should insert the new frames into the specified
+ * child list and then generate a reflow command.
+ *
+ * @param aListID the child list identifier.
+ * @param aPrevFrame the frame to insert frames <b>after</b>
+ * @param aFrameList list of child frames to insert <b>after</b> aPrevFrame.
+ * Each of the frames has its NS_FRAME_IS_DIRTY bit set
+ */
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList);
+
+ /**
+ * This method is responsible for removing a frame in the frame
+ * list. The implementation should do something with the removed frame
+ * and then generate a reflow command. The implementation is responsible
+ * for destroying aOldFrame (the caller mustn't destroy aOldFrame).
+ *
+ * @param aListID the child list identifier.
+ * @param aOldFrame the frame to remove
+ */
+ virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame);
+
+ /**
+ * Helper method to create next-in-flows if necessary. If aFrame
+ * already has a next-in-flow then this method does
+ * nothing. Otherwise, a new continuation frame is created and
+ * linked into the flow. In addition, the new frame is inserted
+ * into the principal child list after aFrame.
+ * @note calling this method on a block frame is illegal. Use
+ * nsBlockFrame::CreateContinuationFor() instead.
+ * @return the next-in-flow <b>if and only if</b> one is created. If
+ * a next-in-flow already exists, nullptr will be returned.
+ */
+ nsIFrame* CreateNextInFlow(nsIFrame* aFrame);
+
+ /**
+ * Delete aNextInFlow and its next-in-flows.
+ * @param aDeletingEmptyFrames if set, then the reflow for aNextInFlow's
+ * content was complete before aNextInFlow, so aNextInFlow and its
+ * next-in-flows no longer map any real content.
+ */
+ virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
+ bool aDeletingEmptyFrames);
+
+ /**
+ * Helper method to wrap views around frames. Used by containers
+ * under special circumstances (can be used by leaf frames as well)
+ */
+ static void CreateViewForFrame(nsIFrame* aFrame,
+ bool aForce);
+
+ // Positions the frame's view based on the frame's origin
+ static void PositionFrameView(nsIFrame* aKidFrame);
+
+ static nsresult ReparentFrameView(nsIFrame* aChildFrame,
+ nsIFrame* aOldParentFrame,
+ nsIFrame* aNewParentFrame);
+
+ static nsresult ReparentFrameViewList(const nsFrameList& aChildFrameList,
+ nsIFrame* aOldParentFrame,
+ nsIFrame* aNewParentFrame);
+
+ // Set the view's size and position after its frame has been reflowed.
+ //
+ // Flags:
+ // NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
+ // don't want to automatically sync the frame and view
+ // NS_FRAME_NO_SIZE_VIEW - don't size the view
+ static void SyncFrameViewAfterReflow(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsView* aView,
+ const nsRect& aVisualOverflowArea,
+ uint32_t aFlags = 0);
+
+ // Syncs properties to the top level view and window, like transparency and
+ // shadow.
+ // The SET_ASYNC indicates that the actual nsIWidget calls to sync the window
+ // properties should be done async.
+ enum {
+ SET_ASYNC = 0x01,
+ };
+ static void SyncWindowProperties(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsView* aView,
+ nsRenderingContext* aRC,
+ uint32_t aFlags);
+
+ // Sets the view's attributes from the frame style.
+ // - visibility
+ // - clip
+ // Call this when one of these styles changes or when the view has just
+ // been created.
+ // @param aStyleContext can be null, in which case the frame's style context is used
+ static void SyncFrameViewProperties(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ nsStyleContext* aStyleContext,
+ nsView* aView,
+ uint32_t aFlags = 0);
+
+ /**
+ * Converts the minimum and maximum sizes given in inner window app units to
+ * outer window device pixel sizes and assigns these constraints to the widget.
+ *
+ * @param aPresContext pres context
+ * @param aWidget widget for this frame
+ * @param minimum size of the window in app units
+ * @param maxmimum size of the window in app units
+ */
+ static void SetSizeConstraints(nsPresContext* aPresContext,
+ nsIWidget* aWidget,
+ const nsSize& aMinSize,
+ const nsSize& aMaxSize);
+
+ // Used by both nsInlineFrame and nsFirstLetterFrame.
+ void DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext,
+ InlineIntrinsicISizeData *aData,
+ nsLayoutUtils::IntrinsicISizeType aType);
+
+ /**
+ * This is the CSS block concept of computing 'auto' widths, which most
+ * classes derived from nsContainerFrame want.
+ */
+ virtual mozilla::LogicalSize
+ ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ /**
+ * Positions aChildFrame and its view (if requested), and then calls Reflow().
+ * If the reflow status after reflowing the child is FULLY_COMPLETE then any
+ * next-in-flows are deleted using DeleteNextInFlowChild().
+ *
+ * @param aContainerSize size of the border-box of the containing frame
+ *
+ * Flags:
+ * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
+ * don't want to automatically sync the frame and view
+ * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this
+ * case. Also implies NS_FRAME_NO_MOVE_VIEW
+ */
+ void ReflowChild(nsIFrame* aChildFrame,
+ nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ const mozilla::WritingMode& aWM,
+ const mozilla::LogicalPoint& aPos,
+ const nsSize& aContainerSize,
+ uint32_t aFlags,
+ nsReflowStatus& aStatus,
+ nsOverflowContinuationTracker* aTracker = nullptr);
+
+ /**
+ * The second half of frame reflow. Does the following:
+ * - sets the frame's bounds
+ * - sizes and positions (if requested) the frame's view. If the frame's final
+ * position differs from the current position and the frame itself does not
+ * have a view, then any child frames with views are positioned so they stay
+ * in sync
+ * - sets the view's visibility, opacity, content transparency, and clip
+ * - invoked the DidReflow() function
+ *
+ * @param aContainerSize size of the border-box of the containing frame
+ *
+ * Flags:
+ * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this
+ * case. Also implies NS_FRAME_NO_MOVE_VIEW
+ * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
+ * don't want to automatically sync the frame and view
+ * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
+ */
+ static void FinishReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ const ReflowOutput& aDesiredSize,
+ const ReflowInput* aReflowInput,
+ const mozilla::WritingMode& aWM,
+ const mozilla::LogicalPoint& aPos,
+ const nsSize& aContainerSize,
+ uint32_t aFlags);
+
+ //XXX temporary: hold on to a copy of the old physical versions of
+ // ReflowChild and FinishReflowChild so that we can convert callers
+ // incrementally.
+ void ReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nscoord aX,
+ nscoord aY,
+ uint32_t aFlags,
+ nsReflowStatus& aStatus,
+ nsOverflowContinuationTracker* aTracker = nullptr);
+
+ static void FinishReflowChild(nsIFrame* aKidFrame,
+ nsPresContext* aPresContext,
+ const ReflowOutput& aDesiredSize,
+ const ReflowInput* aReflowInput,
+ nscoord aX,
+ nscoord aY,
+ uint32_t aFlags);
+
+ static void PositionChildViews(nsIFrame* aFrame);
+
+ // ==========================================================================
+ /* Overflow containers are continuation frames that hold overflow. They
+ * are created when the frame runs out of computed height, but still has
+ * too much content to fit in the availableHeight. The parent creates a
+ * continuation as usual, but marks it as NS_FRAME_IS_OVERFLOW_CONTAINER
+ * and adds it to its next-in-flow's overflow container list, either by
+ * adding it directly or by putting it in its own excess overflow containers
+ * list (to be drained by the next-in-flow when it calls
+ * ReflowOverflowContainerChildren). The parent continues reflow as if
+ * the frame was complete once it ran out of computed height, but returns
+ * either an NS_FRAME_NOT_COMPLETE or NS_FRAME_OVERFLOW_INCOMPLETE reflow
+ * status to request a next-in-flow. The parent's next-in-flow is then
+ * responsible for calling ReflowOverflowContainerChildren to (drain and)
+ * reflow these overflow continuations. Overflow containers do not affect
+ * other frames' size or position during reflow (but do affect their
+ * parent's overflow area).
+ *
+ * Overflow container continuations are different from normal continuations
+ * in that
+ * - more than one child of the frame can have its next-in-flow broken
+ * off and pushed into the frame's next-in-flow
+ * - new continuations may need to be spliced into the middle of the list
+ * or deleted continuations slipped out
+ * e.g. A, B, C are all fixed-size containers on one page, all have
+ * overflow beyond availableHeight, and content is dynamically added
+ * and removed from B
+ * As a result, it is not possible to simply prepend the new continuations
+ * to the old list as with the overflowProperty mechanism. To avoid
+ * complicated list splicing, the code assumes only one overflow containers
+ * list exists for a given frame: either its own overflowContainersProperty
+ * or its prev-in-flow's excessOverflowContainersProperty, not both.
+ *
+ * The nsOverflowContinuationTracker helper class should be used for tracking
+ * overflow containers and adding them to the appropriate list.
+ * See nsBlockFrame::Reflow for a sample implementation.
+ */
+
+ friend class nsOverflowContinuationTracker;
+
+ typedef void (*ChildFrameMerger)(nsFrameList& aDest, nsFrameList& aSrc,
+ nsContainerFrame* aParent);
+ static inline void DefaultChildFrameMerge(nsFrameList& aDest,
+ nsFrameList& aSrc,
+ nsContainerFrame* aParent)
+ {
+ aDest.AppendFrames(nullptr, aSrc);
+ }
+
+ /**
+ * Reflow overflow container children. They are invisible to normal reflow
+ * (i.e. don't affect sizing or placement of other children) and inherit
+ * width and horizontal position from their prev-in-flow.
+ *
+ * This method
+ * 1. Pulls excess overflow containers from the prev-in-flow and adds
+ * them to our overflow container list
+ * 2. Reflows all our overflow container kids
+ * 3. Expands aOverflowRect as necessary to accomodate these children.
+ * 4. Sets aStatus's NS_FRAME_OVERFLOW_IS_INCOMPLETE flag (along with
+ * NS_FRAME_REFLOW_NEXTINFLOW as necessary) if any overflow children
+ * are incomplete and
+ * 5. Prepends a list of their continuations to our excess overflow
+ * container list, to be drained into our next-in-flow when it is
+ * reflowed.
+ *
+ * The caller is responsible for tracking any new overflow container
+ * continuations it makes, removing them from its child list, and
+ * making sure they are stored properly in the overflow container lists.
+ * The nsOverflowContinuationTracker helper class should be used for this.
+ *
+ * @param aFlags is passed through to ReflowChild
+ * @param aMergeFunc is passed to DrainExcessOverflowContainersList
+ */
+ void ReflowOverflowContainerChildren(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsOverflowAreas& aOverflowRects,
+ uint32_t aFlags,
+ nsReflowStatus& aStatus,
+ ChildFrameMerger aMergeFunc =
+ DefaultChildFrameMerge);
+
+ /**
+ * Move any frames on our overflow list to the end of our principal list.
+ * @return true if there were any overflow frames
+ */
+ virtual bool DrainSelfOverflowList() override;
+
+
+ /**
+ * Move all frames on our prev-in-flow's and our own ExcessOverflowContainers
+ * lists to our OverflowContainers list. If there are frames on multiple
+ * lists they are merged using aMergeFunc.
+ * @return a pointer to our OverflowContainers list, if any
+ */
+ nsFrameList* DrainExcessOverflowContainersList(ChildFrameMerger aMergeFunc =
+ DefaultChildFrameMerge);
+
+ /**
+ * Removes aChild without destroying it and without requesting reflow.
+ * Continuations are not affected. Checks the principal and overflow lists,
+ * and also the [excess] overflow containers lists if the frame bit
+ * NS_FRAME_IS_OVERFLOW_CONTAINER is set. It does not check any other lists.
+ * Returns NS_ERROR_UNEXPECTED if aChild wasn't found on any of the lists
+ * mentioned above.
+ */
+ virtual nsresult StealFrame(nsIFrame* aChild);
+
+ /**
+ * Removes the next-siblings of aChild without destroying them and without
+ * requesting reflow. Checks the principal and overflow lists (not
+ * overflow containers / excess overflow containers). Does not check any
+ * other auxiliary lists.
+ * @param aChild a child frame or nullptr
+ * @return If aChild is non-null, the next-siblings of aChild, if any.
+ * If aChild is null, all child frames on the principal list, if any.
+ */
+ nsFrameList StealFramesAfter(nsIFrame* aChild);
+
+ /**
+ * Add overflow containers to the display list
+ */
+ void DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists);
+
+ /**
+ * Builds display lists for the children. The background
+ * of each child is placed in the Content() list (suitable for inline
+ * children and other elements that behave like inlines,
+ * but not for in-flow block children of blocks). DOES NOT
+ * paint the background/borders/outline of this frame. This should
+ * probably be avoided and eventually removed. It's currently here
+ * to emulate what nsContainerFrame::Paint did.
+ */
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ static void PlaceFrameView(nsIFrame* aFrame)
+ {
+ if (aFrame->HasView())
+ nsContainerFrame::PositionFrameView(aFrame);
+ else
+ nsContainerFrame::PositionChildViews(aFrame);
+ }
+
+ static bool FrameStartsCounterScope(nsIFrame* aFrame);
+
+ /**
+ * Renumber the list of the counter scope started by this frame, if any.
+ * If this returns true, the frame it's called on should get the
+ * NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly
+ * if it's already in reflow, or via calling FrameNeedsReflow() to schedule
+ * a reflow.
+ */
+ bool RenumberList();
+
+ /**
+ * Renumber this frame if it's a list-item, then call RenumberChildFrames.
+ * @param aOrdinal Ordinal number to start counting at.
+ * Modifies this number for each associated list
+ * item. Changes in the numbering due to setting
+ * the |value| attribute are included if |aForCounting|
+ * is false. This value is both an input and output
+ * of this function, with the output value being the
+ * next ordinal number to be used.
+ * @param aDepth Current depth in frame tree from root list element.
+ * @param aIncrement Amount to increase by after visiting each associated
+ * list item, unless overridden by |value|.
+ * @param aForCounting Whether we are counting the elements or actually
+ * restyling them. When true, this simply visits all children,
+ * ignoring |<li value="..">| changes, effectively counting them
+ * and storing the result in |aOrdinal|. This is useful for
+ * |<ol reversed>|, where we need to count the number of
+ * applicable child list elements before numbering. When false,
+ * this will restyle all applicable descendants, and the next
+ * ordinal value will be stored in |aOrdinal|, taking into account
+ * any changes from |<li value="..">|.
+ */
+ bool RenumberFrameAndDescendants(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting) override;
+ /**
+ * Renumber the child frames using RenumberFrameAndDescendants.
+ * See RenumberFrameAndDescendants for description of parameters.
+ */
+ virtual bool RenumberChildFrames(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting);
+
+ /**
+ * Returns a CSS Box Alignment constant which the caller can use to align
+ * the absolutely-positioned child (whose ReflowInput is aChildRI) within
+ * a CSS Box Alignment area associated with this container.
+ *
+ * The lower 8 bits of the returned value are guaranteed to form a valid
+ * argument for CSSAlignUtils::AlignJustifySelf(). (The upper 8 bits may
+ * encode an <overflow-position>.)
+ *
+ * NOTE: This default nsContainerFrame implementation is a stub, and isn't
+ * meant to be called. Subclasses must provide their own implementations, if
+ * they use CSS Box Alignment to determine the static position of their
+ * absolutely-positioned children. (Though: if subclasses share enough code,
+ * maybe this nsContainerFrame impl should include some shared code.)
+ *
+ * @param aChildRI A ReflowInput for the positioned child frame that's being
+ * aligned.
+ * @param aLogicalAxis The axis (of this container frame) in which the caller
+ * would like to align the child frame.
+ */
+ virtual uint16_t CSSAlignmentForAbsPosChild(
+ const ReflowInput& aChildRI,
+ mozilla::LogicalAxis aLogicalAxis) const;
+
+#define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \
+ NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(prop, nsFrameList)
+
+ typedef PropertyDescriptor<nsFrameList> FrameListPropertyDescriptor;
+
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty)
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(BackdropProperty)
+
+#ifdef DEBUG
+ // Use this to suppress the CRAZY_SIZE assertions.
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugReflowingWithInfiniteISize, bool)
+ bool IsCrazySizeAssertSuppressed() const {
+ return Properties().Get(DebugReflowingWithInfiniteISize());
+ }
+#endif
+
+protected:
+ explicit nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {}
+ ~nsContainerFrame();
+
+ /**
+ * Helper for DestroyFrom. DestroyAbsoluteFrames is called before
+ * destroying frames on lists that can contain placeholders.
+ * Derived classes must do that too, if they destroy such frame lists.
+ * See nsBlockFrame::DestroyFrom for an example.
+ */
+ void DestroyAbsoluteFrames(nsIFrame* aDestructRoot);
+
+ /**
+ * Helper for StealFrame. Returns true if aChild was removed from its list.
+ */
+ bool MaybeStealOverflowContainerFrame(nsIFrame* aChild);
+
+ /**
+ * Builds a display list for non-block children that behave like
+ * inlines. This puts the background of each child into the
+ * Content() list (suitable for inline children but not for
+ * in-flow block children of blocks).
+ * @param aForcePseudoStack forces each child into a pseudo-stacking-context
+ * so its background and all other display items (except for positioned
+ * display items) go into the Content() list.
+ */
+ void BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists,
+ uint32_t aFlags = 0);
+
+ /**
+ * A version of BuildDisplayList that use DISPLAY_CHILD_INLINE.
+ * Intended as a convenience for derived classes.
+ */
+ void BuildDisplayListForInline(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) {
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+ BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
+ DISPLAY_CHILD_INLINE);
+ }
+
+
+ // ==========================================================================
+ /* Overflow Frames are frames that did not fit and must be pulled by
+ * our next-in-flow during its reflow. (The same concept for overflow
+ * containers is called "excess frames". We should probably make the
+ * names match.)
+ */
+
+ /**
+ * Get the frames on the overflow list. Can return null if there are no
+ * overflow frames. The caller does NOT take ownership of the list; it's
+ * still owned by this frame. A non-null return value indicates that the
+ * list is nonempty.
+ */
+ inline nsFrameList* GetOverflowFrames() const;
+
+ /**
+ * As GetOverflowFrames, but removes the overflow frames property. The
+ * caller is responsible for deleting nsFrameList and either passing
+ * ownership of the frames to someone else or destroying the frames.
+ * A non-null return value indicates that the list is nonempty. The
+ * recommended way to use this function it to assign its return value
+ * into an AutoFrameListPtr.
+ */
+ inline nsFrameList* StealOverflowFrames();
+
+ /**
+ * Set the overflow list. aOverflowFrames must not be an empty list.
+ */
+ void SetOverflowFrames(const nsFrameList& aOverflowFrames);
+
+ /**
+ * Destroy the overflow list, which must be empty.
+ */
+ inline void DestroyOverflowList();
+
+ /**
+ * Moves any frames on both the prev-in-flow's overflow list and the
+ * receiver's overflow to the receiver's child list.
+ *
+ * Resets the overlist pointers to nullptr, and updates the receiver's child
+ * count and content mapping.
+ *
+ * @return true if any frames were moved and false otherwise
+ */
+ bool MoveOverflowToChildList();
+
+ /**
+ * Push aFromChild and its next siblings to the next-in-flow. Change
+ * the geometric parent of each frame that's pushed. If there is no
+ * next-in-flow the frames are placed on the overflow list (and the
+ * geometric parent is left unchanged).
+ *
+ * Updates the next-in-flow's child count. Does <b>not</b> update the
+ * pusher's child count.
+ *
+ * @param aFromChild the first child frame to push. It is disconnected from
+ * aPrevSibling
+ * @param aPrevSibling aFromChild's previous sibling. Must not be null.
+ * It's an error to push a parent's first child frame
+ */
+ void PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling);
+
+ // ==========================================================================
+ /*
+ * Convenience methods for traversing continuations
+ */
+
+ struct ContinuationTraversingState
+ {
+ nsContainerFrame* mNextInFlow;
+ explicit ContinuationTraversingState(nsContainerFrame* aFrame)
+ : mNextInFlow(static_cast<nsContainerFrame*>(aFrame->GetNextInFlow()))
+ { }
+ };
+
+ /**
+ * Find the first frame that is a child of this frame's next-in-flows,
+ * considering both their principal child lists and overflow lists.
+ */
+ nsIFrame* GetNextInFlowChild(ContinuationTraversingState& aState,
+ bool* aIsInOverflow = nullptr);
+
+ /**
+ * Remove the result of GetNextInFlowChild from its current parent and
+ * append it to this frame's principal child list.
+ */
+ nsIFrame* PullNextInFlowChild(ContinuationTraversingState& aState);
+
+ // ==========================================================================
+ /*
+ * Convenience methods for nsFrameLists stored in the
+ * PresContext's proptable
+ */
+
+ /**
+ * Get the PresContext-stored nsFrameList named aPropID for this frame.
+ * May return null.
+ */
+ nsFrameList* GetPropTableFrames(FrameListPropertyDescriptor aProperty) const;
+
+ /**
+ * Remove and return the PresContext-stored nsFrameList named aPropID for
+ * this frame. May return null.
+ */
+ nsFrameList* RemovePropTableFrames(FrameListPropertyDescriptor aProperty);
+
+ /**
+ * Set the PresContext-stored nsFrameList named aPropID for this frame
+ * to the given aFrameList, which must not be null.
+ */
+ void SetPropTableFrames(nsFrameList* aFrameList,
+ FrameListPropertyDescriptor aProperty);
+
+ /**
+ * Safely destroy the frames on the nsFrameList stored on aProp for this
+ * frame then remove the property and delete the frame list.
+ * Nothing happens if the property doesn't exist.
+ */
+ void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
+ nsIPresShell* aPresShell,
+ mozilla::FramePropertyTable* aPropTable,
+ FrameListPropertyDescriptor aProp);
+
+ // ==========================================================================
+
+ // Helper used by Progress and Meter frames. Returns true if the bar should
+ // be rendered vertically, based on writing-mode and -moz-orient properties.
+ bool ResolvedOrientationIsVertical();
+
+ // ==========================================================================
+
+ nsFrameList mFrames;
+};
+
+// ==========================================================================
+/* The out-of-flow-related code below is for a hacky way of splitting
+ * absolutely-positioned frames. Basically what we do is split the frame
+ * in nsAbsoluteContainingBlock and pretend the continuation is an overflow
+ * container. This isn't an ideal solution, but it lets us print the content
+ * at least. See bug 154892.
+ */
+
+#define IS_TRUE_OVERFLOW_CONTAINER(frame) \
+ ( (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) \
+ && !( (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && \
+ frame->IsAbsolutelyPositioned() ) )
+//XXXfr This check isn't quite correct, because it doesn't handle cases
+// where the out-of-flow has overflow.. but that's rare.
+// We'll need to revisit the way abspos continuations are handled later
+// for various reasons, this detail is one of them. See bug 154892
+
+/**
+ * Helper class for tracking overflow container continuations during reflow.
+ *
+ * A frame is related to two sets of overflow containers: those that /are/
+ * its own children, and those that are /continuations/ of its children.
+ * This tracker walks through those continuations (the frame's NIF's children)
+ * and their prev-in-flows (a subset of the frame's normal and overflow
+ * container children) in parallel. It allows the reflower to synchronously
+ * walk its overflow continuations while it loops through and reflows its
+ * children. This makes it possible to insert new continuations at the correct
+ * place in the overflow containers list.
+ *
+ * The reflower is expected to loop through its children in the same order it
+ * looped through them the last time (if there was a last time).
+ * For each child, the reflower should either
+ * - call Skip for the child if was not reflowed in this pass
+ * - call Insert for the overflow continuation if the child was reflowed
+ * but has incomplete overflow
+ * - call Finished for the child if it was reflowed in this pass but
+ * is either complete or has a normal next-in-flow. This call can
+ * be skipped if the child did not previously have an overflow
+ * continuation.
+ */
+class nsOverflowContinuationTracker {
+public:
+ /**
+ * Initializes an nsOverflowContinuationTracker to help track overflow
+ * continuations of aFrame's children. Typically invoked on 'this'.
+ *
+ * aWalkOOFFrames determines whether the walker skips out-of-flow frames
+ * or skips non-out-of-flow frames.
+ *
+ * Don't set aSkipOverflowContainerChildren to false unless you plan
+ * to walk your own overflow container children. (Usually they are handled
+ * by calling ReflowOverflowContainerChildren.) aWalkOOFFrames is ignored
+ * if aSkipOverflowContainerChildren is false.
+ */
+ nsOverflowContinuationTracker(nsContainerFrame* aFrame,
+ bool aWalkOOFFrames,
+ bool aSkipOverflowContainerChildren = true);
+ /**
+ * This function adds an overflow continuation to our running list and
+ * sets its NS_FRAME_IS_OVERFLOW_CONTAINER flag.
+ *
+ * aReflowStatus should preferably be specific to the recently-reflowed
+ * child and not influenced by any of its siblings' statuses. This
+ * function sets the NS_FRAME_IS_DIRTY bit on aOverflowCont if it needs
+ * to be reflowed. (Its need for reflow depends on changes to its
+ * prev-in-flow, not to its parent--for whom it is invisible, reflow-wise.)
+ *
+ * The caller MUST disconnect the frame from its parent's child list
+ * if it was not previously an NS_FRAME_IS_OVERFLOW_CONTAINER (because
+ * StealFrame is much more inefficient than disconnecting in place
+ * during Reflow, which the caller is able to do but we are not).
+ *
+ * The caller MUST NOT disconnect the frame from its parent's
+ * child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER.
+ * (In this case we will disconnect and reconnect it ourselves.)
+ */
+ nsresult Insert(nsIFrame* aOverflowCont,
+ nsReflowStatus& aReflowStatus);
+ /**
+ * Begin/EndFinish() must be called for each child that is reflowed
+ * but no longer has an overflow continuation. (It may be called for
+ * other children, but in that case has no effect.) It increments our
+ * walker and makes sure we drop any dangling pointers to its
+ * next-in-flow. This function MUST be called before stealing or
+ * deleting aChild's next-in-flow.
+ * The AutoFinish helper object does that for you. Use it like so:
+ * if (kidNextInFlow) {
+ * nsOverflowContinuationTracker::AutoFinish fini(tracker, kid);
+ * ... DeleteNextInFlowChild/StealFrame(kidNextInFlow) here ...
+ * }
+ */
+ class MOZ_RAII AutoFinish {
+ public:
+ AutoFinish(nsOverflowContinuationTracker* aTracker, nsIFrame* aChild)
+ : mTracker(aTracker), mChild(aChild)
+ {
+ if (mTracker) mTracker->BeginFinish(mChild);
+ }
+ ~AutoFinish()
+ {
+ if (mTracker) mTracker->EndFinish(mChild);
+ }
+ private:
+ nsOverflowContinuationTracker* mTracker;
+ nsIFrame* mChild;
+ };
+
+ /**
+ * This function should be called for each child that isn't reflowed.
+ * It increments our walker and sets the NS_FRAME_OVERFLOW_INCOMPLETE
+ * reflow flag if it encounters an overflow continuation so that our
+ * next-in-flow doesn't get prematurely deleted. It MUST be called on
+ * each unreflowed child that has an overflow container continuation;
+ * it MAY be called on other children, but it isn't necessary (doesn't
+ * do anything).
+ */
+ void Skip(nsIFrame* aChild, nsReflowStatus& aReflowStatus)
+ {
+ NS_PRECONDITION(aChild, "null ptr");
+ if (aChild == mSentry) {
+ StepForward();
+ NS_MergeReflowStatusInto(&aReflowStatus, NS_FRAME_OVERFLOW_INCOMPLETE);
+ }
+ }
+
+private:
+
+ /**
+ * @see class AutoFinish
+ */
+ void BeginFinish(nsIFrame* aChild);
+ void EndFinish(nsIFrame* aChild);
+
+ void SetupOverflowContList();
+ void SetUpListWalker();
+ void StepForward();
+
+ /* We hold a pointer to either the next-in-flow's overflow containers list
+ or, if that doesn't exist, our frame's excess overflow containers list.
+ We need to make sure that we drop that pointer if the list becomes
+ empty and is deleted elsewhere. */
+ nsFrameList* mOverflowContList;
+ /* We hold a pointer to the most recently-reflowed child that has an
+ overflow container next-in-flow. We do this because it's a known
+ good point; this pointer won't be deleted on us. We can use it to
+ recover our place in the list. */
+ nsIFrame* mPrevOverflowCont;
+ /* This is a pointer to the next overflow container's prev-in-flow, which
+ is (or should be) a child of our frame. When we hit this, we will need
+ to increment this walker to the next overflow container. */
+ nsIFrame* mSentry;
+ /* Parent of all frames in mOverflowContList. If our mOverflowContList
+ is an excessOverflowContainersProperty, or null, then this is our frame
+ (the frame that was passed in to our constructor). Otherwise this is
+ that frame's next-in-flow, and our mOverflowContList is mParent's
+ overflowContainersProperty */
+ nsContainerFrame* mParent;
+ /* Tells SetUpListWalker whether or not to walk us past any continuations
+ of overflow containers. aWalkOOFFrames is ignored when this is false. */
+ bool mSkipOverflowContainerChildren;
+ /* Tells us whether to pay attention to OOF frames or non-OOF frames */
+ bool mWalkOOFFrames;
+};
+
+inline
+nsFrameList*
+nsContainerFrame::GetOverflowFrames() const
+{
+ nsFrameList* list = Properties().Get(OverflowProperty());
+ NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
+ return list;
+}
+
+inline
+nsFrameList*
+nsContainerFrame::StealOverflowFrames()
+{
+ nsFrameList* list = Properties().Remove(OverflowProperty());
+ NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
+ return list;
+}
+
+inline void
+nsContainerFrame::DestroyOverflowList()
+{
+ nsFrameList* list = RemovePropTableFrames(OverflowProperty());
+ MOZ_ASSERT(list && list->IsEmpty());
+ list->Delete(PresContext()->PresShell());
+}
+
+#endif /* nsContainerFrame_h___ */
diff --git a/layout/generic/nsDirection.h b/layout/generic/nsDirection.h
new file mode 100644
index 000000000..07c4ab07a
--- /dev/null
+++ b/layout/generic/nsDirection.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDirection_h___
+#define nsDirection_h___
+
+// This file makes the nsDirection enum present both in nsIFrame.h and
+// nsISelectionPrivate.h.
+
+enum nsDirection {
+ eDirNext = 0,
+ eDirPrevious= 1
+};
+
+#endif
+
diff --git a/layout/generic/nsFirstLetterFrame.cpp b/layout/generic/nsFirstLetterFrame.cpp
new file mode 100644
index 000000000..980e1e9be
--- /dev/null
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -0,0 +1,415 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS :first-letter pseudo-element */
+
+#include "nsFirstLetterFrame.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+#include "nsIContent.h"
+#include "nsLineLayout.h"
+#include "nsGkAtoms.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "nsFrameManager.h"
+#include "mozilla/RestyleManagerHandle.h"
+#include "mozilla/RestyleManagerHandleInlines.h"
+#include "nsPlaceholderFrame.h"
+#include "nsCSSFrameConstructor.h"
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+nsFirstLetterFrame*
+NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsFirstLetterFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
+
+NS_QUERYFRAME_HEAD(nsFirstLetterFrame)
+ NS_QUERYFRAME_ENTRY(nsFirstLetterFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
+}
+#endif
+
+nsIAtom*
+nsFirstLetterFrame::GetType() const
+{
+ return nsGkAtoms::letterFrame;
+}
+
+void
+nsFirstLetterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
+}
+
+void
+nsFirstLetterFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ RefPtr<nsStyleContext> newSC;
+ if (aPrevInFlow) {
+ // Get proper style context for ourselves. We're creating the frame
+ // that represents everything *except* the first letter, so just create
+ // a style context like we would for a text node.
+ nsStyleContext* parentStyleContext = mStyleContext->GetParent();
+ if (parentStyleContext) {
+ newSC = PresContext()->StyleSet()->
+ ResolveStyleForOtherNonElement(parentStyleContext);
+ SetStyleContextWithoutNotification(newSC);
+ }
+ }
+
+ nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+}
+
+void
+nsFirstLetterFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ MOZ_ASSERT(aListID == kPrincipalList, "Principal child list is the only "
+ "list that nsFirstLetterFrame should set via this function");
+ RestyleManagerHandle restyleManager = PresContext()->RestyleManager();
+
+ for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) {
+ NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent");
+ restyleManager->ReparentStyleContext(e.get());
+ nsLayoutUtils::MarkDescendantsDirty(e.get());
+ }
+
+ mFrames.SetFrames(aChildList);
+}
+
+nsresult
+nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
+ bool inHint,
+ int32_t* outFrameContentOffset,
+ nsIFrame **outChildFrame)
+{
+ nsIFrame *kid = mFrames.FirstChild();
+ if (kid) {
+ return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
+ } else {
+ return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
+ }
+}
+
+// Needed for non-floating first-letter frames and for the continuations
+// following the first-letter that we also use nsFirstLetterFrame for.
+/* virtual */ void
+nsFirstLetterFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData)
+{
+ DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::MIN_ISIZE);
+}
+
+// Needed for non-floating first-letter frames and for the continuations
+// following the first-letter that we also use nsFirstLetterFrame for.
+/* virtual */ void
+nsFirstLetterFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlinePrefISizeData *aData)
+{
+ DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::PREF_ISIZE);
+}
+
+// Needed for floating first-letter frames.
+/* virtual */ nscoord
+nsFirstLetterFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
+}
+
+// Needed for floating first-letter frames.
+/* virtual */ nscoord
+nsFirstLetterFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
+}
+
+/* virtual */
+LogicalSize
+nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ if (GetPrevInFlow()) {
+ // We're wrapping the text *after* the first letter, so behave like an
+ // inline frame.
+ return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+ }
+ return nsContainerFrame::ComputeSize(aRenderingContext, aWM,
+ aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags);
+}
+
+void
+nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus);
+
+ // Grab overflow list
+ DrainOverflowFrames(aPresContext);
+
+ nsIFrame* kid = mFrames.FirstChild();
+
+ // Setup reflow state for our child
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalSize availSize = aReflowInput.AvailableSize();
+ const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding();
+ NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE,
+ "should no longer use unconstrained inline size");
+ availSize.ISize(wm) -= bp.IStartEnd(wm);
+ if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
+ availSize.BSize(wm) -= bp.BStartEnd(wm);
+ }
+
+ WritingMode lineWM = aMetrics.GetWritingMode();
+ ReflowOutput kidMetrics(lineWM);
+
+ // Reflow the child
+ if (!aReflowInput.mLineLayout) {
+ // When there is no lineLayout provided, we provide our own. The
+ // only time that the first-letter-frame is not reflowing in a
+ // line context is when its floating.
+ WritingMode kidWritingMode = GetWritingMode(kid);
+ LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm);
+ ReflowInput rs(aPresContext, aReflowInput, kid, kidAvailSize);
+ nsLineLayout ll(aPresContext, nullptr, &aReflowInput, nullptr, nullptr);
+
+ ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm),
+ availSize.ISize(wm), NS_UNCONSTRAINEDSIZE,
+ false, true, kidWritingMode,
+ nsSize(aReflowInput.AvailableWidth(),
+ aReflowInput.AvailableHeight()));
+ rs.mLineLayout = &ll;
+ ll.SetInFirstLetter(true);
+ ll.SetFirstLetterStyleOK(true);
+
+ kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
+
+ ll.EndLineReflow();
+ ll.SetInFirstLetter(false);
+
+ // In the floating first-letter case, we need to set this ourselves;
+ // nsLineLayout::BeginSpan will set it in the other case
+ mBaseline = kidMetrics.BlockStartAscent();
+
+ // Place and size the child and update the output metrics
+ LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM);
+ kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm),
+ convertedSize.ISize(wm), convertedSize.BSize(wm)));
+ kid->FinishAndStoreOverflow(&kidMetrics);
+ kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
+
+ convertedSize.ISize(wm) += bp.IStartEnd(wm);
+ convertedSize.BSize(wm) += bp.BStartEnd(wm);
+ aMetrics.SetSize(wm, convertedSize);
+ aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
+ bp.BStart(wm));
+
+ // Ensure that the overflow rect contains the child textframe's
+ // overflow rect.
+ // Note that if this is floating, the overline/underline drawable
+ // area is in the overflow rect of the child textframe.
+ aMetrics.UnionOverflowAreasWithDesiredBounds();
+ ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
+
+ FinishAndStoreOverflow(&aMetrics);
+ } else {
+ // Pretend we are a span and reflow the child frame
+ nsLineLayout* ll = aReflowInput.mLineLayout;
+ bool pushedFrame;
+
+ ll->SetInFirstLetter(
+ mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
+ ll->BeginSpan(this, &aReflowInput, bp.IStart(wm),
+ availSize.ISize(wm), &mBaseline);
+ ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
+ NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
+ "we're assuming we can mix sizes between lineWM and wm "
+ "since we shouldn't have orthogonal writing modes within "
+ "a line.");
+ aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
+ ll->SetInFirstLetter(false);
+
+ if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) {
+ aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
+ bp.BStart(wm));
+ aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm);
+ } else {
+ nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
+ }
+ }
+
+ if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
+ // Create a continuation or remove existing continuations based on
+ // the reflow completion status.
+ if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
+ if (aReflowInput.mLineLayout) {
+ aReflowInput.mLineLayout->SetFirstLetterStyleOK(false);
+ }
+ nsIFrame* kidNextInFlow = kid->GetNextInFlow();
+ if (kidNextInFlow) {
+ // Remove all of the childs next-in-flows
+ kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
+ }
+ } else {
+ // Create a continuation for the child frame if it doesn't already
+ // have one.
+ if (!IsFloating()) {
+ CreateNextInFlow(kid);
+ // And then push it to our overflow list
+ const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
+ if (overflow.NotEmpty()) {
+ SetOverflowFrames(overflow);
+ }
+ } else if (!kid->GetNextInFlow()) {
+ // For floating first letter frames (if a continuation wasn't already
+ // created for us) we need to put the continuation with the rest of the
+ // text that the first letter frame was made out of.
+ nsIFrame* continuation;
+ CreateContinuationForFloatingParent(aPresContext, kid,
+ &continuation, true);
+ }
+ }
+ }
+
+ NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowInput, aMetrics);
+}
+
+/* virtual */ bool
+nsFirstLetterFrame::CanContinueTextRun() const
+{
+ // We can continue a text run through a first-letter frame.
+ return true;
+}
+
+nsresult
+nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext,
+ nsIFrame* aChild,
+ nsIFrame** aContinuation,
+ bool aIsFluid)
+{
+ NS_ASSERTION(IsFloating(),
+ "can only call this on floating first letter frames");
+ NS_PRECONDITION(aContinuation, "bad args");
+
+ *aContinuation = nullptr;
+
+ nsIPresShell* presShell = aPresContext->PresShell();
+ nsPlaceholderFrame* placeholderFrame =
+ presShell->FrameManager()->GetPlaceholderFrameFor(this);
+ nsContainerFrame* parent = placeholderFrame->GetParent();
+
+ nsIFrame* continuation = presShell->FrameConstructor()->
+ CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid);
+
+ // The continuation will have gotten the first letter style from its
+ // prev continuation, so we need to repair the style context so it
+ // doesn't have the first letter styling.
+ nsStyleContext* parentSC = this->StyleContext()->GetParent();
+ if (parentSC) {
+ RefPtr<nsStyleContext> newSC;
+ newSC = presShell->StyleSet()->ResolveStyleForOtherNonElement(parentSC);
+ continuation->SetStyleContext(newSC);
+ nsLayoutUtils::MarkDescendantsDirty(continuation);
+ }
+
+ //XXX Bidi may not be involved but we have to use the list name
+ // kNoReflowPrincipalList because this is just like creating a continuation
+ // except we have to insert it in a different place and we don't want a
+ // reflow command to try to be issued.
+ nsFrameList temp(continuation, continuation);
+ parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp);
+
+ *aContinuation = continuation;
+ return NS_OK;
+}
+
+void
+nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
+{
+ // Check for an overflow list with our prev-in-flow
+ nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
+ if (prevInFlow) {
+ AutoFrameListPtr overflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
+ if (overflowFrames) {
+ NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
+
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented.
+ nsContainerFrame::ReparentFrameViewList(*overflowFrames, prevInFlow,
+ this);
+ mFrames.InsertFrames(this, nullptr, *overflowFrames);
+ }
+ }
+
+ // It's also possible that we have an overflow list for ourselves
+ AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
+ if (overflowFrames) {
+ NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
+ mFrames.AppendFrames(nullptr, *overflowFrames);
+ }
+
+ // Now repair our first frames style context (since we only reflow
+ // one frame there is no point in doing any other ones until they
+ // are reflowed)
+ nsIFrame* kid = mFrames.FirstChild();
+ if (kid) {
+ RefPtr<nsStyleContext> sc;
+ nsIContent* kidContent = kid->GetContent();
+ if (kidContent) {
+ NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
+ "should contain only text nodes");
+ nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParent() :
+ mStyleContext;
+ sc = aPresContext->StyleSet()->ResolveStyleForText(kidContent, parentSC);
+ kid->SetStyleContext(sc);
+ nsLayoutUtils::MarkDescendantsDirty(kid);
+ }
+ }
+}
+
+nscoord
+nsFirstLetterFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+ return mBaseline;
+}
+
+nsIFrame::LogicalSides
+nsFirstLetterFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
+{
+ if (GetPrevContinuation()) {
+ // We shouldn't get calls to GetSkipSides for later continuations since
+ // they have separate style contexts with initial values for all the
+ // properties that could trigger a call to GetSkipSides. Then again,
+ // it's not really an error to call GetSkipSides on any frame, so
+ // that's why we handle it properly.
+ return LogicalSides(eLogicalSideBitsAll);
+ }
+ return LogicalSides(); // first continuation displays all sides
+}
diff --git a/layout/generic/nsFirstLetterFrame.h b/layout/generic/nsFirstLetterFrame.h
new file mode 100644
index 000000000..40e4ef0cf
--- /dev/null
+++ b/layout/generic/nsFirstLetterFrame.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsFirstLetterFrame_h__
+#define nsFirstLetterFrame_h__
+
+/* rendering object for CSS :first-letter pseudo-element */
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+
+class nsFirstLetterFrame final : public nsContainerFrame {
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsFirstLetterFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsFirstLetterFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+ virtual nsIAtom* GetType() const override;
+
+ bool IsFloating() const { return GetStateBits() & NS_FRAME_OUT_OF_FLOW; }
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ if (!IsFloating())
+ aFlags = aFlags & ~(nsIFrame::eLineParticipant);
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eBidiInlineContainer));
+ }
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual bool CanContinueTextRun() const override;
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+ virtual LogicalSides GetLogicalSkipSides(const ReflowInput* aReflowInput = nullptr) const override;
+
+//override of nsFrame method
+ virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
+ bool inHint,
+ int32_t* outFrameContentOffset,
+ nsIFrame** outChildFrame) override;
+
+ nscoord GetFirstLetterBaseline() const { return mBaseline; }
+
+ // For floating first letter frames, create a continuation for aChild and
+ // place it in the correct place. aContinuation is an outparam for the
+ // continuation that is created. aIsFluid determines if the continuation is
+ // fluid or not.
+ nsresult CreateContinuationForFloatingParent(nsPresContext* aPresContext,
+ nsIFrame* aChild,
+ nsIFrame** aContinuation,
+ bool aIsFluid);
+
+protected:
+ nscoord mBaseline;
+
+ void DrainOverflowFrames(nsPresContext* aPresContext);
+};
+
+#endif /* nsFirstLetterFrame_h__ */
diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp
new file mode 100644
index 000000000..b61024324
--- /dev/null
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -0,0 +1,4804 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: flex" */
+
+#include "mozilla/UniquePtr.h"
+#include "nsFlexContainerFrame.h"
+#include "nsContentUtils.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsDisplayList.h"
+#include "nsIFrameInlines.h"
+#include "nsLayoutUtils.h"
+#include "nsPlaceholderFrame.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsStyleContext.h"
+#include "mozilla/Logging.h"
+#include <algorithm>
+#include "mozilla/LinkedList.h"
+#include "mozilla/FloatingPoint.h"
+#include "WritingModes.h"
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+// Convenience typedefs for helper classes that we forward-declare in .h file
+// (so that nsFlexContainerFrame methods can use them as parameters):
+typedef nsFlexContainerFrame::FlexItem FlexItem;
+typedef nsFlexContainerFrame::FlexLine FlexLine;
+typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker;
+typedef nsFlexContainerFrame::StrutInfo StrutInfo;
+
+static mozilla::LazyLogModule gFlexContainerLog("nsFlexContainerFrame");
+
+// XXXdholbert Some of this helper-stuff should be separated out into a general
+// "main/cross-axis utils" header, shared by grid & flexbox?
+// (Particularly when grid gets support for align-*/justify-* properties.)
+
+// Helper enums
+// ============
+
+// Represents a physical orientation for an axis.
+// The directional suffix indicates the direction in which the axis *grows*.
+// So e.g. eAxis_LR means a horizontal left-to-right axis, whereas eAxis_BT
+// means a vertical bottom-to-top axis.
+// NOTE: The order here is important -- these values are used as indices into
+// the static array 'kAxisOrientationToSidesMap', defined below.
+enum AxisOrientationType {
+ eAxis_LR,
+ eAxis_RL,
+ eAxis_TB,
+ eAxis_BT,
+ eNumAxisOrientationTypes // For sizing arrays that use these values as indices
+};
+
+// Represents one or the other extreme of an axis (e.g. for the main axis, the
+// main-start vs. main-end edge.
+// NOTE: The order here is important -- these values are used as indices into
+// the sub-arrays in 'kAxisOrientationToSidesMap', defined below.
+enum AxisEdgeType {
+ eAxisEdge_Start,
+ eAxisEdge_End,
+ eNumAxisEdges // For sizing arrays that use these values as indices
+};
+
+// This array maps each axis orientation to a pair of corresponding
+// [start, end] physical mozilla::Side values.
+static const mozilla::Side
+kAxisOrientationToSidesMap[eNumAxisOrientationTypes][eNumAxisEdges] = {
+ { eSideLeft, eSideRight }, // eAxis_LR
+ { eSideRight, eSideLeft }, // eAxis_RL
+ { eSideTop, eSideBottom }, // eAxis_TB
+ { eSideBottom, eSideTop } // eAxis_BT
+};
+
+// Helper structs / classes / methods
+// ==================================
+// Returns true iff the given nsStyleDisplay has display:-webkit-{inline-}-box.
+static inline bool
+IsDisplayValueLegacyBox(const nsStyleDisplay* aStyleDisp)
+{
+ return aStyleDisp->mDisplay == mozilla::StyleDisplay::WebkitBox ||
+ aStyleDisp->mDisplay == mozilla::StyleDisplay::WebkitInlineBox;
+}
+
+// Returns true if aFlexContainer is the frame for an element with
+// "display:-webkit-box" or "display:-webkit-inline-box". aFlexContainer is
+// expected to be an instance of nsFlexContainerFrame (enforced with an assert);
+// otherwise, this function's state-bit-check here is bogus.
+static bool
+IsLegacyBox(const nsIFrame* aFlexContainer)
+{
+ MOZ_ASSERT(aFlexContainer->GetType() == nsGkAtoms::flexContainerFrame,
+ "only flex containers may be passed to this function");
+ return aFlexContainer->HasAnyStateBits(NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX);
+}
+
+// Returns the "align-items" value that's equivalent to the legacy "box-align"
+// value in the given style struct.
+static uint8_t
+ConvertLegacyStyleToAlignItems(const nsStyleXUL* aStyleXUL)
+{
+ // -[moz|webkit]-box-align corresponds to modern "align-items"
+ switch (aStyleXUL->mBoxAlign) {
+ case StyleBoxAlign::Stretch:
+ return NS_STYLE_ALIGN_STRETCH;
+ case StyleBoxAlign::Start:
+ return NS_STYLE_ALIGN_FLEX_START;
+ case StyleBoxAlign::Center:
+ return NS_STYLE_ALIGN_CENTER;
+ case StyleBoxAlign::Baseline:
+ return NS_STYLE_ALIGN_BASELINE;
+ case StyleBoxAlign::End:
+ return NS_STYLE_ALIGN_FLEX_END;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxAlign enum value");
+ // Fall back to default value of "align-items" property:
+ return NS_STYLE_ALIGN_STRETCH;
+}
+
+// Returns the "justify-content" value that's equivalent to the legacy
+// "box-pack" value in the given style struct.
+static uint8_t
+ConvertLegacyStyleToJustifyContent(const nsStyleXUL* aStyleXUL)
+{
+ // -[moz|webkit]-box-pack corresponds to modern "justify-content"
+ switch (aStyleXUL->mBoxPack) {
+ case StyleBoxPack::Start:
+ return NS_STYLE_ALIGN_FLEX_START;
+ case StyleBoxPack::Center:
+ return NS_STYLE_ALIGN_CENTER;
+ case StyleBoxPack::End:
+ return NS_STYLE_ALIGN_FLEX_END;
+ case StyleBoxPack::Justify:
+ return NS_STYLE_ALIGN_SPACE_BETWEEN;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxPack enum value");
+ // Fall back to default value of "justify-content" property:
+ return NS_STYLE_ALIGN_FLEX_START;
+}
+
+// Indicates whether advancing along the given axis is equivalent to
+// increasing our X or Y position (as opposed to decreasing it).
+static inline bool
+AxisGrowsInPositiveDirection(AxisOrientationType aAxis)
+{
+ return eAxis_LR == aAxis || eAxis_TB == aAxis;
+}
+
+// Given an AxisOrientationType, returns the "reverse" AxisOrientationType
+// (in the same dimension, but the opposite direction)
+static inline AxisOrientationType
+GetReverseAxis(AxisOrientationType aAxis)
+{
+ AxisOrientationType reversedAxis;
+
+ if (aAxis % 2 == 0) {
+ // even enum value. Add 1 to reverse.
+ reversedAxis = AxisOrientationType(aAxis + 1);
+ } else {
+ // odd enum value. Subtract 1 to reverse.
+ reversedAxis = AxisOrientationType(aAxis - 1);
+ }
+
+ // Check that we're still in the enum's valid range
+ MOZ_ASSERT(reversedAxis >= eAxis_LR &&
+ reversedAxis <= eAxis_BT);
+
+ return reversedAxis;
+}
+
+/**
+ * Converts a "flex-relative" coordinate in a single axis (a main- or cross-axis
+ * coordinate) into a coordinate in the corresponding physical (x or y) axis. If
+ * the flex-relative axis in question already maps *directly* to a physical
+ * axis (i.e. if it's LTR or TTB), then the physical coordinate has the same
+ * numeric value as the provided flex-relative coordinate. Otherwise, we have to
+ * subtract the flex-relative coordinate from the flex container's size in that
+ * axis, to flip the polarity. (So e.g. a main-axis position of 2px in a RTL
+ * 20px-wide container would correspond to a physical coordinate (x-value) of
+ * 18px.)
+ */
+static nscoord
+PhysicalCoordFromFlexRelativeCoord(nscoord aFlexRelativeCoord,
+ nscoord aContainerSize,
+ AxisOrientationType aAxis) {
+ if (AxisGrowsInPositiveDirection(aAxis)) {
+ return aFlexRelativeCoord;
+ }
+ return aContainerSize - aFlexRelativeCoord;
+}
+
+// Helper-macro to let us pick one of two expressions to evaluate
+// (a width expression vs. a height expression), to get a main-axis or
+// cross-axis component.
+// For code that has e.g. a nsSize object, FlexboxAxisTracker::GetMainComponent
+// and GetCrossComponent are cleaner; but in cases where we simply have
+// two separate expressions for width and height (which may be expensive to
+// evaluate), these macros will ensure that only the expression for the correct
+// axis gets evaluated.
+#define GET_MAIN_COMPONENT(axisTracker_, width_, height_) \
+ (axisTracker_).IsMainAxisHorizontal() ? (width_) : (height_)
+
+#define GET_CROSS_COMPONENT(axisTracker_, width_, height_) \
+ (axisTracker_).IsCrossAxisHorizontal() ? (width_) : (height_)
+
+// Logical versions of helper-macros above:
+#define GET_MAIN_COMPONENT_LOGICAL(axisTracker_, wm_, isize_, bsize_) \
+ wm_.IsOrthogonalTo(axisTracker_.GetWritingMode()) != \
+ (axisTracker_).IsRowOriented() ? (isize_) : (bsize_)
+
+#define GET_CROSS_COMPONENT_LOGICAL(axisTracker_, wm_, isize_, bsize_) \
+ wm_.IsOrthogonalTo(axisTracker_.GetWritingMode()) != \
+ (axisTracker_).IsRowOriented() ? (bsize_) : (isize_)
+
+// Flags to customize behavior of the FlexboxAxisTracker constructor:
+enum AxisTrackerFlags {
+ eNoFlags = 0x0,
+
+ // Normally, FlexboxAxisTracker may attempt to reverse axes & iteration order
+ // to avoid bottom-to-top child ordering, for saner pagination. This flag
+ // suppresses that behavior (so that we allow bottom-to-top child ordering).
+ // (This may be helpful e.g. when we're only dealing with a single child.)
+ eAllowBottomToTopChildOrdering = 0x1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AxisTrackerFlags)
+
+// Encapsulates our flex container's main & cross axes.
+class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
+public:
+ FlexboxAxisTracker(const nsFlexContainerFrame* aFlexContainer,
+ const WritingMode& aWM,
+ AxisTrackerFlags aFlags = eNoFlags);
+
+ // Accessors:
+ // XXXdholbert [BEGIN DEPRECATED]
+ AxisOrientationType GetMainAxis() const { return mMainAxis; }
+ AxisOrientationType GetCrossAxis() const { return mCrossAxis; }
+
+ bool IsMainAxisHorizontal() const {
+ // If we're row-oriented, and our writing mode is NOT vertical,
+ // or we're column-oriented and our writing mode IS vertical,
+ // then our main axis is horizontal. This handles all cases:
+ return mIsRowOriented != mWM.IsVertical();
+ }
+ bool IsCrossAxisHorizontal() const {
+ return !IsMainAxisHorizontal();
+ }
+ // XXXdholbert [END DEPRECATED]
+
+ // Returns the flex container's writing mode.
+ WritingMode GetWritingMode() const { return mWM; }
+
+ // Returns true if our main axis is in the reverse direction of our
+ // writing mode's corresponding axis. (From 'flex-direction: *-reverse')
+ bool IsMainAxisReversed() const {
+ return mIsMainAxisReversed;
+ }
+ // Returns true if our cross axis is in the reverse direction of our
+ // writing mode's corresponding axis. (From 'flex-wrap: *-reverse')
+ bool IsCrossAxisReversed() const {
+ return mIsCrossAxisReversed;
+ }
+
+ bool IsRowOriented() const { return mIsRowOriented; }
+ bool IsColumnOriented() const { return !mIsRowOriented; }
+
+ nscoord GetMainComponent(const nsSize& aSize) const {
+ return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height);
+ }
+ int32_t GetMainComponent(const LayoutDeviceIntSize& aIntSize) const {
+ return GET_MAIN_COMPONENT(*this, aIntSize.width, aIntSize.height);
+ }
+
+ nscoord GetCrossComponent(const nsSize& aSize) const {
+ return GET_CROSS_COMPONENT(*this, aSize.width, aSize.height);
+ }
+ int32_t GetCrossComponent(const LayoutDeviceIntSize& aIntSize) const {
+ return GET_CROSS_COMPONENT(*this, aIntSize.width, aIntSize.height);
+ }
+
+ nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const {
+ return IsMainAxisHorizontal() ?
+ aMargin.LeftRight() :
+ aMargin.TopBottom();
+ }
+ nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const {
+ return IsCrossAxisHorizontal() ?
+ aMargin.LeftRight() :
+ aMargin.TopBottom();
+ }
+
+ // Returns aFrame's computed value for 'height' or 'width' -- whichever is in
+ // the cross-axis. (NOTE: This is cross-axis-specific for now. If we need a
+ // main-axis version as well, we could generalize or clone this function.)
+ const nsStyleCoord& ComputedCrossSize(const nsIFrame* aFrame) const {
+ const nsStylePosition* stylePos = aFrame->StylePosition();
+
+ return IsCrossAxisHorizontal() ?
+ stylePos->mWidth :
+ stylePos->mHeight;
+ }
+
+ /**
+ * Converts a "flex-relative" point (a main-axis & cross-axis coordinate)
+ * into a LogicalPoint, using the flex container's writing mode.
+ *
+ * @arg aMainCoord The main-axis coordinate -- i.e an offset from the
+ * main-start edge of the flex container's content box.
+ * @arg aCrossCoord The cross-axis coordinate -- i.e an offset from the
+ * cross-start edge of the flex container's content box.
+ * @arg aContainerMainSize The main size of flex container's content box.
+ * @arg aContainerCrossSize The cross size of flex container's content box.
+ * @return A LogicalPoint, with the flex container's writing mode, that
+ * represents the same position. The logical coordinates are
+ * relative to the flex container's content box.
+ */
+ LogicalPoint
+ LogicalPointFromFlexRelativePoint(nscoord aMainCoord,
+ nscoord aCrossCoord,
+ nscoord aContainerMainSize,
+ nscoord aContainerCrossSize) const {
+ nscoord logicalCoordInMainAxis = mIsMainAxisReversed ?
+ aContainerMainSize - aMainCoord : aMainCoord;
+ nscoord logicalCoordInCrossAxis = mIsCrossAxisReversed ?
+ aContainerCrossSize - aCrossCoord : aCrossCoord;
+
+ return mIsRowOriented ?
+ LogicalPoint(mWM, logicalCoordInMainAxis, logicalCoordInCrossAxis) :
+ LogicalPoint(mWM, logicalCoordInCrossAxis, logicalCoordInMainAxis);
+ }
+
+ /**
+ * Converts a "flex-relative" size (a main-axis & cross-axis size)
+ * into a LogicalSize, using the flex container's writing mode.
+ *
+ * @arg aMainSize The main-axis size.
+ * @arg aCrossSize The cross-axis size.
+ * @return A LogicalSize, with the flex container's writing mode, that
+ * represents the same size.
+ */
+ LogicalSize LogicalSizeFromFlexRelativeSizes(nscoord aMainSize,
+ nscoord aCrossSize) const {
+ return mIsRowOriented ?
+ LogicalSize(mWM, aMainSize, aCrossSize) :
+ LogicalSize(mWM, aCrossSize, aMainSize);
+ }
+
+ // Are my axes reversed with respect to what the author asked for?
+ // (We may reverse the axes in the FlexboxAxisTracker constructor and set
+ // this flag, to avoid reflowing our children in bottom-to-top order.)
+ bool AreAxesInternallyReversed() const
+ {
+ return mAreAxesInternallyReversed;
+ }
+
+private:
+ // Delete copy-constructor & reassignment operator, to prevent accidental
+ // (unnecessary) copying.
+ FlexboxAxisTracker(const FlexboxAxisTracker&) = delete;
+ FlexboxAxisTracker& operator=(const FlexboxAxisTracker&) = delete;
+
+ // Helpers for constructor which determine the orientation of our axes, based
+ // on legacy box properties (-webkit-box-orient, -webkit-box-direction) or
+ // modern flexbox properties (flex-direction, flex-wrap) depending on whether
+ // the flex container is a "legacy box" (as determined by IsLegacyBox).
+ void InitAxesFromLegacyProps(const nsFlexContainerFrame* aFlexContainer);
+ void InitAxesFromModernProps(const nsFlexContainerFrame* aFlexContainer);
+
+ // XXXdholbert [BEGIN DEPRECATED]
+ AxisOrientationType mMainAxis;
+ AxisOrientationType mCrossAxis;
+ // XXXdholbert [END DEPRECATED]
+
+ const WritingMode mWM; // The flex container's writing mode.
+
+ bool mIsRowOriented; // Is our main axis the inline axis?
+ // (Are we 'flex-direction:row[-reverse]'?)
+
+ bool mIsMainAxisReversed; // Is our main axis in the opposite direction
+ // as mWM's corresponding axis? (e.g. RTL vs LTR)
+ bool mIsCrossAxisReversed; // Is our cross axis in the opposite direction
+ // as mWM's corresponding axis? (e.g. BTT vs TTB)
+
+ // Implementation detail -- this indicates whether we've decided to
+ // transparently reverse our axes & our child ordering, to avoid having
+ // frames flow from bottom to top in either axis (& to make pagination saner).
+ bool mAreAxesInternallyReversed;
+};
+
+/**
+ * Represents a flex item.
+ * Includes the various pieces of input that the Flexbox Layout Algorithm uses
+ * to resolve a flexible width.
+ */
+class nsFlexContainerFrame::FlexItem : public LinkedListElement<FlexItem>
+{
+public:
+ // Normal constructor:
+ FlexItem(ReflowInput& aFlexItemReflowInput,
+ float aFlexGrow, float aFlexShrink, nscoord aMainBaseSize,
+ nscoord aMainMinSize, nscoord aMainMaxSize,
+ nscoord aTentativeCrossSize,
+ nscoord aCrossMinSize, nscoord aCrossMaxSize,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ // Simplified constructor, to be used only for generating "struts":
+ // (NOTE: This "strut" constructor uses the *container's* writing mode, which
+ // we'll use on this FlexItem instead of the child frame's real writing mode.
+ // This is fine - it doesn't matter what writing mode we use for a
+ // strut, since it won't render any content and we already know its size.)
+ FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize, WritingMode aContainerWM);
+
+ // Accessors
+ nsIFrame* Frame() const { return mFrame; }
+ nscoord GetFlexBaseSize() const { return mFlexBaseSize; }
+
+ nscoord GetMainMinSize() const {
+ MOZ_ASSERT(!mNeedsMinSizeAutoResolution,
+ "Someone's using an unresolved 'auto' main min-size");
+ return mMainMinSize;
+ }
+ nscoord GetMainMaxSize() const { return mMainMaxSize; }
+
+ // Note: These return the main-axis position and size of our *content box*.
+ nscoord GetMainSize() const { return mMainSize; }
+ nscoord GetMainPosition() const { return mMainPosn; }
+
+ nscoord GetCrossMinSize() const { return mCrossMinSize; }
+ nscoord GetCrossMaxSize() const { return mCrossMaxSize; }
+
+ // Note: These return the cross-axis position and size of our *content box*.
+ nscoord GetCrossSize() const { return mCrossSize; }
+ nscoord GetCrossPosition() const { return mCrossPosn; }
+
+ nscoord ResolvedAscent(bool aUseFirstBaseline) const {
+ if (mAscent == ReflowOutput::ASK_FOR_BASELINE) {
+ // XXXdholbert We should probably be using the *container's* writing-mode
+ // here, instead of the item's -- though it doesn't much matter right
+ // now, because all of the baseline-handling code here essentially
+ // assumes that the container & items have the same writing-mode. This
+ // will matter more (& can be expanded/tested) once we officially support
+ // logical directions & vertical writing-modes in flexbox, in bug 1079155
+ // or a dependency.
+ // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate,
+ // or just GetLogicalBaseline() if that fails.
+ bool found = aUseFirstBaseline ?
+ nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) :
+ nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
+
+ if (!found) {
+ mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM,
+ BaselineSharingGroup::eFirst);
+ }
+ }
+ return mAscent;
+ }
+
+ // Convenience methods to compute the main & cross size of our *margin-box*.
+ // The caller is responsible for telling us the right axis, so that we can
+ // pull out the appropriate components of our margin/border/padding structs.
+ nscoord GetOuterMainSize(AxisOrientationType aMainAxis) const
+ {
+ return mMainSize + GetMarginBorderPaddingSizeInAxis(aMainAxis);
+ }
+
+ nscoord GetOuterCrossSize(AxisOrientationType aCrossAxis) const
+ {
+ return mCrossSize + GetMarginBorderPaddingSizeInAxis(aCrossAxis);
+ }
+
+ // Returns the distance between this FlexItem's baseline and the cross-start
+ // edge of its margin-box. Used in baseline alignment.
+ // (This function needs to be told which edge we're measuring the baseline
+ // from, so that it can look up the appropriate components from mMargin.)
+ nscoord GetBaselineOffsetFromOuterCrossEdge(
+ AxisEdgeType aEdge,
+ const FlexboxAxisTracker& aAxisTracker,
+ bool aUseFirstLineBaseline) const;
+
+ float GetShareOfWeightSoFar() const { return mShareOfWeightSoFar; }
+
+ bool IsFrozen() const { return mIsFrozen; }
+
+ bool HadMinViolation() const { return mHadMinViolation; }
+ bool HadMaxViolation() const { return mHadMaxViolation; }
+
+ // Indicates whether this item received a preliminary "measuring" reflow
+ // before its actual reflow.
+ bool HadMeasuringReflow() const { return mHadMeasuringReflow; }
+
+ // Indicates whether this item's cross-size has been stretched (from having
+ // "align-self: stretch" with an auto cross-size and no auto margins in the
+ // cross axis).
+ bool IsStretched() const { return mIsStretched; }
+
+ // Indicates whether we need to resolve an 'auto' value for the main-axis
+ // min-[width|height] property.
+ bool NeedsMinSizeAutoResolution() const
+ { return mNeedsMinSizeAutoResolution; }
+
+ // Indicates whether this item is a "strut" left behind by an element with
+ // visibility:collapse.
+ bool IsStrut() const { return mIsStrut; }
+
+ WritingMode GetWritingMode() const { return mWM; }
+ uint8_t GetAlignSelf() const { return mAlignSelf; }
+
+ // Returns the flex factor (flex-grow or flex-shrink), depending on
+ // 'aIsUsingFlexGrow'.
+ //
+ // Asserts fatally if called on a frozen item (since frozen items are not
+ // flexible).
+ float GetFlexFactor(bool aIsUsingFlexGrow)
+ {
+ MOZ_ASSERT(!IsFrozen(), "shouldn't need flex factor after item is frozen");
+
+ return aIsUsingFlexGrow ? mFlexGrow : mFlexShrink;
+ }
+
+ // Returns the weight that we should use in the "resolving flexible lengths"
+ // algorithm. If we're using the flex grow factor, we just return that;
+ // otherwise, we return the "scaled flex shrink factor" (scaled by our flex
+ // base size, so that when both large and small items are shrinking, the large
+ // items shrink more).
+ //
+ // I'm calling this a "weight" instead of a "[scaled] flex-[grow|shrink]
+ // factor", to more clearly distinguish it from the actual flex-grow &
+ // flex-shrink factors.
+ //
+ // Asserts fatally if called on a frozen item (since frozen items are not
+ // flexible).
+ float GetWeight(bool aIsUsingFlexGrow)
+ {
+ MOZ_ASSERT(!IsFrozen(), "shouldn't need weight after item is frozen");
+
+ if (aIsUsingFlexGrow) {
+ return mFlexGrow;
+ }
+
+ // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize
+ if (mFlexBaseSize == 0) {
+ // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so
+ // regardless of mFlexShrink, we should just return 0.
+ // (This is really a special-case for when mFlexShrink is infinity, to
+ // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
+ return 0.0f;
+ }
+ return mFlexShrink * mFlexBaseSize;
+ }
+
+ const nsSize& IntrinsicRatio() const { return mIntrinsicRatio; }
+ bool HasIntrinsicRatio() const { return mIntrinsicRatio != nsSize(); }
+
+ // Getters for margin:
+ // ===================
+ const nsMargin& GetMargin() const { return mMargin; }
+
+ // Returns the margin component for a given mozilla::Side
+ nscoord GetMarginComponentForSide(mozilla::Side aSide) const
+ { return mMargin.Side(aSide); }
+
+ // Returns the total space occupied by this item's margins in the given axis
+ nscoord GetMarginSizeInAxis(AxisOrientationType aAxis) const
+ {
+ mozilla::Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
+ mozilla::Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
+ return GetMarginComponentForSide(startSide) +
+ GetMarginComponentForSide(endSide);
+ }
+
+ // Getters for border/padding
+ // ==========================
+ const nsMargin& GetBorderPadding() const { return mBorderPadding; }
+
+ // Returns the border+padding component for a given mozilla::Side
+ nscoord GetBorderPaddingComponentForSide(mozilla::Side aSide) const
+ { return mBorderPadding.Side(aSide); }
+
+ // Returns the total space occupied by this item's borders and padding in
+ // the given axis
+ nscoord GetBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
+ {
+ mozilla::Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
+ mozilla::Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
+ return GetBorderPaddingComponentForSide(startSide) +
+ GetBorderPaddingComponentForSide(endSide);
+ }
+
+ // Getter for combined margin/border/padding
+ // =========================================
+ // Returns the total space occupied by this item's margins, borders and
+ // padding in the given axis
+ nscoord GetMarginBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
+ {
+ return GetMarginSizeInAxis(aAxis) + GetBorderPaddingSizeInAxis(aAxis);
+ }
+
+ // Setters
+ // =======
+ // Helper to set the resolved value of min-[width|height]:auto for the main
+ // axis. (Should only be used if NeedsMinSizeAutoResolution() returns true.)
+ void UpdateMainMinSize(nscoord aNewMinSize)
+ {
+ NS_ASSERTION(aNewMinSize >= 0,
+ "How did we end up with a negative min-size?");
+ MOZ_ASSERT(mMainMaxSize >= aNewMinSize,
+ "Should only use this function for resolving min-size:auto, "
+ "and main max-size should be an upper-bound for resolved val");
+ MOZ_ASSERT(mNeedsMinSizeAutoResolution &&
+ (mMainMinSize == 0 || mFrame->IsThemed(mFrame->StyleDisplay())),
+ "Should only use this function for resolving min-size:auto, "
+ "so we shouldn't already have a nonzero min-size established "
+ "(unless it's a themed-widget-imposed minimum size)");
+
+ if (aNewMinSize > mMainMinSize) {
+ mMainMinSize = aNewMinSize;
+ // Also clamp main-size to be >= new min-size:
+ mMainSize = std::max(mMainSize, aNewMinSize);
+ }
+ mNeedsMinSizeAutoResolution = false;
+ }
+
+ // This sets our flex base size, and then sets our main size to the
+ // resulting "hypothetical main size" (the base size clamped to our
+ // main-axis [min,max] sizing constraints).
+ void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize)
+ {
+ MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_INTRINSICSIZE,
+ "flex base size shouldn't change after we're frozen "
+ "(unless we're just resolving an intrinsic size)");
+ mFlexBaseSize = aNewFlexBaseSize;
+
+ // Before we've resolved flexible lengths, we keep mMainSize set to
+ // the 'hypothetical main size', which is the flex base size, clamped
+ // to the [min,max] range:
+ mMainSize = NS_CSS_MINMAX(mFlexBaseSize, mMainMinSize, mMainMaxSize);
+ }
+
+ // Setters used while we're resolving flexible lengths
+ // ---------------------------------------------------
+
+ // Sets the main-size of our flex item's content-box.
+ void SetMainSize(nscoord aNewMainSize)
+ {
+ MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen");
+ mMainSize = aNewMainSize;
+ }
+
+ void SetShareOfWeightSoFar(float aNewShare)
+ {
+ MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0f,
+ "shouldn't be giving this item any share of the weight "
+ "after it's frozen");
+ mShareOfWeightSoFar = aNewShare;
+ }
+
+ void Freeze() { mIsFrozen = true; }
+
+ void SetHadMinViolation()
+ {
+ MOZ_ASSERT(!mIsFrozen,
+ "shouldn't be changing main size & having violations "
+ "after we're frozen");
+ mHadMinViolation = true;
+ }
+ void SetHadMaxViolation()
+ {
+ MOZ_ASSERT(!mIsFrozen,
+ "shouldn't be changing main size & having violations "
+ "after we're frozen");
+ mHadMaxViolation = true;
+ }
+ void ClearViolationFlags()
+ { mHadMinViolation = mHadMaxViolation = false; }
+
+ // Setters for values that are determined after we've resolved our main size
+ // -------------------------------------------------------------------------
+
+ // Sets the main-axis position of our flex item's content-box.
+ // (This is the distance between the main-start edge of the flex container
+ // and the main-start edge of the flex item's content-box.)
+ void SetMainPosition(nscoord aPosn) {
+ MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
+ mMainPosn = aPosn;
+ }
+
+ // Sets the cross-size of our flex item's content-box.
+ void SetCrossSize(nscoord aCrossSize) {
+ MOZ_ASSERT(!mIsStretched,
+ "Cross size shouldn't be modified after it's been stretched");
+ mCrossSize = aCrossSize;
+ }
+
+ // Sets the cross-axis position of our flex item's content-box.
+ // (This is the distance between the cross-start edge of the flex container
+ // and the cross-start edge of the flex item.)
+ void SetCrossPosition(nscoord aPosn) {
+ MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
+ mCrossPosn = aPosn;
+ }
+
+ // After a FlexItem has had a reflow, this method can be used to cache its
+ // (possibly-unresolved) ascent, in case it's needed later for
+ // baseline-alignment or to establish the container's baseline.
+ // (NOTE: This can be marked 'const' even though it's modifying mAscent,
+ // because mAscent is mutable. It's nice for this to be 'const', because it
+ // means our final reflow can iterate over const FlexItem pointers, and we
+ // can be sure it's not modifying those FlexItems, except via this method.)
+ void SetAscent(nscoord aAscent) const {
+ mAscent = aAscent; // NOTE: this may be ASK_FOR_BASELINE
+ }
+
+ void SetHadMeasuringReflow() {
+ mHadMeasuringReflow = true;
+ }
+
+ void SetIsStretched() {
+ MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
+ mIsStretched = true;
+ }
+
+ // Setter for margin components (for resolving "auto" margins)
+ void SetMarginComponentForSide(mozilla::Side aSide, nscoord aLength)
+ {
+ MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
+ mMargin.Side(aSide) = aLength;
+ }
+
+ void ResolveStretchedCrossSize(nscoord aLineCrossSize,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const;
+
+ // Once the main size has been resolved, should we bother doing layout to
+ // establish the cross size?
+ bool CanMainSizeInfluenceCrossSize(const FlexboxAxisTracker& aAxisTracker) const;
+
+protected:
+ // Helper called by the constructor, to set mNeedsMinSizeAutoResolution:
+ void CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ // Our frame:
+ nsIFrame* const mFrame;
+
+ // Values that we already know in constructor: (and are hence mostly 'const')
+ const float mFlexGrow;
+ const float mFlexShrink;
+
+ const nsSize mIntrinsicRatio;
+
+ const nsMargin mBorderPadding;
+ nsMargin mMargin; // non-const because we need to resolve auto margins
+
+ // These are non-const so that we can lazily update them with the item's
+ // intrinsic size (obtained via a "measuring" reflow), when necessary.
+ // (e.g. for "flex-basis:auto;height:auto" & "min-height:auto")
+ nscoord mFlexBaseSize;
+ nscoord mMainMinSize;
+ nscoord mMainMaxSize;
+
+ const nscoord mCrossMinSize;
+ const nscoord mCrossMaxSize;
+
+ // Values that we compute after constructor:
+ nscoord mMainSize;
+ nscoord mMainPosn;
+ nscoord mCrossSize;
+ nscoord mCrossPosn;
+ mutable nscoord mAscent; // Mutable b/c it's set & resolved lazily, sometimes
+ // via const pointer. See comment above SetAscent().
+
+ // Temporary state, while we're resolving flexible widths (for our main size)
+ // XXXdholbert To save space, we could use a union to make these variables
+ // overlay the same memory as some other member vars that aren't touched
+ // until after main-size has been resolved. In particular, these could share
+ // memory with mMainPosn through mAscent, and mIsStretched.
+ float mShareOfWeightSoFar;
+ bool mIsFrozen;
+ bool mHadMinViolation;
+ bool mHadMaxViolation;
+
+ // Misc:
+ bool mHadMeasuringReflow; // Did this item get a preliminary reflow,
+ // to measure its desired height?
+ bool mIsStretched; // See IsStretched() documentation
+ bool mIsStrut; // Is this item a "strut" left behind by an element
+ // with visibility:collapse?
+
+ // Does this item need to resolve a min-[width|height]:auto (in main-axis).
+ bool mNeedsMinSizeAutoResolution;
+
+ const WritingMode mWM; // The flex item's writing mode.
+ uint8_t mAlignSelf; // My "align-self" computed value (with "auto"
+ // swapped out for parent"s "align-items" value,
+ // in our constructor).
+};
+
+/**
+ * Represents a single flex line in a flex container.
+ * Manages a linked list of the FlexItems that are in the line.
+ */
+class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine>
+{
+public:
+ FlexLine()
+ : mNumItems(0),
+ mNumFrozenItems(0),
+ mTotalInnerHypotheticalMainSize(0),
+ mTotalOuterHypotheticalMainSize(0),
+ mLineCrossSize(0),
+ mFirstBaselineOffset(nscoord_MIN),
+ mLastBaselineOffset(nscoord_MIN)
+ {}
+
+ // Returns the sum of our FlexItems' outer hypothetical main sizes.
+ // ("outer" = margin-box, and "hypothetical" = before flexing)
+ nscoord GetTotalOuterHypotheticalMainSize() const {
+ return mTotalOuterHypotheticalMainSize;
+ }
+
+ // Accessors for our FlexItems & information about them:
+ FlexItem* GetFirstItem()
+ {
+ MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
+ "mNumItems bookkeeping is off");
+ return mItems.getFirst();
+ }
+
+ const FlexItem* GetFirstItem() const
+ {
+ MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
+ "mNumItems bookkeeping is off");
+ return mItems.getFirst();
+ }
+
+ FlexItem* GetLastItem()
+ {
+ MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
+ "mNumItems bookkeeping is off");
+ return mItems.getLast();
+ }
+
+ const FlexItem* GetLastItem() const
+ {
+ MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
+ "mNumItems bookkeeping is off");
+ return mItems.getLast();
+ }
+
+ bool IsEmpty() const
+ {
+ MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
+ "mNumItems bookkeeping is off");
+ return mItems.isEmpty();
+ }
+
+ uint32_t NumItems() const
+ {
+ MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
+ "mNumItems bookkeeping is off");
+ return mNumItems;
+ }
+
+ // Adds the given FlexItem to our list of items (at the front or back
+ // depending on aShouldInsertAtFront), and adds its hypothetical
+ // outer & inner main sizes to our totals. Use this method instead of
+ // directly modifying the item list, so that our bookkeeping remains correct.
+ void AddItem(FlexItem* aItem,
+ bool aShouldInsertAtFront,
+ nscoord aItemInnerHypotheticalMainSize,
+ nscoord aItemOuterHypotheticalMainSize)
+ {
+ if (aShouldInsertAtFront) {
+ mItems.insertFront(aItem);
+ } else {
+ mItems.insertBack(aItem);
+ }
+
+ // Update our various bookkeeping member-vars:
+ mNumItems++;
+ if (aItem->IsFrozen()) {
+ mNumFrozenItems++;
+ }
+ mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize;
+ mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize;
+ }
+
+ // Computes the cross-size and baseline position of this FlexLine, based on
+ // its FlexItems.
+ void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
+
+ // Returns the cross-size of this line.
+ nscoord GetLineCrossSize() const { return mLineCrossSize; }
+
+ // Setter for line cross-size -- needed for cases where the flex container
+ // imposes a cross-size on the line. (e.g. for single-line flexbox, or for
+ // multi-line flexbox with 'align-content: stretch')
+ void SetLineCrossSize(nscoord aLineCrossSize) {
+ mLineCrossSize = aLineCrossSize;
+ }
+
+ /**
+ * Returns the offset within this line where any baseline-aligned FlexItems
+ * should place their baseline. Usually, this represents a distance from the
+ * line's cross-start edge, but if we're internally reversing the axes (see
+ * AreAxesInternallyReversed()), this instead represents the distance from
+ * its cross-end edge.
+ *
+ * If there are no baseline-aligned FlexItems, returns nscoord_MIN.
+ */
+ nscoord GetFirstBaselineOffset() const {
+ return mFirstBaselineOffset;
+ }
+
+ /**
+ * Returns the offset within this line where any last baseline-aligned
+ * FlexItems should place their baseline. Opposite the case of the first
+ * baseline offset, this represents a distance from the line's cross-end
+ * edge (since last baseline-aligned items are flush to the cross-end edge).
+ * If we're internally reversing the axes, this instead represents the
+ * distance from the line's cross-start edge.
+ *
+ * If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
+ */
+ nscoord GetLastBaselineOffset() const {
+ return mLastBaselineOffset;
+ }
+
+ // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
+ // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
+ void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
+
+ void PositionItemsInMainAxis(uint8_t aJustifyContent,
+ nscoord aContentBoxMainSize,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ void PositionItemsInCrossAxis(nscoord aLineStartPosition,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ friend class AutoFlexLineListClearer; // (needs access to mItems)
+
+private:
+ // Helpers for ResolveFlexibleLengths():
+ void FreezeItemsEarly(bool aIsUsingFlexGrow);
+
+ void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
+ bool aIsFinalIteration);
+
+ LinkedList<FlexItem> mItems; // Linked list of this line's flex items.
+
+ uint32_t mNumItems; // Number of FlexItems in this line (in |mItems|).
+ // (Shouldn't change after GenerateFlexLines finishes
+ // with this line -- at least, not until we add support
+ // for splitting lines across continuations. Then we can
+ // update this count carefully.)
+
+ // Number of *frozen* FlexItems in this line, based on FlexItem::IsFrozen().
+ // Mostly used for optimization purposes, e.g. to bail out early from loops
+ // when we can tell they have nothing left to do.
+ uint32_t mNumFrozenItems;
+
+ nscoord mTotalInnerHypotheticalMainSize;
+ nscoord mTotalOuterHypotheticalMainSize;
+ nscoord mLineCrossSize;
+ nscoord mFirstBaselineOffset;
+ nscoord mLastBaselineOffset;
+};
+
+// Information about a strut left behind by a FlexItem that's been collapsed
+// using "visibility:collapse".
+struct nsFlexContainerFrame::StrutInfo {
+ StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
+ : mItemIdx(aItemIdx),
+ mStrutCrossSize(aStrutCrossSize)
+ {
+ }
+
+ uint32_t mItemIdx; // Index in the child list.
+ nscoord mStrutCrossSize; // The cross-size of this strut.
+};
+
+static void
+BuildStrutInfoFromCollapsedItems(const FlexLine* aFirstLine,
+ nsTArray<StrutInfo>& aStruts)
+{
+ MOZ_ASSERT(aFirstLine, "null first line pointer");
+ MOZ_ASSERT(aStruts.IsEmpty(),
+ "We should only build up StrutInfo once per reflow, so "
+ "aStruts should be empty when this is called");
+
+ uint32_t itemIdxInContainer = 0;
+ for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
+ for (const FlexItem* item = line->GetFirstItem(); item;
+ item = item->getNext()) {
+ if (NS_STYLE_VISIBILITY_COLLAPSE ==
+ item->Frame()->StyleVisibility()->mVisible) {
+ // Note the cross size of the line as the item's strut size.
+ aStruts.AppendElement(StrutInfo(itemIdxInContainer,
+ line->GetLineCrossSize()));
+ }
+ itemIdxInContainer++;
+ }
+ }
+}
+
+// Convenience function to get either the "order" or the "box-ordinal-group"
+// property-value for a flex item (depending on whether the container is a
+// modern flex container or a legacy box).
+static int32_t
+GetOrderOrBoxOrdinalGroup(nsIFrame* aFlexItem, bool aIsLegacyBox)
+{
+ if (aFlexItem->GetType() == nsGkAtoms::placeholderFrame) {
+ // Always treat placeholders as having the default value, which is
+ // 1 for (legacy) 'box-ordinal-group' and 0 for 'order'.
+ return aIsLegacyBox ? 1 : 0;
+ }
+ if (aIsLegacyBox) {
+ // We'll be using mBoxOrdinal, which has type uint32_t. However, the modern
+ // 'order' property (whose functionality we're co-opting) has type int32_t.
+ // So: if we happen to have a uint32_t value that's greater than INT32_MAX,
+ // we clamp it rather than letting it overflow. Chances are, this is just
+ // an author using BIG_VALUE anyway, so the clamped value should be fine.
+ // (particularly since sufficiently-huge values are busted in Chrome/WebKit
+ // per https://bugs.chromium.org/p/chromium/issues/detail?id=599645 )
+ uint32_t clampedBoxOrdinal = std::min(aFlexItem->StyleXUL()->mBoxOrdinal,
+ static_cast<uint32_t>(INT32_MAX));
+ return static_cast<int32_t>(clampedBoxOrdinal);
+ }
+
+ // Normal case: just use modern 'order' property.
+ return aFlexItem->StylePosition()->mOrder;
+}
+
+// Helper-function to find the first non-anonymous-box descendent of aFrame.
+static nsIFrame*
+GetFirstNonAnonBoxDescendant(nsIFrame* aFrame)
+{
+ while (aFrame) {
+ nsIAtom* pseudoTag = aFrame->StyleContext()->GetPseudo();
+
+ // If aFrame isn't an anonymous container, then it'll do.
+ if (!pseudoTag || // No pseudotag.
+ !nsCSSAnonBoxes::IsAnonBox(pseudoTag) || // Pseudotag isn't anon.
+ nsCSSAnonBoxes::IsNonElement(pseudoTag)) { // Text, not a container.
+ break;
+ }
+
+ // Otherwise, descend to its first child and repeat.
+
+ // SPECIAL CASE: if we're dealing with an anonymous table, then it might
+ // be wrapping something non-anonymous in its caption or col-group lists
+ // (instead of its principal child list), so we have to look there.
+ // (Note: For anonymous tables that have a non-anon cell *and* a non-anon
+ // column, we'll always return the column. This is fine; we're really just
+ // looking for a handle to *anything* with a meaningful content node inside
+ // the table, for use in DOM comparisons to things outside of the table.)
+ if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableWrapperFrame)) {
+ nsIFrame* captionDescendant =
+ GetFirstNonAnonBoxDescendant(aFrame->GetChildList(kCaptionList).FirstChild());
+ if (captionDescendant) {
+ return captionDescendant;
+ }
+ } else if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableFrame)) {
+ nsIFrame* colgroupDescendant =
+ GetFirstNonAnonBoxDescendant(aFrame->GetChildList(kColGroupList).FirstChild());
+ if (colgroupDescendant) {
+ return colgroupDescendant;
+ }
+ }
+
+ // USUAL CASE: Descend to the first child in principal list.
+ aFrame = aFrame->PrincipalChildList().FirstChild();
+ }
+ return aFrame;
+}
+
+/**
+ * Sorting helper-function that compares two frames' "order" property-values,
+ * and if they're equal, compares the DOM positions of their corresponding
+ * content nodes. Returns true if aFrame1 is "less than or equal to" aFrame2
+ * according to this comparison.
+ *
+ * Note: This can't be a static function, because we need to pass it as a
+ * template argument. (Only functions with external linkage can be passed as
+ * template arguments.)
+ *
+ * @return true if the computed "order" property of aFrame1 is less than that
+ * of aFrame2, or if the computed "order" values are equal and aFrame1's
+ * corresponding DOM node is earlier than aFrame2's in the DOM tree.
+ * Otherwise, returns false.
+ */
+bool
+IsOrderLEQWithDOMFallback(nsIFrame* aFrame1,
+ nsIFrame* aFrame2)
+{
+ MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(),
+ "this method only intended for comparing flex items");
+ MOZ_ASSERT(aFrame1->GetParent() == aFrame2->GetParent(),
+ "this method only intended for comparing siblings");
+ if (aFrame1 == aFrame2) {
+ // Anything is trivially LEQ itself, so we return "true" here... but it's
+ // probably bad if we end up actually needing this, so let's assert.
+ NS_ERROR("Why are we checking if a frame is LEQ itself?");
+ return true;
+ }
+
+ const bool isInLegacyBox = IsLegacyBox(aFrame1->GetParent());
+
+ int32_t order1 = GetOrderOrBoxOrdinalGroup(aFrame1, isInLegacyBox);
+ int32_t order2 = GetOrderOrBoxOrdinalGroup(aFrame2, isInLegacyBox);
+
+ if (order1 != order2) {
+ return order1 < order2;
+ }
+
+ // The "order" values are equal, so we need to fall back on DOM comparison.
+ // For that, we need to dig through any anonymous box wrapper frames to find
+ // the actual frame that corresponds to our child content.
+ aFrame1 = GetFirstNonAnonBoxDescendant(aFrame1);
+ aFrame2 = GetFirstNonAnonBoxDescendant(aFrame2);
+ MOZ_ASSERT(aFrame1 && aFrame2,
+ "why do we have an anonymous box without any "
+ "non-anonymous descendants?");
+
+
+ // Special case:
+ // If either frame is for generated content from ::before or ::after, then
+ // we can't use nsContentUtils::PositionIsBefore(), since that method won't
+ // recognize generated content as being an actual sibling of other nodes.
+ // We know where ::before and ::after nodes *effectively* insert in the DOM
+ // tree, though (at the beginning & end), so we can just special-case them.
+ nsIAtom* pseudo1 =
+ nsPlaceholderFrame::GetRealFrameFor(aFrame1)->StyleContext()->GetPseudo();
+ nsIAtom* pseudo2 =
+ nsPlaceholderFrame::GetRealFrameFor(aFrame2)->StyleContext()->GetPseudo();
+
+ if (pseudo1 == nsCSSPseudoElements::before ||
+ pseudo2 == nsCSSPseudoElements::after) {
+ // frame1 is ::before and/or frame2 is ::after => frame1 is LEQ frame2.
+ return true;
+ }
+ if (pseudo1 == nsCSSPseudoElements::after ||
+ pseudo2 == nsCSSPseudoElements::before) {
+ // frame1 is ::after and/or frame2 is ::before => frame1 is not LEQ frame2.
+ return false;
+ }
+
+ // Usual case: Compare DOM position.
+ nsIContent* content1 = aFrame1->GetContent();
+ nsIContent* content2 = aFrame2->GetContent();
+ MOZ_ASSERT(content1 != content2,
+ "Two different flex items are using the same nsIContent node for "
+ "comparison, so we may be sorting them in an arbitrary order");
+
+ return nsContentUtils::PositionIsBefore(content1, content2);
+}
+
+/**
+ * Sorting helper-function that compares two frames' "order" property-values.
+ * Returns true if aFrame1 is "less than or equal to" aFrame2 according to this
+ * comparison.
+ *
+ * Note: This can't be a static function, because we need to pass it as a
+ * template argument. (Only functions with external linkage can be passed as
+ * template arguments.)
+ *
+ * @return true if the computed "order" property of aFrame1 is less than or
+ * equal to that of aFrame2. Otherwise, returns false.
+ */
+bool
+IsOrderLEQ(nsIFrame* aFrame1,
+ nsIFrame* aFrame2)
+{
+ MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(),
+ "this method only intended for comparing flex items");
+ MOZ_ASSERT(aFrame1->GetParent() == aFrame2->GetParent(),
+ "this method only intended for comparing siblings");
+
+ const bool isInLegacyBox = IsLegacyBox(aFrame1->GetParent());
+
+ int32_t order1 = GetOrderOrBoxOrdinalGroup(aFrame1, isInLegacyBox);
+ int32_t order2 = GetOrderOrBoxOrdinalGroup(aFrame2, isInLegacyBox);
+
+ return order1 <= order2;
+}
+
+uint8_t
+SimplifyAlignOrJustifyContentForOneItem(uint16_t aAlignmentVal,
+ bool aIsAlign)
+{
+ // Mask away any explicit fallback, to get the main (non-fallback) part of
+ // the specified value:
+ uint16_t specified = aAlignmentVal & NS_STYLE_ALIGN_ALL_BITS;
+
+ // XXX strip off <overflow-position> bits until we implement it (bug 1311892)
+ specified &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
+ // FIRST: handle a special-case for "justify-content:stretch" (or equivalent),
+ // which requires that we ignore any author-provided explicit fallback value.
+ if (specified == NS_STYLE_ALIGN_NORMAL) {
+ // In a flex container, *-content: "'normal' behaves as 'stretch'".
+ // Do that conversion early, so it benefits from our 'stretch' special-case.
+ // https://drafts.csswg.org/css-align-3/#distribution-flex
+ specified = NS_STYLE_ALIGN_STRETCH;
+ }
+ if (!aIsAlign && specified == NS_STYLE_ALIGN_STRETCH) {
+ // In a flex container, in "justify-content Axis: [...] 'stretch' behaves
+ // as 'flex-start' (ignoring the specified fallback alignment, if any)."
+ // https://drafts.csswg.org/css-align-3/#distribution-flex
+ // So, we just directly return 'flex-start', & ignore explicit fallback..
+ return NS_STYLE_ALIGN_FLEX_START;
+ }
+
+ // Now check for an explicit fallback value (and if it's present, use it).
+ uint16_t explicitFallback = aAlignmentVal >> NS_STYLE_ALIGN_ALL_SHIFT;
+ if (explicitFallback) {
+ // XXX strip off <overflow-position> bits until we implement it
+ // (bug 1311892)
+ explicitFallback &= ~NS_STYLE_ALIGN_FLAG_BITS;
+ return explicitFallback;
+ }
+
+ // There's no explicit fallback. Use the implied fallback values for
+ // space-{between,around,evenly} (since those values only make sense with
+ // multiple alignment subjects), and otherwise just use the specified value:
+ switch (specified) {
+ case NS_STYLE_ALIGN_SPACE_BETWEEN:
+ return NS_STYLE_ALIGN_START;
+ case NS_STYLE_ALIGN_SPACE_AROUND:
+ case NS_STYLE_ALIGN_SPACE_EVENLY:
+ return NS_STYLE_ALIGN_CENTER;
+ default:
+ return specified;
+ }
+}
+
+uint16_t
+nsFlexContainerFrame::CSSAlignmentForAbsPosChild(
+ const ReflowInput& aChildRI,
+ LogicalAxis aLogicalAxis) const
+{
+ WritingMode wm = GetWritingMode();
+ const FlexboxAxisTracker
+ axisTracker(this, wm, AxisTrackerFlags::eAllowBottomToTopChildOrdering);
+
+ // If we're row-oriented and the caller is asking about our inline axis (or
+ // alternately, if we're column-oriented and the caller is asking about our
+ // block axis), then the caller is really asking about our *main* axis.
+ // Otherwise, the caller is asking about our cross axis.
+ const bool isMainAxis = (axisTracker.IsRowOriented() ==
+ (aLogicalAxis == eLogicalAxisInline));
+ const nsStylePosition* containerStylePos = StylePosition();
+ const bool isAxisReversed = isMainAxis ? axisTracker.IsMainAxisReversed()
+ : axisTracker.IsCrossAxisReversed();
+
+ uint8_t alignment;
+ if (isMainAxis) {
+ alignment = SimplifyAlignOrJustifyContentForOneItem(
+ containerStylePos->mJustifyContent,
+ /*aIsAlign = */false);
+ } else {
+ const uint8_t alignContent = SimplifyAlignOrJustifyContentForOneItem(
+ containerStylePos->mAlignContent,
+ /*aIsAlign = */true);
+ if (NS_STYLE_FLEX_WRAP_NOWRAP != containerStylePos->mFlexWrap &&
+ alignContent != NS_STYLE_ALIGN_STRETCH) {
+ // Multi-line, align-content isn't stretch --> align-content determines
+ // this child's alignment in the cross axis.
+ alignment = alignContent;
+ } else {
+ // Single-line, or multi-line but the (one) line stretches to fill
+ // container. Respect align-self.
+ alignment = aChildRI.mStylePosition->UsedAlignSelf(StyleContext());
+ // XXX strip off <overflow-position> bits until we implement it
+ // (bug 1311892)
+ alignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
+ if (alignment == NS_STYLE_ALIGN_NORMAL) {
+ // "the 'normal' keyword behaves as 'start' on replaced
+ // absolutely-positioned boxes, and behaves as 'stretch' on all other
+ // absolutely-positioned boxes."
+ // https://drafts.csswg.org/css-align/#align-abspos
+ alignment = aChildRI.mFrame->IsFrameOfType(nsIFrame::eReplaced) ?
+ NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_STRETCH;
+ }
+ }
+ }
+
+ // Resolve flex-start, flex-end, auto, left, right, baseline, last baseline;
+ if (alignment == NS_STYLE_ALIGN_FLEX_START) {
+ alignment = isAxisReversed ? NS_STYLE_ALIGN_END : NS_STYLE_ALIGN_START;
+ } else if (alignment == NS_STYLE_ALIGN_FLEX_END) {
+ alignment = isAxisReversed ? NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_END;
+ } else if (alignment == NS_STYLE_ALIGN_LEFT ||
+ alignment == NS_STYLE_ALIGN_RIGHT) {
+ if (aLogicalAxis == eLogicalAxisInline) {
+ const bool isLeft = (alignment == NS_STYLE_ALIGN_LEFT);
+ alignment = (isLeft == wm.IsBidiLTR()) ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END;
+ } else {
+ alignment = NS_STYLE_ALIGN_START;
+ }
+ } else if (alignment == NS_STYLE_ALIGN_BASELINE) {
+ alignment = NS_STYLE_ALIGN_START;
+ } else if (alignment == NS_STYLE_ALIGN_LAST_BASELINE) {
+ alignment = NS_STYLE_ALIGN_END;
+ }
+
+ return alignment;
+}
+
+bool
+nsFlexContainerFrame::IsHorizontal()
+{
+ const FlexboxAxisTracker axisTracker(this, GetWritingMode());
+ return axisTracker.IsMainAxisHorizontal();
+}
+
+UniquePtr<FlexItem>
+nsFlexContainerFrame::GenerateFlexItemForChild(
+ nsPresContext* aPresContext,
+ nsIFrame* aChildFrame,
+ const ReflowInput& aParentReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ // Create temporary reflow state just for sizing -- to get hypothetical
+ // main-size and the computed values of min / max main-size property.
+ // (This reflow state will _not_ be used for reflow.)
+ ReflowInput
+ childRI(aPresContext, aParentReflowInput, aChildFrame,
+ aParentReflowInput.ComputedSize(aChildFrame->GetWritingMode()));
+
+ // FLEX GROW & SHRINK WEIGHTS
+ // --------------------------
+ float flexGrow, flexShrink;
+ if (IsLegacyBox(this)) {
+ flexGrow = flexShrink = aChildFrame->StyleXUL()->mBoxFlex;
+ } else {
+ const nsStylePosition* stylePos = aChildFrame->StylePosition();
+ flexGrow = stylePos->mFlexGrow;
+ flexShrink = stylePos->mFlexShrink;
+ }
+
+ WritingMode childWM = childRI.GetWritingMode();
+
+ // MAIN SIZES (flex base size, min/max size)
+ // -----------------------------------------
+ nscoord flexBaseSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, childWM,
+ childRI.ComputedISize(),
+ childRI.ComputedBSize());
+ nscoord mainMinSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, childWM,
+ childRI.ComputedMinISize(),
+ childRI.ComputedMinBSize());
+ nscoord mainMaxSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, childWM,
+ childRI.ComputedMaxISize(),
+ childRI.ComputedMaxBSize());
+ // This is enforced by the ReflowInput where these values come from:
+ MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size");
+
+ // CROSS SIZES (tentative cross size, min/max cross size)
+ // ------------------------------------------------------
+ // Grab the cross size from the reflow state. This might be the right value,
+ // or we might resolve it to something else in SizeItemInCrossAxis(); hence,
+ // it's tentative. See comment under "Cross Size Determination" for more.
+ nscoord tentativeCrossSize =
+ GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, childWM,
+ childRI.ComputedISize(),
+ childRI.ComputedBSize());
+ nscoord crossMinSize =
+ GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, childWM,
+ childRI.ComputedMinISize(),
+ childRI.ComputedMinBSize());
+ nscoord crossMaxSize =
+ GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, childWM,
+ childRI.ComputedMaxISize(),
+ childRI.ComputedMaxBSize());
+
+ // SPECIAL-CASE FOR WIDGET-IMPOSED SIZES
+ // Check if we're a themed widget, in which case we might have a minimum
+ // main & cross size imposed by our widget (which we can't go below), or
+ // (more severe) our widget might have only a single valid size.
+ bool isFixedSizeWidget = false;
+ const nsStyleDisplay* disp = aChildFrame->StyleDisplay();
+ if (aChildFrame->IsThemed(disp)) {
+ LayoutDeviceIntSize widgetMinSize;
+ bool canOverride = true;
+ aPresContext->GetTheme()->
+ GetMinimumWidgetSize(aPresContext, aChildFrame,
+ disp->mAppearance,
+ &widgetMinSize, &canOverride);
+
+ nscoord widgetMainMinSize =
+ aPresContext->DevPixelsToAppUnits(
+ aAxisTracker.GetMainComponent(widgetMinSize));
+ nscoord widgetCrossMinSize =
+ aPresContext->DevPixelsToAppUnits(
+ aAxisTracker.GetCrossComponent(widgetMinSize));
+
+ // GMWS() returns border-box. We need content-box, so subtract
+ // borderPadding (but don't let that push our min sizes below 0).
+ nsMargin& bp = childRI.ComputedPhysicalBorderPadding();
+ widgetMainMinSize = std::max(widgetMainMinSize -
+ aAxisTracker.GetMarginSizeInMainAxis(bp), 0);
+ widgetCrossMinSize = std::max(widgetCrossMinSize -
+ aAxisTracker.GetMarginSizeInCrossAxis(bp), 0);
+
+ if (!canOverride) {
+ // Fixed-size widget: freeze our main-size at the widget's mandated size.
+ // (Set min and max main-sizes to that size, too, to keep us from
+ // clamping to any other size later on.)
+ flexBaseSize = mainMinSize = mainMaxSize = widgetMainMinSize;
+ tentativeCrossSize = crossMinSize = crossMaxSize = widgetCrossMinSize;
+ isFixedSizeWidget = true;
+ } else {
+ // Variable-size widget: ensure our min/max sizes are at least as large
+ // as the widget's mandated minimum size, so we don't flex below that.
+ mainMinSize = std::max(mainMinSize, widgetMainMinSize);
+ mainMaxSize = std::max(mainMaxSize, widgetMainMinSize);
+
+ if (tentativeCrossSize != NS_INTRINSICSIZE) {
+ tentativeCrossSize = std::max(tentativeCrossSize, widgetCrossMinSize);
+ }
+ crossMinSize = std::max(crossMinSize, widgetCrossMinSize);
+ crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize);
+ }
+ }
+
+ // Construct the flex item!
+ auto item = MakeUnique<FlexItem>(childRI,
+ flexGrow, flexShrink, flexBaseSize,
+ mainMinSize, mainMaxSize,
+ tentativeCrossSize,
+ crossMinSize, crossMaxSize,
+ aAxisTracker);
+
+ // If we're inflexible, we can just freeze to our hypothetical main-size
+ // up-front. Similarly, if we're a fixed-size widget, we only have one
+ // valid size, so we freeze to keep ourselves from flexing.
+ if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) {
+ item->Freeze();
+ }
+
+ // Resolve "flex-basis:auto" and/or "min-[width|height]:auto" (which might
+ // require us to reflow the item to measure content height)
+ ResolveAutoFlexBasisAndMinSize(aPresContext, *item,
+ childRI, aAxisTracker);
+ return item;
+}
+
+// Static helper-functions for ResolveAutoFlexBasisAndMinSize():
+// -------------------------------------------------------------
+// Indicates whether the cross-size property is set to something definite.
+// The logic here should be similar to the logic for isAutoWidth/isAutoHeight
+// in nsFrame::ComputeSizeWithIntrinsicDimensions().
+static bool
+IsCrossSizeDefinite(const ReflowInput& aItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ const nsStylePosition* pos = aItemReflowInput.mStylePosition;
+ if (aAxisTracker.IsCrossAxisHorizontal()) {
+ return pos->mWidth.GetUnit() != eStyleUnit_Auto;
+ }
+ // else, vertical. (We need to use IsAutoHeight() to catch e.g. %-height
+ // applied to indefinite-height containing block, which counts as auto.)
+ nscoord cbHeight = aItemReflowInput.mCBReflowInput->ComputedHeight();
+ return !nsLayoutUtils::IsAutoHeight(pos->mHeight, cbHeight);
+}
+
+// If aFlexItem has a definite cross size, this function returns it, for usage
+// (in combination with an intrinsic ratio) for resolving the item's main size
+// or main min-size.
+//
+// The parameter "aMinSizeFallback" indicates whether we should fall back to
+// returning the cross min-size, when the cross size is indefinite. (This param
+// should be set IFF the caller intends to resolve the main min-size.) If this
+// param is true, then this function is guaranteed to return a definite value
+// (i.e. not NS_AUTOHEIGHT, excluding cases where huge sizes are involved).
+//
+// XXXdholbert the min-size behavior here is based on my understanding in
+// http://lists.w3.org/Archives/Public/www-style/2014Jul/0053.html
+// If my understanding there ends up being wrong, we'll need to update this.
+static nscoord
+CrossSizeToUseWithRatio(const FlexItem& aFlexItem,
+ const ReflowInput& aItemReflowInput,
+ bool aMinSizeFallback,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ if (aFlexItem.IsStretched()) {
+ // Definite cross-size, imposed via 'align-self:stretch' & flex container.
+ return aFlexItem.GetCrossSize();
+ }
+
+ if (IsCrossSizeDefinite(aItemReflowInput, aAxisTracker)) {
+ // Definite cross size.
+ return GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, aFlexItem.GetWritingMode(),
+ aItemReflowInput.ComputedISize(),
+ aItemReflowInput.ComputedBSize());
+ }
+
+ if (aMinSizeFallback) {
+ // Indefinite cross-size, and we're resolving main min-size, so we'll fall
+ // back to ussing the cross min-size (which should be definite).
+ return GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, aFlexItem.GetWritingMode(),
+ aItemReflowInput.ComputedMinISize(),
+ aItemReflowInput.ComputedMinBSize());
+ }
+
+ // Indefinite cross-size.
+ return NS_AUTOHEIGHT;
+}
+
+// Convenience function; returns a main-size, given a cross-size and an
+// intrinsic ratio. The intrinsic ratio must not have 0 in its cross-axis
+// component (or else we'll divide by 0).
+static nscoord
+MainSizeFromAspectRatio(nscoord aCrossSize,
+ const nsSize& aIntrinsicRatio,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ MOZ_ASSERT(aAxisTracker.GetCrossComponent(aIntrinsicRatio) != 0,
+ "Invalid ratio; will divide by 0! Caller should've checked...");
+
+ if (aAxisTracker.IsCrossAxisHorizontal()) {
+ // cross axis horiz --> aCrossSize is a width. Converting to height.
+ return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.height, aIntrinsicRatio.width);
+ }
+ // cross axis vert --> aCrossSize is a height. Converting to width.
+ return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.width, aIntrinsicRatio.height);
+}
+
+// Partially resolves "min-[width|height]:auto" and returns the resulting value.
+// By "partially", I mean we don't consider the min-content size (but we do
+// consider flex-basis, main max-size, and the intrinsic aspect ratio).
+// The caller is responsible for computing & considering the min-content size
+// in combination with the partially-resolved value that this function returns.
+//
+// Spec reference: http://dev.w3.org/csswg/css-flexbox/#min-size-auto
+static nscoord
+PartiallyResolveAutoMinSize(const FlexItem& aFlexItem,
+ const ReflowInput& aItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ MOZ_ASSERT(aFlexItem.NeedsMinSizeAutoResolution(),
+ "only call for FlexItems that need min-size auto resolution");
+
+ nscoord minMainSize = nscoord_MAX; // Intentionally huge; we'll shrink it
+ // from here, w/ std::min().
+
+ // We need the smallest of:
+ // * the used flex-basis, if the computed flex-basis was 'auto':
+ // XXXdholbert ('auto' might be renamed to 'main-size'; see bug 1032922)
+ if (eStyleUnit_Auto ==
+ aItemReflowInput.mStylePosition->mFlexBasis.GetUnit() &&
+ aFlexItem.GetFlexBaseSize() != NS_AUTOHEIGHT) {
+ // NOTE: We skip this if the flex base size depends on content & isn't yet
+ // resolved. This is OK, because the caller is responsible for computing
+ // the min-content height and min()'ing it with the value we return, which
+ // is equivalent to what would happen if we min()'d that at this point.
+ minMainSize = std::min(minMainSize, aFlexItem.GetFlexBaseSize());
+ }
+
+ // * the computed max-width (max-height), if that value is definite:
+ nscoord maxSize =
+ GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, aFlexItem.GetWritingMode(),
+ aItemReflowInput.ComputedMaxISize(),
+ aItemReflowInput.ComputedMaxBSize());
+ if (maxSize != NS_UNCONSTRAINEDSIZE) {
+ minMainSize = std::min(minMainSize, maxSize);
+ }
+
+ // * if the item has no intrinsic aspect ratio, its min-content size:
+ // --- SKIPPING THIS IN THIS FUNCTION --- caller's responsibility.
+
+ // * if the item has an intrinsic aspect ratio, the width (height) calculated
+ // from the aspect ratio and any definite size constraints in the opposite
+ // dimension.
+ if (aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) != 0) {
+ // We have a usable aspect ratio. (not going to divide by 0)
+ const bool useMinSizeIfCrossSizeIsIndefinite = true;
+ nscoord crossSizeToUseWithRatio =
+ CrossSizeToUseWithRatio(aFlexItem, aItemReflowInput,
+ useMinSizeIfCrossSizeIsIndefinite,
+ aAxisTracker);
+ nscoord minMainSizeFromRatio =
+ MainSizeFromAspectRatio(crossSizeToUseWithRatio,
+ aFlexItem.IntrinsicRatio(), aAxisTracker);
+ minMainSize = std::min(minMainSize, minMainSizeFromRatio);
+ }
+
+ return minMainSize;
+}
+
+// Resolves flex-basis:auto, using the given intrinsic ratio and the flex
+// item's cross size. On success, updates the flex item with its resolved
+// flex-basis and returns true. On failure (e.g. if the ratio is invalid or
+// the cross-size is indefinite), returns false.
+static bool
+ResolveAutoFlexBasisFromRatio(FlexItem& aFlexItem,
+ const ReflowInput& aItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ MOZ_ASSERT(NS_AUTOHEIGHT == aFlexItem.GetFlexBaseSize(),
+ "Should only be called to resolve an 'auto' flex-basis");
+ // If the flex item has ...
+ // - an intrinsic aspect ratio,
+ // - a [used] flex-basis of 'main-size' [auto?] [We have this, if we're here.]
+ // - a definite cross size
+ // then the flex base size is calculated from its inner cross size and the
+ // flex item’s intrinsic aspect ratio.
+ if (aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) != 0) {
+ // We have a usable aspect ratio. (not going to divide by 0)
+ const bool useMinSizeIfCrossSizeIsIndefinite = false;
+ nscoord crossSizeToUseWithRatio =
+ CrossSizeToUseWithRatio(aFlexItem, aItemReflowInput,
+ useMinSizeIfCrossSizeIsIndefinite,
+ aAxisTracker);
+ if (crossSizeToUseWithRatio != NS_AUTOHEIGHT) {
+ // We have a definite cross-size
+ nscoord mainSizeFromRatio =
+ MainSizeFromAspectRatio(crossSizeToUseWithRatio,
+ aFlexItem.IntrinsicRatio(), aAxisTracker);
+ aFlexItem.SetFlexBaseSizeAndMainSize(mainSizeFromRatio);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Note: If & when we handle "min-height: min-content" for flex items,
+// we may want to resolve that in this function, too.
+void
+nsFlexContainerFrame::
+ ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext,
+ FlexItem& aFlexItem,
+ const ReflowInput& aItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ // (Note: We should never have a used flex-basis of "auto" if our main axis
+ // is horizontal; width values should always be resolvable without reflow.)
+ const bool isMainSizeAuto = (!aAxisTracker.IsMainAxisHorizontal() &&
+ NS_AUTOHEIGHT == aFlexItem.GetFlexBaseSize());
+
+ const bool isMainMinSizeAuto = aFlexItem.NeedsMinSizeAutoResolution();
+
+ if (!isMainSizeAuto && !isMainMinSizeAuto) {
+ // Nothing to do; this function is only needed for flex items
+ // with a used flex-basis of "auto" or a min-main-size of "auto".
+ return;
+ }
+
+ // We may be about to do computations based on our item's cross-size
+ // (e.g. using it as a contstraint when measuring our content in the
+ // main axis, or using it with the intrinsic ratio to obtain a main size).
+ // BEFORE WE DO THAT, we need let the item "pre-stretch" its cross size (if
+ // it's got 'align-self:stretch'), for a certain case where the spec says
+ // the stretched cross size is considered "definite". That case is if we
+ // have a single-line (nowrap) flex container which itself has a definite
+ // cross-size. Otherwise, we'll wait to do stretching, since (in other
+ // cases) we don't know how much the item should stretch yet.
+ const ReflowInput* flexContainerRI = aItemReflowInput.mParentReflowInput;
+ MOZ_ASSERT(flexContainerRI,
+ "flex item's reflow state should have ptr to container's state");
+ if (NS_STYLE_FLEX_WRAP_NOWRAP == flexContainerRI->mStylePosition->mFlexWrap) {
+ // XXXdholbert Maybe this should share logic with ComputeCrossSize()...
+ // Alternately, maybe tentative container cross size should be passed down.
+ nscoord containerCrossSize =
+ GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, aAxisTracker.GetWritingMode(),
+ flexContainerRI->ComputedISize(),
+ flexContainerRI->ComputedBSize());
+ // Is container's cross size "definite"?
+ // (Container's cross size is definite if cross-axis is horizontal, or if
+ // cross-axis is vertical and the cross-size is not NS_AUTOHEIGHT.)
+ if (aAxisTracker.IsCrossAxisHorizontal() ||
+ containerCrossSize != NS_AUTOHEIGHT) {
+ aFlexItem.ResolveStretchedCrossSize(containerCrossSize, aAxisTracker);
+ }
+ }
+
+ nscoord resolvedMinSize; // (only set/used if isMainMinSizeAuto==true)
+ bool minSizeNeedsToMeasureContent = false; // assume the best
+ if (isMainMinSizeAuto) {
+ // Resolve the min-size, except for considering the min-content size.
+ // (We'll consider that later, if we need to.)
+ resolvedMinSize = PartiallyResolveAutoMinSize(aFlexItem, aItemReflowInput,
+ aAxisTracker);
+ if (resolvedMinSize > 0 &&
+ aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) == 0) {
+ // We don't have a usable aspect ratio, so we need to consider our
+ // min-content size as another candidate min-size, which we'll have to
+ // min() with the current resolvedMinSize.
+ // (If resolvedMinSize were already at 0, we could skip this measurement
+ // because it can't go any lower. But it's not 0, so we need it.)
+ minSizeNeedsToMeasureContent = true;
+ }
+ }
+
+ bool flexBasisNeedsToMeasureContent = false; // assume the best
+ if (isMainSizeAuto) {
+ if (!ResolveAutoFlexBasisFromRatio(aFlexItem, aItemReflowInput,
+ aAxisTracker)) {
+ flexBasisNeedsToMeasureContent = true;
+ }
+ }
+
+ // Measure content, if needed (w/ intrinsic-width method or a reflow)
+ if (minSizeNeedsToMeasureContent || flexBasisNeedsToMeasureContent) {
+ if (aAxisTracker.IsMainAxisHorizontal()) {
+ if (minSizeNeedsToMeasureContent) {
+ nscoord frameMinISize =
+ aFlexItem.Frame()->GetMinISize(aItemReflowInput.mRenderingContext);
+ resolvedMinSize = std::min(resolvedMinSize, frameMinISize);
+ }
+ NS_ASSERTION(!flexBasisNeedsToMeasureContent,
+ "flex-basis:auto should have been resolved in the "
+ "reflow state, for horizontal flexbox. It shouldn't need "
+ "special handling here");
+ } else {
+ // If this item is flexible (vertically), or if we're measuring the
+ // 'auto' min-height and our main-size is something else, then we assume
+ // that the computed-height we're reflowing with now could be different
+ // from the one we'll use for this flex item's "actual" reflow later on.
+ // In that case, we need to be sure the flex item treats this as a
+ // vertical resize, even though none of its ancestors are necessarily
+ // being vertically resized.
+ // (Note: We don't have to do this for width, because InitResizeFlags
+ // will always turn on mHResize on when it sees that the computed width
+ // is different from current width, and that's all we need.)
+ bool forceVerticalResizeForMeasuringReflow =
+ !aFlexItem.IsFrozen() || // Is the item flexible?
+ !flexBasisNeedsToMeasureContent; // Are we *only* measuring it for
+ // 'min-height:auto'?
+
+ nscoord contentHeight =
+ MeasureFlexItemContentHeight(aPresContext, aFlexItem,
+ forceVerticalResizeForMeasuringReflow,
+ *flexContainerRI);
+ if (minSizeNeedsToMeasureContent) {
+ resolvedMinSize = std::min(resolvedMinSize, contentHeight);
+ }
+ if (flexBasisNeedsToMeasureContent) {
+ aFlexItem.SetFlexBaseSizeAndMainSize(contentHeight);
+ }
+ }
+ }
+
+ if (isMainMinSizeAuto) {
+ aFlexItem.UpdateMainMinSize(resolvedMinSize);
+ }
+}
+
+nscoord
+nsFlexContainerFrame::
+ MeasureFlexItemContentHeight(nsPresContext* aPresContext,
+ FlexItem& aFlexItem,
+ bool aForceVerticalResizeForMeasuringReflow,
+ const ReflowInput& aParentReflowInput)
+{
+ // Set up a reflow state for measuring the flex item's auto-height:
+ WritingMode wm = aFlexItem.Frame()->GetWritingMode();
+ LogicalSize availSize = aParentReflowInput.ComputedSize(wm);
+ availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+ ReflowInput
+ childRIForMeasuringHeight(aPresContext, aParentReflowInput,
+ aFlexItem.Frame(), availSize,
+ nullptr, ReflowInput::CALLER_WILL_INIT);
+ childRIForMeasuringHeight.mFlags.mIsFlexContainerMeasuringHeight = true;
+ childRIForMeasuringHeight.Init(aPresContext);
+
+ if (aFlexItem.IsStretched()) {
+ childRIForMeasuringHeight.SetComputedWidth(aFlexItem.GetCrossSize());
+ childRIForMeasuringHeight.SetHResize(true);
+ }
+
+ if (aForceVerticalResizeForMeasuringReflow) {
+ childRIForMeasuringHeight.SetVResize(true);
+ }
+
+ ReflowOutput childDesiredSize(childRIForMeasuringHeight);
+ nsReflowStatus childReflowStatus;
+ const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
+ ReflowChild(aFlexItem.Frame(), aPresContext,
+ childDesiredSize, childRIForMeasuringHeight,
+ 0, 0, flags, childReflowStatus);
+
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
+ "We gave flex item unconstrained available height, so it "
+ "should be complete");
+
+ FinishReflowChild(aFlexItem.Frame(), aPresContext,
+ childDesiredSize, &childRIForMeasuringHeight,
+ 0, 0, flags);
+
+ aFlexItem.SetHadMeasuringReflow();
+ aFlexItem.SetAscent(childDesiredSize.BlockStartAscent());
+
+ // Subtract border/padding in vertical axis, to get _just_
+ // the effective computed value of the "height" property.
+ nscoord childDesiredHeight = childDesiredSize.Height() -
+ childRIForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom();
+
+ return std::max(0, childDesiredHeight);
+}
+
+FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput,
+ float aFlexGrow, float aFlexShrink, nscoord aFlexBaseSize,
+ nscoord aMainMinSize, nscoord aMainMaxSize,
+ nscoord aTentativeCrossSize,
+ nscoord aCrossMinSize, nscoord aCrossMaxSize,
+ const FlexboxAxisTracker& aAxisTracker)
+ : mFrame(aFlexItemReflowInput.mFrame),
+ mFlexGrow(aFlexGrow),
+ mFlexShrink(aFlexShrink),
+ mIntrinsicRatio(mFrame->GetIntrinsicRatio()),
+ mBorderPadding(aFlexItemReflowInput.ComputedPhysicalBorderPadding()),
+ mMargin(aFlexItemReflowInput.ComputedPhysicalMargin()),
+ mMainMinSize(aMainMinSize),
+ mMainMaxSize(aMainMaxSize),
+ mCrossMinSize(aCrossMinSize),
+ mCrossMaxSize(aCrossMaxSize),
+ mMainPosn(0),
+ mCrossSize(aTentativeCrossSize),
+ mCrossPosn(0),
+ mAscent(0),
+ mShareOfWeightSoFar(0.0f),
+ mIsFrozen(false),
+ mHadMinViolation(false),
+ mHadMaxViolation(false),
+ mHadMeasuringReflow(false),
+ mIsStretched(false),
+ mIsStrut(false),
+ // mNeedsMinSizeAutoResolution is initialized in CheckForMinSizeAuto()
+ mWM(aFlexItemReflowInput.GetWritingMode())
+ // mAlignSelf, see below
+{
+ MOZ_ASSERT(mFrame, "expecting a non-null child frame");
+ MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame,
+ "placeholder frames should not be treated as flex items");
+ MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
+ "out-of-flow frames should not be treated as flex items");
+
+ const ReflowInput* containerRS = aFlexItemReflowInput.mParentReflowInput;
+ if (IsLegacyBox(containerRS->mFrame)) {
+ // For -webkit-box/-webkit-inline-box, we need to:
+ // (1) Use "-webkit-box-align" instead of "align-items" to determine the
+ // container's cross-axis alignment behavior.
+ // (2) Suppress the ability for flex items to override that with their own
+ // cross-axis alignment. (The legacy box model doesn't support this.)
+ // So, each FlexItem simply copies the container's converted "align-items"
+ // value and disregards their own "align-self" property.
+ const nsStyleXUL* containerStyleXUL = containerRS->mFrame->StyleXUL();
+ mAlignSelf = ConvertLegacyStyleToAlignItems(containerStyleXUL);
+ } else {
+ mAlignSelf = aFlexItemReflowInput.mStylePosition->UsedAlignSelf(
+ containerRS->mFrame->StyleContext());
+ if (MOZ_LIKELY(mAlignSelf == NS_STYLE_ALIGN_NORMAL)) {
+ mAlignSelf = NS_STYLE_ALIGN_STRETCH;
+ }
+
+ // XXX strip off the <overflow-position> bit until we implement that
+ mAlignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
+ }
+
+ SetFlexBaseSizeAndMainSize(aFlexBaseSize);
+ CheckForMinSizeAuto(aFlexItemReflowInput, aAxisTracker);
+
+ // Assert that any "auto" margin components are set to 0.
+ // (We'll resolve them later; until then, we want to treat them as 0-sized.)
+#ifdef DEBUG
+ {
+ const nsStyleSides& styleMargin =
+ aFlexItemReflowInput.mStyleMargin->mMargin;
+ NS_FOR_CSS_SIDES(side) {
+ if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
+ MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
+ "Someone else tried to resolve our auto margin");
+ }
+ }
+ }
+#endif // DEBUG
+
+ // Map align-self 'baseline' value to 'start' when baseline alignment
+ // is not possible because the FlexItem's writing mode is orthogonal to
+ // the main axis of the container. If that's the case, we just directly
+ // convert our align-self value here, so that we don't have to handle this
+ // with special cases elsewhere.
+ // We are treating this case as one where it is appropriate to use the
+ // fallback values defined at https://www.w3.org/TR/css-align-3/#baseline
+ if (aAxisTracker.IsRowOriented() ==
+ aAxisTracker.GetWritingMode().IsOrthogonalTo(mWM)) {
+ if (mAlignSelf == NS_STYLE_ALIGN_BASELINE) {
+ mAlignSelf = NS_STYLE_ALIGN_FLEX_START;
+ } else if (mAlignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
+ mAlignSelf = NS_STYLE_ALIGN_FLEX_END;
+ }
+ }
+}
+
+// Simplified constructor for creating a special "strut" FlexItem, for a child
+// with visibility:collapse. The strut has 0 main-size, and it only exists to
+// impose a minimum cross size on whichever FlexLine it ends up in.
+FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize,
+ WritingMode aContainerWM)
+ : mFrame(aChildFrame),
+ mFlexGrow(0.0f),
+ mFlexShrink(0.0f),
+ mIntrinsicRatio(),
+ // mBorderPadding uses default constructor,
+ // mMargin uses default constructor,
+ mFlexBaseSize(0),
+ mMainMinSize(0),
+ mMainMaxSize(0),
+ mCrossMinSize(0),
+ mCrossMaxSize(0),
+ mMainSize(0),
+ mMainPosn(0),
+ mCrossSize(aCrossSize),
+ mCrossPosn(0),
+ mAscent(0),
+ mShareOfWeightSoFar(0.0f),
+ mIsFrozen(true),
+ mHadMinViolation(false),
+ mHadMaxViolation(false),
+ mHadMeasuringReflow(false),
+ mIsStretched(false),
+ mIsStrut(true), // (this is the constructor for making struts, after all)
+ mNeedsMinSizeAutoResolution(false),
+ mWM(aContainerWM),
+ mAlignSelf(NS_STYLE_ALIGN_FLEX_START)
+{
+ MOZ_ASSERT(mFrame, "expecting a non-null child frame");
+ MOZ_ASSERT(NS_STYLE_VISIBILITY_COLLAPSE ==
+ mFrame->StyleVisibility()->mVisible,
+ "Should only make struts for children with 'visibility:collapse'");
+ MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame,
+ "placeholder frames should not be treated as flex items");
+ MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
+ "out-of-flow frames should not be treated as flex items");
+}
+
+void
+FlexItem::CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ const nsStylePosition* pos = aFlexItemReflowInput.mStylePosition;
+ const nsStyleDisplay* disp = aFlexItemReflowInput.mStyleDisplay;
+
+ // We'll need special behavior for "min-[width|height]:auto" (whichever is in
+ // the main axis) iff:
+ // (a) its computed value is "auto"
+ // (b) the "overflow" sub-property in the same axis (the main axis) has a
+ // computed value of "visible"
+ const nsStyleCoord& minSize = GET_MAIN_COMPONENT(aAxisTracker,
+ pos->mMinWidth,
+ pos->mMinHeight);
+
+ const uint8_t overflowVal = GET_MAIN_COMPONENT(aAxisTracker,
+ disp->mOverflowX,
+ disp->mOverflowY);
+
+ mNeedsMinSizeAutoResolution = (minSize.GetUnit() == eStyleUnit_Auto &&
+ overflowVal == NS_STYLE_OVERFLOW_VISIBLE);
+}
+
+nscoord
+FlexItem::GetBaselineOffsetFromOuterCrossEdge(
+ AxisEdgeType aEdge,
+ const FlexboxAxisTracker& aAxisTracker,
+ bool aUseFirstLineBaseline) const
+{
+ // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
+ // measurement -- it's the distance from the border-top edge of this FlexItem
+ // to its baseline. So, we can really only do baseline alignment when the
+ // cross axis is vertical. (The FlexItem constructor enforces this when
+ // resolving the item's "mAlignSelf" value).
+ MOZ_ASSERT(!aAxisTracker.IsCrossAxisHorizontal(),
+ "Only expecting to be doing baseline computations when the "
+ "cross axis is vertical");
+
+ AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
+ mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[crossAxis][aEdge];
+
+ nscoord marginTopToBaseline = ResolvedAscent(aUseFirstLineBaseline) +
+ mMargin.top;
+
+ if (sideToMeasureFrom == eSideTop) {
+ // Measuring from top (normal case): the distance from the margin-box top
+ // edge to the baseline is just ascent + margin-top.
+ return marginTopToBaseline;
+ }
+
+ MOZ_ASSERT(sideToMeasureFrom == eSideBottom,
+ "We already checked that we're dealing with a vertical axis, and "
+ "we're not using the top side, so that only leaves the bottom...");
+
+ // Measuring from bottom: The distance from the margin-box bottom edge to the
+ // baseline is just the margin-box cross size (i.e. outer cross size), minus
+ // the already-computed distance from margin-top to baseline.
+ return GetOuterCrossSize(crossAxis) - marginTopToBaseline;
+}
+
+uint32_t
+FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const
+{
+ uint32_t numAutoMargins = 0;
+ const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
+ for (uint32_t i = 0; i < eNumAxisEdges; i++) {
+ mozilla::Side side = kAxisOrientationToSidesMap[aAxis][i];
+ if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
+ numAutoMargins++;
+ }
+ }
+
+ // Mostly for clarity:
+ MOZ_ASSERT(numAutoMargins <= 2,
+ "We're just looking at one item along one dimension, so we "
+ "should only have examined 2 margins");
+
+ return numAutoMargins;
+}
+
+bool
+FlexItem::CanMainSizeInfluenceCrossSize(
+ const FlexboxAxisTracker& aAxisTracker) const
+{
+ if (mIsStretched) {
+ // We've already had our cross-size stretched for "align-self:stretch").
+ // The container is imposing its cross size on us.
+ return false;
+ }
+
+ if (mIsStrut) {
+ // Struts (for visibility:collapse items) have a predetermined size;
+ // no need to measure anything.
+ return false;
+ }
+
+ if (HasIntrinsicRatio()) {
+ // For flex items that have an intrinsic ratio (and maintain it, i.e. are
+ // not stretched, which we already checked above): changes to main-size
+ // *do* influence the cross size.
+ return true;
+ }
+
+ if (aAxisTracker.IsCrossAxisHorizontal()) {
+ // If the cross axis is horizontal, then changes to the item's main size
+ // (height) can't influence its cross size (width), if the item is a block
+ // with a horizontal writing-mode.
+ // XXXdholbert This doesn't account for vertical writing-modes, items with
+ // aspect ratios, items that are multicol elements, & items that are
+ // multi-line vertical flex containers. In all of those cases, a change to
+ // the height could influence the width.
+ return false;
+ }
+
+ // Default assumption, if we haven't proven otherwise: the resolved main size
+ // *can* change the cross size.
+ return true;
+}
+
+// Keeps track of our position along a particular axis (where a '0' position
+// corresponds to the 'start' edge of that axis).
+// This class shouldn't be instantiated directly -- rather, it should only be
+// instantiated via its subclasses defined below.
+class MOZ_STACK_CLASS PositionTracker {
+public:
+ // Accessor for the current value of the position that we're tracking.
+ inline nscoord GetPosition() const { return mPosition; }
+ inline AxisOrientationType GetAxis() const { return mAxis; }
+
+ // Advances our position across the start edge of the given margin, in the
+ // axis we're tracking.
+ void EnterMargin(const nsMargin& aMargin)
+ {
+ mozilla::Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_Start];
+ mPosition += aMargin.Side(side);
+ }
+
+ // Advances our position across the end edge of the given margin, in the axis
+ // we're tracking.
+ void ExitMargin(const nsMargin& aMargin)
+ {
+ mozilla::Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_End];
+ mPosition += aMargin.Side(side);
+ }
+
+ // Advances our current position from the start side of a child frame's
+ // border-box to the frame's upper or left edge (depending on our axis).
+ // (Note that this is a no-op if our axis grows in the same direction as
+ // the corresponding logical axis.)
+ void EnterChildFrame(nscoord aChildFrameSize)
+ {
+ if (mIsAxisReversed) {
+ mPosition += aChildFrameSize;
+ }
+ }
+
+ // Advances our current position from a frame's upper or left border-box edge
+ // (whichever is in the axis we're tracking) to the 'end' side of the frame
+ // in the axis that we're tracking. (Note that this is a no-op if our axis
+ // is reversed with respect to the corresponding logical axis.)
+ void ExitChildFrame(nscoord aChildFrameSize)
+ {
+ if (!mIsAxisReversed) {
+ mPosition += aChildFrameSize;
+ }
+ }
+
+protected:
+ // Protected constructor, to be sure we're only instantiated via a subclass.
+ PositionTracker(AxisOrientationType aAxis, bool aIsAxisReversed)
+ : mPosition(0),
+ mAxis(aAxis),
+ mIsAxisReversed(aIsAxisReversed)
+ {}
+
+ // Delete copy-constructor & reassignment operator, to prevent accidental
+ // (unnecessary) copying.
+ PositionTracker(const PositionTracker&) = delete;
+ PositionTracker& operator=(const PositionTracker&) = delete;
+
+ // Member data:
+ nscoord mPosition; // The position we're tracking
+ // XXXdholbert [BEGIN DEPRECATED]
+ const AxisOrientationType mAxis; // The axis along which we're moving.
+ // XXXdholbert [END DEPRECATED]
+ const bool mIsAxisReversed; // Is the axis along which we're moving reversed
+ // (e.g. LTR vs RTL) with respect to the
+ // corresponding axis on the flex container's WM?
+};
+
+// Tracks our position in the main axis, when we're laying out flex items.
+// The "0" position represents the main-start edge of the flex container's
+// content-box.
+class MOZ_STACK_CLASS MainAxisPositionTracker : public PositionTracker {
+public:
+ MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
+ const FlexLine* aLine,
+ uint8_t aJustifyContent,
+ nscoord aContentBoxMainSize);
+
+ ~MainAxisPositionTracker() {
+ MOZ_ASSERT(mNumPackingSpacesRemaining == 0,
+ "miscounted the number of packing spaces");
+ MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0,
+ "miscounted the number of auto margins");
+ }
+
+ // Advances past the packing space (if any) between two flex items
+ void TraversePackingSpace();
+
+ // If aItem has any 'auto' margins in the main axis, this method updates the
+ // corresponding values in its margin.
+ void ResolveAutoMarginsInMainAxis(FlexItem& aItem);
+
+private:
+ nscoord mPackingSpaceRemaining;
+ uint32_t mNumAutoMarginsInMainAxis;
+ uint32_t mNumPackingSpacesRemaining;
+ // XXX this should be uint16_t when we add explicit fallback handling
+ uint8_t mJustifyContent;
+};
+
+// Utility class for managing our position along the cross axis along
+// the whole flex container (at a higher level than a single line).
+// The "0" position represents the cross-start edge of the flex container's
+// content-box.
+class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker {
+public:
+ CrossAxisPositionTracker(FlexLine* aFirstLine,
+ const ReflowInput& aReflowInput,
+ nscoord aContentBoxCrossSize,
+ bool aIsCrossSizeDefinite,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ // Advances past the packing space (if any) between two flex lines
+ void TraversePackingSpace();
+
+ // Advances past the given FlexLine
+ void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); }
+
+private:
+ // Redeclare the frame-related methods from PositionTracker as private with
+ // = delete, to be sure (at compile time) that no client code can invoke
+ // them. (Unlike the other PositionTracker derived classes, this class here
+ // deals with FlexLines, not with individual FlexItems or frames.)
+ void EnterMargin(const nsMargin& aMargin) = delete;
+ void ExitMargin(const nsMargin& aMargin) = delete;
+ void EnterChildFrame(nscoord aChildFrameSize) = delete;
+ void ExitChildFrame(nscoord aChildFrameSize) = delete;
+
+ nscoord mPackingSpaceRemaining;
+ uint32_t mNumPackingSpacesRemaining;
+ // XXX this should be uint16_t when we add explicit fallback handling
+ uint8_t mAlignContent;
+};
+
+// Utility class for managing our position along the cross axis, *within* a
+// single flex line.
+class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker {
+public:
+ explicit SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker);
+
+ void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
+ FlexItem& aItem);
+
+ void EnterAlignPackingSpace(const FlexLine& aLine,
+ const FlexItem& aItem,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ // Resets our position to the cross-start edge of this line.
+ inline void ResetPosition() { mPosition = 0; }
+};
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsFlexContainerFrame)
+ NS_QUERYFRAME_ENTRY(nsFlexContainerFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
+
+nsContainerFrame*
+NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsFlexContainerFrame(aContext);
+}
+
+//----------------------------------------------------------------------
+
+// nsFlexContainerFrame Method Implementations
+// ===========================================
+
+/* virtual */
+nsFlexContainerFrame::~nsFlexContainerFrame()
+{
+}
+
+/* virtual */
+void
+nsFlexContainerFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ const nsStyleDisplay* styleDisp = StyleContext()->StyleDisplay();
+
+ // Figure out if we should set a frame state bit to indicate that this frame
+ // represents a legacy -webkit-{inline-}box container.
+ // First, the trivial case: just check "display" directly.
+ bool isLegacyBox = IsDisplayValueLegacyBox(styleDisp);
+
+ // If this frame is for a scrollable element, then it will actually have
+ // "display:block", and its *parent* will have the real flex-flavored display
+ // value. So in that case, check the parent to find out if we're legacy.
+ if (!isLegacyBox && styleDisp->mDisplay == mozilla::StyleDisplay::Block) {
+ nsStyleContext* parentStyleContext = mStyleContext->GetParent();
+ NS_ASSERTION(parentStyleContext &&
+ (mStyleContext->GetPseudo() == nsCSSAnonBoxes::buttonContent ||
+ mStyleContext->GetPseudo() == nsCSSAnonBoxes::scrolledContent),
+ "The only way a nsFlexContainerFrame can have 'display:block' "
+ "should be if it's the inner part of a scrollable or button "
+ "element");
+ isLegacyBox = IsDisplayValueLegacyBox(parentStyleContext->StyleDisplay());
+ }
+
+ if (isLegacyBox) {
+ AddStateBits(NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX);
+ }
+}
+
+template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+/* static */ bool
+nsFlexContainerFrame::SortChildrenIfNeeded()
+{
+ if (nsIFrame::IsFrameListSorted<IsLessThanOrEqual>(mFrames)) {
+ return false;
+ }
+
+ nsIFrame::SortFrameList<IsLessThanOrEqual>(mFrames);
+ return true;
+}
+
+/* virtual */
+nsIAtom*
+nsFlexContainerFrame::GetType() const
+{
+ return nsGkAtoms::flexContainerFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsFlexContainerFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("FlexContainer"), aResult);
+}
+#endif
+
+nscoord
+nsFlexContainerFrame::GetLogicalBaseline(mozilla::WritingMode aWM) const
+{
+ NS_ASSERTION(mBaselineFromLastReflow != NS_INTRINSIC_WIDTH_UNKNOWN,
+ "baseline has not been set");
+
+ if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+ // Return a baseline synthesized from our margin-box.
+ return nsContainerFrame::GetLogicalBaseline(aWM);
+ }
+ return mBaselineFromLastReflow;
+}
+
+// Helper for BuildDisplayList, to implement this special-case for flex items
+// from the spec:
+// Flex items paint exactly the same as block-level elements in the
+// normal flow, except that 'z-index' values other than 'auto' create
+// a stacking context even if 'position' is 'static'.
+// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#painting
+uint32_t
+GetDisplayFlagsForFlexItem(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(aFrame->IsFlexItem(), "Should only be called on flex items");
+
+ const nsStylePosition* pos = aFrame->StylePosition();
+ if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) {
+ return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT;
+ }
+ return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
+}
+
+void
+nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ // XXXdholbert hacky temporary band-aid for bug 1059138: Trivially pass this
+ // assertion (skip it, basically) if the first child is part of a shadow DOM.
+ // (IsOrderLEQWithDOMFallback doesn't know how to compare tree-position of a
+ // shadow-DOM element vs. a non-shadow-DOM element.)
+ NS_ASSERTION(
+ (!mFrames.IsEmpty() &&
+ mFrames.FirstChild()->GetContent()->GetContainingShadow()) ||
+ nsIFrame::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
+ "Child frames aren't sorted correctly");
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ // Our children are all block-level, so their borders/backgrounds all go on
+ // the BlockBorderBackgrounds list.
+ nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
+ for (nsIFrame* childFrame : mFrames) {
+ BuildDisplayListForChild(aBuilder, childFrame, aDirtyRect, childLists,
+ GetDisplayFlagsForFlexItem(childFrame));
+ }
+}
+
+void
+FlexLine::FreezeItemsEarly(bool aIsUsingFlexGrow)
+{
+ // After we've established the type of flexing we're doing (growing vs.
+ // shrinking), and before we try to flex any items, we freeze items that
+ // obviously *can't* flex.
+ //
+ // Quoting the spec:
+ // # Freeze, setting its target main size to its hypothetical main size...
+ // # - any item that has a flex factor of zero
+ // # - if using the flex grow factor: any item that has a flex base size
+ // # greater than its hypothetical main size
+ // # - if using the flex shrink factor: any item that has a flex base size
+ // # smaller than its hypothetical main size
+ // http://dev.w3.org/csswg/css-flexbox/#resolve-flexible-lengths-flex-factors
+ //
+ // (NOTE: At this point, item->GetMainSize() *is* the item's hypothetical
+ // main size, since SetFlexBaseSizeAndMainSize() sets it up that way, and the
+ // item hasn't had a chance to flex away from that yet.)
+
+ // Since this loop only operates on unfrozen flex items, we can break as
+ // soon as we have seen all of them.
+ uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
+ for (FlexItem* item = mItems.getFirst();
+ numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
+ MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
+
+ if (!item->IsFrozen()) {
+ numUnfrozenItemsToBeSeen--;
+ bool shouldFreeze = (0.0f == item->GetFlexFactor(aIsUsingFlexGrow));
+ if (!shouldFreeze) {
+ if (aIsUsingFlexGrow) {
+ if (item->GetFlexBaseSize() > item->GetMainSize()) {
+ shouldFreeze = true;
+ }
+ } else { // using flex-shrink
+ if (item->GetFlexBaseSize() < item->GetMainSize()) {
+ shouldFreeze = true;
+ }
+ }
+ }
+ if (shouldFreeze) {
+ // Freeze item! (at its hypothetical main size)
+ item->Freeze();
+ mNumFrozenItems++;
+ }
+ }
+ }
+}
+
+// Based on the sign of aTotalViolation, this function freezes a subset of our
+// flexible sizes, and restores the remaining ones to their initial pref sizes.
+void
+FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
+ bool aIsFinalIteration)
+{
+ enum FreezeType {
+ eFreezeEverything,
+ eFreezeMinViolations,
+ eFreezeMaxViolations
+ };
+
+ FreezeType freezeType;
+ if (aTotalViolation == 0) {
+ freezeType = eFreezeEverything;
+ } else if (aTotalViolation > 0) {
+ freezeType = eFreezeMinViolations;
+ } else { // aTotalViolation < 0
+ freezeType = eFreezeMaxViolations;
+ }
+
+ // Since this loop only operates on unfrozen flex items, we can break as
+ // soon as we have seen all of them.
+ uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
+ for (FlexItem* item = mItems.getFirst();
+ numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
+ MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
+ if (!item->IsFrozen()) {
+ numUnfrozenItemsToBeSeen--;
+
+ MOZ_ASSERT(!item->HadMinViolation() || !item->HadMaxViolation(),
+ "Can have either min or max violation, but not both");
+
+ if (eFreezeEverything == freezeType ||
+ (eFreezeMinViolations == freezeType && item->HadMinViolation()) ||
+ (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) {
+
+ MOZ_ASSERT(item->GetMainSize() >= item->GetMainMinSize(),
+ "Freezing item at a size below its minimum");
+ MOZ_ASSERT(item->GetMainSize() <= item->GetMainMaxSize(),
+ "Freezing item at a size above its maximum");
+
+ item->Freeze();
+ mNumFrozenItems++;
+ } else if (MOZ_UNLIKELY(aIsFinalIteration)) {
+ // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
+ // assertion to be fatal except in documents with enormous lengths.
+ NS_ERROR("Final iteration still has unfrozen items, this shouldn't"
+ " happen unless there was nscoord under/overflow.");
+ item->Freeze();
+ mNumFrozenItems++;
+ } // else, we'll reset this item's main size to its flex base size on the
+ // next iteration of this algorithm.
+
+ // Clear this item's violation(s), now that we've dealt with them
+ item->ClearViolationFlags();
+ }
+ }
+}
+
+void
+FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
+{
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug, ("ResolveFlexibleLengths\n"));
+
+ // Determine whether we're going to be growing or shrinking items.
+ const bool isUsingFlexGrow =
+ (mTotalOuterHypotheticalMainSize < aFlexContainerMainSize);
+
+ // Do an "early freeze" for flex items that obviously can't flex in the
+ // direction we've chosen:
+ FreezeItemsEarly(isUsingFlexGrow);
+
+ if (mNumFrozenItems == mNumItems) {
+ // All our items are frozen, so we have no flexible lengths to resolve.
+ return;
+ }
+ MOZ_ASSERT(!IsEmpty(), "empty lines should take the early-return above");
+
+ // Subtract space occupied by our items' margins/borders/padding, so we can
+ // just be dealing with the space available for our flex items' content
+ // boxes.
+ nscoord spaceReservedForMarginBorderPadding =
+ mTotalOuterHypotheticalMainSize - mTotalInnerHypotheticalMainSize;
+
+ nscoord spaceAvailableForFlexItemsContentBoxes =
+ aFlexContainerMainSize - spaceReservedForMarginBorderPadding;
+
+ nscoord origAvailableFreeSpace;
+ bool isOrigAvailFreeSpaceInitialized = false;
+
+ // NOTE: I claim that this chunk of the algorithm (the looping part) needs to
+ // run the loop at MOST mNumItems times. This claim should hold up
+ // because we'll freeze at least one item on each loop iteration, and once
+ // we've run out of items to freeze, there's nothing left to do. However,
+ // in most cases, we'll break out of this loop long before we hit that many
+ // iterations.
+ for (uint32_t iterationCounter = 0;
+ iterationCounter < mNumItems; iterationCounter++) {
+ // Set every not-yet-frozen item's used main size to its
+ // flex base size, and subtract all the used main sizes from our
+ // total amount of space to determine the 'available free space'
+ // (positive or negative) to be distributed among our flexible items.
+ nscoord availableFreeSpace = spaceAvailableForFlexItemsContentBoxes;
+ for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
+ if (!item->IsFrozen()) {
+ item->SetMainSize(item->GetFlexBaseSize());
+ }
+ availableFreeSpace -= item->GetMainSize();
+ }
+
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
+ (" available free space = %d\n", availableFreeSpace));
+
+
+ // The sign of our free space should agree with the type of flexing
+ // (grow/shrink) that we're doing (except if we've had integer overflow;
+ // then, all bets are off). Any disagreement should've made us use the
+ // other type of flexing, or should've been resolved in FreezeItemsEarly.
+ // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
+ // assertion to be fatal except in documents with enormous lengths.
+ NS_ASSERTION((isUsingFlexGrow && availableFreeSpace >= 0) ||
+ (!isUsingFlexGrow && availableFreeSpace <= 0),
+ "availableFreeSpace's sign should match isUsingFlexGrow");
+
+ // If we have any free space available, give each flexible item a portion
+ // of availableFreeSpace.
+ if (availableFreeSpace != 0) {
+ // The first time we do this, we initialize origAvailableFreeSpace.
+ if (!isOrigAvailFreeSpaceInitialized) {
+ origAvailableFreeSpace = availableFreeSpace;
+ isOrigAvailFreeSpaceInitialized = true;
+ }
+
+ // STRATEGY: On each item, we compute & store its "share" of the total
+ // weight that we've seen so far:
+ // curWeight / weightSum
+ //
+ // Then, when we go to actually distribute the space (in the next loop),
+ // we can simply walk backwards through the elements and give each item
+ // its "share" multiplied by the remaining available space.
+ //
+ // SPECIAL CASE: If the sum of the weights is larger than the
+ // maximum representable float (overflowing to infinity), then we can't
+ // sensibly divide out proportional shares anymore. In that case, we
+ // simply treat the flex item(s) with the largest weights as if
+ // their weights were infinite (dwarfing all the others), and we
+ // distribute all of the available space among them.
+ float weightSum = 0.0f;
+ float flexFactorSum = 0.0f;
+ float largestWeight = 0.0f;
+ uint32_t numItemsWithLargestWeight = 0;
+
+ // Since this loop only operates on unfrozen flex items, we can break as
+ // soon as we have seen all of them.
+ uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
+ for (FlexItem* item = mItems.getFirst();
+ numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
+ MOZ_ASSERT(item,
+ "numUnfrozenItemsToBeSeen says items remain to be seen");
+ if (!item->IsFrozen()) {
+ numUnfrozenItemsToBeSeen--;
+
+ float curWeight = item->GetWeight(isUsingFlexGrow);
+ float curFlexFactor = item->GetFlexFactor(isUsingFlexGrow);
+ MOZ_ASSERT(curWeight >= 0.0f, "weights are non-negative");
+ MOZ_ASSERT(curFlexFactor >= 0.0f, "flex factors are non-negative");
+
+ weightSum += curWeight;
+ flexFactorSum += curFlexFactor;
+
+ if (IsFinite(weightSum)) {
+ if (curWeight == 0.0f) {
+ item->SetShareOfWeightSoFar(0.0f);
+ } else {
+ item->SetShareOfWeightSoFar(curWeight / weightSum);
+ }
+ } // else, the sum of weights overflows to infinity, in which
+ // case we don't bother with "SetShareOfWeightSoFar" since
+ // we know we won't use it. (instead, we'll just give every
+ // item with the largest weight an equal share of space.)
+
+ // Update our largest-weight tracking vars
+ if (curWeight > largestWeight) {
+ largestWeight = curWeight;
+ numItemsWithLargestWeight = 1;
+ } else if (curWeight == largestWeight) {
+ numItemsWithLargestWeight++;
+ }
+ }
+ }
+
+ if (weightSum != 0.0f) {
+ MOZ_ASSERT(flexFactorSum != 0.0f,
+ "flex factor sum can't be 0, if a weighted sum "
+ "of its components (weightSum) is nonzero");
+ if (flexFactorSum < 1.0f) {
+ // Our unfrozen flex items don't want all of the original free space!
+ // (Their flex factors add up to something less than 1.)
+ // Hence, make sure we don't distribute any more than the portion of
+ // our original free space that these items actually want.
+ nscoord totalDesiredPortionOfOrigFreeSpace =
+ NSToCoordRound(origAvailableFreeSpace * flexFactorSum);
+
+ // Clamp availableFreeSpace to be no larger than that ^^.
+ // (using min or max, depending on sign).
+ // This should not change the sign of availableFreeSpace (except
+ // possibly by setting it to 0), as enforced by this assertion:
+ MOZ_ASSERT(totalDesiredPortionOfOrigFreeSpace == 0 ||
+ ((totalDesiredPortionOfOrigFreeSpace > 0) ==
+ (availableFreeSpace > 0)),
+ "When we reduce available free space for flex factors < 1,"
+ "we shouldn't change the sign of the free space...");
+
+ if (availableFreeSpace > 0) {
+ availableFreeSpace = std::min(availableFreeSpace,
+ totalDesiredPortionOfOrigFreeSpace);
+ } else {
+ availableFreeSpace = std::max(availableFreeSpace,
+ totalDesiredPortionOfOrigFreeSpace);
+ }
+ }
+
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
+ (" Distributing available space:"));
+ // Since this loop only operates on unfrozen flex items, we can break as
+ // soon as we have seen all of them.
+ numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
+
+ // NOTE: It's important that we traverse our items in *reverse* order
+ // here, for correct width distribution according to the items'
+ // "ShareOfWeightSoFar" progressively-calculated values.
+ for (FlexItem* item = mItems.getLast();
+ numUnfrozenItemsToBeSeen > 0; item = item->getPrevious()) {
+ MOZ_ASSERT(item,
+ "numUnfrozenItemsToBeSeen says items remain to be seen");
+ if (!item->IsFrozen()) {
+ numUnfrozenItemsToBeSeen--;
+
+ // To avoid rounding issues, we compute the change in size for this
+ // item, and then subtract it from the remaining available space.
+ nscoord sizeDelta = 0;
+ if (IsFinite(weightSum)) {
+ float myShareOfRemainingSpace =
+ item->GetShareOfWeightSoFar();
+
+ MOZ_ASSERT(myShareOfRemainingSpace >= 0.0f &&
+ myShareOfRemainingSpace <= 1.0f,
+ "my share should be nonnegative fractional amount");
+
+ if (myShareOfRemainingSpace == 1.0f) {
+ // (We special-case 1.0f to avoid float error from converting
+ // availableFreeSpace from integer*1.0f --> float --> integer)
+ sizeDelta = availableFreeSpace;
+ } else if (myShareOfRemainingSpace > 0.0f) {
+ sizeDelta = NSToCoordRound(availableFreeSpace *
+ myShareOfRemainingSpace);
+ }
+ } else if (item->GetWeight(isUsingFlexGrow) == largestWeight) {
+ // Total flexibility is infinite, so we're just distributing
+ // the available space equally among the items that are tied for
+ // having the largest weight (and this is one of those items).
+ sizeDelta =
+ NSToCoordRound(availableFreeSpace /
+ float(numItemsWithLargestWeight));
+ numItemsWithLargestWeight--;
+ }
+
+ availableFreeSpace -= sizeDelta;
+
+ item->SetMainSize(item->GetMainSize() + sizeDelta);
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
+ (" child %p receives %d, for a total of %d\n",
+ item, sizeDelta, item->GetMainSize()));
+ }
+ }
+ }
+ }
+
+ // Fix min/max violations:
+ nscoord totalViolation = 0; // keeps track of adjustments for min/max
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
+ (" Checking for violations:"));
+
+ // Since this loop only operates on unfrozen flex items, we can break as
+ // soon as we have seen all of them.
+ uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
+ for (FlexItem* item = mItems.getFirst();
+ numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
+ MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
+ if (!item->IsFrozen()) {
+ numUnfrozenItemsToBeSeen--;
+
+ if (item->GetMainSize() < item->GetMainMinSize()) {
+ // min violation
+ totalViolation += item->GetMainMinSize() - item->GetMainSize();
+ item->SetMainSize(item->GetMainMinSize());
+ item->SetHadMinViolation();
+ } else if (item->GetMainSize() > item->GetMainMaxSize()) {
+ // max violation
+ totalViolation += item->GetMainMaxSize() - item->GetMainSize();
+ item->SetMainSize(item->GetMainMaxSize());
+ item->SetHadMaxViolation();
+ }
+ }
+ }
+
+ FreezeOrRestoreEachFlexibleSize(totalViolation,
+ iterationCounter + 1 == mNumItems);
+
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
+ (" Total violation: %d\n", totalViolation));
+
+ if (mNumFrozenItems == mNumItems) {
+ break;
+ }
+
+ MOZ_ASSERT(totalViolation != 0,
+ "Zero violation should've made us freeze all items & break");
+ }
+
+#ifdef DEBUG
+ // Post-condition: all items should've been frozen.
+ // Make sure the counts match:
+ MOZ_ASSERT(mNumFrozenItems == mNumItems, "All items should be frozen");
+
+ // For good measure, check each item directly, in case our counts are busted:
+ for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
+ MOZ_ASSERT(item->IsFrozen(), "All items should be frozen");
+ }
+#endif // DEBUG
+}
+
+MainAxisPositionTracker::
+ MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
+ const FlexLine* aLine,
+ uint8_t aJustifyContent,
+ nscoord aContentBoxMainSize)
+ : PositionTracker(aAxisTracker.GetMainAxis(),
+ aAxisTracker.IsMainAxisReversed()),
+ mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below
+ mNumAutoMarginsInMainAxis(0),
+ mNumPackingSpacesRemaining(0),
+ mJustifyContent(aJustifyContent)
+{
+ // 'normal' behaves as 'stretch', and 'stretch' behaves as 'flex-start',
+ // in the main axis
+ // https://drafts.csswg.org/css-align-3/#propdef-justify-content
+ if (mJustifyContent == NS_STYLE_JUSTIFY_NORMAL ||
+ mJustifyContent == NS_STYLE_JUSTIFY_STRETCH) {
+ mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
+ }
+
+ // XXX strip off the <overflow-position> bit until we implement that
+ mJustifyContent &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
+
+ // mPackingSpaceRemaining is initialized to the container's main size. Now
+ // we'll subtract out the main sizes of our flex items, so that it ends up
+ // with the *actual* amount of packing space.
+ for (const FlexItem* item = aLine->GetFirstItem(); item;
+ item = item->getNext()) {
+ mPackingSpaceRemaining -= item->GetOuterMainSize(mAxis);
+ mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis);
+ }
+
+ if (mPackingSpaceRemaining <= 0) {
+ // No available packing space to use for resolving auto margins.
+ mNumAutoMarginsInMainAxis = 0;
+ }
+
+ // If packing space is negative, 'space-between' falls back to 'flex-start',
+ // and 'space-around' & 'space-evenly' fall back to 'center'. In those cases,
+ // it's simplest to just pretend we have a different 'justify-content' value
+ // and share code.
+ if (mPackingSpaceRemaining < 0) {
+ if (mJustifyContent == NS_STYLE_JUSTIFY_SPACE_BETWEEN) {
+ mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
+ } else if (mJustifyContent == NS_STYLE_JUSTIFY_SPACE_AROUND ||
+ mJustifyContent == NS_STYLE_JUSTIFY_SPACE_EVENLY) {
+ mJustifyContent = NS_STYLE_JUSTIFY_CENTER;
+ }
+ }
+
+ // Map 'left'/'right' to 'start'/'end'
+ if (mJustifyContent == NS_STYLE_JUSTIFY_LEFT ||
+ mJustifyContent == NS_STYLE_JUSTIFY_RIGHT) {
+ if (aAxisTracker.IsColumnOriented()) {
+ // Container's alignment axis is not parallel to the inline axis,
+ // so we map both 'left' and 'right' to 'start'.
+ mJustifyContent = NS_STYLE_JUSTIFY_START;
+ } else {
+ // Row-oriented, so we map 'left' and 'right' to 'start' or 'end',
+ // depending on left-to-right writing mode.
+ const bool isLTR = aAxisTracker.GetWritingMode().IsBidiLTR();
+ const bool isJustifyLeft = (mJustifyContent == NS_STYLE_JUSTIFY_LEFT);
+ mJustifyContent = (isJustifyLeft == isLTR) ? NS_STYLE_JUSTIFY_START
+ : NS_STYLE_JUSTIFY_END;
+ }
+ }
+
+ // Map 'start'/'end' to 'flex-start'/'flex-end'.
+ if (mJustifyContent == NS_STYLE_JUSTIFY_START) {
+ mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
+ } else if (mJustifyContent == NS_STYLE_JUSTIFY_END) {
+ mJustifyContent = NS_STYLE_JUSTIFY_FLEX_END;
+ }
+
+ // If our main axis is (internally) reversed, swap the justify-content
+ // "flex-start" and "flex-end" behaviors:
+ if (aAxisTracker.AreAxesInternallyReversed()) {
+ if (mJustifyContent == NS_STYLE_JUSTIFY_FLEX_START) {
+ mJustifyContent = NS_STYLE_JUSTIFY_FLEX_END;
+ } else if (mJustifyContent == NS_STYLE_JUSTIFY_FLEX_END) {
+ mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
+ }
+ }
+
+ // Figure out how much space we'll set aside for auto margins or
+ // packing spaces, and advance past any leading packing-space.
+ if (mNumAutoMarginsInMainAxis == 0 &&
+ mPackingSpaceRemaining != 0 &&
+ !aLine->IsEmpty()) {
+ switch (mJustifyContent) {
+ case NS_STYLE_JUSTIFY_BASELINE:
+ case NS_STYLE_JUSTIFY_LAST_BASELINE:
+ NS_WARNING("NYI: justify-content:left/right/baseline/last baseline");
+ MOZ_FALLTHROUGH;
+ case NS_STYLE_JUSTIFY_FLEX_START:
+ // All packing space should go at the end --> nothing to do here.
+ break;
+ case NS_STYLE_JUSTIFY_FLEX_END:
+ // All packing space goes at the beginning
+ mPosition += mPackingSpaceRemaining;
+ break;
+ case NS_STYLE_JUSTIFY_CENTER:
+ // Half the packing space goes at the beginning
+ mPosition += mPackingSpaceRemaining / 2;
+ break;
+ case NS_STYLE_JUSTIFY_SPACE_BETWEEN:
+ case NS_STYLE_JUSTIFY_SPACE_AROUND:
+ case NS_STYLE_JUSTIFY_SPACE_EVENLY:
+ nsFlexContainerFrame::CalculatePackingSpace(aLine->NumItems(),
+ mJustifyContent,
+ &mPosition,
+ &mNumPackingSpacesRemaining,
+ &mPackingSpaceRemaining);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected justify-content value");
+ }
+ }
+
+ MOZ_ASSERT(mNumPackingSpacesRemaining == 0 ||
+ mNumAutoMarginsInMainAxis == 0,
+ "extra space should either go to packing space or to "
+ "auto margins, but not to both");
+}
+
+void
+MainAxisPositionTracker::ResolveAutoMarginsInMainAxis(FlexItem& aItem)
+{
+ if (mNumAutoMarginsInMainAxis) {
+ const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
+ for (uint32_t i = 0; i < eNumAxisEdges; i++) {
+ mozilla::Side side = kAxisOrientationToSidesMap[mAxis][i];
+ if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
+ // NOTE: This integer math will skew the distribution of remainder
+ // app-units towards the end, which is fine.
+ nscoord curAutoMarginSize =
+ mPackingSpaceRemaining / mNumAutoMarginsInMainAxis;
+
+ MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
+ "Expecting auto margins to have value '0' before we "
+ "resolve them");
+ aItem.SetMarginComponentForSide(side, curAutoMarginSize);
+
+ mNumAutoMarginsInMainAxis--;
+ mPackingSpaceRemaining -= curAutoMarginSize;
+ }
+ }
+ }
+}
+
+void
+MainAxisPositionTracker::TraversePackingSpace()
+{
+ if (mNumPackingSpacesRemaining) {
+ MOZ_ASSERT(mJustifyContent == NS_STYLE_JUSTIFY_SPACE_BETWEEN ||
+ mJustifyContent == NS_STYLE_JUSTIFY_SPACE_AROUND ||
+ mJustifyContent == NS_STYLE_JUSTIFY_SPACE_EVENLY,
+ "mNumPackingSpacesRemaining only applies for "
+ "space-between/space-around/space-evenly");
+
+ MOZ_ASSERT(mPackingSpaceRemaining >= 0,
+ "ran out of packing space earlier than we expected");
+
+ // NOTE: This integer math will skew the distribution of remainder
+ // app-units towards the end, which is fine.
+ nscoord curPackingSpace =
+ mPackingSpaceRemaining / mNumPackingSpacesRemaining;
+
+ mPosition += curPackingSpace;
+ mNumPackingSpacesRemaining--;
+ mPackingSpaceRemaining -= curPackingSpace;
+ }
+}
+
+CrossAxisPositionTracker::
+ CrossAxisPositionTracker(FlexLine* aFirstLine,
+ const ReflowInput& aReflowInput,
+ nscoord aContentBoxCrossSize,
+ bool aIsCrossSizeDefinite,
+ const FlexboxAxisTracker& aAxisTracker)
+ : PositionTracker(aAxisTracker.GetCrossAxis(),
+ aAxisTracker.IsCrossAxisReversed()),
+ mPackingSpaceRemaining(0),
+ mNumPackingSpacesRemaining(0),
+ mAlignContent(aReflowInput.mStylePosition->mAlignContent)
+{
+ MOZ_ASSERT(aFirstLine, "null first line pointer");
+
+ // 'normal' behaves as 'stretch'
+ if (mAlignContent == NS_STYLE_ALIGN_NORMAL) {
+ mAlignContent = NS_STYLE_ALIGN_STRETCH;
+ }
+
+ // XXX strip of the <overflow-position> bit until we implement that
+ mAlignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
+ const bool isSingleLine =
+ NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
+ if (isSingleLine) {
+ MOZ_ASSERT(!aFirstLine->getNext(),
+ "If we're styled as single-line, we should only have 1 line");
+ // "If the flex container is single-line and has a definite cross size, the
+ // cross size of the flex line is the flex container's inner cross size."
+ //
+ // SOURCE: https://drafts.csswg.org/css-flexbox/#algo-cross-line
+ // NOTE: This means (by definition) that there's no packing space, which
+ // means we don't need to be concerned with "align-conent" at all and we
+ // can return early. This is handy, because this is the usual case (for
+ // single-line flexbox).
+ if (aIsCrossSizeDefinite) {
+ aFirstLine->SetLineCrossSize(aContentBoxCrossSize);
+ return;
+ }
+
+ // "If the flex container is single-line, then clamp the line's
+ // cross-size to be within the container's computed min and max cross-size
+ // properties."
+ aFirstLine->SetLineCrossSize(NS_CSS_MINMAX(aFirstLine->GetLineCrossSize(),
+ aReflowInput.ComputedMinBSize(),
+ aReflowInput.ComputedMaxBSize()));
+ }
+
+ // NOTE: The rest of this function should essentially match
+ // MainAxisPositionTracker's constructor, though with FlexLines instead of
+ // FlexItems, and with the additional value "stretch" (and of course with
+ // cross sizes instead of main sizes.)
+
+ // Figure out how much packing space we have (container's cross size minus
+ // all the lines' cross sizes). Also, share this loop to count how many
+ // lines we have. (We need that count in some cases below.)
+ mPackingSpaceRemaining = aContentBoxCrossSize;
+ uint32_t numLines = 0;
+ for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
+ mPackingSpaceRemaining -= line->GetLineCrossSize();
+ numLines++;
+ }
+
+ // If packing space is negative, 'space-between' and 'stretch' behave like
+ // 'flex-start', and 'space-around' and 'space-evenly' behave like 'center'.
+ // In those cases, it's simplest to just pretend we have a different
+ // 'align-content' value and share code.
+ if (mPackingSpaceRemaining < 0) {
+ if (mAlignContent == NS_STYLE_ALIGN_SPACE_BETWEEN ||
+ mAlignContent == NS_STYLE_ALIGN_STRETCH) {
+ mAlignContent = NS_STYLE_ALIGN_FLEX_START;
+ } else if (mAlignContent == NS_STYLE_ALIGN_SPACE_AROUND ||
+ mAlignContent == NS_STYLE_ALIGN_SPACE_EVENLY) {
+ mAlignContent = NS_STYLE_ALIGN_CENTER;
+ }
+ }
+
+ // Map 'left'/'right' to 'start'/'end'
+ if (mAlignContent == NS_STYLE_ALIGN_LEFT ||
+ mAlignContent == NS_STYLE_ALIGN_RIGHT) {
+ if (aAxisTracker.IsRowOriented()) {
+ // Container's alignment axis is not parallel to the inline axis,
+ // so we map both 'left' and 'right' to 'start'.
+ mAlignContent = NS_STYLE_ALIGN_START;
+ } else {
+ // Column-oriented, so we map 'left' and 'right' to 'start' or 'end',
+ // depending on left-to-right writing mode.
+ const bool isLTR = aAxisTracker.GetWritingMode().IsBidiLTR();
+ const bool isAlignLeft = (mAlignContent == NS_STYLE_ALIGN_LEFT);
+ mAlignContent = (isAlignLeft == isLTR) ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END;
+ }
+ }
+
+ // Map 'start'/'end' to 'flex-start'/'flex-end'.
+ if (mAlignContent == NS_STYLE_ALIGN_START) {
+ mAlignContent = NS_STYLE_ALIGN_FLEX_START;
+ } else if (mAlignContent == NS_STYLE_ALIGN_END) {
+ mAlignContent = NS_STYLE_ALIGN_FLEX_END;
+ }
+
+ // If our cross axis is (internally) reversed, swap the align-content
+ // "flex-start" and "flex-end" behaviors:
+ if (aAxisTracker.AreAxesInternallyReversed()) {
+ if (mAlignContent == NS_STYLE_ALIGN_FLEX_START) {
+ mAlignContent = NS_STYLE_ALIGN_FLEX_END;
+ } else if (mAlignContent == NS_STYLE_ALIGN_FLEX_END) {
+ mAlignContent = NS_STYLE_ALIGN_FLEX_START;
+ }
+ }
+
+ // Figure out how much space we'll set aside for packing spaces, and advance
+ // past any leading packing-space.
+ if (mPackingSpaceRemaining != 0) {
+ switch (mAlignContent) {
+ case NS_STYLE_ALIGN_SELF_START:
+ case NS_STYLE_ALIGN_SELF_END:
+ case NS_STYLE_ALIGN_BASELINE:
+ case NS_STYLE_ALIGN_LAST_BASELINE:
+ NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end/baseline/last baseline");
+ MOZ_FALLTHROUGH;
+ case NS_STYLE_ALIGN_FLEX_START:
+ // All packing space should go at the end --> nothing to do here.
+ break;
+ case NS_STYLE_ALIGN_FLEX_END:
+ // All packing space goes at the beginning
+ mPosition += mPackingSpaceRemaining;
+ break;
+ case NS_STYLE_ALIGN_CENTER:
+ // Half the packing space goes at the beginning
+ mPosition += mPackingSpaceRemaining / 2;
+ break;
+ case NS_STYLE_ALIGN_SPACE_BETWEEN:
+ case NS_STYLE_ALIGN_SPACE_AROUND:
+ case NS_STYLE_ALIGN_SPACE_EVENLY:
+ nsFlexContainerFrame::CalculatePackingSpace(numLines,
+ mAlignContent,
+ &mPosition,
+ &mNumPackingSpacesRemaining,
+ &mPackingSpaceRemaining);
+ break;
+ case NS_STYLE_ALIGN_STRETCH: {
+ // Split space equally between the lines:
+ MOZ_ASSERT(mPackingSpaceRemaining > 0,
+ "negative packing space should make us use 'flex-start' "
+ "instead of 'stretch' (and we shouldn't bother with this "
+ "code if we have 0 packing space)");
+
+ uint32_t numLinesLeft = numLines;
+ for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
+ // Our share is the amount of space remaining, divided by the number
+ // of lines remainig.
+ MOZ_ASSERT(numLinesLeft > 0, "miscalculated num lines");
+ nscoord shareOfExtraSpace = mPackingSpaceRemaining / numLinesLeft;
+ nscoord newSize = line->GetLineCrossSize() + shareOfExtraSpace;
+ line->SetLineCrossSize(newSize);
+
+ mPackingSpaceRemaining -= shareOfExtraSpace;
+ numLinesLeft--;
+ }
+ MOZ_ASSERT(numLinesLeft == 0, "miscalculated num lines");
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected align-content value");
+ }
+ }
+}
+
+void
+CrossAxisPositionTracker::TraversePackingSpace()
+{
+ if (mNumPackingSpacesRemaining) {
+ MOZ_ASSERT(mAlignContent == NS_STYLE_ALIGN_SPACE_BETWEEN ||
+ mAlignContent == NS_STYLE_ALIGN_SPACE_AROUND ||
+ mAlignContent == NS_STYLE_ALIGN_SPACE_EVENLY,
+ "mNumPackingSpacesRemaining only applies for "
+ "space-between/space-around/space-evenly");
+
+ MOZ_ASSERT(mPackingSpaceRemaining >= 0,
+ "ran out of packing space earlier than we expected");
+
+ // NOTE: This integer math will skew the distribution of remainder
+ // app-units towards the end, which is fine.
+ nscoord curPackingSpace =
+ mPackingSpaceRemaining / mNumPackingSpacesRemaining;
+
+ mPosition += curPackingSpace;
+ mNumPackingSpacesRemaining--;
+ mPackingSpaceRemaining -= curPackingSpace;
+ }
+}
+
+SingleLineCrossAxisPositionTracker::
+ SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker)
+ : PositionTracker(aAxisTracker.GetCrossAxis(),
+ aAxisTracker.IsCrossAxisReversed())
+{
+}
+
+void
+FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
+{
+ nscoord crossStartToFurthestFirstBaseline = nscoord_MIN;
+ nscoord crossEndToFurthestFirstBaseline = nscoord_MIN;
+ nscoord crossStartToFurthestLastBaseline = nscoord_MIN;
+ nscoord crossEndToFurthestLastBaseline = nscoord_MIN;
+ nscoord largestOuterCrossSize = 0;
+ for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
+ nscoord curOuterCrossSize =
+ item->GetOuterCrossSize(aAxisTracker.GetCrossAxis());
+
+ if ((item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE ||
+ item->GetAlignSelf() == NS_STYLE_ALIGN_LAST_BASELINE) &&
+ item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) {
+ const bool useFirst = (item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE);
+ // FIXME: Once we support "writing-mode", we'll have to do baseline
+ // alignment in vertical flex containers here (w/ horizontal cross-axes).
+
+ // Find distance from our item's cross-start and cross-end margin-box
+ // edges to its baseline.
+ //
+ // Here's a diagram of a flex-item that we might be doing this on.
+ // "mmm" is the margin-box, "bbb" is the border-box. The bottom of
+ // the text "BASE" is the baseline.
+ //
+ // ---(cross-start)---
+ // ___ ___ ___
+ // mmmmmmmmmmmm | |margin-start |
+ // m m | _|_ ___ |
+ // m bbbbbbbb m |curOuterCrossSize | |crossStartToBaseline
+ // m b b m | |ascent |
+ // m b BASE b m | _|_ _|_
+ // m b b m | |
+ // m bbbbbbbb m | |crossEndToBaseline
+ // m m | |
+ // mmmmmmmmmmmm _|_ _|_
+ //
+ // ---(cross-end)---
+ //
+ // We already have the curOuterCrossSize, margin-start, and the ascent.
+ // * We can get crossStartToBaseline by adding margin-start + ascent.
+ // * If we subtract that from the curOuterCrossSize, we get
+ // crossEndToBaseline.
+
+ nscoord crossStartToBaseline =
+ item->GetBaselineOffsetFromOuterCrossEdge(eAxisEdge_Start,
+ aAxisTracker,
+ useFirst);
+ nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
+
+ // Now, update our "largest" values for these (across all the flex items
+ // in this flex line), so we can use them in computing the line's cross
+ // size below:
+ if (useFirst) {
+ crossStartToFurthestFirstBaseline =
+ std::max(crossStartToFurthestFirstBaseline, crossStartToBaseline);
+ crossEndToFurthestFirstBaseline =
+ std::max(crossEndToFurthestFirstBaseline, crossEndToBaseline);
+ } else {
+ crossStartToFurthestLastBaseline =
+ std::max(crossStartToFurthestLastBaseline, crossStartToBaseline);
+ crossEndToFurthestLastBaseline =
+ std::max(crossEndToFurthestLastBaseline, crossEndToBaseline);
+ }
+ } else {
+ largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize);
+ }
+ }
+
+ // The line's baseline offset is the distance from the line's edge (start or
+ // end, depending on whether we've flipped the axes) to the furthest
+ // item-baseline. The item(s) with that baseline will be exactly aligned with
+ // the line's edge.
+ mFirstBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
+ crossEndToFurthestFirstBaseline : crossStartToFurthestFirstBaseline;
+
+ mLastBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
+ crossStartToFurthestLastBaseline : crossEndToFurthestLastBaseline;
+
+ // The line's cross-size is the larger of:
+ // (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
+ // all baseline-aligned items with no cross-axis auto margins...
+ // and
+ // (b) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
+ // all last baseline-aligned items with no cross-axis auto margins...
+ // and
+ // (c) largest cross-size of all other children.
+ mLineCrossSize = std::max(
+ std::max(crossStartToFurthestFirstBaseline + crossEndToFurthestFirstBaseline,
+ crossStartToFurthestLastBaseline + crossEndToFurthestLastBaseline),
+ largestOuterCrossSize);
+}
+
+void
+FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
+ // We stretch IFF we are align-self:stretch, have no auto margins in
+ // cross axis, and have cross-axis size property == "auto". If any of those
+ // conditions don't hold up, we won't stretch.
+ if (mAlignSelf != NS_STYLE_ALIGN_STRETCH ||
+ GetNumAutoMarginsInAxis(crossAxis) != 0 ||
+ eStyleUnit_Auto != aAxisTracker.ComputedCrossSize(mFrame).GetUnit()) {
+ return;
+ }
+
+ // If we've already been stretched, we can bail out early, too.
+ // No need to redo the calculation.
+ if (mIsStretched) {
+ return;
+ }
+
+ // Reserve space for margins & border & padding, and then use whatever
+ // remains as our item's cross-size (clamped to its min/max range).
+ nscoord stretchedSize = aLineCrossSize -
+ GetMarginBorderPaddingSizeInAxis(crossAxis);
+
+ stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize);
+
+ // Update the cross-size & make a note that it's stretched, so we know to
+ // override the reflow state's computed cross-size in our final reflow.
+ SetCrossSize(stretchedSize);
+ mIsStretched = true;
+}
+
+void
+SingleLineCrossAxisPositionTracker::
+ ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
+ FlexItem& aItem)
+{
+ // Subtract the space that our item is already occupying, to see how much
+ // space (if any) is available for its auto margins.
+ nscoord spaceForAutoMargins = aLine.GetLineCrossSize() -
+ aItem.GetOuterCrossSize(mAxis);
+
+ if (spaceForAutoMargins <= 0) {
+ return; // No available space --> nothing to do
+ }
+
+ uint32_t numAutoMargins = aItem.GetNumAutoMarginsInAxis(mAxis);
+ if (numAutoMargins == 0) {
+ return; // No auto margins --> nothing to do.
+ }
+
+ // OK, we have at least one auto margin and we have some available space.
+ // Give each auto margin a share of the space.
+ const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
+ for (uint32_t i = 0; i < eNumAxisEdges; i++) {
+ mozilla::Side side = kAxisOrientationToSidesMap[mAxis][i];
+ if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
+ MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
+ "Expecting auto margins to have value '0' before we "
+ "update them");
+
+ // NOTE: integer divison is fine here; numAutoMargins is either 1 or 2.
+ // If it's 2 & spaceForAutoMargins is odd, 1st margin gets smaller half.
+ nscoord curAutoMarginSize = spaceForAutoMargins / numAutoMargins;
+ aItem.SetMarginComponentForSide(side, curAutoMarginSize);
+ numAutoMargins--;
+ spaceForAutoMargins -= curAutoMarginSize;
+ }
+ }
+}
+
+void
+SingleLineCrossAxisPositionTracker::
+ EnterAlignPackingSpace(const FlexLine& aLine,
+ const FlexItem& aItem,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ // We don't do align-self alignment on items that have auto margins
+ // in the cross axis.
+ if (aItem.GetNumAutoMarginsInAxis(mAxis)) {
+ return;
+ }
+
+ uint8_t alignSelf = aItem.GetAlignSelf();
+ // NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
+ // auto-sized items (which we've already done).
+ if (alignSelf == NS_STYLE_ALIGN_STRETCH) {
+ alignSelf = NS_STYLE_ALIGN_FLEX_START;
+ }
+
+ // Map 'left'/'right' to 'start'/'end'
+ if (alignSelf == NS_STYLE_ALIGN_LEFT || alignSelf == NS_STYLE_ALIGN_RIGHT) {
+ if (aAxisTracker.IsRowOriented()) {
+ // Container's alignment axis is not parallel to the inline axis,
+ // so we map both 'left' and 'right' to 'start'.
+ alignSelf = NS_STYLE_ALIGN_START;
+ } else {
+ // Column-oriented, so we map 'left' and 'right' to 'start' or 'end',
+ // depending on left-to-right writing mode.
+ const bool isLTR = aAxisTracker.GetWritingMode().IsBidiLTR();
+ const bool isAlignLeft = (alignSelf == NS_STYLE_ALIGN_LEFT);
+ alignSelf = (isAlignLeft == isLTR) ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END;
+ }
+ }
+
+ // Map 'start'/'end' to 'flex-start'/'flex-end'.
+ if (alignSelf == NS_STYLE_ALIGN_START) {
+ alignSelf = NS_STYLE_ALIGN_FLEX_START;
+ } else if (alignSelf == NS_STYLE_ALIGN_END) {
+ alignSelf = NS_STYLE_ALIGN_FLEX_END;
+ }
+
+ // If our cross axis is (internally) reversed, swap the align-self
+ // "flex-start" and "flex-end" behaviors:
+ if (aAxisTracker.AreAxesInternallyReversed()) {
+ if (alignSelf == NS_STYLE_ALIGN_FLEX_START) {
+ alignSelf = NS_STYLE_ALIGN_FLEX_END;
+ } else if (alignSelf == NS_STYLE_ALIGN_FLEX_END) {
+ alignSelf = NS_STYLE_ALIGN_FLEX_START;
+ }
+ }
+
+ switch (alignSelf) {
+ case NS_STYLE_ALIGN_SELF_START:
+ case NS_STYLE_ALIGN_SELF_END:
+ NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end");
+ MOZ_FALLTHROUGH;
+ case NS_STYLE_ALIGN_FLEX_START:
+ // No space to skip over -- we're done.
+ break;
+ case NS_STYLE_ALIGN_FLEX_END:
+ mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
+ break;
+ case NS_STYLE_ALIGN_CENTER:
+ // Note: If cross-size is odd, the "after" space will get the extra unit.
+ mPosition +=
+ (aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
+ break;
+ case NS_STYLE_ALIGN_BASELINE:
+ case NS_STYLE_ALIGN_LAST_BASELINE: {
+ const bool useFirst = (alignSelf == NS_STYLE_ALIGN_BASELINE);
+
+ // Normally, baseline-aligned items are collectively aligned with the
+ // line's cross-start edge; however, if our cross axis is (internally)
+ // reversed, we instead align them with the cross-end edge.
+ // A similar logic holds for last baseline-aligned items, but in reverse.
+ AxisEdgeType baselineAlignEdge =
+ aAxisTracker.AreAxesInternallyReversed() == useFirst ?
+ eAxisEdge_End : eAxisEdge_Start;
+
+ nscoord itemBaselineOffset =
+ aItem.GetBaselineOffsetFromOuterCrossEdge(baselineAlignEdge,
+ aAxisTracker,
+ useFirst);
+
+ nscoord lineBaselineOffset = useFirst ? aLine.GetFirstBaselineOffset()
+ : aLine.GetLastBaselineOffset();
+
+ NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
+ "failed at finding largest baseline offset");
+
+ // How much do we need to adjust our position (from the line edge),
+ // to get the item's baseline to hit the line's baseline offset:
+ nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
+
+ if (aAxisTracker.AreAxesInternallyReversed() == useFirst) {
+ // Advance to align item w/ line's flex-end edge (as in FLEX_END case):
+ mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
+ // ...and step *back* by the baseline adjustment:
+ mPosition -= baselineDiff;
+ } else {
+ // mPosition is already at line's flex-start edge.
+ // From there, we step *forward* by the baseline adjustment:
+ mPosition += baselineDiff;
+ }
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected align-self value");
+ break;
+ }
+}
+
+// Utility function to convert an InlineDir to an AxisOrientationType
+static inline AxisOrientationType
+InlineDirToAxisOrientation(WritingMode::InlineDir aInlineDir)
+{
+ switch (aInlineDir) {
+ case WritingMode::eInlineLTR:
+ return eAxis_LR;
+ case WritingMode::eInlineRTL:
+ return eAxis_RL;
+ case WritingMode::eInlineTTB:
+ return eAxis_TB;
+ case WritingMode::eInlineBTT:
+ return eAxis_BT;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unhandled InlineDir");
+ return eAxis_LR; // in case of unforseen error, assume English LTR text flow.
+}
+
+// Utility function to convert a BlockDir to an AxisOrientationType
+static inline AxisOrientationType
+BlockDirToAxisOrientation(WritingMode::BlockDir aBlockDir)
+{
+ switch (aBlockDir) {
+ case WritingMode::eBlockLR:
+ return eAxis_LR;
+ case WritingMode::eBlockRL:
+ return eAxis_RL;
+ case WritingMode::eBlockTB:
+ return eAxis_TB;
+ // NOTE: WritingMode::eBlockBT (bottom-to-top) does not exist.
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unhandled BlockDir");
+ return eAxis_TB; // in case of unforseen error, assume English TTB block-flow
+}
+
+FlexboxAxisTracker::FlexboxAxisTracker(
+ const nsFlexContainerFrame* aFlexContainer,
+ const WritingMode& aWM,
+ AxisTrackerFlags aFlags)
+ : mWM(aWM),
+ mAreAxesInternallyReversed(false)
+{
+ if (IsLegacyBox(aFlexContainer)) {
+ InitAxesFromLegacyProps(aFlexContainer);
+ } else {
+ InitAxesFromModernProps(aFlexContainer);
+ }
+
+ // Master switch to enable/disable bug 983427's code for reversing our axes
+ // and reversing some logic, to avoid reflowing children in bottom-to-top
+ // order. (This switch can be removed eventually, but for now, it allows
+ // this special-case code path to be compared against the normal code path.)
+ static bool sPreventBottomToTopChildOrdering = true;
+
+ // Note: if the eAllowBottomToTopChildOrdering flag is set, that overrides
+ // the static boolean and makes us skip this special case.
+ if (!(aFlags & AxisTrackerFlags::eAllowBottomToTopChildOrdering) &&
+ sPreventBottomToTopChildOrdering) {
+ // If either axis is bottom-to-top, we flip both axes (and set a flag
+ // so that we can flip some logic to make the reversal transparent).
+ if (eAxis_BT == mMainAxis || eAxis_BT == mCrossAxis) {
+ mMainAxis = GetReverseAxis(mMainAxis);
+ mCrossAxis = GetReverseAxis(mCrossAxis);
+ mAreAxesInternallyReversed = true;
+ mIsMainAxisReversed = !mIsMainAxisReversed;
+ mIsCrossAxisReversed = !mIsCrossAxisReversed;
+ }
+ }
+}
+
+void
+FlexboxAxisTracker::InitAxesFromLegacyProps(
+ const nsFlexContainerFrame* aFlexContainer)
+{
+ const nsStyleXUL* styleXUL = aFlexContainer->StyleXUL();
+
+ const bool boxOrientIsVertical = (styleXUL->mBoxOrient ==
+ StyleBoxOrient::Vertical);
+ const bool wmIsVertical = mWM.IsVertical();
+
+ // If box-orient agrees with our writing-mode, then we're "row-oriented"
+ // (i.e. the flexbox main axis is the same as our writing mode's inline
+ // direction). Otherwise, we're column-oriented (i.e. the flexbox's main
+ // axis is perpendicular to the writing-mode's inline direction).
+ mIsRowOriented = (boxOrientIsVertical == wmIsVertical);
+
+ // XXXdholbert BEGIN CODE TO SET DEPRECATED MEMBER-VARS
+ if (boxOrientIsVertical) {
+ mMainAxis = eAxis_TB;
+ mCrossAxis = eAxis_LR;
+ } else {
+ mMainAxis = eAxis_LR;
+ mCrossAxis = eAxis_TB;
+ }
+ // "direction: rtl" reverses the writing-mode's inline axis.
+ // So, we need to reverse the corresponding flex axis to match.
+ // (Note this we don't toggle "mIsMainAxisReversed" for this condition,
+ // because the main axis will still match mWM's inline direction.)
+ if (!mWM.IsBidiLTR()) {
+ AxisOrientationType& axisToFlip = mIsRowOriented ? mMainAxis : mCrossAxis;
+ axisToFlip = GetReverseAxis(axisToFlip);
+ }
+ // XXXdholbert END CODE TO SET DEPRECATED MEMBER-VARS
+
+ // Legacy flexbox can use "-webkit-box-direction: reverse" to reverse the
+ // main axis (so it runs in the reverse direction of the inline axis):
+ if (styleXUL->mBoxDirection == StyleBoxDirection::Reverse) {
+ mMainAxis = GetReverseAxis(mMainAxis);
+ mIsMainAxisReversed = true;
+ } else {
+ mIsMainAxisReversed = false;
+ }
+
+ // Legacy flexbox does not support reversing the cross axis -- it has no
+ // equivalent of modern flexbox's "flex-wrap: wrap-reverse".
+ mIsCrossAxisReversed = false;
+}
+
+void
+FlexboxAxisTracker::InitAxesFromModernProps(
+ const nsFlexContainerFrame* aFlexContainer)
+{
+ const nsStylePosition* stylePos = aFlexContainer->StylePosition();
+ uint32_t flexDirection = stylePos->mFlexDirection;
+
+ // Inline dimension ("start-to-end"):
+ // (NOTE: I'm intentionally not calling these "inlineAxis"/"blockAxis", since
+ // those terms have explicit definition in the writing-modes spec, which are
+ // the opposite of how I'd be using them here.)
+ AxisOrientationType inlineDimension =
+ InlineDirToAxisOrientation(mWM.GetInlineDir());
+ AxisOrientationType blockDimension =
+ BlockDirToAxisOrientation(mWM.GetBlockDir());
+
+ // Determine main axis:
+ switch (flexDirection) {
+ case NS_STYLE_FLEX_DIRECTION_ROW:
+ mMainAxis = inlineDimension;
+ mIsRowOriented = true;
+ mIsMainAxisReversed = false;
+ break;
+ case NS_STYLE_FLEX_DIRECTION_ROW_REVERSE:
+ mMainAxis = GetReverseAxis(inlineDimension);
+ mIsRowOriented = true;
+ mIsMainAxisReversed = true;
+ break;
+ case NS_STYLE_FLEX_DIRECTION_COLUMN:
+ mMainAxis = blockDimension;
+ mIsRowOriented = false;
+ mIsMainAxisReversed = false;
+ break;
+ case NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE:
+ mMainAxis = GetReverseAxis(blockDimension);
+ mIsRowOriented = false;
+ mIsMainAxisReversed = true;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected flex-direction value");
+ }
+
+ // Determine cross axis:
+ // (This is set up so that a bogus |flexDirection| value will
+ // give us blockDimension.
+ if (flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN ||
+ flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE) {
+ mCrossAxis = inlineDimension;
+ } else {
+ mCrossAxis = blockDimension;
+ }
+
+ // "flex-wrap: wrap-reverse" reverses our cross axis.
+ if (stylePos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) {
+ mCrossAxis = GetReverseAxis(mCrossAxis);
+ mIsCrossAxisReversed = true;
+ } else {
+ mIsCrossAxisReversed = false;
+ }
+}
+
+// Allocates a new FlexLine, adds it to the given LinkedList (at the front or
+// back depending on aShouldInsertAtFront), and returns a pointer to it.
+static FlexLine*
+AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
+ bool aShouldInsertAtFront)
+{
+ FlexLine* newLine = new FlexLine();
+ if (aShouldInsertAtFront) {
+ aLines.insertFront(newLine);
+ } else {
+ aLines.insertBack(newLine);
+ }
+ return newLine;
+}
+
+void
+nsFlexContainerFrame::GenerateFlexLines(
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nscoord aContentBoxMainSize,
+ nscoord aAvailableBSizeForContent,
+ const nsTArray<StrutInfo>& aStruts,
+ const FlexboxAxisTracker& aAxisTracker,
+ nsTArray<nsIFrame*>& aPlaceholders, /* out */
+ LinkedList<FlexLine>& aLines /* out */)
+{
+ MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty");
+
+ const bool isSingleLine =
+ NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
+
+ // If we're transparently reversing axes, then we'll need to link up our
+ // FlexItems and FlexLines in the reverse order, so that the rest of flex
+ // layout (with flipped axes) will still produce the correct result.
+ // Here, we declare a convenience bool that we'll pass when adding a new
+ // FlexLine or FlexItem, to make us insert it at the beginning of its list
+ // (so the list ends up reversed).
+ const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed();
+
+ // We have at least one FlexLine. Even an empty flex container has a single
+ // (empty) flex line.
+ FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+
+ nscoord wrapThreshold;
+ if (isSingleLine) {
+ // Not wrapping. Set threshold to sentinel value that tells us not to wrap.
+ wrapThreshold = NS_UNCONSTRAINEDSIZE;
+ } else {
+ // Wrapping! Set wrap threshold to flex container's content-box main-size.
+ wrapThreshold = aContentBoxMainSize;
+
+ // If the flex container doesn't have a definite content-box main-size
+ // (e.g. if main axis is vertical & 'height' is 'auto'), make sure we at
+ // least wrap when we hit its max main-size.
+ if (wrapThreshold == NS_UNCONSTRAINEDSIZE) {
+ const nscoord flexContainerMaxMainSize =
+ GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, aAxisTracker.GetWritingMode(),
+ aReflowInput.ComputedMaxISize(),
+ aReflowInput.ComputedMaxBSize());
+
+ wrapThreshold = flexContainerMaxMainSize;
+ }
+
+ // Also: if we're column-oriented and paginating in the block dimension,
+ // we may need to wrap to a new flex line sooner (before we grow past the
+ // available BSize, potentially running off the end of the page).
+ if (aAxisTracker.IsColumnOriented() &&
+ aAvailableBSizeForContent != NS_UNCONSTRAINEDSIZE) {
+ wrapThreshold = std::min(wrapThreshold, aAvailableBSizeForContent);
+ }
+ }
+
+ // Tracks the index of the next strut, in aStruts (and when this hits
+ // aStruts.Length(), that means there are no more struts):
+ uint32_t nextStrutIdx = 0;
+
+ // Overall index of the current flex item in the flex container. (This gets
+ // checked against entries in aStruts.)
+ uint32_t itemIdxInContainer = 0;
+
+ for (nsIFrame* childFrame : mFrames) {
+ // Don't create flex items / lines for placeholder frames:
+ if (childFrame->GetType() == nsGkAtoms::placeholderFrame) {
+ aPlaceholders.AppendElement(childFrame);
+ continue;
+ }
+
+ // Honor "page-break-before", if we're multi-line and this line isn't empty:
+ if (!isSingleLine && !curLine->IsEmpty() &&
+ childFrame->StyleDisplay()->mBreakBefore) {
+ curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ }
+
+ UniquePtr<FlexItem> item;
+ if (nextStrutIdx < aStruts.Length() &&
+ aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
+
+ // Use the simplified "strut" FlexItem constructor:
+ item = MakeUnique<FlexItem>(childFrame, aStruts[nextStrutIdx].mStrutCrossSize,
+ aReflowInput.GetWritingMode());
+ nextStrutIdx++;
+ } else {
+ item = GenerateFlexItemForChild(aPresContext, childFrame,
+ aReflowInput, aAxisTracker);
+ }
+
+ nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
+ nscoord itemOuterHypotheticalMainSize =
+ item->GetOuterMainSize(aAxisTracker.GetMainAxis());
+
+ // Check if we need to wrap |item| to a new line
+ // (i.e. check if its outer hypothetical main size pushes our line over
+ // the threshold)
+ if (wrapThreshold != NS_UNCONSTRAINEDSIZE &&
+ !curLine->IsEmpty() && // No need to wrap at start of a line.
+ wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
+ itemOuterHypotheticalMainSize)) {
+ curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ }
+
+ // Add item to current flex line (and update the line's bookkeeping about
+ // how large its items collectively are).
+ curLine->AddItem(item.release(), shouldInsertAtFront,
+ itemInnerHypotheticalMainSize,
+ itemOuterHypotheticalMainSize);
+
+ // Honor "page-break-after", if we're multi-line and have more children:
+ if (!isSingleLine && childFrame->GetNextSibling() &&
+ childFrame->StyleDisplay()->mBreakAfter) {
+ curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+ }
+ itemIdxInContainer++;
+ }
+}
+
+// Retrieves the content-box main-size of our flex container from the
+// reflow state (specifically, the main-size of *this continuation* of the
+// flex container).
+nscoord
+nsFlexContainerFrame::GetMainSizeFromReflowInput(
+ const ReflowInput& aReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ if (aAxisTracker.IsRowOriented()) {
+ // Row-oriented --> our main axis is the inline axis, so our main size
+ // is our inline size (which should already be resolved).
+ NS_WARNING_ASSERTION(
+ aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
+ "Unconstrained inline size; this should only result from huge sizes "
+ "(not intrinsic sizing w/ orthogonal flows)");
+ return aReflowInput.ComputedISize();
+ }
+
+ // Note: This may be unconstrained, if our block size is "auto":
+ return GetEffectiveComputedBSize(aReflowInput);
+}
+
+// Returns the largest outer hypothetical main-size of any line in |aLines|.
+// (i.e. the hypothetical main-size of the largest line)
+static nscoord
+GetLargestLineMainSize(const FlexLine* aFirstLine)
+{
+ nscoord largestLineOuterSize = 0;
+ for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
+ largestLineOuterSize = std::max(largestLineOuterSize,
+ line->GetTotalOuterHypotheticalMainSize());
+ }
+ return largestLineOuterSize;
+}
+
+/* Resolves the content-box main-size of a flex container frame,
+ * primarily based on:
+ * - the "tentative" main size, taken from the reflow state ("tentative"
+ * because it may be unconstrained or may run off the page).
+ * - the available BSize (needed if the main axis is the block axis).
+ * - the sizes of our lines of flex items.
+ *
+ * Guaranteed to return a definite length, i.e. not NS_UNCONSTRAINEDSIZE,
+ * aside from cases with huge lengths which happen to compute to that value.
+ *
+ * (Note: This function should be structurally similar to 'ComputeCrossSize()',
+ * except that here, the caller has already grabbed the tentative size from the
+ * reflow state.)
+ */
+static nscoord
+ResolveFlexContainerMainSize(const ReflowInput& aReflowInput,
+ const FlexboxAxisTracker& aAxisTracker,
+ nscoord aTentativeMainSize,
+ nscoord aAvailableBSizeForContent,
+ const FlexLine* aFirstLine,
+ nsReflowStatus& aStatus)
+{
+ MOZ_ASSERT(aFirstLine, "null first line pointer");
+
+ if (aAxisTracker.IsRowOriented()) {
+ // Row-oriented --> our main axis is the inline axis, so our main size
+ // is our inline size (which should already be resolved).
+ return aTentativeMainSize;
+ }
+
+ if (aTentativeMainSize != NS_INTRINSICSIZE) {
+ // Column-oriented case, with fixed BSize:
+ if (aAvailableBSizeForContent == NS_UNCONSTRAINEDSIZE ||
+ aTentativeMainSize < aAvailableBSizeForContent) {
+ // Not in a fragmenting context, OR no need to fragment because we have
+ // more available BSize than we need. Either way, we don't need to clamp.
+ // (Note that the reflow state has already done the appropriate
+ // min/max-BSize clamping.)
+ return aTentativeMainSize;
+ }
+
+ // Fragmenting *and* our fixed BSize is larger than available BSize:
+ // Mark incomplete so we get a next-in-flow, and take up all of the
+ // available BSize (or the amount of BSize required by our children, if
+ // that's larger; but of course not more than our own computed BSize).
+ // XXXdholbert For now, we don't support pushing children to our next
+ // continuation or splitting children, so "amount of BSize required by
+ // our children" is just the main-size (BSize) of our longest flex line.
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
+
+ if (largestLineOuterSize <= aAvailableBSizeForContent) {
+ return aAvailableBSizeForContent;
+ }
+ return std::min(aTentativeMainSize, largestLineOuterSize);
+ }
+
+ // Column-oriented case, with auto BSize:
+ // Resolve auto BSize to the largest FlexLine length, clamped to our
+ // computed min/max main-size properties.
+ // XXXdholbert Handle constrained-aAvailableBSizeForContent case here.
+ nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
+ return NS_CSS_MINMAX(largestLineOuterSize,
+ aReflowInput.ComputedMinBSize(),
+ aReflowInput.ComputedMaxBSize());
+}
+
+nscoord
+nsFlexContainerFrame::ComputeCrossSize(const ReflowInput& aReflowInput,
+ const FlexboxAxisTracker& aAxisTracker,
+ nscoord aSumLineCrossSizes,
+ nscoord aAvailableBSizeForContent,
+ bool* aIsDefinite,
+ nsReflowStatus& aStatus)
+{
+ MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null");
+
+ if (aAxisTracker.IsColumnOriented()) {
+ // Column-oriented --> our cross axis is the inline axis, so our cross size
+ // is our inline size (which should already be resolved).
+ NS_WARNING_ASSERTION(
+ aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
+ "Unconstrained inline size; this should only result from huge sizes "
+ "(not intrinsic sizing w/ orthogonal flows)");
+ *aIsDefinite = true;
+ return aReflowInput.ComputedISize();
+ }
+
+ nscoord effectiveComputedBSize = GetEffectiveComputedBSize(aReflowInput);
+ if (effectiveComputedBSize != NS_INTRINSICSIZE) {
+ // Row-oriented case (cross axis is block-axis), with fixed BSize:
+ *aIsDefinite = true;
+ if (aAvailableBSizeForContent == NS_UNCONSTRAINEDSIZE ||
+ effectiveComputedBSize < aAvailableBSizeForContent) {
+ // Not in a fragmenting context, OR no need to fragment because we have
+ // more available BSize than we need. Either way, just use our fixed
+ // BSize. (Note that the reflow state has already done the appropriate
+ // min/max-BSize clamping.)
+ return effectiveComputedBSize;
+ }
+
+ // Fragmenting *and* our fixed BSize is too tall for available BSize:
+ // Mark incomplete so we get a next-in-flow, and take up all of the
+ // available BSize (or the amount of BSize required by our children, if
+ // that's larger; but of course not more than our own computed BSize).
+ // XXXdholbert For now, we don't support pushing children to our next
+ // continuation or splitting children, so "amount of BSize required by
+ // our children" is just the sum of our FlexLines' BSizes (cross sizes).
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ if (aSumLineCrossSizes <= aAvailableBSizeForContent) {
+ return aAvailableBSizeForContent;
+ }
+ return std::min(effectiveComputedBSize, aSumLineCrossSizes);
+ }
+
+ // Row-oriented case (cross axis is block axis), with auto BSize:
+ // Shrink-wrap our line(s), subject to our min-size / max-size
+ // constraints in that (block) axis.
+ // XXXdholbert Handle constrained-aAvailableBSizeForContent case here.
+ *aIsDefinite = false;
+ return NS_CSS_MINMAX(aSumLineCrossSizes,
+ aReflowInput.ComputedMinBSize(),
+ aReflowInput.ComputedMaxBSize());
+}
+
+void
+FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent,
+ nscoord aContentBoxMainSize,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ MainAxisPositionTracker mainAxisPosnTracker(aAxisTracker, this,
+ aJustifyContent,
+ aContentBoxMainSize);
+ for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
+ nscoord itemMainBorderBoxSize =
+ item->GetMainSize() +
+ item->GetBorderPaddingSizeInAxis(mainAxisPosnTracker.GetAxis());
+
+ // Resolve any main-axis 'auto' margins on aChild to an actual value.
+ mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(*item);
+
+ // Advance our position tracker to child's upper-left content-box corner,
+ // and use that as its position in the main axis.
+ mainAxisPosnTracker.EnterMargin(item->GetMargin());
+ mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize);
+
+ item->SetMainPosition(mainAxisPosnTracker.GetPosition());
+
+ mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
+ mainAxisPosnTracker.ExitMargin(item->GetMargin());
+ mainAxisPosnTracker.TraversePackingSpace();
+ }
+}
+
+/**
+ * Given the flex container's "flex-relative ascent" (i.e. distance from the
+ * flex container's content-box cross-start edge to its baseline), returns
+ * its actual physical ascent value (the distance from the *border-box* top
+ * edge to its baseline).
+ */
+static nscoord
+ComputePhysicalAscentFromFlexRelativeAscent(
+ nscoord aFlexRelativeAscent,
+ nscoord aContentBoxCrossSize,
+ const ReflowInput& aReflowInput,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ return aReflowInput.ComputedPhysicalBorderPadding().top +
+ PhysicalCoordFromFlexRelativeCoord(aFlexRelativeAscent,
+ aContentBoxCrossSize,
+ aAxisTracker.GetCrossAxis());
+}
+
+void
+nsFlexContainerFrame::SizeItemInCrossAxis(
+ nsPresContext* aPresContext,
+ const FlexboxAxisTracker& aAxisTracker,
+ ReflowInput& aChildReflowInput,
+ FlexItem& aItem)
+{
+ if (aAxisTracker.IsCrossAxisHorizontal()) {
+ MOZ_ASSERT(aItem.HasIntrinsicRatio(),
+ "For now, caller's CanMainSizeInfluenceCrossSize check should "
+ "only allow us to get here for items with intrinsic ratio");
+ // XXXdholbert When we finish support for vertical writing-modes,
+ // (in bug 1079155 or a dependency), we'll relax the horizontal check in
+ // CanMainSizeInfluenceCrossSize, and this function will need to be able
+ // to measure the baseline & width (given our resolved height)
+ // of vertical-writing-mode flex items here.
+ // For now, we only expect to get here for items with an intrinsic aspect
+ // ratio; and for those items, we can just read the size off of the reflow
+ // state, without performing reflow.
+ aItem.SetCrossSize(aChildReflowInput.ComputedWidth());
+ return;
+ }
+
+ MOZ_ASSERT(!aItem.HadMeasuringReflow(),
+ "We shouldn't need more than one measuring reflow");
+
+ if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_STRETCH) {
+ // This item's got "align-self: stretch", so we probably imposed a
+ // stretched computed height on it during its previous reflow. We're
+ // not imposing that height for *this* measuring reflow, so we need to
+ // tell it to treat this reflow as a vertical resize (regardless of
+ // whether any of its ancestors are being resized).
+ aChildReflowInput.SetVResize(true);
+ }
+ ReflowOutput childDesiredSize(aChildReflowInput);
+ nsReflowStatus childReflowStatus;
+ const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
+ ReflowChild(aItem.Frame(), aPresContext,
+ childDesiredSize, aChildReflowInput,
+ 0, 0, flags, childReflowStatus);
+ aItem.SetHadMeasuringReflow();
+
+ // XXXdholbert Once we do pagination / splitting, we'll need to actually
+ // handle incomplete childReflowStatuses. But for now, we give our kids
+ // unconstrained available height, which means they should always complete.
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
+ "We gave flex item unconstrained available height, so it "
+ "should be complete");
+
+ // Tell the child we're done with its initial reflow.
+ // (Necessary for e.g. GetBaseline() to work below w/out asserting)
+ FinishReflowChild(aItem.Frame(), aPresContext,
+ childDesiredSize, &aChildReflowInput, 0, 0, flags);
+
+ // Save the sizing info that we learned from this reflow
+ // -----------------------------------------------------
+
+ // Tentatively store the child's desired content-box cross-size.
+ // Note that childDesiredSize is the border-box size, so we have to
+ // subtract border & padding to get the content-box size.
+ // (Note that at this point in the code, we know our cross axis is vertical,
+ // so we don't bother with making aAxisTracker pick the cross-axis component
+ // for us.)
+ nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom();
+ if (childDesiredSize.Height() < crossAxisBorderPadding) {
+ // Child's requested size isn't large enough for its border/padding!
+ // This is OK for the trivial nsFrame::Reflow() impl, but other frame
+ // classes should know better. So, if we get here, the child had better be
+ // an instance of nsFrame (i.e. it should return null from GetType()).
+ // XXXdholbert Once we've fixed bug 765861, we should upgrade this to an
+ // assertion that trivially passes if bug 765861's flag has been flipped.
+ NS_WARNING_ASSERTION(
+ !aItem.Frame()->GetType(),
+ "Child should at least request space for border/padding");
+ aItem.SetCrossSize(0);
+ } else {
+ // (normal case)
+ aItem.SetCrossSize(childDesiredSize.Height() - crossAxisBorderPadding);
+ }
+
+ aItem.SetAscent(childDesiredSize.BlockStartAscent());
+}
+
+void
+FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker);
+
+ for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
+ // First, stretch the item's cross size (if appropriate), and resolve any
+ // auto margins in this axis.
+ item->ResolveStretchedCrossSize(mLineCrossSize, aAxisTracker);
+ lineCrossAxisPosnTracker.ResolveAutoMarginsInCrossAxis(*this, *item);
+
+ // Compute the cross-axis position of this item
+ nscoord itemCrossBorderBoxSize =
+ item->GetCrossSize() +
+ item->GetBorderPaddingSizeInAxis(aAxisTracker.GetCrossAxis());
+ lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item, aAxisTracker);
+ lineCrossAxisPosnTracker.EnterMargin(item->GetMargin());
+ lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize);
+
+ item->SetCrossPosition(aLineStartPosition +
+ lineCrossAxisPosnTracker.GetPosition());
+
+ // Back out to cross-axis edge of the line.
+ lineCrossAxisPosnTracker.ResetPosition();
+ }
+}
+
+void
+nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
+ ("Reflow() for nsFlexContainerFrame %p\n", this));
+
+ if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
+ return;
+ }
+
+ // We (and our children) can only depend on our ancestor's bsize if we have
+ // a percent-bsize, or if we're positioned and we have "block-start" and "block-end"
+ // set and have block-size:auto. (There are actually other cases, too -- e.g. if
+ // our parent is itself a block-dir flex container and we're flexible -- but
+ // we'll let our ancestors handle those sorts of cases.)
+ WritingMode wm = aReflowInput.GetWritingMode();
+ const nsStylePosition* stylePos = StylePosition();
+ const nsStyleCoord& bsize = stylePos->BSize(wm);
+ if (bsize.HasPercent() ||
+ (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
+ eStyleUnit_Auto == bsize.GetUnit() &&
+ eStyleUnit_Auto != stylePos->mOffset.GetBStartUnit(wm) &&
+ eStyleUnit_Auto != stylePos->mOffset.GetBEndUnit(wm))) {
+ AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ }
+
+ // If we've never reordered our children, then we can trust that they're
+ // already in DOM-order, and we only need to consider their "order" property
+ // when checking them for sortedness & sorting them.
+ //
+ // After we actually sort them, though, we can't trust that they're in DOM
+ // order anymore. So, from that point on, our sort & sorted-order-checking
+ // operations need to use a fancier LEQ function that also takes DOM order
+ // into account, so that we can honor the spec's requirement that frames w/
+ // equal "order" values are laid out in DOM order.
+
+ if (!HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED)) {
+ if (SortChildrenIfNeeded<IsOrderLEQ>()) {
+ AddStateBits(NS_STATE_FLEX_CHILDREN_REORDERED);
+ }
+ } else {
+ SortChildrenIfNeeded<IsOrderLEQWithDOMFallback>();
+ }
+
+ RenumberList();
+
+ const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode());
+
+ // If we're being fragmented into a constrained BSize, then subtract off
+ // borderpadding BStart from that constrained BSize, to get the available
+ // BSize for our content box. (No need to subtract the borderpadding BStart
+ // if we're already skipping it via GetLogicalSkipSides, though.)
+ nscoord availableBSizeForContent = aReflowInput.AvailableBSize();
+ if (availableBSizeForContent != NS_UNCONSTRAINEDSIZE &&
+ !(GetLogicalSkipSides(&aReflowInput).BStart())) {
+ availableBSizeForContent -=
+ aReflowInput.ComputedLogicalBorderPadding().BStart(wm);
+ // (Don't let that push availableBSizeForContent below zero, though):
+ availableBSizeForContent = std::max(availableBSizeForContent, 0);
+ }
+
+ nscoord contentBoxMainSize = GetMainSizeFromReflowInput(aReflowInput,
+ axisTracker);
+
+ AutoTArray<StrutInfo, 1> struts;
+ DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
+ contentBoxMainSize, availableBSizeForContent,
+ struts, axisTracker);
+
+ if (!struts.IsEmpty()) {
+ // We're restarting flex layout, with new knowledge of collapsed items.
+ DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
+ contentBoxMainSize, availableBSizeForContent,
+ struts, axisTracker);
+ }
+}
+
+// RAII class to clean up a list of FlexLines.
+// Specifically, this removes each line from the list, deletes all the
+// FlexItems in its list, and deletes the FlexLine.
+class MOZ_RAII AutoFlexLineListClearer
+{
+public:
+ explicit AutoFlexLineListClearer(LinkedList<FlexLine>& aLines
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLines(aLines)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ }
+
+ ~AutoFlexLineListClearer()
+ {
+ while (FlexLine* line = mLines.popFirst()) {
+ while (FlexItem* item = line->mItems.popFirst()) {
+ delete item;
+ }
+ delete line;
+ }
+ }
+
+private:
+ LinkedList<FlexLine>& mLines;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+// Class to let us temporarily provide an override value for the the main-size
+// CSS property ('width' or 'height') on a flex item, for use in
+// nsFrame::ComputeSizeWithIntrinsicDimensions.
+// (We could use this overridden size more broadly, too, but it's probably
+// better to avoid property-table accesses. So, where possible, we communicate
+// the resolved main-size to the child via modifying its reflow state directly,
+// instead of using this class.)
+class MOZ_RAII AutoFlexItemMainSizeOverride final
+{
+public:
+ explicit AutoFlexItemMainSizeOverride(FlexItem& aItem
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mItemProps(aItem.Frame()->Properties())
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+ MOZ_ASSERT(!mItemProps.Has(nsIFrame::FlexItemMainSizeOverride()),
+ "FlexItemMainSizeOverride prop shouldn't be set already; "
+ "it should only be set temporarily (& not recursively)");
+ NS_ASSERTION(aItem.HasIntrinsicRatio(),
+ "This should only be needed for items with an aspect ratio");
+
+ mItemProps.Set(nsIFrame::FlexItemMainSizeOverride(), aItem.GetMainSize());
+ }
+
+ ~AutoFlexItemMainSizeOverride() {
+ mItemProps.Remove(nsIFrame::FlexItemMainSizeOverride());
+ }
+
+private:
+ const FrameProperties mItemProps;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+void
+nsFlexContainerFrame::CalculatePackingSpace(uint32_t aNumThingsToPack,
+ uint8_t aAlignVal,
+ nscoord* aFirstSubjectOffset,
+ uint32_t* aNumPackingSpacesRemaining,
+ nscoord* aPackingSpaceRemaining)
+{
+ MOZ_ASSERT(NS_STYLE_ALIGN_SPACE_BETWEEN == NS_STYLE_JUSTIFY_SPACE_BETWEEN &&
+ NS_STYLE_ALIGN_SPACE_AROUND == NS_STYLE_JUSTIFY_SPACE_AROUND &&
+ NS_STYLE_ALIGN_SPACE_EVENLY == NS_STYLE_JUSTIFY_SPACE_EVENLY,
+ "CalculatePackingSpace assumes that NS_STYLE_ALIGN_SPACE and "
+ "NS_STYLE_JUSTIFY_SPACE constants are interchangeable");
+
+ MOZ_ASSERT(aAlignVal == NS_STYLE_ALIGN_SPACE_BETWEEN ||
+ aAlignVal == NS_STYLE_ALIGN_SPACE_AROUND ||
+ aAlignVal == NS_STYLE_ALIGN_SPACE_EVENLY,
+ "Unexpected alignment value");
+
+ MOZ_ASSERT(*aPackingSpaceRemaining >= 0,
+ "Should not be called with negative packing space");
+
+ MOZ_ASSERT(aNumThingsToPack >= 1,
+ "Should not be called with less than 1 thing to pack");
+
+ // Packing spaces between items:
+ *aNumPackingSpacesRemaining = aNumThingsToPack - 1;
+
+ if (aAlignVal == NS_STYLE_ALIGN_SPACE_BETWEEN) {
+ // No need to reserve space at beginning/end, so we're done.
+ return;
+ }
+
+ // We need to add 1 or 2 packing spaces, split between beginning/end, for
+ // space-around / space-evenly:
+ size_t numPackingSpacesForEdges =
+ aAlignVal == NS_STYLE_JUSTIFY_SPACE_AROUND ? 1 : 2;
+
+ // How big will each "full" packing space be:
+ nscoord packingSpaceSize = *aPackingSpaceRemaining /
+ (*aNumPackingSpacesRemaining + numPackingSpacesForEdges);
+ // How much packing-space are we allocating to the edges:
+ nscoord totalEdgePackingSpace = numPackingSpacesForEdges * packingSpaceSize;
+
+ // Use half of that edge packing space right now:
+ *aFirstSubjectOffset += totalEdgePackingSpace / 2;
+ // ...but we need to subtract all of it right away, so that we won't
+ // hand out any of it to intermediate packing spaces.
+ *aPackingSpaceRemaining -= totalEdgePackingSpace;
+}
+
+void
+nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ nscoord aContentBoxMainSize,
+ nscoord aAvailableBSizeForContent,
+ nsTArray<StrutInfo>& aStruts,
+ const FlexboxAxisTracker& aAxisTracker)
+{
+ aStatus = NS_FRAME_COMPLETE;
+
+ LinkedList<FlexLine> lines;
+ nsTArray<nsIFrame*> placeholderKids;
+ AutoFlexLineListClearer cleanupLines(lines);
+
+ GenerateFlexLines(aPresContext, aReflowInput,
+ aContentBoxMainSize,
+ aAvailableBSizeForContent,
+ aStruts, aAxisTracker,
+ placeholderKids, lines);
+
+ if (lines.getFirst()->IsEmpty() &&
+ !lines.getFirst()->getNext()) {
+ // We have no flex items, our parent should synthesize a baseline if needed.
+ AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
+ } else {
+ RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
+ }
+
+ aContentBoxMainSize =
+ ResolveFlexContainerMainSize(aReflowInput, aAxisTracker,
+ aContentBoxMainSize, aAvailableBSizeForContent,
+ lines.getFirst(), aStatus);
+
+ for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
+ line->ResolveFlexibleLengths(aContentBoxMainSize);
+ }
+
+ // Cross Size Determination - Flexbox spec section 9.4
+ // ===================================================
+ // Calculate the hypothetical cross size of each item:
+ nscoord sumLineCrossSizes = 0;
+ for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
+ for (FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) {
+ // The item may already have the correct cross-size; only recalculate
+ // if the item's main size resolution (flexing) could have influenced it:
+ if (item->CanMainSizeInfluenceCrossSize(aAxisTracker)) {
+ Maybe<AutoFlexItemMainSizeOverride> sizeOverride;
+ if (item->HasIntrinsicRatio()) {
+ // For flex items with an aspect ratio, we have to impose an override
+ // for the main-size property *before* we even instantiate the reflow
+ // state, in order for aspect ratio calculations to produce the right
+ // cross size in the reflow state. (For other flex items, it's OK
+ // (and cheaper) to impose our main size *after* the reflow state has
+ // been constructed, since the main size shouldn't influence anything
+ // about cross-size measurement until we actually reflow the child.)
+ sizeOverride.emplace(*item);
+ }
+
+ WritingMode wm = item->Frame()->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(wm);
+ availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+ ReflowInput childReflowInput(aPresContext, aReflowInput,
+ item->Frame(), availSize);
+ if (!sizeOverride) {
+ // Directly override the computed main-size, by tweaking reflow state:
+ if (aAxisTracker.IsMainAxisHorizontal()) {
+ childReflowInput.SetComputedWidth(item->GetMainSize());
+ } else {
+ childReflowInput.SetComputedHeight(item->GetMainSize());
+ }
+ }
+
+ SizeItemInCrossAxis(aPresContext, aAxisTracker,
+ childReflowInput, *item);
+ }
+ }
+ // Now that we've finished with this line's items, size the line itself:
+ line->ComputeCrossSizeAndBaseline(aAxisTracker);
+ sumLineCrossSizes += line->GetLineCrossSize();
+ }
+
+ bool isCrossSizeDefinite;
+ const nscoord contentBoxCrossSize =
+ ComputeCrossSize(aReflowInput, aAxisTracker, sumLineCrossSizes,
+ aAvailableBSizeForContent, &isCrossSizeDefinite, aStatus);
+
+ // Set up state for cross-axis alignment, at a high level (outside the
+ // scope of a particular flex line)
+ CrossAxisPositionTracker
+ crossAxisPosnTracker(lines.getFirst(),
+ aReflowInput, contentBoxCrossSize,
+ isCrossSizeDefinite, aAxisTracker);
+
+ // Now that we know the cross size of each line (including
+ // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
+ // constructor), we can create struts for any flex items with
+ // "visibility: collapse" (and restart flex layout).
+ if (aStruts.IsEmpty()) { // (Don't make struts if we already did)
+ BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts);
+ if (!aStruts.IsEmpty()) {
+ // Restart flex layout, using our struts.
+ return;
+ }
+ }
+
+ // If the container should derive its baseline from the first FlexLine,
+ // do that here (while crossAxisPosnTracker is conveniently pointing
+ // at the cross-start edge of that line, which the line's baseline offset is
+ // measured from):
+ nscoord flexContainerAscent;
+ if (!aAxisTracker.AreAxesInternallyReversed()) {
+ nscoord firstLineBaselineOffset = lines.getFirst()->GetFirstBaselineOffset();
+ if (firstLineBaselineOffset == nscoord_MIN) {
+ // No baseline-aligned items in line. Use sentinel value to prompt us to
+ // get baseline from the first FlexItem after we've reflowed it.
+ flexContainerAscent = nscoord_MIN;
+ } else {
+ flexContainerAscent =
+ ComputePhysicalAscentFromFlexRelativeAscent(
+ crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
+ contentBoxCrossSize, aReflowInput, aAxisTracker);
+ }
+ }
+
+ const auto justifyContent = IsLegacyBox(aReflowInput.mFrame) ?
+ ConvertLegacyStyleToJustifyContent(StyleXUL()) :
+ aReflowInput.mStylePosition->mJustifyContent;
+
+ for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
+ // Main-Axis Alignment - Flexbox spec section 9.5
+ // ==============================================
+ line->PositionItemsInMainAxis(justifyContent,
+ aContentBoxMainSize,
+ aAxisTracker);
+
+ // Cross-Axis Alignment - Flexbox spec section 9.6
+ // ===============================================
+ line->PositionItemsInCrossAxis(crossAxisPosnTracker.GetPosition(),
+ aAxisTracker);
+ crossAxisPosnTracker.TraverseLine(*line);
+ crossAxisPosnTracker.TraversePackingSpace();
+ }
+
+ // If the container should derive its baseline from the last FlexLine,
+ // do that here (while crossAxisPosnTracker is conveniently pointing
+ // at the cross-end edge of that line, which the line's baseline offset is
+ // measured from):
+ if (aAxisTracker.AreAxesInternallyReversed()) {
+ nscoord lastLineBaselineOffset = lines.getLast()->GetFirstBaselineOffset();
+ if (lastLineBaselineOffset == nscoord_MIN) {
+ // No baseline-aligned items in line. Use sentinel value to prompt us to
+ // get baseline from the last FlexItem after we've reflowed it.
+ flexContainerAscent = nscoord_MIN;
+ } else {
+ flexContainerAscent =
+ ComputePhysicalAscentFromFlexRelativeAscent(
+ crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset,
+ contentBoxCrossSize, aReflowInput, aAxisTracker);
+ }
+ }
+
+ // Before giving each child a final reflow, calculate the origin of the
+ // flex container's content box (with respect to its border-box), so that
+ // we can compute our flex item's final positions.
+ WritingMode flexWM = aReflowInput.GetWritingMode();
+ LogicalMargin containerBP = aReflowInput.ComputedLogicalBorderPadding();
+
+ // Unconditionally skip block-end border & padding for now, regardless of
+ // writing-mode/GetLogicalSkipSides. We add it lower down, after we've
+ // established baseline and decided whether bottom border-padding fits (if
+ // we're fragmented).
+ const nscoord blockEndContainerBP = containerBP.BEnd(flexWM);
+ const LogicalSides skipSides =
+ GetLogicalSkipSides(&aReflowInput) | LogicalSides(eLogicalSideBitsBEnd);
+ containerBP.ApplySkipSides(skipSides);
+
+ const LogicalPoint containerContentBoxOrigin(flexWM,
+ containerBP.IStart(flexWM),
+ containerBP.BStart(flexWM));
+
+ // Determine flex container's border-box size (used in positioning children):
+ LogicalSize logSize =
+ aAxisTracker.LogicalSizeFromFlexRelativeSizes(aContentBoxMainSize,
+ contentBoxCrossSize);
+ logSize += aReflowInput.ComputedLogicalBorderPadding().Size(flexWM);
+ nsSize containerSize = logSize.GetPhysicalSize(flexWM);
+
+ // If the flex container has no baseline-aligned items, it will use this item
+ // (the first item, discounting any under-the-hood reversing that we've done)
+ // to determine its baseline:
+ const FlexItem* const firstItem =
+ aAxisTracker.AreAxesInternallyReversed()
+ ? lines.getLast()->GetLastItem()
+ : lines.getFirst()->GetFirstItem();
+
+ // FINAL REFLOW: Give each child frame another chance to reflow, now that
+ // we know its final size and position.
+ for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
+ for (const FlexItem* item = line->GetFirstItem(); item;
+ item = item->getNext()) {
+ LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint(
+ item->GetMainPosition(),
+ item->GetCrossPosition(),
+ aContentBoxMainSize,
+ contentBoxCrossSize);
+ // Adjust framePos to be relative to the container's border-box
+ // (i.e. its frame rect), instead of the container's content-box:
+ framePos += containerContentBoxOrigin;
+
+ // (Intentionally snapshotting this before ApplyRelativePositioning, to
+ // maybe use for setting the flex container's baseline.)
+ const nscoord itemNormalBPos = framePos.B(flexWM);
+
+ // Check if we actually need to reflow the item -- if we already reflowed
+ // it with the right size, we can just reposition it as-needed.
+ bool itemNeedsReflow = true; // (Start out assuming the worst.)
+ if (item->HadMeasuringReflow()) {
+ LogicalSize finalFlexItemCBSize =
+ aAxisTracker.LogicalSizeFromFlexRelativeSizes(item->GetMainSize(),
+ item->GetCrossSize());
+ // We've already reflowed the child once. Was the size we gave it in
+ // that reflow the same as its final (post-flexing/stretching) size?
+ if (finalFlexItemCBSize ==
+ LogicalSize(flexWM,
+ item->Frame()->GetContentRectRelativeToSelf().Size())) {
+ // Even if our size hasn't changed, some of our descendants might
+ // care that our bsize is now considered "definite" (whereas it
+ // wasn't in our previous "measuring" reflow), if they have a
+ // relative bsize.
+ if (!(item->Frame()->GetStateBits() &
+ NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
+ // Item has the correct size (and its children don't care that
+ // it's now "definite"). Let's just make sure it's at the right
+ // position.
+ itemNeedsReflow = false;
+ MoveFlexItemToFinalPosition(aReflowInput, *item, framePos,
+ containerSize);
+ }
+ }
+ }
+ if (itemNeedsReflow) {
+ ReflowFlexItem(aPresContext, aAxisTracker, aReflowInput,
+ *item, framePos, containerSize);
+ }
+
+ // If this is our first item and we haven't established a baseline for
+ // the container yet (i.e. if we don't have 'align-self: baseline' on any
+ // children), then use this child's first baseline as the container's
+ // baseline.
+ if (item == firstItem &&
+ flexContainerAscent == nscoord_MIN) {
+ flexContainerAscent = itemNormalBPos + item->ResolvedAscent(true);
+ }
+ }
+ }
+
+ if (!placeholderKids.IsEmpty()) {
+ ReflowPlaceholders(aPresContext, aReflowInput,
+ placeholderKids, containerContentBoxOrigin,
+ containerSize);
+ }
+
+ // Compute flex container's desired size (in its own writing-mode),
+ // starting w/ content-box size & growing from there:
+ LogicalSize desiredSizeInFlexWM =
+ aAxisTracker.LogicalSizeFromFlexRelativeSizes(aContentBoxMainSize,
+ contentBoxCrossSize);
+ // Add border/padding (w/ skipSides already applied):
+ desiredSizeInFlexWM.ISize(flexWM) += containerBP.IStartEnd(flexWM);
+ desiredSizeInFlexWM.BSize(flexWM) += containerBP.BStartEnd(flexWM);
+
+ if (flexContainerAscent == nscoord_MIN) {
+ // Still don't have our baseline set -- this happens if we have no
+ // children (or if our children are huge enough that they have nscoord_MIN
+ // as their baseline... in which case, we'll use the wrong baseline, but no
+ // big deal)
+ NS_WARNING_ASSERTION(
+ lines.getFirst()->IsEmpty(),
+ "Have flex items but didn't get an ascent - that's odd (or there are "
+ "just gigantic sizes involved)");
+ // Per spec, synthesize baseline from the flex container's content box
+ // (i.e. use block-end side of content-box)
+ // XXXdholbert This only makes sense if parent's writing mode is
+ // horizontal (& even then, really we should be using the BSize in terms
+ // of the parent's writing mode, not ours). Clean up in bug 1155322.
+ flexContainerAscent = desiredSizeInFlexWM.BSize(flexWM);
+ }
+
+ if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+ // This will force our parent to call GetLogicalBaseline, which will
+ // synthesize a margin-box baseline.
+ aDesiredSize.SetBlockStartAscent(ReflowOutput::ASK_FOR_BASELINE);
+ } else {
+ // XXXdholbert flexContainerAscent needs to be in terms of
+ // our parent's writing-mode here. See bug 1155322.
+ aDesiredSize.SetBlockStartAscent(flexContainerAscent);
+ }
+
+ // Now: If we're complete, add bottom border/padding to desired height (which
+ // we skipped via skipSides) -- unless that pushes us over available height,
+ // in which case we become incomplete (unless we already weren't asking for
+ // any height, in which case we stay complete to avoid looping forever).
+ // NOTE: If we're auto-height, we allow our bottom border/padding to push us
+ // over the available height without requesting a continuation, for
+ // consistency with the behavior of "display:block" elements.
+ if (NS_FRAME_IS_COMPLETE(aStatus)) {
+ nscoord desiredBSizeWithBEndBP =
+ desiredSizeInFlexWM.BSize(flexWM) + blockEndContainerBP;
+
+ if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE ||
+ desiredSizeInFlexWM.BSize(flexWM) == 0 ||
+ desiredBSizeWithBEndBP <= aReflowInput.AvailableBSize() ||
+ aReflowInput.ComputedBSize() == NS_INTRINSICSIZE) {
+ // Update desired height to include block-end border/padding
+ desiredSizeInFlexWM.BSize(flexWM) = desiredBSizeWithBEndBP;
+ } else {
+ // We couldn't fit bottom border/padding, so we'll need a continuation.
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ }
+ }
+
+ // Calculate the container baselines so that our parent can baseline-align us.
+ mBaselineFromLastReflow = flexContainerAscent;
+ mLastBaselineFromLastReflow = lines.getLast()->GetLastBaselineOffset();
+ if (mLastBaselineFromLastReflow == nscoord_MIN) {
+ // XXX we fall back to a mirrored first baseline here for now, but this
+ // should probably use the last baseline of the last item or something.
+ mLastBaselineFromLastReflow =
+ desiredSizeInFlexWM.BSize(flexWM) - flexContainerAscent;
+ }
+
+ // Convert flex container's final desired size to parent's WM, for outparam.
+ aDesiredSize.SetSize(flexWM, desiredSizeInFlexWM);
+
+ // Overflow area = union(my overflow area, kids' overflow areas)
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ for (nsIFrame* childFrame : mFrames) {
+ ConsiderChildOverflow(aDesiredSize.mOverflowAreas, childFrame);
+ }
+
+ FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
+ aReflowInput, aStatus);
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize)
+}
+
+void
+nsFlexContainerFrame::MoveFlexItemToFinalPosition(
+ const ReflowInput& aReflowInput,
+ const FlexItem& aItem,
+ LogicalPoint& aFramePos,
+ const nsSize& aContainerSize)
+{
+ WritingMode outerWM = aReflowInput.GetWritingMode();
+
+ // If item is relpos, look up its offsets (cached from prev reflow)
+ LogicalMargin logicalOffsets(outerWM);
+ if (NS_STYLE_POSITION_RELATIVE == aItem.Frame()->StyleDisplay()->mPosition) {
+ FrameProperties props = aItem.Frame()->Properties();
+ nsMargin* cachedOffsets = props.Get(nsIFrame::ComputedOffsetProperty());
+ MOZ_ASSERT(cachedOffsets,
+ "relpos previously-reflowed frame should've cached its offsets");
+ logicalOffsets = LogicalMargin(outerWM, *cachedOffsets);
+ }
+ ReflowInput::ApplyRelativePositioning(aItem.Frame(), outerWM,
+ logicalOffsets, &aFramePos,
+ aContainerSize);
+ aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerSize);
+ PositionFrameView(aItem.Frame());
+ PositionChildViews(aItem.Frame());
+}
+
+void
+nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext,
+ const FlexboxAxisTracker& aAxisTracker,
+ const ReflowInput& aReflowInput,
+ const FlexItem& aItem,
+ LogicalPoint& aFramePos,
+ const nsSize& aContainerSize)
+{
+ WritingMode outerWM = aReflowInput.GetWritingMode();
+ WritingMode wm = aItem.Frame()->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(wm);
+ availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+ ReflowInput childReflowInput(aPresContext, aReflowInput,
+ aItem.Frame(), availSize);
+
+ // Keep track of whether we've overriden the child's computed height
+ // and/or width, so we can set its resize flags accordingly.
+ bool didOverrideComputedWidth = false;
+ bool didOverrideComputedHeight = false;
+
+ // Override computed main-size
+ if (aAxisTracker.IsMainAxisHorizontal()) {
+ childReflowInput.SetComputedWidth(aItem.GetMainSize());
+ didOverrideComputedWidth = true;
+ } else {
+ childReflowInput.SetComputedHeight(aItem.GetMainSize());
+ didOverrideComputedHeight = true;
+ }
+
+ // Override reflow state's computed cross-size if either:
+ // - the item was stretched (in which case we're imposing a cross size)
+ // ...or...
+ // - the item it has an aspect ratio (in which case the cross-size that's
+ // currently in the reflow state is based on arithmetic involving a stale
+ // main-size value that we just stomped on above). (Note that we could handle
+ // this case using an AutoFlexItemMainSizeOverride, as we do elsewhere; but
+ // given that we *already know* the correct cross size to use here, it's
+ // cheaper to just directly set it instead of setting a frame property.)
+ if (aItem.IsStretched() ||
+ aItem.HasIntrinsicRatio()) {
+ if (aAxisTracker.IsCrossAxisHorizontal()) {
+ childReflowInput.SetComputedWidth(aItem.GetCrossSize());
+ didOverrideComputedWidth = true;
+ } else {
+ childReflowInput.SetComputedHeight(aItem.GetCrossSize());
+ didOverrideComputedHeight = true;
+ }
+ }
+ if (aItem.IsStretched() && !aAxisTracker.IsCrossAxisHorizontal()) {
+ // If this item's height is stretched, it's a relative height.
+ aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ }
+
+ // XXXdholbert Might need to actually set the correct margins in the
+ // reflow state at some point, so that they can be saved on the frame for
+ // UsedMarginProperty(). Maybe doesn't matter though...?
+
+ // If we're overriding the computed width or height, *and* we had an
+ // earlier "measuring" reflow, then this upcoming reflow needs to be
+ // treated as a resize.
+ if (aItem.HadMeasuringReflow()) {
+ if (didOverrideComputedWidth) {
+ // (This is somewhat redundant, since the reflow state already
+ // sets mHResize whenever our computed width has changed since the
+ // previous reflow. Still, it's nice for symmetry, and it may become
+ // necessary once we support orthogonal flows.)
+ childReflowInput.SetHResize(true);
+ }
+ if (didOverrideComputedHeight) {
+ childReflowInput.SetVResize(true);
+ }
+ }
+ // NOTE: Be very careful about doing anything else with childReflowInput
+ // after this point, because some of its methods (e.g. SetComputedWidth)
+ // internally call InitResizeFlags and stomp on mVResize & mHResize.
+
+ ReflowOutput childDesiredSize(childReflowInput);
+ nsReflowStatus childReflowStatus;
+ ReflowChild(aItem.Frame(), aPresContext,
+ childDesiredSize, childReflowInput,
+ outerWM, aFramePos, aContainerSize,
+ 0, childReflowStatus);
+
+ // XXXdholbert Once we do pagination / splitting, we'll need to actually
+ // handle incomplete childReflowStatuses. But for now, we give our kids
+ // unconstrained available height, which means they should always
+ // complete.
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
+ "We gave flex item unconstrained available height, so it "
+ "should be complete");
+
+ LogicalMargin offsets =
+ childReflowInput.ComputedLogicalOffsets().ConvertTo(outerWM, wm);
+ ReflowInput::ApplyRelativePositioning(aItem.Frame(), outerWM,
+ offsets, &aFramePos,
+ aContainerSize);
+
+ FinishReflowChild(aItem.Frame(), aPresContext,
+ childDesiredSize, &childReflowInput,
+ outerWM, aFramePos, aContainerSize, 0);
+
+ aItem.SetAscent(childDesiredSize.BlockStartAscent());
+}
+
+void
+nsFlexContainerFrame::ReflowPlaceholders(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsTArray<nsIFrame*>& aPlaceholders,
+ const LogicalPoint& aContentBoxOrigin,
+ const nsSize& aContainerSize)
+{
+ WritingMode outerWM = aReflowInput.GetWritingMode();
+
+ // As noted in this method's documentation, we'll reflow every entry in
+ // |aPlaceholders| at the container's content-box origin.
+ for (nsIFrame* placeholder : aPlaceholders) {
+ MOZ_ASSERT(placeholder->GetType() == nsGkAtoms::placeholderFrame,
+ "placeholders array should only contain placeholder frames");
+ WritingMode wm = placeholder->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(wm);
+ ReflowInput childReflowInput(aPresContext, aReflowInput,
+ placeholder, availSize);
+ ReflowOutput childDesiredSize(childReflowInput);
+ nsReflowStatus childReflowStatus;
+ ReflowChild(placeholder, aPresContext,
+ childDesiredSize, childReflowInput,
+ outerWM, aContentBoxOrigin, aContainerSize, 0,
+ childReflowStatus);
+
+ FinishReflowChild(placeholder, aPresContext,
+ childDesiredSize, &childReflowInput,
+ outerWM, aContentBoxOrigin, aContainerSize, 0);
+
+ // Mark the placeholder frame to indicate that it's not actually at the
+ // element's static position, because we need to apply CSS Alignment after
+ // we determine the OOF's size:
+ placeholder->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
+ }
+}
+
+/* virtual */ nscoord
+nsFlexContainerFrame::GetMinISize(nsRenderingContext* aRenderingContext)
+{
+ nscoord minISize = 0;
+ DISPLAY_MIN_WIDTH(this, minISize);
+
+ RenumberList();
+
+ const nsStylePosition* stylePos = StylePosition();
+ const FlexboxAxisTracker axisTracker(this, GetWritingMode());
+
+ for (nsIFrame* childFrame : mFrames) {
+ nscoord childMinISize =
+ nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+ nsLayoutUtils::MIN_ISIZE);
+ // For a horizontal single-line flex container, the intrinsic min
+ // isize is the sum of its items' min isizes.
+ // For a column-oriented flex container, or for a multi-line row-
+ // oriented flex container, the intrinsic min isize is the max of
+ // its items' min isizes.
+ if (axisTracker.IsRowOriented() &&
+ NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap) {
+ minISize += childMinISize;
+ } else {
+ minISize = std::max(minISize, childMinISize);
+ }
+ }
+ return minISize;
+}
+
+/* virtual */ nscoord
+nsFlexContainerFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
+{
+ nscoord prefISize = 0;
+ DISPLAY_PREF_WIDTH(this, prefISize);
+
+ RenumberList();
+
+ // XXXdholbert Optimization: We could cache our intrinsic widths like
+ // nsBlockFrame does (and return it early from this function if it's set).
+ // Whenever anything happens that might change it, set it to
+ // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicISizesDirty
+ // does)
+ const FlexboxAxisTracker axisTracker(this, GetWritingMode());
+
+ for (nsIFrame* childFrame : mFrames) {
+ nscoord childPrefISize =
+ nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+ nsLayoutUtils::PREF_ISIZE);
+ if (axisTracker.IsRowOriented()) {
+ prefISize += childPrefISize;
+ } else {
+ prefISize = std::max(prefISize, childPrefISize);
+ }
+ }
+ return prefISize;
+}
diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h
new file mode 100644
index 000000000..22b420d85
--- /dev/null
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: flex" and "display: -webkit-box" */
+
+#ifndef nsFlexContainerFrame_h___
+#define nsFlexContainerFrame_h___
+
+#include "nsContainerFrame.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+template <class T> class LinkedList;
+class LogicalPoint;
+} // namespace mozilla
+
+nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+
+/**
+ * This is the rendering object used for laying out elements with
+ * "display: flex" or "display: inline-flex".
+ *
+ * We also use this class for elements with "display: -webkit-box" or
+ * "display: -webkit-inline-box" (but not "-moz-box" / "-moz-inline-box" --
+ * those are rendered with old-school XUL frame classes).
+ *
+ * Note: we represent the -webkit-box family of properties (-webkit-box-orient,
+ * -webkit-box-flex, etc.) as aliases for their -moz equivalents. And for
+ * -webkit-{inline-}box containers, nsFlexContainerFrame will honor those
+ * "legacy" properties for alignment/flexibility/etc. *instead of* honoring the
+ * modern flexbox & alignment properties. For brevity, many comments in
+ * nsFlexContainerFrame.cpp simply refer to these properties using their
+ * "-webkit" versions, since we're mostly expecting to encounter them in that
+ * form. (Technically, the "-moz" versions of these properties *can* influence
+ * layout here as well (since that's what the -webkit versions are aliased to)
+ * -- but only inside of a "display:-webkit-{inline-}box" container.)
+ */
+class nsFlexContainerFrame : public nsContainerFrame {
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsFlexContainerFrame)
+ NS_DECL_QUERYFRAME
+
+ // Factory method:
+ friend nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+ // Forward-decls of helper classes
+ class FlexItem;
+ class FlexLine;
+ class FlexboxAxisTracker;
+ struct StrutInfo;
+
+ // nsIFrame overrides
+ void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nscoord
+ GetMinISize(nsRenderingContext* aRenderingContext) override;
+ virtual nscoord
+ GetPrefISize(nsRenderingContext* aRenderingContext) override;
+
+ virtual nsIAtom* GetType() const override;
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override;
+
+ bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+ nscoord* aBaseline) const override
+ {
+ return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
+ }
+
+ bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ nscoord* aBaseline) const override
+ {
+ if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+ return false;
+ }
+ *aBaseline = aBaselineGroup == BaselineSharingGroup::eFirst ?
+ mBaselineFromLastReflow : mLastBaselineFromLastReflow;
+ return true;
+ }
+
+ // nsContainerFrame overrides
+ uint16_t CSSAlignmentForAbsPosChild(
+ const ReflowInput& aChildRI,
+ mozilla::LogicalAxis aLogicalAxis) const override;
+
+ // Flexbox-specific public methods
+ bool IsHorizontal();
+
+ /**
+ * Helper function to calculate packing space and initial offset of alignment
+ * subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for
+ * space-between, space-around, and space-evenly.
+ *
+ * @param aNumThingsToPack Number of alignment subjects.
+ * @param aAlignVal Value for align-self or justify-self.
+ * @param aFirstSubjectOffset Outparam for first subject offset.
+ * @param aNumPackingSpacesRemaining Outparam for number of equal-sized
+ * packing spaces to apply between each
+ * alignment subject.
+ * @param aPackingSpaceRemaining Outparam for total amount of packing
+ * space to be divided up.
+ */
+ static void CalculatePackingSpace(uint32_t aNumThingsToPack,
+ uint8_t aAlignVal,
+ nscoord* aFirstSubjectOffset,
+ uint32_t* aNumPackingSpacesRemaining,
+ nscoord* aPackingSpaceRemaining);
+
+protected:
+ // Protected constructor & destructor
+ explicit nsFlexContainerFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+ , mBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
+ , mLastBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
+ {}
+ virtual ~nsFlexContainerFrame();
+
+ /*
+ * This method does the bulk of the flex layout, implementing the algorithm
+ * described at:
+ * http://dev.w3.org/csswg/css-flexbox/#layout-algorithm
+ * (with a few initialization pieces happening in the caller, Reflow().
+ *
+ * Since this is a helper for Reflow(), this takes all the same parameters
+ * as Reflow(), plus a few more parameters that Reflow() sets up for us.
+ *
+ * (The logic behind the division of work between Reflow and DoFlexLayout is
+ * as follows: DoFlexLayout() begins at the step that we have to jump back
+ * to, if we find any visibility:collapse children, and Reflow() does
+ * everything before that point.)
+ */
+ void DoFlexLayout(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ nscoord aContentBoxMainSize,
+ nscoord aAvailableBSizeForContent,
+ nsTArray<StrutInfo>& aStruts,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ /**
+ * Checks whether our child-frame list "mFrames" is sorted, using the given
+ * IsLessThanOrEqual function, and sorts it if it's not already sorted.
+ *
+ * XXXdholbert Once we support pagination, we need to make this function
+ * check our continuations as well (or wrap it in a function that does).
+ *
+ * @return true if we had to sort mFrames, false if it was already sorted.
+ */
+ template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+ bool SortChildrenIfNeeded();
+
+ // Protected flex-container-specific methods / member-vars
+#ifdef DEBUG
+ void SanityCheckAnonymousFlexItems() const;
+#endif // DEBUG
+
+ /*
+ * Returns a new FlexItem for the given child frame, allocated on the heap.
+ * Guaranteed to return non-null. Caller is responsible for managing the
+ * FlexItem's lifetime.
+ *
+ * Before returning, this method also processes the FlexItem to resolve its
+ * flex basis (including e.g. auto-height) as well as to resolve
+ * "min-height:auto", via ResolveAutoFlexBasisAndMinSize(). (Basically, the
+ * returned FlexItem will be ready to participate in the "Resolve the
+ * Flexible Lengths" step of the Flex Layout Algorithm.)
+ */
+ mozilla::UniquePtr<FlexItem> GenerateFlexItemForChild(nsPresContext* aPresContext,
+ nsIFrame* aChildFrame,
+ const ReflowInput& aParentReflowInput,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ /**
+ * This method performs a "measuring" reflow to get the content height of
+ * aFlexItem.Frame() (treating it as if it had auto-height), & returns the
+ * resulting height.
+ * (Helper for ResolveAutoFlexBasisAndMinSize().)
+ */
+ nscoord MeasureFlexItemContentHeight(nsPresContext* aPresContext,
+ FlexItem& aFlexItem,
+ bool aForceVerticalResizeForMeasuringReflow,
+ const ReflowInput& aParentReflowInput);
+
+ /**
+ * This method resolves an "auto" flex-basis and/or min-main-size value
+ * on aFlexItem, if needed.
+ * (Helper for GenerateFlexItemForChild().)
+ */
+ void ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext,
+ FlexItem& aFlexItem,
+ const ReflowInput& aItemReflowInput,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ /**
+ * This method:
+ * - Creates FlexItems for all of our child frames (except placeholders).
+ * - Groups those FlexItems into FlexLines.
+ * - Returns those FlexLines in the outparam |aLines|.
+ *
+ * For any child frames which are placeholders, this method will instead just
+ * append that child to the outparam |aPlaceholders| for separate handling.
+ * (Absolutely positioned children of a flex container are *not* flex items.)
+ */
+ void GenerateFlexLines(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nscoord aContentBoxMainSize,
+ nscoord aAvailableBSizeForContent,
+ const nsTArray<StrutInfo>& aStruts,
+ const FlexboxAxisTracker& aAxisTracker,
+ nsTArray<nsIFrame*>& aPlaceholders,
+ mozilla::LinkedList<FlexLine>& aLines);
+
+ nscoord GetMainSizeFromReflowInput(const ReflowInput& aReflowInput,
+ const FlexboxAxisTracker& aAxisTracker);
+
+ nscoord ComputeCrossSize(const ReflowInput& aReflowInput,
+ const FlexboxAxisTracker& aAxisTracker,
+ nscoord aSumLineCrossSizes,
+ nscoord aAvailableBSizeForContent,
+ bool* aIsDefinite,
+ nsReflowStatus& aStatus);
+
+ void SizeItemInCrossAxis(nsPresContext* aPresContext,
+ const FlexboxAxisTracker& aAxisTracker,
+ ReflowInput& aChildReflowInput,
+ FlexItem& aItem);
+
+ /**
+ * Moves the given flex item's frame to the given LogicalPosition (modulo any
+ * relative positioning).
+ *
+ * This can be used in cases where we've already done a "measuring reflow"
+ * for the flex item at the correct size, and hence can skip its final reflow
+ * (but still need to move it to the right final position).
+ *
+ * @param aReflowInput The flex container's reflow state.
+ * @param aItem The flex item whose frame should be moved.
+ * @param aFramePos The position where the flex item's frame should
+ * be placed. (pre-relative positioning)
+ * @param aContainerSize The flex container's size (required by some methods
+ * that we call, to interpret aFramePos correctly).
+ */
+ void MoveFlexItemToFinalPosition(const ReflowInput& aReflowInput,
+ const FlexItem& aItem,
+ mozilla::LogicalPoint& aFramePos,
+ const nsSize& aContainerSize);
+ /**
+ * Helper-function to reflow a child frame, at its final position determined
+ * by flex layout.
+ *
+ * @param aPresContext The presentation context being used in reflow.
+ * @param aAxisTracker A FlexboxAxisTracker with the flex container's axes.
+ * @param aReflowInput The flex container's reflow state.
+ * @param aItem The flex item to be reflowed.
+ * @param aFramePos The position where the flex item's frame should
+ * be placed. (pre-relative positioning)
+ * @param aContainerSize The flex container's size (required by some methods
+ * that we call, to interpret aFramePos correctly).
+ */
+ void ReflowFlexItem(nsPresContext* aPresContext,
+ const FlexboxAxisTracker& aAxisTracker,
+ const ReflowInput& aReflowInput,
+ const FlexItem& aItem,
+ mozilla::LogicalPoint& aFramePos,
+ const nsSize& aContainerSize);
+
+ /**
+ * Helper-function to perform a "dummy reflow" on all our nsPlaceholderFrame
+ * children, at the container's content-box origin.
+ *
+ * This doesn't actually represent the static position of the placeholders'
+ * out-of-flow (OOF) frames -- we can't compute that until we've reflowed the
+ * OOF, because (depending on the CSS Align properties) the static position
+ * may be influenced by the OOF's size. So for now, we just co-opt the
+ * placeholder to store the flex container's logical content-box origin, and
+ * we defer to nsAbsoluteContainingBlock to determine the OOF's actual static
+ * position (using this origin, the OOF's size, and the CSS Align
+ * properties).
+ *
+ * @param aPresContext The presentation context being used in reflow.
+ * @param aReflowInput The flex container's reflow input.
+ * @param aPlaceholders An array of all the flex container's
+ * nsPlaceholderFrame children.
+ * @param aContentBoxOrigin The flex container's logical content-box
+ * origin (in its own coordinate space).
+ * @param aContainerSize The flex container's size (required by some
+ * reflow methods to interpret positions correctly).
+ */
+ void ReflowPlaceholders(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsTArray<nsIFrame*>& aPlaceholders,
+ const mozilla::LogicalPoint& aContentBoxOrigin,
+ const nsSize& aContainerSize);
+
+ bool mChildrenHaveBeenReordered; // Have we ever had to reorder our kids
+ // to satisfy their 'order' values?
+
+ nscoord mBaselineFromLastReflow;
+ // Note: the last baseline is a distance from our border-box end edge.
+ nscoord mLastBaselineFromLastReflow;
+};
+
+#endif /* nsFlexContainerFrame_h___ */
diff --git a/layout/generic/nsFloatManager.cpp b/layout/generic/nsFloatManager.cpp
new file mode 100644
index 000000000..2c0ff1f38
--- /dev/null
+++ b/layout/generic/nsFloatManager.cpp
@@ -0,0 +1,603 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* class that manages rules for positioning floats */
+
+#include "nsFloatManager.h"
+
+#include <algorithm>
+
+#include "mozilla/ReflowInput.h"
+#include "nsBlockFrame.h"
+#include "nsError.h"
+#include "nsIPresShell.h"
+#include "nsMemory.h"
+
+using namespace mozilla;
+
+int32_t nsFloatManager::sCachedFloatManagerCount = 0;
+void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
+
+/////////////////////////////////////////////////////////////////////////////
+
+// PresShell Arena allocate callback (for nsIntervalSet use below)
+static void*
+PSArenaAllocCB(size_t aSize, void* aClosure)
+{
+ return static_cast<nsIPresShell*>(aClosure)->AllocateMisc(aSize);
+}
+
+// PresShell Arena free callback (for nsIntervalSet use below)
+static void
+PSArenaFreeCB(size_t aSize, void* aPtr, void* aClosure)
+{
+ static_cast<nsIPresShell*>(aClosure)->FreeMisc(aSize, aPtr);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// nsFloatManager
+
+nsFloatManager::nsFloatManager(nsIPresShell* aPresShell,
+ mozilla::WritingMode aWM)
+ :
+#ifdef DEBUG
+ mWritingMode(aWM),
+#endif
+ mLineLeft(0), mBlockStart(0),
+ mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell),
+ mPushedLeftFloatPastBreak(false),
+ mPushedRightFloatPastBreak(false),
+ mSplitLeftFloatAcrossBreak(false),
+ mSplitRightFloatAcrossBreak(false)
+{
+ MOZ_COUNT_CTOR(nsFloatManager);
+}
+
+nsFloatManager::~nsFloatManager()
+{
+ MOZ_COUNT_DTOR(nsFloatManager);
+}
+
+// static
+void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW
+{
+ if (sCachedFloatManagerCount > 0) {
+ // We have cached unused instances of this class, return a cached
+ // instance in stead of always creating a new one.
+ return sCachedFloatManagers[--sCachedFloatManagerCount];
+ }
+
+ // The cache is empty, this means we have to create a new instance using
+ // the global |operator new|.
+ return moz_xmalloc(aSize);
+}
+
+void
+nsFloatManager::operator delete(void* aPtr, size_t aSize)
+{
+ if (!aPtr)
+ return;
+ // This float manager is no longer used, if there's still room in
+ // the cache we'll cache this float manager, unless the layout
+ // module was already shut down.
+
+ if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE &&
+ sCachedFloatManagerCount >= 0) {
+ // There's still space in the cache for more instances, put this
+ // instance in the cache in stead of deleting it.
+
+ sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr;
+ return;
+ }
+
+ // The cache is full, or the layout module has been shut down,
+ // delete this float manager.
+ free(aPtr);
+}
+
+
+/* static */
+void nsFloatManager::Shutdown()
+{
+ // The layout module is being shut down, clean up the cache and
+ // disable further caching.
+
+ int32_t i;
+
+ for (i = 0; i < sCachedFloatManagerCount; i++) {
+ void* floatManager = sCachedFloatManagers[i];
+ if (floatManager)
+ free(floatManager);
+ }
+
+ // Disable further caching.
+ sCachedFloatManagerCount = -1;
+}
+
+#define CHECK_BLOCK_DIR(aWM) \
+ NS_ASSERTION((aWM).GetBlockDir() == mWritingMode.GetBlockDir(), \
+ "incompatible writing modes")
+
+nsFlowAreaRect
+nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBOffset,
+ BandInfoType aInfoType, nscoord aBSize,
+ LogicalRect aContentArea, SavedState* aState,
+ const nsSize& aContainerSize) const
+{
+ CHECK_BLOCK_DIR(aWM);
+ NS_ASSERTION(aBSize >= 0, "unexpected max block size");
+ NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
+ "unexpected content area inline size");
+
+ nscoord blockStart = aBOffset + mBlockStart;
+ if (blockStart < nscoord_MIN) {
+ NS_WARNING("bad value");
+ blockStart = nscoord_MIN;
+ }
+
+ // Determine the last float that we should consider.
+ uint32_t floatCount;
+ if (aState) {
+ // Use the provided state.
+ floatCount = aState->mFloatInfoCount;
+ MOZ_ASSERT(floatCount <= mFloats.Length(), "bad state");
+ } else {
+ // Use our current state.
+ floatCount = mFloats.Length();
+ }
+
+ // If there are no floats at all, or we're below the last one, return
+ // quickly.
+ if (floatCount == 0 ||
+ (mFloats[floatCount-1].mLeftBEnd <= blockStart &&
+ mFloats[floatCount-1].mRightBEnd <= blockStart)) {
+ return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBOffset,
+ aContentArea.ISize(aWM), aBSize, false);
+ }
+
+ nscoord blockEnd;
+ if (aBSize == nscoord_MAX) {
+ // This warning (and the two below) are possible to hit on pages
+ // with really large objects.
+ NS_WARNING_ASSERTION(aInfoType == BAND_FROM_POINT, "bad height");
+ blockEnd = nscoord_MAX;
+ } else {
+ blockEnd = blockStart + aBSize;
+ if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
+ NS_WARNING("bad value");
+ blockEnd = nscoord_MAX;
+ }
+ }
+ nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize);
+ nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize);
+ if (lineRight < lineLeft) {
+ NS_WARNING("bad value");
+ lineRight = lineLeft;
+ }
+
+ // Walk backwards through the floats until we either hit the front of
+ // the list or we're above |blockStart|.
+ bool haveFloats = false;
+ for (uint32_t i = floatCount; i > 0; --i) {
+ const FloatInfo &fi = mFloats[i-1];
+ if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) {
+ // There aren't any more floats that could intersect this band.
+ break;
+ }
+ if (fi.IsEmpty()) {
+ // For compatibility, ignore floats with empty rects, even though it
+ // disagrees with the spec. (We might want to fix this in the
+ // future, though.)
+ continue;
+ }
+
+ nscoord floatBStart = fi.BStart();
+ nscoord floatBEnd = fi.BEnd();
+ if (blockStart < floatBStart && aInfoType == BAND_FROM_POINT) {
+ // This float is below our band. Shrink our band's height if needed.
+ if (floatBStart < blockEnd) {
+ blockEnd = floatBStart;
+ }
+ }
+ // If blockStart == blockEnd (which happens only with WIDTH_WITHIN_HEIGHT),
+ // we include floats that begin at our 0-height vertical area. We
+ // need to to this to satisfy the invariant that a
+ // WIDTH_WITHIN_HEIGHT call is at least as narrow on both sides as a
+ // BAND_WITHIN_POINT call beginning at its blockStart.
+ else if (blockStart < floatBEnd &&
+ (floatBStart < blockEnd ||
+ (floatBStart == blockEnd && blockStart == blockEnd))) {
+ // This float is in our band.
+
+ // Shrink our band's height if needed.
+ if (floatBEnd < blockEnd && aInfoType == BAND_FROM_POINT) {
+ blockEnd = floatBEnd;
+ }
+
+ // Shrink our band's width if needed.
+ StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM);
+ if (floatStyle == StyleFloat::Left) {
+ // A left float
+ nscoord lineRightEdge = fi.LineRight();
+ if (lineRightEdge > lineLeft) {
+ lineLeft = lineRightEdge;
+ // Only set haveFloats to true if the float is inside our
+ // containing block. This matches the spec for what some
+ // callers want and disagrees for other callers, so we should
+ // probably provide better information at some point.
+ haveFloats = true;
+ }
+ } else {
+ // A right float
+ nscoord lineLeftEdge = fi.LineLeft();
+ if (lineLeftEdge < lineRight) {
+ lineRight = lineLeftEdge;
+ // See above.
+ haveFloats = true;
+ }
+ }
+ }
+ }
+
+ nscoord blockSize = (blockEnd == nscoord_MAX) ?
+ nscoord_MAX : (blockEnd - blockStart);
+ // convert back from LineLeft/Right to IStart
+ nscoord inlineStart = aWM.IsBidiLTR()
+ ? lineLeft - mLineLeft
+ : mLineLeft - lineRight +
+ LogicalSize(aWM, aContainerSize).ISize(aWM);
+
+ return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
+ lineRight - lineLeft, blockSize, haveFloats);
+}
+
+nsresult
+nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
+ WritingMode aWM, const nsSize& aContainerSize)
+{
+ CHECK_BLOCK_DIR(aWM);
+ NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
+ NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
+
+ FloatInfo info(aFloatFrame,
+ aMarginRect.LineLeft(aWM, aContainerSize) + mLineLeft,
+ aMarginRect.BStart(aWM) + mBlockStart,
+ aMarginRect.ISize(aWM),
+ aMarginRect.BSize(aWM));
+
+ // Set mLeftBEnd and mRightBEnd.
+ if (HasAnyFloats()) {
+ FloatInfo &tail = mFloats[mFloats.Length() - 1];
+ info.mLeftBEnd = tail.mLeftBEnd;
+ info.mRightBEnd = tail.mRightBEnd;
+ } else {
+ info.mLeftBEnd = nscoord_MIN;
+ info.mRightBEnd = nscoord_MIN;
+ }
+ StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM);
+ MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right,
+ "Unexpected float style!");
+ nscoord& sideBEnd =
+ floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd;
+ nscoord thisBEnd = info.BEnd();
+ if (thisBEnd > sideBEnd)
+ sideBEnd = thisBEnd;
+
+ if (!mFloats.AppendElement(info))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+// static
+LogicalRect
+nsFloatManager::CalculateRegionFor(WritingMode aWM,
+ nsIFrame* aFloat,
+ const LogicalMargin& aMargin,
+ const nsSize& aContainerSize)
+{
+ // We consider relatively positioned frames at their original position.
+ LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(),
+ aFloat->GetSize()),
+ aContainerSize);
+
+ // Float region includes its margin
+ region.Inflate(aWM, aMargin);
+
+ // Don't store rectangles with negative margin-box width or height in
+ // the float manager; it can't deal with them.
+ if (region.ISize(aWM) < 0) {
+ // Preserve the right margin-edge for left floats and the left
+ // margin-edge for right floats
+ const nsStyleDisplay* display = aFloat->StyleDisplay();
+ StyleFloat floatStyle = display->PhysicalFloats(aWM);
+ if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) {
+ region.IStart(aWM) = region.IEnd(aWM);
+ }
+ region.ISize(aWM) = 0;
+ }
+ if (region.BSize(aWM) < 0) {
+ region.BSize(aWM) = 0;
+ }
+ return region;
+}
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(FloatRegionProperty, nsMargin)
+
+LogicalRect
+nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
+ const nsSize& aContainerSize)
+{
+ LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
+ void* storedRegion = aFloat->Properties().Get(FloatRegionProperty());
+ if (storedRegion) {
+ nsMargin margin = *static_cast<nsMargin*>(storedRegion);
+ region.Inflate(aWM, LogicalMargin(aWM, margin));
+ }
+ return region;
+}
+
+void
+nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
+ const LogicalRect& aRegion,
+ const nsSize& aContainerSize)
+{
+ nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
+ nsRect rect = aFloat->GetRect();
+ FrameProperties props = aFloat->Properties();
+ if (region.IsEqualEdges(rect)) {
+ props.Delete(FloatRegionProperty());
+ }
+ else {
+ nsMargin* storedMargin = props.Get(FloatRegionProperty());
+ if (!storedMargin) {
+ storedMargin = new nsMargin();
+ props.Set(FloatRegionProperty(), storedMargin);
+ }
+ *storedMargin = region - rect;
+ }
+}
+
+nsresult
+nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList)
+{
+ if (!aFrameList) {
+ return NS_OK;
+ }
+ // This could be a good bit simpler if we could guarantee that the
+ // floats given were at the end of our list, so we could just search
+ // for the head of aFrameList. (But we can't;
+ // layout/reftests/bugs/421710-1.html crashes.)
+ nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1);
+
+ for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) {
+ frameSet.PutEntry(f);
+ }
+
+ uint32_t newLength = mFloats.Length();
+ while (newLength > 0) {
+ if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) {
+ break;
+ }
+ --newLength;
+ }
+ mFloats.TruncateLength(newLength);
+
+#ifdef DEBUG
+ for (uint32_t i = 0; i < mFloats.Length(); ++i) {
+ NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame),
+ "Frame region deletion was requested but we couldn't delete it");
+ }
+#endif
+
+ return NS_OK;
+}
+
+void
+nsFloatManager::PushState(SavedState* aState)
+{
+ NS_PRECONDITION(aState, "Need a place to save state");
+
+ // This is a cheap push implementation, which
+ // only saves the (x,y) and last frame in the mFrameInfoMap
+ // which is enough info to get us back to where we should be
+ // when pop is called.
+ //
+ // This push/pop mechanism is used to undo any
+ // floats that were added during the unconstrained reflow
+ // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
+ //
+ // It should also be noted that the state for mFloatDamage is
+ // intentionally not saved or restored in PushState() and PopState(),
+ // since that could lead to bugs where damage is missed/dropped when
+ // we move from position A to B (during the intermediate incremental
+ // reflow mentioned above) and then from B to C during the subsequent
+ // reflow. In the typical case A and C will be the same, but not always.
+ // Allowing mFloatDamage to accumulate the damage incurred during both
+ // reflows ensures that nothing gets missed.
+ aState->mLineLeft = mLineLeft;
+ aState->mBlockStart = mBlockStart;
+ aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
+ aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
+ aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak;
+ aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak;
+ aState->mFloatInfoCount = mFloats.Length();
+}
+
+void
+nsFloatManager::PopState(SavedState* aState)
+{
+ NS_PRECONDITION(aState, "No state to restore?");
+
+ mLineLeft = aState->mLineLeft;
+ mBlockStart = aState->mBlockStart;
+ mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
+ mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
+ mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak;
+ mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak;
+
+ NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
+ "somebody misused PushState/PopState");
+ mFloats.TruncateLength(aState->mFloatInfoCount);
+}
+
+nscoord
+nsFloatManager::GetLowestFloatTop() const
+{
+ if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
+ return nscoord_MAX;
+ }
+ if (!HasAnyFloats()) {
+ return nscoord_MIN;
+ }
+ return mFloats[mFloats.Length() -1].BStart() - mBlockStart;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+void
+DebugListFloatManager(const nsFloatManager *aFloatManager)
+{
+ aFloatManager->List(stdout);
+}
+
+nsresult
+nsFloatManager::List(FILE* out) const
+{
+ if (!HasAnyFloats())
+ return NS_OK;
+
+ for (uint32_t i = 0; i < mFloats.Length(); ++i) {
+ const FloatInfo &fi = mFloats[i];
+ fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} BEnd={l:%d, r:%d}\n",
+ i, static_cast<void*>(fi.mFrame),
+ fi.LineLeft(), fi.BStart(), fi.ISize(), fi.BSize(),
+ fi.mLeftBEnd, fi.mRightBEnd);
+ }
+ return NS_OK;
+}
+#endif
+
+nscoord
+nsFloatManager::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
+ uint32_t aFlags) const
+{
+ if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
+ return nscoord_MAX;
+ }
+ if (!HasAnyFloats()) {
+ return aBCoord;
+ }
+
+ nscoord blockEnd = aBCoord + mBlockStart;
+
+ const FloatInfo &tail = mFloats[mFloats.Length() - 1];
+ switch (aBreakType) {
+ case StyleClear::Both:
+ blockEnd = std::max(blockEnd, tail.mLeftBEnd);
+ blockEnd = std::max(blockEnd, tail.mRightBEnd);
+ break;
+ case StyleClear::Left:
+ blockEnd = std::max(blockEnd, tail.mLeftBEnd);
+ break;
+ case StyleClear::Right:
+ blockEnd = std::max(blockEnd, tail.mRightBEnd);
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+
+ blockEnd -= mBlockStart;
+
+ return blockEnd;
+}
+
+bool
+nsFloatManager::ClearContinues(StyleClear aBreakType) const
+{
+ return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
+ (aBreakType == StyleClear::Both ||
+ aBreakType == StyleClear::Left)) ||
+ ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
+ (aBreakType == StyleClear::Both ||
+ aBreakType == StyleClear::Right));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// FloatInfo
+
+nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
+ nscoord aLineLeft, nscoord aBStart,
+ nscoord aISize, nscoord aBSize)
+ : mFrame(aFrame)
+ , mRect(aLineLeft, aBStart, aISize, aBSize)
+{
+ MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
+}
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatManager::FloatInfo::FloatInfo(const FloatInfo& aOther)
+ : mFrame(aOther.mFrame),
+ mLeftBEnd(aOther.mLeftBEnd),
+ mRightBEnd(aOther.mRightBEnd),
+ mRect(aOther.mRect)
+{
+ MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
+}
+
+nsFloatManager::FloatInfo::~FloatInfo()
+{
+ MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
+}
+#endif
+
+//----------------------------------------------------------------------
+
+nsAutoFloatManager::~nsAutoFloatManager()
+{
+ // Restore the old float manager in the reflow input if necessary.
+ if (mNew) {
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyFloatManager) {
+ printf("restoring old float manager %p\n", mOld);
+ }
+#endif
+
+ mReflowInput.mFloatManager = mOld;
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyFloatManager) {
+ if (mOld) {
+ mReflowInput.mFrame->ListTag(stdout);
+ printf(": float manager %p after reflow\n", mOld);
+ mOld->List(stdout);
+ }
+ }
+#endif
+
+ delete mNew;
+ }
+}
+
+void
+nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext)
+{
+ // Create a new float manager and install it in the reflow
+ // input. `Remember' the old float manager so we can restore it
+ // later.
+ mNew = new nsFloatManager(aPresContext->PresShell(),
+ mReflowInput.GetWritingMode());
+
+#ifdef DEBUG
+ if (nsBlockFrame::gNoisyFloatManager) {
+ printf("constructed new float manager %p (replacing %p)\n",
+ mNew, mReflowInput.mFloatManager);
+ }
+#endif
+
+ // Set the float manager in the existing reflow input.
+ mOld = mReflowInput.mFloatManager;
+ mReflowInput.mFloatManager = mNew;
+}
diff --git a/layout/generic/nsFloatManager.h b/layout/generic/nsFloatManager.h
new file mode 100644
index 000000000..32c1387e9
--- /dev/null
+++ b/layout/generic/nsFloatManager.h
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* class that manages rules for positioning floats */
+
+#ifndef nsFloatManager_h_
+#define nsFloatManager_h_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/WritingModes.h"
+#include "nsCoord.h"
+#include "nsFrameList.h" // for DEBUG_FRAME_DUMP
+#include "nsIntervalSet.h"
+#include "nsTArray.h"
+
+class nsIPresShell;
+class nsIFrame;
+class nsPresContext;
+namespace mozilla {
+struct ReflowInput;
+} // namespace mozilla
+
+/**
+ * The available space for content not occupied by floats is divided
+ * into a sequence of rectangles in the block direction. However, we
+ * need to know not only the rectangle, but also whether it was reduced
+ * (from the content rectangle) by floats that actually intruded into
+ * the content rectangle.
+ */
+struct nsFlowAreaRect {
+ mozilla::LogicalRect mRect;
+ bool mHasFloats;
+
+ nsFlowAreaRect(mozilla::WritingMode aWritingMode,
+ nscoord aICoord, nscoord aBCoord,
+ nscoord aISize, nscoord aBSize,
+ bool aHasFloats)
+ : mRect(aWritingMode, aICoord, aBCoord, aISize, aBSize)
+ , mHasFloats(aHasFloats) {}
+};
+
+#define NS_FLOAT_MANAGER_CACHE_SIZE 4
+
+class nsFloatManager {
+public:
+ explicit nsFloatManager(nsIPresShell* aPresShell, mozilla::WritingMode aWM);
+ ~nsFloatManager();
+
+ void* operator new(size_t aSize) CPP_THROW_NEW;
+ void operator delete(void* aPtr, size_t aSize);
+
+ static void Shutdown();
+
+ /**
+ * Get float region stored on the frame. (Defaults to mRect if it's
+ * not there.) The float region is the area impacted by this float;
+ * the coordinates are relative to the containing block frame.
+ */
+ static mozilla::LogicalRect GetRegionFor(mozilla::WritingMode aWM,
+ nsIFrame* aFloatFrame,
+ const nsSize& aContainerSize);
+ /**
+ * Calculate the float region for this frame using aMargin and the
+ * frame's mRect. The region includes the margins around the float,
+ * but doesn't include the relative offsets.
+ * Note that if the frame is or has a continuation, aMargin's top
+ * and/or bottom must be zeroed by the caller.
+ */
+ static mozilla::LogicalRect CalculateRegionFor(
+ mozilla::WritingMode aWM,
+ nsIFrame* aFloatFrame,
+ const mozilla::LogicalMargin& aMargin,
+ const nsSize& aContainerSize);
+ /**
+ * Store the float region on the frame. The region is stored
+ * as a delta against the mRect, so repositioning the frame will
+ * also reposition the float region.
+ */
+ static void StoreRegionFor(mozilla::WritingMode aWM,
+ nsIFrame* aFloat,
+ const mozilla::LogicalRect& aRegion,
+ const nsSize& aContainerSize);
+
+ // Structure that stores the current state of a frame manager for
+ // Save/Restore purposes.
+ struct SavedState {
+ explicit SavedState() {}
+ private:
+ uint32_t mFloatInfoCount;
+ nscoord mLineLeft, mBlockStart;
+ bool mPushedLeftFloatPastBreak;
+ bool mPushedRightFloatPastBreak;
+ bool mSplitLeftFloatAcrossBreak;
+ bool mSplitRightFloatAcrossBreak;
+
+ friend class nsFloatManager;
+ };
+
+ /**
+ * Translate the current origin by the specified offsets. This
+ * creates a new local coordinate space relative to the current
+ * coordinate space.
+ */
+ void Translate(nscoord aLineLeft, nscoord aBlockStart)
+ {
+ mLineLeft += aLineLeft;
+ mBlockStart += aBlockStart;
+ }
+
+ /**
+ * Returns the current translation from local coordinate space to
+ * world coordinate space. This represents the accumulated calls to
+ * Translate().
+ */
+ void GetTranslation(nscoord& aLineLeft, nscoord& aBlockStart) const
+ {
+ aLineLeft = mLineLeft;
+ aBlockStart = mBlockStart;
+ }
+
+ /**
+ * Get information about the area available to content that flows
+ * around floats. Two different types of space can be requested:
+ * BAND_FROM_POINT: returns the band containing block-dir coordinate
+ * |aBCoord| (though actually with the top truncated to begin at
+ * aBCoord), but up to at most |aBSize| (which may be nscoord_MAX).
+ * This will return the tallest rectangle whose block start is
+ * |aBCoord| and in which there are no changes in what floats are
+ * on the sides of that rectangle, but will limit the block size
+ * of the rectangle to |aBSize|. The inline start and end edges
+ * of the rectangle give the area available for line boxes in that
+ * space. The inline size of this resulting rectangle will not be
+ * negative.
+ * WIDTH_WITHIN_HEIGHT: This returns a rectangle whose block start
+ * is aBCoord and whose block size is exactly aBSize. Its inline
+ * start and end edges give the corresponding edges of the space
+ * that can be used for line boxes *throughout* that space. (It
+ * is possible that more inline space could be used in part of the
+ * space if a float begins or ends in it.) The inline size of the
+ * resulting rectangle can be negative.
+ *
+ * @param aBCoord [in] block-dir coordinate for block start of
+ * available space desired
+ * @param aBSize [in] see above
+ * @param aContentArea [in] an nsRect representing the content area
+ * @param aState [in] If null, use the current state, otherwise, do
+ * computation based only on floats present in the given
+ * saved state.
+ * @return An nsFlowAreaRect whose:
+ * mRect is the resulting rectangle for line boxes. It will not
+ * extend beyond aContentArea's inline bounds, but may be
+ * narrower when floats are present.
+ * mBandHasFloats is whether there are floats at the sides of the
+ * return value including those that do not reduce the line box
+ * inline size at all (because they are entirely in the margins)
+ *
+ * aBCoord and aAvailSpace are positioned relative to the current translation
+ */
+ enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT };
+ nsFlowAreaRect GetFlowArea(mozilla::WritingMode aWM,
+ nscoord aBCoord, BandInfoType aInfoType,
+ nscoord aBSize, mozilla::LogicalRect aContentArea,
+ SavedState* aState,
+ const nsSize& aContainerSize) const;
+
+ /**
+ * Add a float that comes after all floats previously added. Its
+ * block start must be even with or below the top of all previous
+ * floats.
+ *
+ * aMarginRect is relative to the current translation. The caller
+ * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0.
+ */
+ nsresult AddFloat(nsIFrame* aFloatFrame,
+ const mozilla::LogicalRect& aMarginRect,
+ mozilla::WritingMode aWM, const nsSize& aContainerSize);
+
+ /**
+ * Notify that we tried to place a float that could not fit at all and
+ * had to be pushed to the next page/column? (If so, we can't place
+ * any more floats in this page/column because of the rule that the
+ * top of a float cannot be above the top of an earlier float. It
+ * also means that any clear needs to continue to the next column.)
+ */
+ void SetPushedLeftFloatPastBreak()
+ { mPushedLeftFloatPastBreak = true; }
+ void SetPushedRightFloatPastBreak()
+ { mPushedRightFloatPastBreak = true; }
+
+ /**
+ * Notify that we split a float, with part of it needing to be pushed
+ * to the next page/column. (This means that any 'clear' needs to
+ * continue to the next page/column.)
+ */
+ void SetSplitLeftFloatAcrossBreak()
+ { mSplitLeftFloatAcrossBreak = true; }
+ void SetSplitRightFloatAcrossBreak()
+ { mSplitRightFloatAcrossBreak = true; }
+
+ /**
+ * Remove the regions associated with this floating frame and its
+ * next-sibling list. Some of the frames may never have been added;
+ * we just skip those. This is not fully general; it only works as
+ * long as the N frames to be removed are the last N frames to have
+ * been added; if there's a frame in the middle of them that should
+ * not be removed, YOU LOSE.
+ */
+ nsresult RemoveTrailingRegions(nsIFrame* aFrameList);
+
+ bool HasAnyFloats() const { return !mFloats.IsEmpty(); }
+
+ /**
+ * Methods for dealing with the propagation of float damage during
+ * reflow.
+ */
+ bool HasFloatDamage() const
+ {
+ return !mFloatDamage.IsEmpty();
+ }
+
+ void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd)
+ {
+ mFloatDamage.IncludeInterval(aIntervalBegin + mBlockStart,
+ aIntervalEnd + mBlockStart);
+ }
+
+ bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const
+ {
+ return mFloatDamage.Intersects(aIntervalBegin + mBlockStart,
+ aIntervalEnd + mBlockStart);
+ }
+
+ /**
+ * Saves the current state of the float manager into aState.
+ */
+ void PushState(SavedState* aState);
+
+ /**
+ * Restores the float manager to the saved state.
+ *
+ * These states must be managed using stack discipline. PopState can only
+ * be used after PushState has been used to save the state, and it can only
+ * be used once --- although it can be omitted; saved states can be ignored.
+ * States must be popped in the reverse order they were pushed. A
+ * call to PopState invalidates any saved states Pushed after the
+ * state passed to PopState was pushed.
+ */
+ void PopState(SavedState* aState);
+
+ /**
+ * Get the block start of the last float placed into the float
+ * manager, to enforce the rule that a float can't be above an earlier
+ * float. Returns the minimum nscoord value if there are no floats.
+ *
+ * The result is relative to the current translation.
+ */
+ nscoord GetLowestFloatTop() const;
+
+ /**
+ * Return the coordinate of the lowest float matching aBreakType in
+ * this float manager. Returns aBCoord if there are no matching
+ * floats.
+ *
+ * Both aBCoord and the result are relative to the current translation.
+ */
+ enum {
+ // Tell ClearFloats not to push to nscoord_MAX when floats have been
+ // pushed to the next page/column.
+ DONT_CLEAR_PUSHED_FLOATS = (1<<0)
+ };
+ nscoord ClearFloats(nscoord aBCoord, mozilla::StyleClear aBreakType,
+ uint32_t aFlags = 0) const;
+
+ /**
+ * Checks if clear would pass into the floats' BFC's next-in-flow,
+ * i.e. whether floats affecting this clear have continuations.
+ */
+ bool ClearContinues(mozilla::StyleClear aBreakType) const;
+
+ void AssertStateMatches(SavedState *aState) const
+ {
+ NS_ASSERTION(aState->mLineLeft == mLineLeft &&
+ aState->mBlockStart == mBlockStart &&
+ aState->mPushedLeftFloatPastBreak ==
+ mPushedLeftFloatPastBreak &&
+ aState->mPushedRightFloatPastBreak ==
+ mPushedRightFloatPastBreak &&
+ aState->mSplitLeftFloatAcrossBreak ==
+ mSplitLeftFloatAcrossBreak &&
+ aState->mSplitRightFloatAcrossBreak ==
+ mSplitRightFloatAcrossBreak &&
+ aState->mFloatInfoCount == mFloats.Length(),
+ "float manager state should match saved state");
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ /**
+ * Dump the state of the float manager out to a file.
+ */
+ nsresult List(FILE* out) const;
+#endif
+
+private:
+
+ struct FloatInfo {
+ nsIFrame *const mFrame;
+ // The lowest block-ends of left/right floats up to and including
+ // this one.
+ nscoord mLeftBEnd, mRightBEnd;
+
+ FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBStart,
+ nscoord aISize, nscoord aBSize);
+
+ nscoord LineLeft() const { return mRect.x; }
+ nscoord LineRight() const { return mRect.XMost(); }
+ nscoord ISize() const { return mRect.width; }
+ nscoord BStart() const { return mRect.y; }
+ nscoord BEnd() const { return mRect.YMost(); }
+ nscoord BSize() const { return mRect.height; }
+ bool IsEmpty() const { return mRect.IsEmpty(); }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ FloatInfo(const FloatInfo& aOther);
+ ~FloatInfo();
+#endif
+
+ // NB! This is really a logical rect in a writing mode suitable for
+ // placing floats, which is not necessarily the actual writing mode
+ // either of the block which created the frame manager or the block
+ // that is calling the frame manager. The inline coordinates are in
+ // the line-relative axis of the frame manager and its block
+ // coordinates are in the frame manager's real block direction.
+ nsRect mRect;
+ };
+
+#ifdef DEBUG
+ mozilla::WritingMode mWritingMode;
+#endif
+
+ // Translation from local to global coordinate space.
+ nscoord mLineLeft, mBlockStart;
+ nsTArray<FloatInfo> mFloats;
+ nsIntervalSet mFloatDamage;
+
+ // Did we try to place a float that could not fit at all and had to be
+ // pushed to the next page/column? If so, we can't place any more
+ // floats in this page/column because of the rule that the top of a
+ // float cannot be above the top of an earlier float. And we also
+ // need to apply this information to 'clear', and thus need to
+ // separate left and right floats.
+ bool mPushedLeftFloatPastBreak;
+ bool mPushedRightFloatPastBreak;
+
+ // Did we split a float, with part of it needing to be pushed to the
+ // next page/column. This means that any 'clear' needs to continue to
+ // the next page/column.
+ bool mSplitLeftFloatAcrossBreak;
+ bool mSplitRightFloatAcrossBreak;
+
+ static int32_t sCachedFloatManagerCount;
+ static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
+
+ nsFloatManager(const nsFloatManager&) = delete;
+ void operator=(const nsFloatManager&) = delete;
+};
+
+/**
+ * A helper class to manage maintenance of the float manager during
+ * nsBlockFrame::Reflow. It automatically restores the old float
+ * manager in the reflow input when the object goes out of scope.
+ */
+class nsAutoFloatManager {
+ using ReflowInput = mozilla::ReflowInput;
+
+public:
+ explicit nsAutoFloatManager(ReflowInput& aReflowInput)
+ : mReflowInput(aReflowInput),
+ mNew(nullptr),
+ mOld(nullptr) {}
+
+ ~nsAutoFloatManager();
+
+ /**
+ * Create a new float manager for the specified frame. This will
+ * `remember' the old float manager, and install the new float
+ * manager in the reflow input.
+ */
+ void
+ CreateFloatManager(nsPresContext *aPresContext);
+
+protected:
+ ReflowInput &mReflowInput;
+ nsFloatManager *mNew;
+ nsFloatManager *mOld;
+};
+
+#endif /* !defined(nsFloatManager_h_) */
diff --git a/layout/generic/nsFontInflationData.cpp b/layout/generic/nsFontInflationData.cpp
new file mode 100644
index 000000000..9e9a51999
--- /dev/null
+++ b/layout/generic/nsFontInflationData.cpp
@@ -0,0 +1,379 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Per-block-formatting-context manager of font size inflation for pan and zoom UI. */
+
+#include "nsFontInflationData.h"
+#include "FramePropertyTable.h"
+#include "nsTextControlFrame.h"
+#include "nsListControlFrame.h"
+#include "nsComboboxControlFrame.h"
+#include "mozilla/ReflowInput.h"
+#include "nsTextFrameUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(FontInflationDataProperty,
+ nsFontInflationData)
+
+/* static */ nsFontInflationData*
+nsFontInflationData::FindFontInflationDataFor(const nsIFrame *aFrame)
+{
+ // We have one set of font inflation data per block formatting context.
+ const nsIFrame *bfc = FlowRootFor(aFrame);
+ NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
+ "should have found a flow root");
+
+ return bfc->Properties().Get(FontInflationDataProperty());
+}
+
+/* static */ bool
+nsFontInflationData::UpdateFontInflationDataISizeFor(const ReflowInput& aReflowInput)
+{
+ nsIFrame *bfc = aReflowInput.mFrame;
+ NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
+ "should have been given a flow root");
+ FrameProperties bfcProps(bfc->Properties());
+ nsFontInflationData *data = bfcProps.Get(FontInflationDataProperty());
+ bool oldInflationEnabled;
+ nscoord oldNCAISize;
+ if (data) {
+ oldNCAISize = data->mNCAISize;
+ oldInflationEnabled = data->mInflationEnabled;
+ } else {
+ data = new nsFontInflationData(bfc);
+ bfcProps.Set(FontInflationDataProperty(), data);
+ oldNCAISize = -1;
+ oldInflationEnabled = true; /* not relevant */
+ }
+
+ data->UpdateISize(aReflowInput);
+
+ if (oldInflationEnabled != data->mInflationEnabled)
+ return true;
+
+ return oldInflationEnabled &&
+ oldNCAISize != data->mNCAISize;
+}
+
+/* static */ void
+nsFontInflationData::MarkFontInflationDataTextDirty(nsIFrame *aBFCFrame)
+{
+ NS_ASSERTION(aBFCFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
+ "should have been given a flow root");
+
+ FrameProperties bfcProps(aBFCFrame->Properties());
+ nsFontInflationData *data = bfcProps.Get(FontInflationDataProperty());
+ if (data) {
+ data->MarkTextDirty();
+ }
+}
+
+nsFontInflationData::nsFontInflationData(nsIFrame *aBFCFrame)
+ : mBFCFrame(aBFCFrame)
+ , mNCAISize(0)
+ , mTextAmount(0)
+ , mTextThreshold(0)
+ , mInflationEnabled(false)
+ , mTextDirty(true)
+{
+}
+
+/**
+ * Find the closest common ancestor between aFrame1 and aFrame2, except
+ * treating the parent of a frame as the first-in-flow of its parent (so
+ * the result doesn't change when breaking changes).
+ *
+ * aKnownCommonAncestor is a known common ancestor of both.
+ */
+static nsIFrame*
+NearestCommonAncestorFirstInFlow(nsIFrame *aFrame1, nsIFrame *aFrame2,
+ nsIFrame *aKnownCommonAncestor)
+{
+ aFrame1 = aFrame1->FirstInFlow();
+ aFrame2 = aFrame2->FirstInFlow();
+ aKnownCommonAncestor = aKnownCommonAncestor->FirstInFlow();
+
+ AutoTArray<nsIFrame*, 32> ancestors1, ancestors2;
+ for (nsIFrame *f = aFrame1; f != aKnownCommonAncestor;
+ (f = f->GetParent()) && (f = f->FirstInFlow())) {
+ ancestors1.AppendElement(f);
+ }
+ for (nsIFrame *f = aFrame2; f != aKnownCommonAncestor;
+ (f = f->GetParent()) && (f = f->FirstInFlow())) {
+ ancestors2.AppendElement(f);
+ }
+
+ nsIFrame *result = aKnownCommonAncestor;
+ uint32_t i1 = ancestors1.Length(),
+ i2 = ancestors2.Length();
+ while (i1-- != 0 && i2-- != 0) {
+ if (ancestors1[i1] != ancestors2[i2]) {
+ break;
+ }
+ result = ancestors1[i1];
+ }
+
+ return result;
+}
+
+static nscoord
+ComputeDescendantISize(const ReflowInput& aAncestorReflowInput,
+ nsIFrame *aDescendantFrame)
+{
+ nsIFrame *ancestorFrame = aAncestorReflowInput.mFrame->FirstInFlow();
+ if (aDescendantFrame == ancestorFrame) {
+ return aAncestorReflowInput.ComputedISize();
+ }
+
+ AutoTArray<nsIFrame*, 16> frames;
+ for (nsIFrame *f = aDescendantFrame; f != ancestorFrame;
+ f = f->GetParent()->FirstInFlow()) {
+ frames.AppendElement(f);
+ }
+
+ // This ignores the inline-size contributions made by scrollbars, though in
+ // reality we don't have any scrollbars on the sorts of devices on
+ // which we use font inflation, so it's not a problem. But it may
+ // occasionally cause problems when writing tests on desktop.
+
+ uint32_t len = frames.Length();
+ ReflowInput *reflowInputs = static_cast<ReflowInput*>
+ (moz_xmalloc(sizeof(ReflowInput) * len));
+ nsPresContext *presContext = aDescendantFrame->PresContext();
+ for (uint32_t i = 0; i < len; ++i) {
+ const ReflowInput &parentReflowInput =
+ (i == 0) ? aAncestorReflowInput : reflowInputs[i - 1];
+ nsIFrame *frame = frames[len - i - 1];
+ WritingMode wm = frame->GetWritingMode();
+ LogicalSize availSize = parentReflowInput.ComputedSize(wm);
+ availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+ MOZ_ASSERT(frame->GetParent()->FirstInFlow() ==
+ parentReflowInput.mFrame->FirstInFlow(),
+ "bad logic in this function");
+ new (reflowInputs + i) ReflowInput(presContext, parentReflowInput,
+ frame, availSize);
+ }
+
+ MOZ_ASSERT(reflowInputs[len - 1].mFrame == aDescendantFrame,
+ "bad logic in this function");
+ nscoord result = reflowInputs[len - 1].ComputedISize();
+
+ for (uint32_t i = len; i-- != 0; ) {
+ reflowInputs[i].~ReflowInput();
+ }
+ free(reflowInputs);
+
+ return result;
+}
+
+void
+nsFontInflationData::UpdateISize(const ReflowInput &aReflowInput)
+{
+ nsIFrame *bfc = aReflowInput.mFrame;
+ NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
+ "must be block formatting context");
+
+ nsIFrame *firstInflatableDescendant =
+ FindEdgeInflatableFrameIn(bfc, eFromStart);
+ if (!firstInflatableDescendant) {
+ mTextAmount = 0;
+ mTextThreshold = 0; // doesn't matter
+ mTextDirty = false;
+ mInflationEnabled = false;
+ return;
+ }
+ nsIFrame *lastInflatableDescendant =
+ FindEdgeInflatableFrameIn(bfc, eFromEnd);
+ MOZ_ASSERT(!firstInflatableDescendant == !lastInflatableDescendant,
+ "null-ness should match; NearestCommonAncestorFirstInFlow"
+ " will crash when passed null");
+
+ // Particularly when we're computing for the root BFC, the inline-size of
+ // nca might differ significantly for the inline-size of bfc.
+ nsIFrame *nca = NearestCommonAncestorFirstInFlow(firstInflatableDescendant,
+ lastInflatableDescendant,
+ bfc);
+ while (!nca->IsContainerForFontSizeInflation()) {
+ nca = nca->GetParent()->FirstInFlow();
+ }
+
+ nscoord newNCAISize = ComputeDescendantISize(aReflowInput, nca);
+
+ // See comment above "font.size.inflation.lineThreshold" in
+ // modules/libpref/src/init/all.js .
+ nsIPresShell* presShell = bfc->PresContext()->PresShell();
+ uint32_t lineThreshold = presShell->FontSizeInflationLineThreshold();
+ nscoord newTextThreshold = (newNCAISize * lineThreshold) / 100;
+
+ if (mTextThreshold <= mTextAmount && mTextAmount < newTextThreshold) {
+ // Because we truncate our scan when we hit sufficient text, we now
+ // need to rescan.
+ mTextDirty = true;
+ }
+
+ mNCAISize = newNCAISize;
+ mTextThreshold = newTextThreshold;
+ mInflationEnabled = mTextAmount >= mTextThreshold;
+}
+
+/* static */ nsIFrame*
+nsFontInflationData::FindEdgeInflatableFrameIn(nsIFrame* aFrame,
+ SearchDirection aDirection)
+{
+ // NOTE: This function has a similar structure to ScanTextIn!
+
+ // FIXME: Should probably only scan the text that's actually going to
+ // be inflated!
+
+ nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
+ if (fcf) {
+ return aFrame;
+ }
+
+ // FIXME: aDirection!
+ AutoTArray<FrameChildList, 4> lists;
+ aFrame->GetChildLists(&lists);
+ for (uint32_t i = 0, len = lists.Length(); i < len; ++i) {
+ const nsFrameList& list =
+ lists[(aDirection == eFromStart) ? i : len - i - 1].mList;
+ for (nsIFrame *kid = (aDirection == eFromStart) ? list.FirstChild()
+ : list.LastChild();
+ kid;
+ kid = (aDirection == eFromStart) ? kid->GetNextSibling()
+ : kid->GetPrevSibling()) {
+ if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
+ // Goes in a different set of inflation data.
+ continue;
+ }
+
+ if (kid->GetType() == nsGkAtoms::textFrame) {
+ nsIContent *content = kid->GetContent();
+ if (content && kid == content->GetPrimaryFrame()) {
+ uint32_t len = nsTextFrameUtils::
+ ComputeApproximateLengthWithWhitespaceCompression(
+ content, kid->StyleText());
+ if (len != 0) {
+ return kid;
+ }
+ }
+ } else {
+ nsIFrame *kidResult =
+ FindEdgeInflatableFrameIn(kid, aDirection);
+ if (kidResult) {
+ return kidResult;
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+void
+nsFontInflationData::ScanText()
+{
+ mTextDirty = false;
+ mTextAmount = 0;
+ ScanTextIn(mBFCFrame);
+ mInflationEnabled = mTextAmount >= mTextThreshold;
+}
+
+static uint32_t
+DoCharCountOfLargestOption(nsIFrame *aContainer)
+{
+ uint32_t result = 0;
+ for (nsIFrame* option : aContainer->PrincipalChildList()) {
+ uint32_t optionResult;
+ if (option->GetContent()->IsHTMLElement(nsGkAtoms::optgroup)) {
+ optionResult = DoCharCountOfLargestOption(option);
+ } else {
+ // REVIEW: Check the frame structure for this!
+ optionResult = 0;
+ for (nsIFrame* optionChild : option->PrincipalChildList()) {
+ if (optionChild->GetType() == nsGkAtoms::textFrame) {
+ optionResult += nsTextFrameUtils::
+ ComputeApproximateLengthWithWhitespaceCompression(
+ optionChild->GetContent(), optionChild->StyleText());
+ }
+ }
+ }
+ if (optionResult > result) {
+ result = optionResult;
+ }
+ }
+ return result;
+}
+
+static uint32_t
+CharCountOfLargestOption(nsIFrame *aListControlFrame)
+{
+ return DoCharCountOfLargestOption(
+ static_cast<nsListControlFrame*>(aListControlFrame)->GetOptionsContainer());
+}
+
+void
+nsFontInflationData::ScanTextIn(nsIFrame *aFrame)
+{
+ // NOTE: This function has a similar structure to FindEdgeInflatableFrameIn!
+
+ // FIXME: Should probably only scan the text that's actually going to
+ // be inflated!
+
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator kids(lists.CurrentList());
+ for (; !kids.AtEnd(); kids.Next()) {
+ nsIFrame *kid = kids.get();
+ if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
+ // Goes in a different set of inflation data.
+ continue;
+ }
+
+ nsIAtom *fType = kid->GetType();
+ if (fType == nsGkAtoms::textFrame) {
+ nsIContent *content = kid->GetContent();
+ if (content && kid == content->GetPrimaryFrame()) {
+ uint32_t len = nsTextFrameUtils::
+ ComputeApproximateLengthWithWhitespaceCompression(
+ content, kid->StyleText());
+ if (len != 0) {
+ nscoord fontSize = kid->StyleFont()->mFont.size;
+ if (fontSize > 0) {
+ mTextAmount += fontSize * len;
+ }
+ }
+ }
+ } else if (fType == nsGkAtoms::textInputFrame) {
+ // We don't want changes to the amount of text in a text input
+ // to change what we count towards inflation.
+ nscoord fontSize = kid->StyleFont()->mFont.size;
+ int32_t charCount = static_cast<nsTextControlFrame*>(kid)->GetCols();
+ mTextAmount += charCount * fontSize;
+ } else if (fType == nsGkAtoms::comboboxControlFrame) {
+ // See textInputFrame above (with s/amount of text/selected option/).
+ // Don't just recurse down to the list control inside, since we
+ // need to exclude the display frame.
+ nscoord fontSize = kid->StyleFont()->mFont.size;
+ int32_t charCount = CharCountOfLargestOption(
+ static_cast<nsComboboxControlFrame*>(kid)->GetDropDown());
+ mTextAmount += charCount * fontSize;
+ } else if (fType == nsGkAtoms::listControlFrame) {
+ // See textInputFrame above (with s/amount of text/selected option/).
+ nscoord fontSize = kid->StyleFont()->mFont.size;
+ int32_t charCount = CharCountOfLargestOption(kid);
+ mTextAmount += charCount * fontSize;
+ } else {
+ // recursive step
+ ScanTextIn(kid);
+ }
+
+ if (mTextAmount >= mTextThreshold) {
+ return;
+ }
+ }
+ }
+}
diff --git a/layout/generic/nsFontInflationData.h b/layout/generic/nsFontInflationData.h
new file mode 100644
index 000000000..e8c457620
--- /dev/null
+++ b/layout/generic/nsFontInflationData.h
@@ -0,0 +1,75 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Per-block-formatting-context manager of font size inflation for pan and zoom UI. */
+
+#ifndef nsFontInflationData_h_
+#define nsFontInflationData_h_
+
+#include "nsContainerFrame.h"
+
+class nsFontInflationData
+{
+ using ReflowInput = mozilla::ReflowInput;
+
+public:
+
+ static nsFontInflationData* FindFontInflationDataFor(const nsIFrame *aFrame);
+
+ // Returns whether the effective width changed (which requires the
+ // caller to mark its descendants dirty
+ static bool
+ UpdateFontInflationDataISizeFor(const ReflowInput& aReflowInput);
+
+ static void MarkFontInflationDataTextDirty(nsIFrame *aFrame);
+
+ bool InflationEnabled() {
+ if (mTextDirty) {
+ ScanText();
+ }
+ return mInflationEnabled;
+ }
+
+ nscoord EffectiveISize() const {
+ return mNCAISize;
+ }
+
+private:
+
+ explicit nsFontInflationData(nsIFrame* aBFCFrame);
+
+ nsFontInflationData(const nsFontInflationData&) = delete;
+ void operator=(const nsFontInflationData&) = delete;
+
+ void UpdateISize(const ReflowInput &aReflowInput);
+ enum SearchDirection { eFromStart, eFromEnd };
+ static nsIFrame* FindEdgeInflatableFrameIn(nsIFrame *aFrame,
+ SearchDirection aDirection);
+
+ void MarkTextDirty() { mTextDirty = true; }
+ void ScanText();
+ // Scan text in the subtree rooted at aFrame. Increment mTextAmount
+ // by multiplying the number of characters found by the font size
+ // (yielding the inline-size that would be occupied by the characters if
+ // they were all em squares). But stop scanning if mTextAmount
+ // crosses mTextThreshold.
+ void ScanTextIn(nsIFrame *aFrame);
+
+ static const nsIFrame* FlowRootFor(const nsIFrame *aFrame)
+ {
+ while (!(aFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
+ aFrame = aFrame->GetParent();
+ }
+ return aFrame;
+ }
+
+ nsIFrame *mBFCFrame;
+ nscoord mNCAISize;
+ nscoord mTextAmount, mTextThreshold;
+ bool mInflationEnabled; // for this BFC
+ bool mTextDirty;
+};
+
+#endif /* !defined(nsFontInflationData_h_) */
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
new file mode 100644
index 000000000..69791d5c5
--- /dev/null
+++ b/layout/generic/nsFrame.cpp
@@ -0,0 +1,11380 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class of all rendering objects */
+
+#include "nsFrame.h"
+
+#include <stdarg.h>
+#include <algorithm>
+
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/Sprintf.h"
+
+#include "nsCOMPtr.h"
+#include "nsFrameList.h"
+#include "nsPlaceholderFrame.h"
+#include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsContentUtils.h"
+#include "nsCSSPseudoElements.h"
+#include "nsIAtom.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsStyleContext.h"
+#include "nsTableWrapperFrame.h"
+#include "nsView.h"
+#include "nsViewManager.h"
+#include "nsIScrollableFrame.h"
+#include "nsPresContext.h"
+#include "nsStyleConsts.h"
+#include "nsIPresShell.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Sprintf.h"
+#include "nsFrameManager.h"
+#include "nsLayoutUtils.h"
+#include "LayoutLogging.h"
+#include "mozilla/RestyleManager.h"
+#include "mozilla/RestyleManagerHandle.h"
+#include "mozilla/RestyleManagerHandleInlines.h"
+
+#include "nsIDOMNode.h"
+#include "nsISelection.h"
+#include "nsISelectionPrivate.h"
+#include "nsFrameSelection.h"
+#include "nsGkAtoms.h"
+#include "nsHtml5Atoms.h"
+#include "nsCSSAnonBoxes.h"
+
+#include "nsFrameTraversal.h"
+#include "nsRange.h"
+#include "nsITextControlFrame.h"
+#include "nsNameSpaceManager.h"
+#include "nsIPercentBSizeObserver.h"
+#include "nsStyleStructInlines.h"
+#include "FrameLayerBuilder.h"
+#include "ImageLayers.h"
+
+#include "nsBidiPresUtils.h"
+#include "RubyUtils.h"
+#include "nsAnimationManager.h"
+
+// For triple-click pref
+#include "imgIContainer.h"
+#include "imgIRequest.h"
+#include "nsError.h"
+#include "nsContainerFrame.h"
+#include "nsBoxLayoutState.h"
+#include "nsBlockFrame.h"
+#include "nsDisplayList.h"
+#include "nsSVGIntegrationUtils.h"
+#include "nsSVGEffects.h"
+#include "nsChangeHint.h"
+#include "nsDeckFrame.h"
+#include "nsSubDocumentFrame.h"
+#include "SVGTextFrame.h"
+
+#include "gfxContext.h"
+#include "nsRenderingContext.h"
+#include "nsAbsoluteContainingBlock.h"
+#include "DisplayItemScrollClip.h"
+#include "StickyScrollContainer.h"
+#include "nsFontInflationData.h"
+#include "nsRegion.h"
+#include "nsIFrameInlines.h"
+
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/EffectCompositor.h"
+#include "mozilla/EffectSet.h"
+#include "mozilla/EventListenerManager.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/css/ImageLoader.h"
+#include "mozilla/gfx/Tools.h"
+#include "nsPrintfCString.h"
+#include "ActiveLayerTracker.h"
+
+#include "nsITheme.h"
+#include "nsThemeConstants.h"
+
+using namespace mozilla;
+using namespace mozilla::css;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
+
+// Struct containing cached metrics for box-wrapped frames.
+struct nsBoxLayoutMetrics
+{
+ nsSize mPrefSize;
+ nsSize mMinSize;
+ nsSize mMaxSize;
+
+ nsSize mBlockMinSize;
+ nsSize mBlockPrefSize;
+ nscoord mBlockAscent;
+
+ nscoord mFlex;
+ nscoord mAscent;
+
+ nsSize mLastSize;
+};
+
+struct nsContentAndOffset
+{
+ nsIContent* mContent;
+ int32_t mOffset;
+};
+
+// Some Misc #defines
+#define SELECTION_DEBUG 0
+#define FORCE_SELECTION_UPDATE 1
+#define CALC_DEBUG 0
+
+// This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
+// because it uses the frame pointer passed in without drilling down to
+// the leaf frame.
+static bool
+IsReversedDirectionFrame(nsIFrame* aFrame)
+{
+ FrameBidiData bidiData = aFrame->GetBidiData();
+ return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
+}
+
+#include "nsILineIterator.h"
+
+//non Hack prototypes
+#if 0
+static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
+#endif
+
+#include "prenv.h"
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
+
+static void
+InitBoxMetrics(nsIFrame* aFrame, bool aClear)
+{
+ FrameProperties props = aFrame->Properties();
+ if (aClear) {
+ props.Delete(BoxMetricsProperty());
+ }
+
+ nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
+ props.Set(BoxMetricsProperty(), metrics);
+
+ static_cast<nsFrame*>(aFrame)->nsFrame::MarkIntrinsicISizesDirty();
+ metrics->mBlockAscent = 0;
+ metrics->mLastSize.SizeTo(0, 0);
+}
+
+static bool
+IsXULBoxWrapped(const nsIFrame* aFrame)
+{
+ return aFrame->GetParent() &&
+ aFrame->GetParent()->IsXULBoxFrame() &&
+ !aFrame->IsXULBoxFrame();
+}
+
+// Formerly the nsIFrameDebug interface
+
+#ifdef DEBUG
+static bool gShowFrameBorders = false;
+
+void nsFrame::ShowFrameBorders(bool aEnable)
+{
+ gShowFrameBorders = aEnable;
+}
+
+bool nsFrame::GetShowFrameBorders()
+{
+ return gShowFrameBorders;
+}
+
+static bool gShowEventTargetFrameBorder = false;
+
+void nsFrame::ShowEventTargetFrameBorder(bool aEnable)
+{
+ gShowEventTargetFrameBorder = aEnable;
+}
+
+bool nsFrame::GetShowEventTargetFrameBorder()
+{
+ return gShowEventTargetFrameBorder;
+}
+
+/**
+ * Note: the log module is created during library initialization which
+ * means that you cannot perform logging before then.
+ */
+mozilla::LazyLogModule nsFrame::sFrameLogModule("frame");
+
+static mozilla::LazyLogModule sStyleVerifyTreeLogModuleInfo("styleverifytree");
+
+static uint32_t gStyleVerifyTreeEnable = 0x55;
+
+bool
+nsFrame::GetVerifyStyleTreeEnable()
+{
+ if (gStyleVerifyTreeEnable == 0x55) {
+ gStyleVerifyTreeEnable = 0 != (int)((mozilla::LogModule*)sStyleVerifyTreeLogModuleInfo)->Level();
+ }
+ return gStyleVerifyTreeEnable;
+}
+
+void
+nsFrame::SetVerifyStyleTreeEnable(bool aEnabled)
+{
+ gStyleVerifyTreeEnable = aEnabled;
+}
+
+#endif
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
+ nsAbsoluteContainingBlock)
+
+bool
+nsIFrame::HasAbsolutelyPositionedChildren() const {
+ return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames();
+}
+
+nsAbsoluteContainingBlock*
+nsIFrame::GetAbsoluteContainingBlock() const {
+ NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly");
+ nsAbsoluteContainingBlock* absCB = Properties().Get(AbsoluteContainingBlockProperty());
+ NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property");
+ return absCB;
+}
+
+void
+nsIFrame::MarkAsAbsoluteContainingBlock()
+{
+ MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+ NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()),
+ "Already has an abs-pos containing block property?");
+ NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
+ "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
+ AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
+ Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID()));
+}
+
+void
+nsIFrame::MarkAsNotAbsoluteContainingBlock()
+{
+ NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
+ NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()),
+ "Should have an abs-pos containing block property");
+ NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
+ "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
+ MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
+ RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
+ Properties().Delete(AbsoluteContainingBlockProperty());
+}
+
+bool
+nsIFrame::CheckAndClearPaintedState()
+{
+ bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES);
+ RemoveStateBits(NS_FRAME_PAINTED_THEBES);
+
+ nsIFrame::ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* child = childFrames.get();
+ if (child->CheckAndClearPaintedState()) {
+ result = true;
+ }
+ }
+ }
+ return result;
+}
+
+bool
+nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const
+{
+ if (!StyleVisibility()->IsVisible()) {
+ return false;
+ }
+
+ const nsIFrame* frame = this;
+ while (frame) {
+ nsView* view = frame->GetView();
+ if (view && view->GetVisibility() == nsViewVisibility_kHide)
+ return false;
+
+ nsIFrame* parent = frame->GetParent();
+ nsDeckFrame* deck = do_QueryFrame(parent);
+ if (deck) {
+ if (deck->GetSelectedBox() != frame)
+ return false;
+ }
+
+ if (parent) {
+ frame = parent;
+ } else {
+ parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
+ if (!parent)
+ break;
+
+ if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
+ parent->PresContext()->IsChrome() && !frame->PresContext()->IsChrome()) {
+ break;
+ }
+
+ if (!parent->StyleVisibility()->IsVisible())
+ return false;
+
+ frame = parent;
+ }
+ }
+
+ return true;
+}
+
+void
+nsIFrame::FindCloserFrameForSelection(
+ nsPoint aPoint,
+ nsIFrame::FrameWithDistance* aCurrentBestFrame)
+{
+ if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
+ aCurrentBestFrame->mXDistance,
+ aCurrentBestFrame->mYDistance)) {
+ aCurrentBestFrame->mFrame = this;
+ }
+}
+
+void
+nsIFrame::ContentStatesChanged(mozilla::EventStates aStates)
+{
+}
+
+void
+NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, nsReflowStatus aSecondary)
+{
+ *aPrimary |= aSecondary &
+ (NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE |
+ NS_FRAME_TRUNCATED | NS_FRAME_REFLOW_NEXTINFLOW);
+ if (*aPrimary & NS_FRAME_NOT_COMPLETE) {
+ *aPrimary &= ~NS_FRAME_OVERFLOW_INCOMPLETE;
+ }
+}
+
+void
+nsWeakFrame::Init(nsIFrame* aFrame)
+{
+ Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
+ mFrame = aFrame;
+ if (mFrame) {
+ nsIPresShell* shell = mFrame->PresContext()->GetPresShell();
+ NS_WARNING_ASSERTION(shell, "Null PresShell in nsWeakFrame!");
+ if (shell) {
+ shell->AddWeakFrame(this);
+ } else {
+ mFrame = nullptr;
+ }
+ }
+}
+
+nsIFrame*
+NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsFrame(aContext);
+}
+
+nsFrame::nsFrame(nsStyleContext* aContext)
+{
+ MOZ_COUNT_CTOR(nsFrame);
+
+ mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY;
+ mStyleContext = aContext;
+ mStyleContext->AddRef();
+#ifdef DEBUG
+ mStyleContext->FrameAddRef();
+#endif
+}
+
+nsFrame::~nsFrame()
+{
+ MOZ_COUNT_DTOR(nsFrame);
+
+ MOZ_ASSERT(GetVisibility() != Visibility::APPROXIMATELY_VISIBLE,
+ "Visible nsFrame is being destroyed");
+
+ NS_IF_RELEASE(mContent);
+#ifdef DEBUG
+ mStyleContext->FrameRelease();
+#endif
+ mStyleContext->Release();
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsFrame)
+
+// Dummy operator delete. Will never be called, but must be defined
+// to satisfy some C++ ABIs.
+void
+nsFrame::operator delete(void *, size_t)
+{
+ NS_RUNTIMEABORT("nsFrame::operator delete should never be called");
+}
+
+NS_QUERYFRAME_HEAD(nsFrame)
+ NS_QUERYFRAME_ENTRY(nsIFrame)
+NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
+
+/////////////////////////////////////////////////////////////////////////////
+// nsIFrame
+
+static bool
+IsFontSizeInflationContainer(nsIFrame* aFrame,
+ const nsStyleDisplay* aStyleDisplay)
+{
+ /*
+ * Font size inflation is built around the idea that we're inflating
+ * the fonts for a pan-and-zoom UI so that when the user scales up a
+ * block or other container to fill the width of the device, the fonts
+ * will be readable. To do this, we need to pick what counts as a
+ * container.
+ *
+ * From a code perspective, the only hard requirement is that frames
+ * that are line participants
+ * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
+ * containers, since line layout assumes that the inflation is
+ * consistent within a line.
+ *
+ * This is not an imposition, since we obviously want a bunch of text
+ * (possibly with inline elements) flowing within a block to count the
+ * block (or higher) as its container.
+ *
+ * We also want form controls, including the text in the anonymous
+ * content inside of them, to match each other and the text next to
+ * them, so they and their anonymous content should also not be a
+ * container.
+ *
+ * However, because we can't reliably compute sizes across XUL during
+ * reflow, any XUL frame with a XUL parent is always a container.
+ *
+ * There are contexts where it would be nice if some blocks didn't
+ * count as a container, so that, for example, an indented quotation
+ * didn't end up with a smaller font size. However, it's hard to
+ * distinguish these situations where we really do want the indented
+ * thing to count as a container, so we don't try, and blocks are
+ * always containers.
+ */
+
+ // The root frame should always be an inflation container.
+ if (!aFrame->GetParent()) {
+ return true;
+ }
+
+ nsIContent *content = aFrame->GetContent();
+ nsIAtom* frameType = aFrame->GetType();
+ bool isInline = (aFrame->GetDisplay() == StyleDisplay::Inline ||
+ RubyUtils::IsRubyBox(frameType) ||
+ (aFrame->IsFloating() &&
+ frameType == nsGkAtoms::letterFrame) ||
+ // Given multiple frames for the same node, only the
+ // outer one should be considered a container.
+ // (Important, e.g., for nsSelectsAreaFrame.)
+ (aFrame->GetParent()->GetContent() == content) ||
+ (content && (content->IsAnyOfHTMLElements(nsGkAtoms::option,
+ nsGkAtoms::optgroup,
+ nsGkAtoms::select) ||
+ content->IsInNativeAnonymousSubtree()))) &&
+ !(aFrame->IsXULBoxFrame() && aFrame->GetParent()->IsXULBoxFrame());
+ NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
+ isInline ||
+ // br frames and mathml frames report being line
+ // participants even when their position or display is
+ // set
+ aFrame->GetType() == nsGkAtoms::brFrame ||
+ aFrame->IsFrameOfType(nsIFrame::eMathML),
+ "line participants must not be containers");
+ NS_ASSERTION(aFrame->GetType() != nsGkAtoms::bulletFrame || isInline,
+ "bullets should not be containers");
+ return !isInline;
+}
+
+void
+nsFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ NS_PRECONDITION(!mContent, "Double-initing a frame?");
+ NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) &&
+ !IsFrameOfType(eDEBUGNoFrames),
+ "IsFrameOfType implementation that doesn't call base class");
+
+ mContent = aContent;
+ mParent = aParent;
+
+ if (aContent) {
+ NS_ADDREF(aContent);
+ }
+
+ if (aPrevInFlow) {
+ // Make sure the general flags bits are the same
+ nsFrameState state = aPrevInFlow->GetStateBits();
+
+ // Make bits that are currently off (see constructor) the same:
+ mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
+ NS_FRAME_PART_OF_IBSPLIT |
+ NS_FRAME_MAY_BE_TRANSFORMED |
+ NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
+ NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+ } else {
+ PresContext()->ConstructedFrame();
+ }
+ if (GetParent()) {
+ nsFrameState state = GetParent()->GetStateBits();
+
+ // Make bits that are currently off (see constructor) the same:
+ mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
+ NS_FRAME_GENERATED_CONTENT |
+ NS_FRAME_IS_SVG_TEXT |
+ NS_FRAME_IN_POPUP |
+ NS_FRAME_IS_NONDISPLAY);
+
+ if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
+ // Assume all frames in popups are visible.
+ IncApproximateVisibleCount();
+ }
+ }
+ const nsStyleDisplay *disp = StyleDisplay();
+ if (disp->HasTransform(this) ||
+ (IsFrameOfType(eSupportsCSSTransforms) &&
+ nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_transform))) {
+ // The frame gets reconstructed if we toggle the -moz-transform
+ // property, so we can set this bit here and then ignore it.
+ mState |= NS_FRAME_MAY_BE_TRANSFORMED;
+ }
+ if (disp->mPosition == NS_STYLE_POSITION_STICKY &&
+ !aPrevInFlow &&
+ !(mState & NS_FRAME_IS_NONDISPLAY) &&
+ !disp->IsInnerTableStyle()) {
+ // Note that we only add first continuations, but we really only
+ // want to add first continuation-or-ib-split-siblings. But since we
+ // don't yet know if we're a later part of a block-in-inline split,
+ // we'll just add later members of a block-in-inline split here, and
+ // then StickyScrollContainer will remove them later.
+ // We don't currently support relative positioning of inner table
+ // elements (bug 35168), so exclude them from sticky positioning too.
+ StickyScrollContainer* ssc =
+ StickyScrollContainer::GetStickyScrollContainerForFrame(this);
+ if (ssc) {
+ ssc->AddFrame(this);
+ }
+ }
+
+ if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent()
+#ifdef DEBUG
+ // We have assertions that check inflation invariants even when
+ // font size inflation is not enabled.
+ || true
+#endif
+ ) {
+ if (IsFontSizeInflationContainer(this, disp)) {
+ AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
+ if (!GetParent() ||
+ // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
+ disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this)) {
+ AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
+ }
+ }
+ NS_ASSERTION(GetParent() ||
+ (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
+ "root frame should always be a container");
+ }
+
+ if (PresContext()->PresShell()->AssumeAllFramesVisible() &&
+ TrackingVisibility()) {
+ IncApproximateVisibleCount();
+ }
+
+ DidSetStyleContext(nullptr);
+
+ if (::IsXULBoxWrapped(this))
+ ::InitBoxMetrics(this, false);
+}
+
+void
+nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
+ "destroy called on frame while scripts not blocked");
+ NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
+ "Frames should be removed before destruction.");
+ NS_ASSERTION(aDestructRoot, "Must specify destruct root");
+ MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
+
+ nsSVGEffects::InvalidateDirectRenderingObservers(this);
+
+ if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) {
+ StickyScrollContainer* ssc =
+ StickyScrollContainer::GetStickyScrollContainerForFrame(this);
+ if (ssc) {
+ ssc->RemoveFrame(this);
+ }
+ }
+
+ // Get the view pointer now before the frame properties disappear
+ // when we call NotifyDestroyingFrame()
+ nsView* view = GetView();
+ nsPresContext* presContext = PresContext();
+
+ nsIPresShell *shell = presContext->GetPresShell();
+ if (mState & NS_FRAME_OUT_OF_FLOW) {
+ nsPlaceholderFrame* placeholder =
+ shell->FrameManager()->GetPlaceholderFrameFor(this);
+ NS_ASSERTION(!placeholder || (aDestructRoot != this),
+ "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
+ NS_ASSERTION(!placeholder ||
+ nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder),
+ "Placeholder relationship should have been torn down already; "
+ "this might mean we have a stray placeholder in the tree.");
+ if (placeholder) {
+ shell->FrameManager()->UnregisterPlaceholderFrame(placeholder);
+ placeholder->SetOutOfFlowFrame(nullptr);
+ }
+ }
+
+ // If we have any IB split siblings, clear their references to us.
+ // (Note: This has to happen before we call shell->NotifyDestroyingFrame,
+ // because that clears our Properties() table.)
+ if (mState & NS_FRAME_PART_OF_IBSPLIT) {
+ // Delete previous sibling's reference to me.
+ nsIFrame* prevSib = Properties().Get(nsIFrame::IBSplitPrevSibling());
+ if (prevSib) {
+ NS_WARNING_ASSERTION(
+ this == prevSib->Properties().Get(nsIFrame::IBSplitSibling()),
+ "IB sibling chain is inconsistent");
+ prevSib->Properties().Delete(nsIFrame::IBSplitSibling());
+ }
+
+ // Delete next sibling's reference to me.
+ nsIFrame* nextSib = Properties().Get(nsIFrame::IBSplitSibling());
+ if (nextSib) {
+ NS_WARNING_ASSERTION(
+ this == nextSib->Properties().Get(nsIFrame::IBSplitPrevSibling()),
+ "IB sibling chain is inconsistent");
+ nextSib->Properties().Delete(nsIFrame::IBSplitPrevSibling());
+ }
+ }
+
+ bool isPrimaryFrame = (mContent && mContent->GetPrimaryFrame() == this);
+ if (isPrimaryFrame) {
+ // This needs to happen before shell->NotifyDestroyingFrame because
+ // that clears our Properties() table.
+ ActiveLayerTracker::TransferActivityToContent(this, mContent);
+
+ // Unfortunately, we need to do this for all frames being reframed
+ // and not only those whose current style involves CSS transitions,
+ // because what matters is whether the new style (not the old)
+ // specifies CSS transitions.
+ if (presContext->RestyleManager()->IsGecko()) {
+ // stylo: ServoRestyleManager does not handle transitions yet, and when
+ // it does it probably won't need to track reframed style contexts to
+ // initiate transitions correctly.
+ RestyleManager::ReframingStyleContexts* rsc =
+ presContext->RestyleManager()->AsGecko()->GetReframingStyleContexts();
+ if (rsc) {
+ rsc->Put(mContent, mStyleContext);
+ }
+ }
+ }
+
+ if (HasCSSAnimations() || HasCSSTransitions() ||
+ EffectSet::GetEffectSet(this)) {
+ // If no new frame for this element is created by the end of the
+ // restyling process, stop animations and transitions for this frame
+ if (presContext->RestyleManager()->IsGecko()) {
+ RestyleManager::AnimationsWithDestroyedFrame* adf =
+ presContext->RestyleManager()->AsGecko()->GetAnimationsWithDestroyedFrame();
+ // AnimationsWithDestroyedFrame only lives during the restyling process.
+ if (adf) {
+ adf->Put(mContent, mStyleContext);
+ }
+ } else {
+ NS_ERROR("stylo: ServoRestyleManager does not support animations yet");
+ }
+ }
+
+ // Disable visibility tracking. Note that we have to do this before calling
+ // NotifyDestroyingFrame(), which will clear frame properties and make us lose
+ // track of whether we were previously visible or not.
+ // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
+ // here, but it's unfortunately tricky to guarantee in the face of things like
+ // frame reconstruction induced by style changes.
+ DisableVisibilityTracking();
+
+ // Ensure that we're not in the approximately visible list anymore.
+ PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
+
+ shell->NotifyDestroyingFrame(this);
+
+ if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
+ shell->ClearFrameRefs(this);
+ }
+
+ if (view) {
+ // Break association between view and frame
+ view->SetFrame(nullptr);
+
+ // Destroy the view
+ view->Destroy();
+ }
+
+ // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
+ if (isPrimaryFrame) {
+ mContent->SetPrimaryFrame(nullptr);
+ }
+
+ // Must retrieve the object ID before calling destructors, so the
+ // vtable is still valid.
+ //
+ // Note to future tweakers: having the method that returns the
+ // object size call the destructor will not avoid an indirect call;
+ // the compiler cannot devirtualize the call to the destructor even
+ // if it's from a method defined in the same class.
+
+ nsQueryFrame::FrameIID id = GetFrameId();
+ this->~nsFrame();
+
+ // Now that we're totally cleaned out, we need to add ourselves to
+ // the presshell's recycler.
+ shell->FreeFrame(id, this);
+}
+
+nsresult
+nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const
+{
+ aStart = 0;
+ aEnd = 0;
+ return NS_OK;
+}
+
+static
+void
+AddAndRemoveImageAssociations(nsFrame* aFrame,
+ const nsStyleImageLayers* aOldLayers,
+ const nsStyleImageLayers* aNewLayers)
+{
+ ImageLoader* imageLoader =
+ aFrame->PresContext()->Document()->StyleImageLoader();
+
+ // If the old context had a background-image image, or mask-image image,
+ // and new context does not have the same image, clear the image load
+ // notifier (which keeps the image loading, if it still is) for the frame.
+ // We want to do this conservatively because some frames paint their
+ // backgrounds from some other frame's style data, and we don't want
+ // to clear those notifiers unless we have to. (They'll be reset
+ // when we paint, although we could miss a notification in that
+ // interval.)
+
+ if (aOldLayers) {
+ NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aOldLayers)) {
+ // If there is an image in oldBG that's not in newBG, drop it.
+ if (i >= aNewLayers->mImageCount ||
+ !aOldLayers->mLayers[i].mImage.ImageDataEquals(
+ aNewLayers->mLayers[i].mImage)) {
+ const nsStyleImage& oldImage = aOldLayers->mLayers[i].mImage;
+ if (oldImage.GetType() != eStyleImageType_Image) {
+ continue;
+ }
+
+ if (imgRequestProxy* req = oldImage.GetImageData()) {
+ imageLoader->DisassociateRequestFromFrame(req, aFrame);
+ }
+ }
+ }
+ }
+
+ NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aNewLayers)) {
+ // If there is an image in newBG that's not in oldBG, add it.
+ if (!aOldLayers || i >= aOldLayers->mImageCount ||
+ !aNewLayers->mLayers[i].mImage.ImageDataEquals(
+ aOldLayers->mLayers[i].mImage)) {
+ const nsStyleImage& newImage = aNewLayers->mLayers[i].mImage;
+ if (newImage.GetType() != eStyleImageType_Image) {
+ continue;
+ }
+
+ if (imgRequestProxy* req = newImage.GetImageData()) {
+ imageLoader->AssociateRequestToFrame(req, aFrame);
+ }
+ }
+ }
+}
+
+// Subclass hook for style post processing
+/* virtual */ void
+nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+ if (IsSVGText()) {
+ SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
+ nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame));
+ nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
+ // Just as in SVGTextFrame::DidSetStyleContext, we need to ensure that
+ // any non-display SVGTextFrames get reflowed when a child text frame
+ // gets new style.
+ //
+ // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
+ // anonymous block frame rather than our self, since NS_FRAME_FIRST_REFLOW
+ // may be set on us if we're a new frame that has been inserted after the
+ // document's first reflow. (In which case this DidSetStyleContext call may
+ // be happening under frame construction under a Reflow() call.)
+ if (anonBlock && !(anonBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
+ (svgTextFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
+ !(svgTextFrame->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW)) {
+ svgTextFrame->ScheduleReflowSVGNonDisplayText(nsIPresShell::eStyleChange);
+ }
+ }
+
+ const nsStyleImageLayers *oldLayers = aOldStyleContext ?
+ &aOldStyleContext->StyleBackground()->mImage :
+ nullptr;
+ const nsStyleImageLayers *newLayers = &StyleBackground()->mImage;
+ AddAndRemoveImageAssociations(this, oldLayers, newLayers);
+
+ oldLayers = aOldStyleContext ? &aOldStyleContext->StyleSVGReset()->mMask :
+ nullptr;
+ newLayers = &StyleSVGReset()->mMask;
+ AddAndRemoveImageAssociations(this, oldLayers, newLayers);
+
+ if (aOldStyleContext) {
+ // If we detect a change on margin, padding or border, we store the old
+ // values on the frame itself between now and reflow, so if someone
+ // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
+ // can give an accurate answer.
+ // We don't want to set the property if one already exists.
+ FrameProperties props = Properties();
+ nsMargin oldValue(0, 0, 0, 0);
+ nsMargin newValue(0, 0, 0, 0);
+ const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin();
+ if (oldMargin && oldMargin->GetMargin(oldValue)) {
+ if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) &&
+ !props.Get(UsedMarginProperty())) {
+ props.Set(UsedMarginProperty(), new nsMargin(oldValue));
+ }
+ }
+
+ const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding();
+ if (oldPadding && oldPadding->GetPadding(oldValue)) {
+ if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) &&
+ !props.Get(UsedPaddingProperty())) {
+ props.Set(UsedPaddingProperty(), new nsMargin(oldValue));
+ }
+ }
+
+ const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder();
+ if (oldBorder) {
+ oldValue = oldBorder->GetComputedBorder();
+ newValue = StyleBorder()->GetComputedBorder();
+ if (oldValue != newValue &&
+ !props.Get(UsedBorderProperty())) {
+ props.Set(UsedBorderProperty(), new nsMargin(oldValue));
+ }
+ }
+ }
+
+ ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
+ imgIRequest *oldBorderImage = aOldStyleContext
+ ? aOldStyleContext->StyleBorder()->GetBorderImageRequest()
+ : nullptr;
+ imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest();
+ // FIXME (Bug 759996): The following is no longer true.
+ // For border-images, we can't be as conservative (we need to set the
+ // new loaders if there has been any change) since the CalcDifference
+ // call depended on the result of GetComputedBorder() and that result
+ // depends on whether the image has loaded, start the image load now
+ // so that we'll get notified when it completes loading and can do a
+ // restyle. Otherwise, the image might finish loading from the
+ // network before we start listening to its notifications, and then
+ // we'll never know that it's finished loading. Likewise, we want to
+ // do this for freshly-created frames to prevent a similar race if the
+ // image loads between reflow (which can depend on whether the image
+ // is loaded) and paint. We also don't really care about any callers
+ // who try to paint borders with a different style context, because
+ // they won't have the correct size for the border either.
+ if (oldBorderImage != newBorderImage) {
+ // stop and restart the image loading/notification
+ if (oldBorderImage) {
+ imageLoader->DisassociateRequestFromFrame(oldBorderImage, this);
+ }
+ if (newBorderImage) {
+ imageLoader->AssociateRequestToFrame(newBorderImage, this);
+ }
+ }
+
+ // If the page contains markup that overrides text direction, and
+ // does not contain any characters that would activate the Unicode
+ // bidi algorithm, we need to call |SetBidiEnabled| on the pres
+ // context before reflow starts. See bug 115921.
+ if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
+ PresContext()->SetBidiEnabled();
+ }
+
+ RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
+}
+
+// MSVC fails with link error "one or more multiply defined symbols found",
+// gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
+// etc if they are not defined.
+#ifndef _MSC_VER
+// static nsIFrame constants; initialized in the header file.
+const nsIFrame::ChildListID nsIFrame::kPrincipalList;
+const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
+const nsIFrame::ChildListID nsIFrame::kBulletList;
+const nsIFrame::ChildListID nsIFrame::kCaptionList;
+const nsIFrame::ChildListID nsIFrame::kColGroupList;
+const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList;
+const nsIFrame::ChildListID nsIFrame::kFixedList;
+const nsIFrame::ChildListID nsIFrame::kFloatList;
+const nsIFrame::ChildListID nsIFrame::kOverflowContainersList;
+const nsIFrame::ChildListID nsIFrame::kOverflowList;
+const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList;
+const nsIFrame::ChildListID nsIFrame::kPopupList;
+const nsIFrame::ChildListID nsIFrame::kPushedFloatsList;
+const nsIFrame::ChildListID nsIFrame::kSelectPopupList;
+const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
+#endif
+
+/* virtual */ nsMargin
+nsIFrame::GetUsedMargin() const
+{
+ nsMargin margin(0, 0, 0, 0);
+ if (((mState & NS_FRAME_FIRST_REFLOW) &&
+ !(mState & NS_FRAME_IN_REFLOW)) ||
+ IsSVGText())
+ return margin;
+
+ nsMargin *m = Properties().Get(UsedMarginProperty());
+ if (m) {
+ margin = *m;
+ } else {
+ if (!StyleMargin()->GetMargin(margin)) {
+ // If we get here, our caller probably shouldn't be calling us...
+ NS_ERROR("Returning bogus 0-sized margin, because this margin "
+ "depends on layout & isn't cached!");
+ }
+ }
+ return margin;
+}
+
+/* virtual */ nsMargin
+nsIFrame::GetUsedBorder() const
+{
+ nsMargin border(0, 0, 0, 0);
+ if (((mState & NS_FRAME_FIRST_REFLOW) &&
+ !(mState & NS_FRAME_IN_REFLOW)) ||
+ IsSVGText())
+ return border;
+
+ // Theme methods don't use const-ness.
+ nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
+
+ const nsStyleDisplay *disp = StyleDisplay();
+ if (mutable_this->IsThemed(disp)) {
+ nsIntMargin result;
+ nsPresContext *presContext = PresContext();
+ presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
+ mutable_this, disp->mAppearance,
+ &result);
+ border.left = presContext->DevPixelsToAppUnits(result.left);
+ border.top = presContext->DevPixelsToAppUnits(result.top);
+ border.right = presContext->DevPixelsToAppUnits(result.right);
+ border.bottom = presContext->DevPixelsToAppUnits(result.bottom);
+ return border;
+ }
+
+ nsMargin *b = Properties().Get(UsedBorderProperty());
+ if (b) {
+ border = *b;
+ } else {
+ border = StyleBorder()->GetComputedBorder();
+ }
+ return border;
+}
+
+/* virtual */ nsMargin
+nsIFrame::GetUsedPadding() const
+{
+ nsMargin padding(0, 0, 0, 0);
+ if (((mState & NS_FRAME_FIRST_REFLOW) &&
+ !(mState & NS_FRAME_IN_REFLOW)) ||
+ IsSVGText())
+ return padding;
+
+ // Theme methods don't use const-ness.
+ nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
+
+ const nsStyleDisplay *disp = StyleDisplay();
+ if (mutable_this->IsThemed(disp)) {
+ nsPresContext *presContext = PresContext();
+ nsIntMargin widget;
+ if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
+ mutable_this,
+ disp->mAppearance,
+ &widget)) {
+ padding.top = presContext->DevPixelsToAppUnits(widget.top);
+ padding.right = presContext->DevPixelsToAppUnits(widget.right);
+ padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom);
+ padding.left = presContext->DevPixelsToAppUnits(widget.left);
+ return padding;
+ }
+ }
+
+ nsMargin *p = Properties().Get(UsedPaddingProperty());
+ if (p) {
+ padding = *p;
+ } else {
+ if (!StylePadding()->GetPadding(padding)) {
+ // If we get here, our caller probably shouldn't be calling us...
+ NS_ERROR("Returning bogus 0-sized padding, because this padding "
+ "depends on layout & isn't cached!");
+ }
+ }
+ return padding;
+}
+
+nsIFrame::Sides
+nsIFrame::GetSkipSides(const ReflowInput* aReflowInput) const
+{
+ if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone) &&
+ !(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ return Sides();
+ }
+
+ // Convert the logical skip sides to physical sides using the frame's
+ // writing mode
+ WritingMode writingMode = GetWritingMode();
+ LogicalSides logicalSkip = GetLogicalSkipSides(aReflowInput);
+ Sides skip;
+
+ if (logicalSkip.BStart()) {
+ if (writingMode.IsVertical()) {
+ skip |= writingMode.IsVerticalLR() ? eSideBitsLeft : eSideBitsRight;
+ } else {
+ skip |= eSideBitsTop;
+ }
+ }
+
+ if (logicalSkip.BEnd()) {
+ if (writingMode.IsVertical()) {
+ skip |= writingMode.IsVerticalLR() ? eSideBitsRight : eSideBitsLeft;
+ } else {
+ skip |= eSideBitsBottom;
+ }
+ }
+
+ if (logicalSkip.IStart()) {
+ if (writingMode.IsVertical()) {
+ skip |= eSideBitsTop;
+ } else {
+ skip |= writingMode.IsBidiLTR() ? eSideBitsLeft : eSideBitsRight;
+ }
+ }
+
+ if (logicalSkip.IEnd()) {
+ if (writingMode.IsVertical()) {
+ skip |= eSideBitsBottom;
+ } else {
+ skip |= writingMode.IsBidiLTR() ? eSideBitsRight : eSideBitsLeft;
+ }
+ }
+ return skip;
+}
+
+nsRect
+nsIFrame::GetPaddingRectRelativeToSelf() const
+{
+ nsMargin border(GetUsedBorder());
+ border.ApplySkipSides(GetSkipSides());
+ nsRect r(0, 0, mRect.width, mRect.height);
+ r.Deflate(border);
+ return r;
+}
+
+nsRect
+nsIFrame::GetPaddingRect() const
+{
+ return GetPaddingRectRelativeToSelf() + GetPosition();
+}
+
+WritingMode
+nsIFrame::GetWritingMode(nsIFrame* aSubFrame) const
+{
+ WritingMode writingMode = GetWritingMode();
+
+ if (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
+ nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
+ writingMode.SetDirectionFromBidiLevel(frameLevel);
+ }
+
+ return writingMode;
+}
+
+nsRect
+nsIFrame::GetMarginRectRelativeToSelf() const
+{
+ nsMargin m = GetUsedMargin();
+ m.ApplySkipSides(GetSkipSides());
+ nsRect r(0, 0, mRect.width, mRect.height);
+ r.Inflate(m);
+ return r;
+}
+
+bool
+nsIFrame::IsTransformed() const
+{
+ return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
+ (StyleDisplay()->HasTransform(this) ||
+ IsSVGTransformed() ||
+ (mContent &&
+ nsLayoutUtils::HasAnimationOfProperty(this,
+ eCSSProperty_transform) &&
+ IsFrameOfType(eSupportsCSSTransforms) &&
+ mContent->GetPrimaryFrame() == this)));
+}
+
+bool
+nsIFrame::HasOpacityInternal(float aThreshold) const
+{
+ MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
+ return StyleEffects()->mOpacity < aThreshold ||
+ (StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) ||
+ (mContent &&
+ nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_opacity) &&
+ mContent->GetPrimaryFrame() == this);
+}
+
+bool
+nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms,
+ gfx::Matrix *aFromParentTransforms) const
+{
+ return false;
+}
+
+bool
+nsIFrame::Extend3DContext() const
+{
+ const nsStyleDisplay* disp = StyleDisplay();
+ if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
+ !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
+ return false;
+ }
+
+ // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d.
+ if (GetType() == nsGkAtoms::scrollFrame) {
+ return false;
+ }
+
+ if (HasOpacity()) {
+ return false;
+ }
+
+ const nsStyleEffects* effects = StyleEffects();
+ return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
+ !GetClipPropClipRect(disp, effects, GetSize()) &&
+ !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
+}
+
+bool
+nsIFrame::Combines3DTransformWithAncestors() const
+{
+ if (!GetParent() || !GetParent()->Extend3DContext()) {
+ return false;
+ }
+ return IsTransformed() || BackfaceIsHidden();
+}
+
+bool
+nsIFrame::In3DContextAndBackfaceIsHidden() const
+{
+ return Combines3DTransformWithAncestors() && BackfaceIsHidden();
+}
+
+bool
+nsIFrame::HasPerspective() const
+{
+ if (!IsTransformed()) {
+ return false;
+ }
+ nsIFrame* containingBlock = GetContainingBlock(SKIP_SCROLLED_FRAME);
+ if (!containingBlock) {
+ return false;
+ }
+ return containingBlock->ChildrenHavePerspective();
+}
+
+bool
+nsIFrame::ChildrenHavePerspective() const
+{
+ return StyleDisplay()->HasPerspectiveStyle();
+}
+
+nsRect
+nsIFrame::GetContentRectRelativeToSelf() const
+{
+ nsMargin bp(GetUsedBorderAndPadding());
+ bp.ApplySkipSides(GetSkipSides());
+ nsRect r(0, 0, mRect.width, mRect.height);
+ r.Deflate(bp);
+ return r;
+}
+
+nsRect
+nsIFrame::GetContentRect() const
+{
+ return GetContentRectRelativeToSelf() + GetPosition();
+}
+
+bool
+nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
+ const nsSize& aFrameSize,
+ const nsSize& aBorderArea,
+ Sides aSkipSides,
+ nscoord aRadii[8])
+{
+ // Percentages are relative to whichever side they're on.
+ NS_FOR_CSS_HALF_CORNERS(i) {
+ const nsStyleCoord c = aBorderRadius.Get(i);
+ nscoord axis =
+ NS_HALF_CORNER_IS_X(i) ? aFrameSize.width : aFrameSize.height;
+
+ if (c.IsCoordPercentCalcUnit()) {
+ aRadii[i] = nsRuleNode::ComputeCoordPercentCalc(c, axis);
+ if (aRadii[i] < 0) {
+ // clamp calc()
+ aRadii[i] = 0;
+ }
+ } else {
+ NS_NOTREACHED("ComputeBorderRadii: bad unit");
+ aRadii[i] = 0;
+ }
+ }
+
+ if (aSkipSides.Top()) {
+ aRadii[NS_CORNER_TOP_LEFT_X] = 0;
+ aRadii[NS_CORNER_TOP_LEFT_Y] = 0;
+ aRadii[NS_CORNER_TOP_RIGHT_X] = 0;
+ aRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
+ }
+
+ if (aSkipSides.Right()) {
+ aRadii[NS_CORNER_TOP_RIGHT_X] = 0;
+ aRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
+ aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
+ aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
+ }
+
+ if (aSkipSides.Bottom()) {
+ aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
+ aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
+ aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
+ aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
+ }
+
+ if (aSkipSides.Left()) {
+ aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
+ aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
+ aRadii[NS_CORNER_TOP_LEFT_X] = 0;
+ aRadii[NS_CORNER_TOP_LEFT_Y] = 0;
+ }
+
+ // css3-background specifies this algorithm for reducing
+ // corner radii when they are too big.
+ bool haveRadius = false;
+ double ratio = 1.0f;
+ NS_FOR_CSS_SIDES(side) {
+ uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, true);
+ uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, true);
+ nscoord length =
+ NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width;
+ nscoord sum = aRadii[hc1] + aRadii[hc2];
+ if (sum)
+ haveRadius = true;
+
+ // avoid floating point division in the normal case
+ if (length < sum)
+ ratio = std::min(ratio, double(length)/sum);
+ }
+ if (ratio < 1.0) {
+ NS_FOR_CSS_HALF_CORNERS(corner) {
+ aRadii[corner] *= ratio;
+ }
+ }
+
+ return haveRadius;
+}
+
+/* static */ void
+nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
+{
+ NS_FOR_CSS_SIDES(side) {
+ nscoord offset = aOffsets.Side(side);
+ uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false);
+ uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false);
+ aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
+ aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
+ }
+}
+
+/* static */ void
+nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
+{
+ NS_FOR_CSS_SIDES(side) {
+ nscoord offset = aOffsets.Side(side);
+ uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false);
+ uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false);
+ if (aRadii[hc1] > 0)
+ aRadii[hc1] += offset;
+ if (aRadii[hc2] > 0)
+ aRadii[hc2] += offset;
+ }
+}
+
+/* virtual */ bool
+nsIFrame::GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
+ Sides aSkipSides, nscoord aRadii[8]) const
+{
+ if (IsThemed()) {
+ // When we're themed, the native theme code draws the border and
+ // background, and therefore it doesn't make sense to tell other
+ // code that's interested in border-radius that we have any radii.
+ //
+ // In an ideal world, we might have a way for the them to tell us an
+ // border radius, but since we don't, we're better off assuming
+ // zero.
+ NS_FOR_CSS_HALF_CORNERS(corner) {
+ aRadii[corner] = 0;
+ }
+ return false;
+ }
+ return ComputeBorderRadii(StyleBorder()->mBorderRadius,
+ aFrameSize, aBorderArea,
+ aSkipSides, aRadii);
+}
+
+bool
+nsIFrame::GetBorderRadii(nscoord aRadii[8]) const
+{
+ nsSize sz = GetSize();
+ return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
+}
+
+bool
+nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const
+{
+ if (!GetBorderRadii(aRadii))
+ return false;
+ InsetBorderRadii(aRadii, GetUsedBorder());
+ NS_FOR_CSS_HALF_CORNERS(corner) {
+ if (aRadii[corner])
+ return true;
+ }
+ return false;
+}
+
+bool
+nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const
+{
+ if (!GetBorderRadii(aRadii))
+ return false;
+ InsetBorderRadii(aRadii, GetUsedBorderAndPadding());
+ NS_FOR_CSS_HALF_CORNERS(corner) {
+ if (aRadii[corner])
+ return true;
+ }
+ return false;
+}
+
+nsStyleContext*
+nsFrame::GetAdditionalStyleContext(int32_t aIndex) const
+{
+ NS_PRECONDITION(aIndex >= 0, "invalid index number");
+ return nullptr;
+}
+
+void
+nsFrame::SetAdditionalStyleContext(int32_t aIndex,
+ nsStyleContext* aStyleContext)
+{
+ NS_PRECONDITION(aIndex >= 0, "invalid index number");
+}
+
+nscoord
+nsFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+ NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
+ "frame must not be dirty");
+ // Baseline for inverted line content is the top (block-start) margin edge,
+ // as the frame is in effect "flipped" for alignment purposes.
+ if (aWritingMode.IsLineInverted()) {
+ return -GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
+ }
+ // Otherwise, the bottom margin edge, per CSS2.1's definition of the
+ // 'baseline' value of 'vertical-align'.
+ return BSize(aWritingMode) +
+ GetLogicalUsedMargin(aWritingMode).BEnd(aWritingMode);
+}
+
+const nsFrameList&
+nsFrame::GetChildList(ChildListID aListID) const
+{
+ if (IsAbsoluteContainer() &&
+ aListID == GetAbsoluteListID()) {
+ return GetAbsoluteContainingBlock()->GetChildList();
+ } else {
+ return nsFrameList::EmptyList();
+ }
+}
+
+void
+nsFrame::GetChildLists(nsTArray<ChildList>* aLists) const
+{
+ if (IsAbsoluteContainer()) {
+ nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
+ absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
+ }
+}
+
+void
+nsIFrame::GetCrossDocChildLists(nsTArray<ChildList>* aLists)
+{
+ nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
+ if (subdocumentFrame) {
+ // Descend into the subdocument
+ nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
+ if (root) {
+ aLists->AppendElement(nsIFrame::ChildList(
+ nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
+ nsIFrame::kPrincipalList));
+ }
+ }
+
+ GetChildLists(aLists);
+}
+
+Visibility
+nsIFrame::GetVisibility() const
+{
+ if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
+ return Visibility::UNTRACKED;
+ }
+
+ bool isSet = false;
+ FrameProperties props = Properties();
+ uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
+
+ MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
+ "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
+
+ return visibleCount > 0
+ ? Visibility::APPROXIMATELY_VISIBLE
+ : Visibility::APPROXIMATELY_NONVISIBLE;
+}
+
+void
+nsIFrame::UpdateVisibilitySynchronously()
+{
+ nsIPresShell* presShell = PresContext()->PresShell();
+ if (!presShell) {
+ return;
+ }
+
+ if (presShell->AssumeAllFramesVisible()) {
+ presShell->EnsureFrameInApproximatelyVisibleList(this);
+ return;
+ }
+
+ bool visible = true;
+ nsIFrame* f = GetParent();
+ nsRect rect = GetRectRelativeToSelf();
+ nsIFrame* rectFrame = this;
+ while (f) {
+ nsIScrollableFrame* sf = do_QueryFrame(f);
+ if (sf) {
+ nsRect transformedRect =
+ nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
+ if (!sf->IsRectNearlyVisible(transformedRect)) {
+ visible = false;
+ break;
+ }
+
+ // In this code we're trying to synchronously update *approximate*
+ // visibility. (In the future we may update precise visibility here as
+ // well, which is why the method name does not contain 'approximate'.) The
+ // IsRectNearlyVisible() check above tells us that the rect we're checking
+ // is approximately visible within the scrollframe, but we still need to
+ // ensure that, even if it was scrolled into view, it'd be visible when we
+ // consider the rest of the document. To do that, we move transformedRect
+ // to be contained in the scrollport as best we can (it might not fit) to
+ // pretend that it was scrolled into view.
+ rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
+ rectFrame = f;
+ }
+ nsIFrame* parent = f->GetParent();
+ if (!parent) {
+ parent = nsLayoutUtils::GetCrossDocParentFrame(f);
+ if (parent && parent->PresContext()->IsChrome()) {
+ break;
+ }
+ }
+ f = parent;
+ }
+
+ if (visible) {
+ presShell->EnsureFrameInApproximatelyVisibleList(this);
+ } else {
+ presShell->RemoveFrameFromApproximatelyVisibleList(this);
+ }
+}
+
+void
+nsIFrame::EnableVisibilityTracking()
+{
+ if (GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED) {
+ return; // Nothing to do.
+ }
+
+ FrameProperties props = Properties();
+ MOZ_ASSERT(!props.Has(VisibilityStateProperty()),
+ "Shouldn't have a VisibilityStateProperty value "
+ "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
+
+ // Add the state bit so we know to track visibility for this frame, and
+ // initialize the frame property.
+ AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
+ props.Set(VisibilityStateProperty(), 0);
+
+ nsIPresShell* presShell = PresContext()->PresShell();
+ if (!presShell) {
+ return;
+ }
+
+ // Schedule a visibility update. This method will virtually always be called
+ // when layout has changed anyway, so it's very unlikely that any additional
+ // visibility updates will be triggered by this, but this way we guarantee
+ // that if this frame is currently visible we'll eventually find out.
+ presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
+}
+
+void
+nsIFrame::DisableVisibilityTracking()
+{
+ if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
+ return; // Nothing to do.
+ }
+
+ bool isSet = false;
+ FrameProperties props = Properties();
+ uint32_t visibleCount = props.Remove(VisibilityStateProperty(), &isSet);
+
+ MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
+ "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
+
+ RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
+
+ if (visibleCount == 0) {
+ return; // We were nonvisible.
+ }
+
+ // We were visible, so send an OnVisibilityChange() notification.
+ OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE);
+}
+
+void
+nsIFrame::DecApproximateVisibleCount(Maybe<OnNonvisible> aNonvisibleAction
+ /* = Nothing() */)
+{
+ MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
+
+ bool isSet = false;
+ FrameProperties props = Properties();
+ uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
+
+ MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
+ "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
+ MOZ_ASSERT(visibleCount > 0, "Frame is already nonvisible and we're "
+ "decrementing its visible count?");
+
+ visibleCount--;
+ props.Set(VisibilityStateProperty(), visibleCount);
+ if (visibleCount > 0) {
+ return;
+ }
+
+ // We just became nonvisible, so send an OnVisibilityChange() notification.
+ OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE, aNonvisibleAction);
+}
+
+void
+nsIFrame::IncApproximateVisibleCount()
+{
+ MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
+
+ bool isSet = false;
+ FrameProperties props = Properties();
+ uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
+
+ MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
+ "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
+
+ visibleCount++;
+ props.Set(VisibilityStateProperty(), visibleCount);
+ if (visibleCount > 1) {
+ return;
+ }
+
+ // We just became visible, so send an OnVisibilityChange() notification.
+ OnVisibilityChange(Visibility::APPROXIMATELY_VISIBLE);
+}
+
+void
+nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
+ Maybe<OnNonvisible> aNonvisibleAction
+ /* = Nothing() */)
+{
+ // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
+ // images here.
+}
+
+static nsIFrame*
+GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
+{
+ nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
+ if (capturingContent) {
+ nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
+ return activeFrame ? activeFrame : aFrame;
+ }
+
+ return aFrame;
+}
+
+int16_t
+nsFrame::DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn)
+{
+ int16_t selType = nsISelectionController::SELECTION_OFF;
+
+ nsCOMPtr<nsISelectionController> selCon;
+ nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
+ if (NS_SUCCEEDED(result) && selCon) {
+ result = selCon->GetDisplaySelection(&selType);
+ if (NS_SUCCEEDED(result) && (selType != nsISelectionController::SELECTION_OFF)) {
+ // Check whether style allows selection.
+ bool selectable;
+ IsSelectable(&selectable, nullptr);
+ if (!selectable) {
+ selType = nsISelectionController::SELECTION_OFF;
+ isOkToTurnOn = false;
+ }
+ }
+ if (isOkToTurnOn && (selType == nsISelectionController::SELECTION_OFF)) {
+ selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+ selType = nsISelectionController::SELECTION_ON;
+ }
+ }
+ return selType;
+}
+
+class nsDisplaySelectionOverlay : public nsDisplayItem {
+public:
+ nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
+ nsFrame* aFrame, int16_t aSelectionValue)
+ : nsDisplayItem(aBuilder, aFrame), mSelectionValue(aSelectionValue) {
+ MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplaySelectionOverlay() {
+ MOZ_COUNT_DTOR(nsDisplaySelectionOverlay);
+ }
+#endif
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
+private:
+ int16_t mSelectionValue;
+};
+
+void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
+
+ LookAndFeel::ColorID colorID;
+ if (mSelectionValue == nsISelectionController::SELECTION_ON) {
+ colorID = LookAndFeel::eColorID_TextSelectBackground;
+ } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
+ colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention;
+ } else {
+ colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled;
+ }
+
+ Color c = Color::FromABGR(LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
+ c.a = .5;
+ ColorPattern color(ToDeviceColor(c));
+
+ nsIntRect pxRect =
+ mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
+ Rect rect(pxRect.x, pxRect.y, pxRect.width, pxRect.height);
+ MaybeSnapToDevicePixels(rect, aDrawTarget, true);
+
+ aDrawTarget.FillRect(rect, color);
+}
+
+/********************************************************
+* Refreshes each content's frame
+*********************************************************/
+
+void
+nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
+ nsDisplayList* aList,
+ uint16_t aContentType)
+{
+ if (!IsSelected() || !IsVisibleForPainting(aBuilder))
+ return;
+
+ nsPresContext* presContext = PresContext();
+ nsIPresShell *shell = presContext->PresShell();
+ if (!shell)
+ return;
+
+ int16_t displaySelection = shell->GetSelectionFlags();
+ if (!(displaySelection & aContentType))
+ return;
+
+ const nsFrameSelection* frameSelection = GetConstFrameSelection();
+ int16_t selectionValue = frameSelection->GetDisplaySelection();
+
+ if (selectionValue <= nsISelectionController::SELECTION_HIDDEN)
+ return; // selection is hidden or off
+
+ nsIContent *newContent = mContent->GetParent();
+
+ //check to see if we are anonymous content
+ int32_t offset = 0;
+ if (newContent) {
+ // XXXbz there has GOT to be a better way of determining this!
+ offset = newContent->IndexOf(mContent);
+ }
+
+ SelectionDetails *details;
+ //look up to see what selection(s) are on this frame
+ details = frameSelection->LookUpSelection(newContent, offset, 1, false);
+ if (!details)
+ return;
+
+ bool normal = false;
+ while (details) {
+ if (details->mSelectionType == SelectionType::eNormal) {
+ normal = true;
+ }
+ SelectionDetails *next = details->mNext;
+ delete details;
+ details = next;
+ }
+
+ if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
+ // Don't overlay an image if it's not in the primary selection.
+ return;
+ }
+
+ aList->AppendNewToTop(new (aBuilder)
+ nsDisplaySelectionOverlay(aBuilder, this, selectionValue));
+}
+
+void
+nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists)
+{
+ if (StyleOutline()->mOutlineStyle == NS_STYLE_BORDER_STYLE_NONE) {
+ return;
+ }
+
+ aLists.Outlines()->AppendNewToTop(
+ new (aBuilder) nsDisplayOutline(aBuilder, this));
+}
+
+void
+nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ DisplayOutlineUnconditional(aBuilder, aLists);
+}
+
+void
+nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect, nsDisplayList* aList)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ aList->AppendNewToTop(new (aBuilder) nsDisplayCaret(aBuilder, this));
+}
+
+nscolor
+nsIFrame::GetCaretColorAt(int32_t aOffset)
+{
+ // Use text color.
+ return StyleColor()->mColor;
+}
+
+bool
+nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists,
+ bool aForceBackground)
+{
+ // Here we don't try to detect background propagation. Frames that might
+ // receive a propagated background should just set aForceBackground to
+ // true.
+ if (aBuilder->IsForEventDelivery() || aForceBackground ||
+ !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) {
+ return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
+ aBuilder, this, GetRectRelativeToSelf(), aLists.BorderBackground());
+ }
+ return false;
+}
+
+void
+nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists,
+ bool aForceBackground)
+{
+ // The visibility check belongs here since child elements have the
+ // opportunity to override the visibility property and display even if
+ // their parent is hidden.
+ if (!IsVisibleForPainting(aBuilder)) {
+ return;
+ }
+
+ nsCSSShadowArray* shadows = StyleEffects()->mBoxShadow;
+ if (shadows && shadows->HasShadowWithInset(false)) {
+ aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+ nsDisplayBoxShadowOuter(aBuilder, this));
+ }
+
+ bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists,
+ aForceBackground);
+
+ if (shadows && shadows->HasShadowWithInset(true)) {
+ aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+ nsDisplayBoxShadowInner(aBuilder, this));
+ }
+
+ // If there's a themed background, we should not create a border item.
+ // It won't be rendered.
+ if (!bgIsThemed && StyleBorder()->HasBorder()) {
+ aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+ nsDisplayBorder(aBuilder, this));
+ }
+
+ DisplayOutlineUnconditional(aBuilder, aLists);
+}
+
+inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame)
+{
+ // The CSS spec says that the 'clip' property only applies to absolutely
+ // positioned elements, whereas the SVG spec says that it applies to SVG
+ // elements regardless of the value of the 'position' property. Here we obey
+ // the CSS spec for outer-<svg> (since that's what we generally do), but
+ // obey the SVG spec for other SVG elements to which 'clip' applies.
+ return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
+ aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
+ nsGkAtoms::foreignObject);
+}
+
+Maybe<nsRect>
+nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
+ const nsStyleEffects* aEffects,
+ const nsSize& aSize) const
+{
+ if (!(aEffects->mClipFlags & NS_STYLE_CLIP_RECT) ||
+ !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
+ return Nothing();
+ }
+
+ nsRect rect = aEffects->mClip;
+ if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Slice)) {
+ // The clip applies to the joined boxes so it's relative the first
+ // continuation.
+ nscoord y = 0;
+ for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
+ y += f->GetRect().height;
+ }
+ rect.MoveBy(nsPoint(0, -y));
+ }
+
+ if (NS_STYLE_CLIP_RIGHT_AUTO & aEffects->mClipFlags) {
+ rect.width = aSize.width - rect.x;
+ }
+ if (NS_STYLE_CLIP_BOTTOM_AUTO & aEffects->mClipFlags) {
+ rect.height = aSize.height - rect.y;
+ }
+ return Some(rect);
+}
+
+/**
+ * If the CSS 'overflow' property applies to this frame, and is not
+ * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
+ * for that overflow in aBuilder->ClipState() to clip all containing-block
+ * descendants.
+ */
+static void
+ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
+ const nsIFrame* aFrame,
+ const nsStyleDisplay* aDisp,
+ DisplayListClipState::AutoClipMultiple& aClipState)
+{
+ // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
+ // frames, and any non-visible value for blocks in a paginated context).
+ // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
+ // is required by comboboxes which make their display text (an inline frame)
+ // have clipping.
+ if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) {
+ return;
+ }
+ nsRect clipRect;
+ bool haveRadii = false;
+ nscoord radii[8];
+ if (aFrame->StyleDisplay()->mOverflowClipBox ==
+ NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX) {
+ clipRect = aFrame->GetPaddingRectRelativeToSelf() +
+ aBuilder->ToReferenceFrame(aFrame);
+ haveRadii = aFrame->GetPaddingBoxBorderRadii(radii);
+ } else {
+ clipRect = aFrame->GetContentRectRelativeToSelf() +
+ aBuilder->ToReferenceFrame(aFrame);
+ // XXX border-radius
+ }
+ aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr);
+}
+
+#ifdef DEBUG
+static void PaintDebugBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ nsRect r(aPt, aFrame->GetSize());
+ int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+ Color blueOrRed(aFrame->HasView() ? Color(0.f, 0.f, 1.f, 1.f) :
+ Color(1.f, 0.f, 0.f, 1.f));
+ aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
+ ColorPattern(ToDeviceColor(blueOrRed)));
+}
+
+static void PaintEventTargetBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ nsRect r(aPt, aFrame->GetSize());
+ int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+ ColorPattern purple(ToDeviceColor(Color(.5f, 0.f, .5f, 1.f)));
+ aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
+}
+
+static void
+DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+ const nsDisplayListSet& aLists) {
+ // Draw a border around the child
+ // REVIEW: From nsContainerFrame::PaintChild
+ if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
+ aLists.Outlines()->AppendNewToTop(new (aBuilder)
+ nsDisplayGeneric(aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
+ nsDisplayItem::TYPE_DEBUG_BORDER));
+ }
+ // Draw a border around the current event target
+ if (nsFrame::GetShowEventTargetFrameBorder() &&
+ aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) {
+ aLists.Outlines()->AppendNewToTop(new (aBuilder)
+ nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
+ nsDisplayItem::TYPE_EVENT_TARGET_BORDER));
+ }
+}
+#endif
+
+static bool
+IsScrollFrameActive(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
+{
+ return aScrollableFrame && aScrollableFrame->IsScrollingActive(aBuilder);
+}
+
+class AutoSaveRestoreContainsBlendMode
+{
+ nsDisplayListBuilder& mBuilder;
+ bool mSavedContainsBlendMode;
+public:
+ explicit AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder& aBuilder)
+ : mBuilder(aBuilder)
+ , mSavedContainsBlendMode(aBuilder.ContainsBlendMode())
+ { }
+
+ ~AutoSaveRestoreContainsBlendMode() {
+ mBuilder.SetContainsBlendMode(mSavedContainsBlendMode);
+ }
+};
+
+static void
+CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+{
+ nsIContent* content = aFrame->GetContent();
+ if (!content) {
+ return;
+ }
+
+ if (content->IsNodeApzAware()) {
+ aBuilder->SetAncestorHasApzAwareEventHandler(true);
+ }
+}
+
+/**
+ * True if aDescendant participates the context aAncestor participating.
+ */
+static bool
+FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
+ MOZ_ASSERT(aAncestor != aDescendant);
+ MOZ_ASSERT(aAncestor->Extend3DContext());
+ nsIFrame* frame;
+ for (frame = nsLayoutUtils::GetCrossDocParentFrame(aDescendant);
+ frame && aAncestor != frame;
+ frame = nsLayoutUtils::GetCrossDocParentFrame(frame)) {
+ if (!frame->Extend3DContext()) {
+ return false;
+ }
+ }
+ MOZ_ASSERT(frame == aAncestor);
+ return true;
+}
+
+static bool
+ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
+{
+ nsIFrame* transformFrame;
+ if (aItem->GetType() == nsDisplayItem::TYPE_TRANSFORM) {
+ transformFrame = aItem->Frame();
+ } else if (aItem->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
+ transformFrame = static_cast<nsDisplayPerspective*>(aItem)->TransformFrame();
+ } else {
+ return false;
+ }
+ if (aAncestor == transformFrame) {
+ return true;
+ }
+ return FrameParticipatesIn3DContext(aAncestor, transformFrame);
+}
+
+static void
+WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+ nsRect& aDirtyRect,
+ nsDisplayList* aSource, nsDisplayList* aTarget,
+ int aIndex) {
+ if (!aSource->IsEmpty()) {
+ nsDisplayTransform *sepIdItem =
+ new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aSource,
+ aDirtyRect, Matrix4x4(), aIndex);
+ sepIdItem->SetNoExtendContext();
+ aTarget->AppendToTop(sepIdItem);
+ }
+}
+
+void
+nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ nsDisplayList* aList) {
+ if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
+ return;
+
+ // Replaced elements have their visibility handled here, because
+ // they're visually atomic
+ if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder))
+ return;
+
+ const nsStyleDisplay* disp = StyleDisplay();
+ const nsStyleEffects* effects = StyleEffects();
+ // We can stop right away if this is a zero-opacity stacking context and
+ // we're painting, and we're not animating opacity. Don't do this
+ // if we're going to compute plugin geometry, since opacity-0 plugins
+ // need to have display items built for them.
+ bool needEventRegions =
+ aBuilder->IsBuildingLayerEventRegions() &&
+ StyleUserInterface()->GetEffectivePointerEvents(this) !=
+ NS_STYLE_POINTER_EVENTS_NONE;
+ bool opacityItemForEventsAndPluginsOnly = false;
+ if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
+ !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
+ !nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_opacity)) {
+ if (needEventRegions ||
+ aBuilder->WillComputePluginGeometry()) {
+ opacityItemForEventsAndPluginsOnly = true;
+ } else {
+ return;
+ }
+ }
+
+ if (disp->mWillChangeBitField != 0) {
+ aBuilder->AddToWillChangeBudget(this, GetSize());
+ }
+
+ bool extend3DContext = Extend3DContext();
+ Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
+ if (extend3DContext && !Combines3DTransformWithAncestors()) {
+ // Start a new preserves3d context to keep informations on
+ // nsDisplayListBuilder.
+ autoPreserves3DContext.emplace(aBuilder);
+ // Save dirty rect on the builder to avoid being distorted for
+ // multiple transforms along the chain.
+ aBuilder->SetPreserves3DDirtyRect(aDirtyRect);
+ }
+
+ // For preserves3d, use the dirty rect already installed on the
+ // builder, since aDirtyRect maybe distorted for transforms along
+ // the chain.
+ nsRect dirtyRect = aDirtyRect;
+
+ bool inTransform = aBuilder->IsInTransform();
+ bool isTransformed = IsTransformed();
+ bool hasPerspective = HasPerspective();
+ // reset blend mode so we can keep track if this stacking context needs have
+ // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
+ // so we keep track if the parent stacking context needs a container too.
+ AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
+ aBuilder->SetContainsBlendMode(false);
+
+ nsRect dirtyRectOutsideTransform = dirtyRect;
+ if (isTransformed) {
+ const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
+ if (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder,
+ this)) {
+ dirtyRect = overflow;
+ } else {
+ if (overflow.IsEmpty() && !extend3DContext) {
+ return;
+ }
+
+ // If we're in preserve-3d then grab the dirty rect that was given to the root
+ // and transform using the combined transform.
+ if (Combines3DTransformWithAncestors()) {
+ dirtyRect = aBuilder->GetPreserves3DDirtyRect(this);
+ }
+
+ nsRect untransformedDirtyRect;
+ if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
+ &untransformedDirtyRect)) {
+ dirtyRect = untransformedDirtyRect;
+ } else {
+ NS_WARNING("Unable to untransform dirty rect!");
+ // This should only happen if the transform is singular, in which case nothing is visible anyway
+ dirtyRect.SetEmpty();
+ }
+ }
+ inTransform = true;
+ }
+ bool usingFilter = StyleEffects()->HasFilters();
+ bool usingMask = nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
+ bool usingSVGEffects = usingFilter || usingMask;
+
+ nsRect dirtyRectOutsideSVGEffects = dirtyRect;
+ nsDisplayList hoistedScrollInfoItemsStorage;
+ if (usingSVGEffects) {
+ dirtyRect =
+ nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
+ aBuilder->EnterSVGEffectsContents(&hoistedScrollInfoItemsStorage);
+ }
+
+ // We build an opacity item if it's not going to be drawn by SVG content, or
+ // SVG effects. SVG effects won't handle the opacity if we want an active
+ // layer (for async animations), see
+ // nsSVGIntegrationsUtils::PaintMaskAndClipPath or
+ // nsSVGIntegrationsUtils::PaintFilter.
+ bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this) &&
+ (!usingSVGEffects || nsDisplayOpacity::NeedsActiveLayer(aBuilder, this));
+ bool useBlendMode = effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
+ bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
+ IsScrollFrameActive(aBuilder,
+ nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
+ bool useFixedPosition = nsLayoutUtils::IsFixedPosFrameInDisplayPort(this);
+
+ nsDisplayListBuilder::AutoBuildingDisplayList
+ buildingDisplayList(aBuilder, this, dirtyRect, true);
+
+ // Depending on the effects that are applied to this frame, we can create
+ // multiple container display items and wrap them around our contents.
+ // This enum lists all the potential container display items, in the order
+ // outside to inside.
+ enum class ContainerItemType : uint8_t {
+ eNone = 0,
+ eOwnLayerIfNeeded,
+ eBlendMode,
+ eFixedPosition,
+ eStickyPosition,
+ eOwnLayerForTransformWithRoundedClip,
+ ePerspective,
+ eTransform,
+ eSeparatorTransforms,
+ eOpacity,
+ eFilter,
+ eBlendContainer
+ };
+
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+
+ // If there is a current clip, then depending on the container items we
+ // create, different things can happen to it. Some container items simply
+ // propagate the clip to their children and aren't clipped themselves.
+ // But other container items, especially those that establish a different
+ // geometry for their contents (e.g. transforms), capture the clip on
+ // themselves and unset the clip for their contents. If we create more than
+ // one of those container items, the clip will be captured on the outermost
+ // one and the inner container items will be unclipped.
+ ContainerItemType clipCapturedBy = ContainerItemType::eNone;
+ if (useFixedPosition) {
+ clipCapturedBy = ContainerItemType::eFixedPosition;
+ } else if (useStickyPosition) {
+ clipCapturedBy = ContainerItemType::eStickyPosition;
+ } else if (isTransformed) {
+ if ((hasPerspective || extend3DContext) && clipState.SavedStateHasRoundedCorners()) {
+ // If we're creating an nsDisplayTransform item that is going to combine
+ // its transform with its children (preserve-3d or perspective), then we
+ // can't have an intermediate surface. Mask layers force an intermediate
+ // surface, so if we're going to need both then create a separate
+ // wrapping layer for the mask.
+ clipCapturedBy = ContainerItemType::eOwnLayerForTransformWithRoundedClip;
+ } else if (hasPerspective) {
+ clipCapturedBy = ContainerItemType::ePerspective;
+ } else {
+ clipCapturedBy = ContainerItemType::eTransform;
+ }
+ } else if (usingFilter) {
+ clipCapturedBy = ContainerItemType::eFilter;
+ }
+
+ bool clearClip = false;
+ if (clipCapturedBy != ContainerItemType::eNone) {
+ // We don't need to pass ancestor clipping down to our children;
+ // everything goes inside a display item's child list, and the display
+ // item itself will be clipped.
+ // For transforms we also need to clear ancestor clipping because it's
+ // relative to the wrong display item reference frame anyway.
+ clearClip = true;
+ }
+
+ clipState.EnterStackingContextContents(clearClip);
+
+ nsDisplayListCollection set;
+ {
+ DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
+ nsDisplayListBuilder::AutoInTransformSetter
+ inTransformSetter(aBuilder, inTransform);
+ nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex
+ perspectiveIndex(aBuilder, this);
+
+ CheckForApzAwareEventHandlers(aBuilder, this);
+
+ Maybe<nsRect> clipPropClip = GetClipPropClipRect(disp, effects, GetSize());
+ if (clipPropClip) {
+ dirtyRect.IntersectRect(dirtyRect, *clipPropClip);
+ nestedClipState.ClipContentDescendants(
+ *clipPropClip + aBuilder->ToReferenceFrame(this));
+ }
+
+ // extend3DContext also guarantees that applyAbsPosClipping and usingSVGEffects are false
+ // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
+ if (extend3DContext) {
+ // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
+ // going to be forced to descend into frames.
+ aBuilder->MarkPreserve3DFramesForDisplayList(this);
+ }
+
+ MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
+
+ nsDisplayLayerEventRegions* eventRegions = nullptr;
+ if (aBuilder->IsBuildingLayerEventRegions()) {
+ eventRegions = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
+ eventRegions->AddFrame(aBuilder, this);
+ aBuilder->SetLayerEventRegions(eventRegions);
+ }
+ aBuilder->AdjustWindowDraggingRegion(this);
+ BuildDisplayList(aBuilder, dirtyRect, set);
+ if (eventRegions) {
+ // If the event regions item ended up empty, throw it away rather than
+ // adding it to the display list.
+ if (!eventRegions->IsEmpty()) {
+ set.BorderBackground()->AppendToBottom(eventRegions);
+ } else {
+ aBuilder->SetLayerEventRegions(nullptr);
+ eventRegions->~nsDisplayLayerEventRegions();
+ eventRegions = nullptr;
+ }
+ }
+ }
+
+ if (aBuilder->IsBackgroundOnly()) {
+ set.BlockBorderBackgrounds()->DeleteAll();
+ set.Floats()->DeleteAll();
+ set.Content()->DeleteAll();
+ set.PositionedDescendants()->DeleteAll();
+ set.Outlines()->DeleteAll();
+ }
+
+ // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
+ // in content document order and SortByZOrder is a stable sort which
+ // guarantees that boxes produced by the same element are placed together
+ // in the sort. Consider a position:relative inline element that breaks
+ // across lines and has absolutely positioned children; all the abs-pos
+ // children should be z-ordered after all the boxes for the position:relative
+ // element itself.
+ set.PositionedDescendants()->SortByZOrder();
+
+ nsDisplayList resultList;
+ // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
+ // 1,2: backgrounds and borders
+ resultList.AppendToTop(set.BorderBackground());
+ // 3: negative z-index children.
+ for (;;) {
+ nsDisplayItem* item = set.PositionedDescendants()->GetBottom();
+ if (item && item->ZIndex() < 0) {
+ set.PositionedDescendants()->RemoveBottom();
+ resultList.AppendToTop(item);
+ continue;
+ }
+ break;
+ }
+ // 4: block backgrounds
+ resultList.AppendToTop(set.BlockBorderBackgrounds());
+ // 5: floats
+ resultList.AppendToTop(set.Floats());
+ // 7: general content
+ resultList.AppendToTop(set.Content());
+ // 7.5: outlines, in content tree order. We need to sort by content order
+ // because an element with outline that breaks and has children with outline
+ // might have placed child outline items between its own outline items.
+ // The element's outline items need to all come before any child outline
+ // items.
+ nsIContent* content = GetContent();
+ if (!content) {
+ content = PresContext()->Document()->GetRootElement();
+ }
+ if (content) {
+ set.Outlines()->SortByContentOrder(content);
+ }
+#ifdef DEBUG
+ DisplayDebugBorders(aBuilder, this, set);
+#endif
+ resultList.AppendToTop(set.Outlines());
+ // 8, 9: non-negative z-index children
+ resultList.AppendToTop(set.PositionedDescendants());
+
+ // Get the scroll clip to use for the container items that we create here.
+ // If we cleared the clip, and we create multiple container items, then the
+ // items we create before we restore the clip will have a different scroll
+ // clip from the items we create after we restore the clip.
+ const DisplayItemScrollClip* containerItemScrollClip =
+ aBuilder->ClipState().CurrentAncestorScrollClipForStackingContextContents();
+
+ /* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
+ * same list, the nsDisplayBlendContainer should be added first. This only
+ * happens when the element creating this stacking context has mix-blend-mode
+ * and also contains a child which has mix-blend-mode.
+ * The nsDisplayBlendContainer must be added to the list first, so it does not
+ * isolate the containing element blending as well.
+ */
+
+ if (aBuilder->ContainsBlendMode()) {
+ DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
+ blendContainerClipState.Clear();
+ resultList.AppendNewToTop(
+ nsDisplayBlendContainer::CreateForMixBlendMode(aBuilder, this, &resultList,
+ containerItemScrollClip));
+ }
+
+ /* If there are any SVG effects, wrap the list up in an SVG effects item
+ * (which also handles CSS group opacity). Note that we create an SVG effects
+ * item even if resultList is empty, since a filter can produce graphical
+ * output even if the element being filtered wouldn't otherwise do so.
+ */
+ if (usingSVGEffects) {
+ MOZ_ASSERT(usingFilter ||usingMask,
+ "Beside filter & mask/clip-path, what else effect do we have?");
+
+ if (clipCapturedBy == ContainerItemType::eFilter) {
+ clipState.ExitStackingContextContents(&containerItemScrollClip);
+ }
+ // Revert to the post-filter dirty rect.
+ buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects);
+
+ // Skip all filter effects while generating glyph mask.
+ if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
+ // If we are going to create a mask display item, handle opacity effect
+ // in that mask display item; Otherwise, take care of opacity in this
+ // filter display item.
+ bool handleOpacity = !usingMask && !useOpacity;
+
+ /* List now emptied, so add the new list to the top. */
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayFilter(aBuilder, this, &resultList,
+ handleOpacity));
+ }
+
+ if (usingMask) {
+ DisplayListClipState::AutoSaveRestore maskClipState(aBuilder);
+ maskClipState.Clear();
+ /* List now emptied, so add the new list to the top. */
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayMask(aBuilder, this, &resultList,
+ !useOpacity, containerItemScrollClip));
+ }
+
+ // Also add the hoisted scroll info items. We need those for APZ scrolling
+ // because nsDisplayMask items can't build active layers.
+ aBuilder->ExitSVGEffectsContents();
+ resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
+ }
+
+ /* If the list is non-empty and there is CSS group opacity without SVG
+ * effects, wrap it up in an opacity item.
+ */
+ if (useOpacity && !resultList.IsEmpty()) {
+ // Don't clip nsDisplayOpacity items. We clip their descendants instead.
+ // The clip we would set on an element with opacity would clip
+ // all descendant content, but some should not be clipped.
+ DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
+ opacityClipState.Clear();
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList,
+ containerItemScrollClip, opacityItemForEventsAndPluginsOnly));
+ }
+
+ /* If we're going to apply a transformation and don't have preserve-3d set, wrap
+ * everything in an nsDisplayTransform. If there's nothing in the list, don't add
+ * anything.
+ *
+ * For the preserve-3d case we want to individually wrap every child in the list with
+ * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
+ * we can skip this step, as the computed transform will already include our own.
+ *
+ * We also traverse into sublists created by nsDisplayWrapList, so that we find all the
+ * correct children.
+ */
+ if (isTransformed && !resultList.IsEmpty() && extend3DContext) {
+ // Install dummy nsDisplayTransform as a leaf containing
+ // descendants not participating this 3D rendering context.
+ nsDisplayList nonparticipants;
+ nsDisplayList participants;
+ int index = 1;
+
+ while (nsDisplayItem* item = resultList.RemoveBottom()) {
+ if (ItemParticipatesIn3DContext(this, item) && !item->GetClip().HasClip()) {
+ // The frame of this item participates the same 3D context.
+ WrapSeparatorTransform(aBuilder, this, dirtyRect,
+ &nonparticipants, &participants, index++);
+ participants.AppendToTop(item);
+ } else {
+ // The frame of the item doesn't participate the current
+ // context, or has no transform.
+ //
+ // For items participating but not transformed, they are add
+ // to nonparticipants to get a separator layer for handling
+ // clips, if there is, on an intermediate surface.
+ // \see ContainerLayer::DefaultComputeEffectiveTransforms().
+ nonparticipants.AppendToTop(item);
+ }
+ }
+ WrapSeparatorTransform(aBuilder, this, dirtyRect,
+ &nonparticipants, &participants, index++);
+ resultList.AppendToTop(&participants);
+ }
+
+ if (isTransformed && !resultList.IsEmpty()) {
+ if (clipCapturedBy == ContainerItemType::eTransform) {
+ // Restore clip state now so nsDisplayTransform is clipped properly.
+ clipState.ExitStackingContextContents(&containerItemScrollClip);
+ }
+ // Revert to the dirtyrect coming in from the parent, without our transform
+ // taken into account.
+ buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
+ // Revert to the outer reference frame and offset because all display
+ // items we create from now on are outside the transform.
+ nsPoint toOuterReferenceFrame;
+ const nsIFrame* outerReferenceFrame = this;
+ if (this != aBuilder->RootReferenceFrame()) {
+ outerReferenceFrame =
+ aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
+ }
+ buildingDisplayList.SetReferenceFrameAndCurrentOffset(outerReferenceFrame,
+ GetOffsetToCrossDoc(outerReferenceFrame));
+
+ bool isFullyVisible =
+ dirtyRectOutsideSVGEffects.Contains(GetVisualOverflowRectRelativeToSelf());
+ nsDisplayTransform *transformItem =
+ new (aBuilder) nsDisplayTransform(aBuilder, this,
+ &resultList, dirtyRect, 0,
+ isFullyVisible);
+ resultList.AppendNewToTop(transformItem);
+
+ if (hasPerspective) {
+ if (clipCapturedBy == ContainerItemType::ePerspective) {
+ clipState.ExitStackingContextContents(&containerItemScrollClip);
+ }
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayPerspective(
+ aBuilder, this,
+ GetContainingBlock()->GetContent()->GetPrimaryFrame(), &resultList));
+ }
+ }
+
+ if (clipCapturedBy == ContainerItemType::eOwnLayerForTransformWithRoundedClip) {
+ clipState.ExitStackingContextContents(&containerItemScrollClip);
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayOwnLayer(aBuilder, this, &resultList, 0,
+ mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
+ 0.0f, /* aForceActive = */ false));
+ }
+
+ /* If we have sticky positioning, wrap it in a sticky position item.
+ */
+ if (useFixedPosition) {
+ if (clipCapturedBy == ContainerItemType::eFixedPosition) {
+ clipState.ExitStackingContextContents(&containerItemScrollClip);
+ }
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayFixedPosition(aBuilder, this, &resultList));
+ } else if (useStickyPosition) {
+ if (clipCapturedBy == ContainerItemType::eStickyPosition) {
+ clipState.ExitStackingContextContents(&containerItemScrollClip);
+ }
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList));
+ }
+
+ /* If there's blending, wrap up the list in a blend-mode item. Note
+ * that opacity can be applied before blending as the blend color is
+ * not affected by foreground opacity (only background alpha).
+ */
+
+ if (useBlendMode && !resultList.IsEmpty()) {
+ DisplayListClipState::AutoSaveRestore mixBlendClipState(aBuilder);
+ mixBlendClipState.Clear();
+ resultList.AppendNewToTop(
+ new (aBuilder) nsDisplayBlendMode(aBuilder, this, &resultList,
+ effects->mMixBlendMode,
+ containerItemScrollClip));
+ }
+
+ CreateOwnLayerIfNeeded(aBuilder, &resultList);
+
+ aList->AppendToTop(&resultList);
+}
+
+static nsDisplayItem*
+WrapInWrapList(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame, nsDisplayList* aList,
+ const DisplayItemScrollClip* aScrollClip)
+{
+ nsDisplayItem* item = aList->GetBottom();
+ if (!item) {
+ return nullptr;
+ }
+
+ // For perspective items we want to treat the 'frame' as being the transform
+ // frame that created it. This stops the transform frame from wrapping another
+ // nsDisplayWrapList around it (with mismatching reference frames), but still
+ // makes the perspective frame create one (so we have an atomic entry for z-index
+ // sorting).
+ nsIFrame *itemFrame = item->Frame();
+ if (item->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
+ itemFrame = static_cast<nsDisplayPerspective*>(item)->TransformFrame();
+ }
+
+ if (item->GetAbove() || itemFrame != aFrame) {
+ return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip);
+ }
+ aList->RemoveBottom();
+ return item;
+}
+
+void
+nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aChild,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists,
+ uint32_t aFlags) {
+ // If painting is restricted to just the background of the top level frame,
+ // then we have nothing to do here.
+ if (aBuilder->IsBackgroundOnly())
+ return;
+
+ if (aBuilder->IsForGenerateGlyphMask() ||
+ aBuilder->IsForPaintingSelectionBG()) {
+ if (nsGkAtoms::textFrame != aChild->GetType() && aChild->IsLeaf()) {
+ return;
+ }
+ }
+
+ nsIFrame* child = aChild;
+ if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
+ return;
+
+ bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT);
+
+ // true if this is a real or pseudo stacking context
+ bool pseudoStackingContext =
+ (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
+ if (!isSVG &&
+ (aFlags & DISPLAY_CHILD_INLINE) &&
+ !child->IsFrameOfType(eLineParticipant)) {
+ // child is a non-inline frame in an inline context, i.e.,
+ // it acts like inline-block or inline-table. Therefore it is a
+ // pseudo-stacking-context.
+ pseudoStackingContext = true;
+ }
+
+ // dirty rect in child-relative coordinates
+ nsRect dirty = aDirtyRect - child->GetOffsetTo(this);
+
+ nsIAtom* childType = child->GetType();
+ nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
+ bool isPlaceholder = false;
+ if (childType == nsGkAtoms::placeholderFrame) {
+ isPlaceholder = true;
+ nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child);
+ child = placeholder->GetOutOfFlowFrame();
+ NS_ASSERTION(child, "No out of flow frame?");
+ // If 'child' is a pushed float then it's owned by a block that's not an
+ // ancestor of the placeholder, and it will be painted by that block and
+ // should not be painted through the placeholder.
+ if (!child || nsLayoutUtils::IsPopup(child) ||
+ (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT))
+ return;
+ MOZ_ASSERT(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW);
+ // If the out-of-flow frame is in the top layer, the viewport frame
+ // will paint it. Skip it here. Note that, only out-of-flow frames
+ // with this property should be skipped, because non-HTML elements
+ // may stop their children from being out-of-flow. Those frames
+ // should still be handled in the normal in-flow path.
+ if (placeholder->GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) {
+ return;
+ }
+ // Make sure that any attempt to use childType below is disappointed. We
+ // could call GetType again but since we don't currently need it, let's
+ // avoid the virtual call.
+ childType = nullptr;
+ // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
+ if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
+ return;
+ savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
+ if (savedOutOfFlowData) {
+ dirty = savedOutOfFlowData->mDirtyRect;
+ } else {
+ // The out-of-flow frame did not intersect the dirty area. We may still
+ // need to traverse into it, since it may contain placeholders we need
+ // to enter to reach other out-of-flow frames that are visible.
+ dirty.SetEmpty();
+ }
+ pseudoStackingContext = true;
+ }
+
+ NS_ASSERTION(childType != nsGkAtoms::placeholderFrame,
+ "Should have dealt with placeholders already");
+ if (aBuilder->GetSelectedFramesOnly() &&
+ child->IsLeaf() &&
+ !aChild->IsSelected()) {
+ return;
+ }
+
+ if (aBuilder->GetIncludeAllOutOfFlows() &&
+ (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ dirty = child->GetVisualOverflowRect();
+ } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
+ // No need to descend into child to catch placeholders for visible
+ // positioned stuff. So see if we can short-circuit frame traversal here.
+
+ // We can stop if child's frame subtree's intersection with the
+ // dirty area is empty.
+ // If the child is a scrollframe that we want to ignore, then we need
+ // to descend into it because its scrolled child may intersect the dirty
+ // area even if the scrollframe itself doesn't.
+ // There are cases where the "ignore scroll frame" on the builder is not set
+ // correctly, and so we additionally want to catch cases where the child is
+ // a root scrollframe and we are ignoring scrolling on the viewport.
+ nsIPresShell* shell = PresContext()->PresShell();
+ bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() ||
+ (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame());
+ if (!keepDescending) {
+ nsRect childDirty;
+ if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect()))
+ return;
+ // Usually we could set dirty to childDirty now but there's no
+ // benefit, and it can be confusing. It can especially confuse
+ // situations where we're going to ignore a scrollframe's clipping;
+ // we wouldn't want to clip the dirty area to the scrollframe's
+ // bounds in that case.
+ }
+ }
+
+ // XXX need to have inline-block and inline-table set pseudoStackingContext
+
+ const nsStyleDisplay* ourDisp = StyleDisplay();
+ // REVIEW: Taken from nsBoxFrame::Paint
+ // Don't paint our children if the theme object is a leaf.
+ if (IsThemed(ourDisp) &&
+ !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
+ return;
+
+ // Since we're now sure that we're adding this frame to the display list
+ // (which means we're painting it, modulo occlusion), mark it as visible
+ // within the displayport.
+ if (aBuilder->IsPaintingToWindow() && child->TrackingVisibility()) {
+ child->PresContext()->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
+ }
+
+ // Child is composited if it's transformed, partially transparent, or has
+ // SVG effects or a blend mode..
+ const nsStyleDisplay* disp = child->StyleDisplay();
+ const nsStyleEffects* effects = child->StyleEffects();
+ const nsStylePosition* pos = child->StylePosition();
+ bool isVisuallyAtomic = child->HasOpacity()
+ || child->IsTransformed()
+ // strictly speaking, 'perspective' doesn't require visual atomicity,
+ // but the spec says it acts like the rest of these
+ || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord
+ || effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL
+ || nsSVGIntegrationUtils::UsingEffectsForFrame(child);
+
+ bool isPositioned = disp->IsAbsPosContainingBlock(child);
+ bool isStackingContext =
+ (isPositioned && (disp->IsPositionForcingStackingContext() ||
+ pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
+ (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
+ disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
+ isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
+
+ if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
+ ((effects->mClipFlags & NS_STYLE_CLIP_RECT) &&
+ IsSVGContentWithCSSClip(child)) ||
+ disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
+ (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
+ (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
+ // If you change this, also change IsPseudoStackingContextFromStyle()
+ pseudoStackingContext = true;
+ }
+ NS_ASSERTION(!isStackingContext || pseudoStackingContext,
+ "Stacking contexts must also be pseudo-stacking-contexts");
+
+ nsDisplayListBuilder::AutoBuildingDisplayList
+ buildingForChild(aBuilder, child, dirty, pseudoStackingContext);
+ DisplayListClipState::AutoClipMultiple clipState(aBuilder);
+ CheckForApzAwareEventHandlers(aBuilder, child);
+
+ if (savedOutOfFlowData) {
+ aBuilder->SetBuildingInvisibleItems(false);
+
+ clipState.SetClipForContainingBlockDescendants(
+ &savedOutOfFlowData->mContainingBlockClip);
+ clipState.SetScrollClipForContainingBlockDescendants(aBuilder,
+ savedOutOfFlowData->mContainingBlockScrollClip);
+ } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
+ isPlaceholder) {
+ NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect");
+ // Every item we build from now until we descent into an out of flow that
+ // does have saved out of flow data should be invisible. This state gets
+ // restored when AutoBuildingDisplayList gets out of scope.
+ aBuilder->SetBuildingInvisibleItems(true);
+
+ // If we have nested out-of-flow frames and the outer one isn't visible
+ // then we won't have stored clip data for it. We can just clear the clip
+ // instead since we know we won't render anything, and the inner out-of-flow
+ // frame will setup the correct clip for itself.
+ clipState.SetClipForContainingBlockDescendants(nullptr);
+ clipState.SetScrollClipForContainingBlockDescendants(aBuilder, nullptr);
+ }
+
+ // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
+ // or overflow:hidden on elements that don't support scrolling (and therefore
+ // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
+ // anything directly rendered by the parent, only the rendering of its
+ // children.
+ // Don't use overflowClip to restrict the dirty rect, since some of the
+ // descendants may not be clipped by it. Even if we end up with unnecessary
+ // display items, they'll be pruned during ComputeVisibility.
+ nsIFrame* parent = child->GetParent();
+ const nsStyleDisplay* parentDisp =
+ parent == this ? ourDisp : parent->StyleDisplay();
+ ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState);
+
+ nsDisplayList list;
+ nsDisplayList extraPositionedDescendants;
+ if (isStackingContext) {
+ if (effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
+ aBuilder->SetContainsBlendMode(true);
+ }
+ // True stacking context.
+ // For stacking contexts, BuildDisplayListForStackingContext handles
+ // clipping and MarkAbsoluteFramesForDisplayList.
+ child->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
+ aBuilder->DisplayCaret(child, dirty, &list);
+ } else {
+ Maybe<nsRect> clipPropClip =
+ child->GetClipPropClipRect(disp, effects, child->GetSize());
+ if (clipPropClip) {
+ dirty.IntersectRect(dirty, *clipPropClip);
+ clipState.ClipContentDescendants(
+ *clipPropClip + aBuilder->ToReferenceFrame(child));
+ }
+
+ child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
+
+ if (aBuilder->IsBuildingLayerEventRegions()) {
+ // If this frame has a different animated geometry root than its parent,
+ // make sure we accumulate event regions for its layer.
+ if (buildingForChild.IsAnimatedGeometryRoot() || isPositioned) {
+ nsDisplayLayerEventRegions* eventRegions =
+ new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child);
+ eventRegions->AddFrame(aBuilder, child);
+ aBuilder->SetLayerEventRegions(eventRegions);
+
+ if (isPositioned) {
+ // We need this nsDisplayLayerEventRegions to be sorted with the positioned
+ // elements as positioned elements will be sorted on top of normal elements
+ list.AppendNewToTop(eventRegions);
+ } else {
+ aLists.BorderBackground()->AppendNewToTop(eventRegions);
+ }
+ } else {
+ nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
+ if (eventRegions) {
+ eventRegions->AddFrame(aBuilder, child);
+ }
+ }
+ }
+
+ if (!pseudoStackingContext) {
+ // THIS IS THE COMMON CASE.
+ // Not a pseudo or real stacking context. Do the simple thing and
+ // return early.
+
+ aBuilder->AdjustWindowDraggingRegion(child);
+ child->BuildDisplayList(aBuilder, dirty, aLists);
+ aBuilder->DisplayCaret(child, dirty, aLists.Content());
+#ifdef DEBUG
+ DisplayDebugBorders(aBuilder, child, aLists);
+#endif
+ return;
+ }
+
+ // A pseudo-stacking context (e.g., a positioned element with z-index auto).
+ // We allow positioned descendants of the child to escape to our parent
+ // stacking context's positioned descendant list, because they might be
+ // z-index:non-auto
+ nsDisplayListCollection pseudoStack;
+ aBuilder->AdjustWindowDraggingRegion(child);
+ child->BuildDisplayList(aBuilder, dirty, pseudoStack);
+ aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());
+
+ list.AppendToTop(pseudoStack.BorderBackground());
+ list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
+ list.AppendToTop(pseudoStack.Floats());
+ list.AppendToTop(pseudoStack.Content());
+ list.AppendToTop(pseudoStack.Outlines());
+ extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
+#ifdef DEBUG
+ DisplayDebugBorders(aBuilder, child, aLists);
+#endif
+ }
+
+ buildingForChild.RestoreBuildingInvisibleItemsValue();
+
+ // Clear clip rect for the construction of the items below. Since we're
+ // clipping all their contents, they themselves don't need to be clipped.
+ clipState.Clear();
+
+ const DisplayItemScrollClip* containerItemScrollClip =
+ aBuilder->ClipState().CurrentAncestorScrollClipForStackingContextContents();
+
+ if (isPositioned || isVisuallyAtomic ||
+ (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
+ // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
+ // go in this level.
+ if (!list.IsEmpty()) {
+ nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, containerItemScrollClip);
+ if (isSVG) {
+ aLists.Content()->AppendNewToTop(item);
+ } else {
+ aLists.PositionedDescendants()->AppendNewToTop(item);
+ }
+ }
+ } else if (!isSVG && disp->IsFloating(child)) {
+ if (!list.IsEmpty()) {
+ aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list, containerItemScrollClip));
+ }
+ } else {
+ aLists.Content()->AppendToTop(&list);
+ }
+ // We delay placing the positioned descendants of positioned frames to here,
+ // because in the absence of z-index this is the correct order for them.
+ // This doesn't affect correctness because the positioned descendants list
+ // is sorted by z-order and content in BuildDisplayListForStackingContext,
+ // but it means that sort routine needs to do less work.
+ aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
+}
+
+void
+nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect)
+{
+ if (IsAbsoluteContainer()) {
+ aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect);
+ }
+}
+
+nsresult
+nsFrame::GetContentForEvent(WidgetEvent* aEvent,
+ nsIContent** aContent)
+{
+ nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
+ *aContent = f->GetContent();
+ NS_IF_ADDREF(*aContent);
+ return NS_OK;
+}
+
+void
+nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent)
+{
+ nsIContent* target = aContent ? aContent : mContent;
+
+ if (target) {
+ RefPtr<AsyncEventDispatcher> asyncDispatcher =
+ new AsyncEventDispatcher(target, aDOMEventName, true, false);
+ DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
+ }
+}
+
+nsresult
+nsFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+
+ if (aEvent->mMessage == eMouseMove) {
+ // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
+ // the implementation becomes simpler.
+ return HandleDrag(aPresContext, aEvent, aEventStatus);
+ }
+
+ if ((aEvent->mClass == eMouseEventClass &&
+ aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
+ aEvent->mClass == eTouchEventClass) {
+ if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
+ HandlePress(aPresContext, aEvent, aEventStatus);
+ } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
+ HandleRelease(aPresContext, aEvent, aEventStatus);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
+ nsIPresShell* aPresShell,
+ WidgetMouseEvent* aMouseEvent,
+ nsIContent** aParentContent,
+ int32_t* aContentOffset,
+ int32_t* aTarget)
+{
+ if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ *aParentContent = nullptr;
+ *aContentOffset = 0;
+ *aTarget = 0;
+
+ int16_t displaySelection = aPresShell->GetSelectionFlags();
+
+ bool selectingTableCells = aFrameSelection->GetTableCellSelection();
+
+ // DISPLAY_ALL means we're in an editor.
+ // If already in cell selection mode,
+ // continue selecting with mouse drag or end on mouse up,
+ // or when using shift key to extend block of cells
+ // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
+ bool doTableSelection =
+ displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells &&
+ (aMouseEvent->mMessage == eMouseMove ||
+ (aMouseEvent->mMessage == eMouseUp &&
+ aMouseEvent->button == WidgetMouseEvent::eLeftButton) ||
+ aMouseEvent->IsShift());
+
+ if (!doTableSelection)
+ {
+ // In Browser, special 'table selection' key must be pressed for table selection
+ // or when just Shift is pressed and we're already in table/cell selection mode
+#ifdef XP_MACOSX
+ doTableSelection = aMouseEvent->IsMeta() || (aMouseEvent->IsShift() && selectingTableCells);
+#else
+ doTableSelection = aMouseEvent->IsControl() || (aMouseEvent->IsShift() && selectingTableCells);
+#endif
+ }
+ if (!doTableSelection)
+ return NS_OK;
+
+ // Get the cell frame or table frame (or parent) of the current content node
+ nsIFrame *frame = this;
+ bool foundCell = false;
+ bool foundTable = false;
+
+ // Get the limiting node to stop parent frame search
+ nsIContent* limiter = aFrameSelection->GetLimiter();
+
+ // If our content node is an ancestor of the limiting node,
+ // we should stop the search right now.
+ if (limiter && nsContentUtils::ContentIsDescendantOf(limiter, GetContent()))
+ return NS_OK;
+
+ //We don't initiate row/col selection from here now,
+ // but we may in future
+ //bool selectColumn = false;
+ //bool selectRow = false;
+
+ while (frame)
+ {
+ // Check for a table cell by querying to a known CellFrame interface
+ nsITableCellLayout *cellElement = do_QueryFrame(frame);
+ if (cellElement)
+ {
+ foundCell = true;
+ //TODO: If we want to use proximity to top or left border
+ // for row and column selection, this is the place to do it
+ break;
+ }
+ else
+ {
+ // If not a cell, check for table
+ // This will happen when starting frame is the table or child of a table,
+ // such as a row (we were inbetween cells or in table border)
+ nsTableWrapperFrame *tableFrame = do_QueryFrame(frame);
+ if (tableFrame)
+ {
+ foundTable = true;
+ //TODO: How can we select row when along left table edge
+ // or select column when along top edge?
+ break;
+ } else {
+ frame = frame->GetParent();
+ // Stop if we have hit the selection's limiting content node
+ if (frame && frame->GetContent() == limiter)
+ break;
+ }
+ }
+ }
+ // We aren't in a cell or table
+ if (!foundCell && !foundTable) return NS_OK;
+
+ nsIContent* tableOrCellContent = frame->GetContent();
+ if (!tableOrCellContent) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
+ if (!parentContent) return NS_ERROR_FAILURE;
+
+ int32_t offset = parentContent->IndexOf(tableOrCellContent);
+ // Not likely?
+ if (offset < 0) return NS_ERROR_FAILURE;
+
+ // Everything is OK -- set the return values
+ parentContent.forget(aParentContent);
+
+ *aContentOffset = offset;
+
+#if 0
+ if (selectRow)
+ *aTarget = nsISelectionPrivate::TABLESELECTION_ROW;
+ else if (selectColumn)
+ *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN;
+ else
+#endif
+ if (foundCell)
+ *aTarget = nsISelectionPrivate::TABLESELECTION_CELL;
+ else if (foundTable)
+ *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE;
+
+ return NS_OK;
+}
+
+nsresult
+nsFrame::IsSelectable(bool* aSelectable, StyleUserSelect* aSelectStyle) const
+{
+ if (!aSelectable) //it's ok if aSelectStyle is null
+ return NS_ERROR_NULL_POINTER;
+
+ // Like 'visibility', we must check all the parents: if a parent
+ // is not selectable, none of its children is selectable.
+ //
+ // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all',
+ // all its children are selectable, even those with 'user-select:none'.
+ //
+ // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy,
+ // aSelectStyle returns the first style that is not AUTO. If these values
+ // are present in the frame hierarchy, aSelectStyle returns the style of the
+ // topmost parent that has either 'none' or '-moz-all'.
+ //
+ // The -moz-text value acts as a way to override an ancestor's all/-moz-all value.
+ //
+ // For instance, if the frame hierarchy is:
+ // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is ALL
+ // AUTO -> _MOZ_ALL -> NONE -> _MOZ_TEXT, the returned value is TEXT.
+ // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT
+ // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is ALL
+ // _MOZ_ALL -> _MOZ_TEXT -> AUTO -> AUTO, the returned value is TEXT.
+ // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT
+ //
+ StyleUserSelect selectStyle = StyleUserSelect::Auto;
+ nsIFrame* frame = const_cast<nsFrame*>(this);
+ bool containsEditable = false;
+
+ while (frame) {
+ const nsStyleUIReset* userinterface = frame->StyleUIReset();
+ switch (userinterface->mUserSelect) {
+ case StyleUserSelect::All:
+ case StyleUserSelect::MozAll:
+ {
+ // override the previous values
+ if (selectStyle != StyleUserSelect::MozText) {
+ selectStyle = userinterface->mUserSelect;
+ }
+ nsIContent* frameContent = frame->GetContent();
+ containsEditable = frameContent &&
+ frameContent->EditableDescendantCount() > 0;
+ break;
+ }
+ default:
+ // otherwise return the first value which is not 'auto'
+ if (selectStyle == StyleUserSelect::Auto) {
+ selectStyle = userinterface->mUserSelect;
+ }
+ break;
+ }
+ frame = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
+ }
+
+ // convert internal values to standard values
+ if (selectStyle == StyleUserSelect::Auto ||
+ selectStyle == StyleUserSelect::MozText) {
+ selectStyle = StyleUserSelect::Text;
+ } else if (selectStyle == StyleUserSelect::MozAll) {
+ selectStyle = StyleUserSelect::All;
+ }
+
+ // If user tries to select all of a non-editable content,
+ // prevent selection if it contains editable content.
+ bool allowSelection = true;
+ if (selectStyle == StyleUserSelect::All) {
+ allowSelection = !containsEditable;
+ }
+
+ // return stuff
+ if (aSelectStyle) {
+ *aSelectStyle = selectStyle;
+ }
+
+ if (mState & NS_FRAME_GENERATED_CONTENT) {
+ *aSelectable = false;
+ } else {
+ *aSelectable = allowSelection && (selectStyle != StyleUserSelect::None);
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Handles the Mouse Press Event for the frame
+ */
+NS_IMETHODIMP
+nsFrame::HandlePress(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+ if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
+ return NS_OK;
+ }
+
+ NS_ENSURE_ARG_POINTER(aEvent);
+ if (aEvent->mClass == eTouchEventClass) {
+ return NS_OK;
+ }
+
+ //We often get out of sync state issues with mousedown events that
+ //get interrupted by alerts/dialogs.
+ //Check with the ESM to see if we should process this one
+ if (!aPresContext->EventStateManager()->EventStatusOK(aEvent))
+ return NS_OK;
+
+ nsresult rv;
+ nsIPresShell *shell = aPresContext->GetPresShell();
+ if (!shell)
+ return NS_ERROR_FAILURE;
+
+ // if we are in Navigator and the click is in a draggable node, we don't want
+ // to start selection because we don't want to interfere with a potential
+ // drag of said node and steal all its glory.
+ int16_t isEditor = shell->GetSelectionFlags();
+ //weaaak. only the editor can display frame selection not just text and images
+ isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
+
+ WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+
+ if (!mouseEvent->IsAlt()) {
+ for (nsIContent* content = mContent; content;
+ content = content->GetParent()) {
+ if (nsContentUtils::ContentIsDraggable(content) &&
+ !content->IsEditable()) {
+ // coordinate stuff is the fix for bug #55921
+ if ((mRect - GetPosition()).Contains(
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this))) {
+ return NS_OK;
+ }
+ }
+ }
+ }
+
+ // check whether style allows selection
+ // if not, don't tell selection the mouse event even occurred.
+ bool selectable;
+ StyleUserSelect selectStyle;
+ rv = IsSelectable(&selectable, &selectStyle);
+ if (NS_FAILED(rv)) return rv;
+
+ // check for select: none
+ if (!selectable)
+ return NS_OK;
+
+ // When implementing StyleUserSelect::Element, StyleUserSelect::Elements and
+ // StyleUserSelect::Toggle, need to change this logic
+ bool useFrameSelection = (selectStyle == StyleUserSelect::Text);
+
+ // If the mouse is dragged outside the nearest enclosing scrollable area
+ // while making a selection, the area will be scrolled. To do this, capture
+ // the mouse on the nearest scrollable frame. If there isn't a scrollable
+ // frame, or something else is already capturing the mouse, there's no
+ // reason to capture.
+ bool hasCapturedContent = false;
+ if (!nsIPresShell::GetCapturingContent()) {
+ nsIScrollableFrame* scrollFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(this,
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+ if (scrollFrame) {
+ nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
+ nsIPresShell::SetCapturingContent(capturingFrame->GetContent(),
+ CAPTURE_IGNOREALLOWED);
+ hasCapturedContent = true;
+ }
+ }
+
+ // XXX This is screwy; it really should use the selection frame, not the
+ // event frame
+ const nsFrameSelection* frameselection = nullptr;
+ if (useFrameSelection)
+ frameselection = GetConstFrameSelection();
+ else
+ frameselection = shell->ConstFrameSelection();
+
+ if (!frameselection || frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF)
+ return NS_OK;//nothing to do we cannot affect selection from here
+
+#ifdef XP_MACOSX
+ if (mouseEvent->IsControl())
+ return NS_OK;//short circuit. hard coded for mac due to time restraints.
+ bool control = mouseEvent->IsMeta();
+#else
+ bool control = mouseEvent->IsControl();
+#endif
+
+ RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
+ if (mouseEvent->mClickCount > 1) {
+ // These methods aren't const but can't actually delete anything,
+ // so no need for nsWeakFrame.
+ fc->SetDragState(true);
+ fc->SetMouseDoubleDown(true);
+ return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control);
+ }
+
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
+ ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
+
+ if (!offsets.content)
+ return NS_ERROR_FAILURE;
+
+ // On touchables devices, touch the screen is usually a pan action,
+ // so let's reposition the caret if needed but do not select text
+ // if the touch did not happen over an editable element. Otherwise,
+ // let the user move the caret by tapping and dragging.
+ if (!offsets.content->IsEditable() &&
+ Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) {
+ // On touchables devices, mouse events are generated if the gesture is a tap.
+ // Such events are never going to generate a drag action, so let's release
+ // captured content if any.
+ if (hasCapturedContent) {
+ nsIPresShell::SetCapturingContent(nullptr, 0);
+ }
+
+ return fc->HandleClick(offsets.content, offsets.StartOffset(),
+ offsets.EndOffset(), false, false,
+ offsets.associate);
+ }
+
+ // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
+ nsCOMPtr<nsIContent>parentContent;
+ int32_t contentOffset;
+ int32_t target;
+ rv = GetDataForTableSelection(frameselection, shell, mouseEvent,
+ getter_AddRefs(parentContent), &contentOffset,
+ &target);
+ if (NS_SUCCEEDED(rv) && parentContent)
+ {
+ fc->SetDragState(true);
+ return fc->HandleTableSelection(parentContent, contentOffset, target,
+ mouseEvent);
+ }
+
+ fc->SetDelayedCaretData(0);
+
+ // Check if any part of this frame is selected, and if the
+ // user clicked inside the selected region. If so, we delay
+ // starting a new selection since the user may be trying to
+ // drag the selected region to some other app.
+
+ SelectionDetails *details = 0;
+ if (GetContent()->IsSelectionDescendant())
+ {
+ bool inSelection = false;
+ details = frameselection->LookUpSelection(offsets.content, 0,
+ offsets.EndOffset(), false);
+
+ //
+ // If there are any details, check to see if the user clicked
+ // within any selected region of the frame.
+ //
+
+ SelectionDetails *curDetail = details;
+
+ while (curDetail)
+ {
+ //
+ // If the user clicked inside a selection, then just
+ // return without doing anything. We will handle placing
+ // the caret later on when the mouse is released. We ignore
+ // the spellcheck, find and url formatting selections.
+ //
+ if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
+ curDetail->mSelectionType != SelectionType::eFind &&
+ curDetail->mSelectionType != SelectionType::eURLSecondary &&
+ curDetail->mSelectionType != SelectionType::eURLStrikeout &&
+ curDetail->mStart <= offsets.StartOffset() &&
+ offsets.EndOffset() <= curDetail->mEnd)
+ {
+ inSelection = true;
+ }
+
+ SelectionDetails *nextDetail = curDetail->mNext;
+ delete curDetail;
+ curDetail = nextDetail;
+ }
+
+ if (inSelection) {
+ fc->SetDragState(false);
+ fc->SetDelayedCaretData(mouseEvent);
+ return NS_OK;
+ }
+ }
+
+ fc->SetDragState(true);
+
+ // Do not touch any nsFrame members after this point without adding
+ // weakFrame checks.
+ rv = fc->HandleClick(offsets.content, offsets.StartOffset(),
+ offsets.EndOffset(), mouseEvent->IsShift(), control,
+ offsets.associate);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (offsets.offset != offsets.secondaryOffset)
+ fc->MaintainSelection();
+
+ if (isEditor && !mouseEvent->IsShift() &&
+ (offsets.EndOffset() - offsets.StartOffset()) == 1)
+ {
+ // A single node is selected and we aren't extending an existing
+ // selection, which means the user clicked directly on an object (either
+ // -moz-user-select: all or a non-text node without children).
+ // Therefore, disable selection extension during mouse moves.
+ // XXX This is a bit hacky; shouldn't editor be able to deal with this?
+ fc->SetDragState(false);
+ }
+
+ return rv;
+}
+
+/*
+ * SelectByTypeAtPoint
+ *
+ * Search for selectable content at point and attempt to select
+ * based on the start and end selection behaviours.
+ *
+ * @param aPresContext Presentation context
+ * @param aPoint Point at which selection will occur. Coordinates
+ * should be relaitve to this frame.
+ * @param aBeginAmountType, aEndAmountType Selection behavior, see
+ * nsIFrame for definitions.
+ * @param aSelectFlags Selection flags defined in nsFame.h.
+ * @return success or failure at finding suitable content to select.
+ */
+nsresult
+nsFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
+ const nsPoint& aPoint,
+ nsSelectionAmount aBeginAmountType,
+ nsSelectionAmount aEndAmountType,
+ uint32_t aSelectFlags)
+{
+ NS_ENSURE_ARG_POINTER(aPresContext);
+
+ // No point in selecting if selection is turned off
+ if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF)
+ return NS_OK;
+
+ ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
+ if (!offsets.content)
+ return NS_ERROR_FAILURE;
+
+ int32_t offset;
+ const nsFrameSelection* frameSelection =
+ PresContext()->GetPresShell()->ConstFrameSelection();
+ nsIFrame* theFrame = frameSelection->
+ GetFrameForNodeOffset(offsets.content, offsets.offset,
+ offsets.associate, &offset);
+ if (!theFrame)
+ return NS_ERROR_FAILURE;
+
+ nsFrame* frame = static_cast<nsFrame*>(theFrame);
+ return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, offset,
+ aBeginAmountType != eSelectWord,
+ aSelectFlags);
+}
+
+/**
+ * Multiple Mouse Press -- line or paragraph selection -- for the frame.
+ * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
+ */
+NS_IMETHODIMP
+nsFrame::HandleMultiplePress(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus,
+ bool aControlHeld)
+{
+ NS_ENSURE_ARG_POINTER(aEvent);
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+
+ if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
+ DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
+ return NS_OK;
+ }
+
+ // Find out whether we're doing line or paragraph selection.
+ // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph.
+ // Otherwise, triple-click selects line, and quadruple-click selects paragraph
+ // (on platforms that support quadruple-click).
+ nsSelectionAmount beginAmount, endAmount;
+ WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+ if (!mouseEvent) {
+ return NS_OK;
+ }
+
+ if (mouseEvent->mClickCount == 4) {
+ beginAmount = endAmount = eSelectParagraph;
+ } else if (mouseEvent->mClickCount == 3) {
+ if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
+ beginAmount = endAmount = eSelectParagraph;
+ } else {
+ beginAmount = eSelectBeginLine;
+ endAmount = eSelectEndLine;
+ }
+ } else if (mouseEvent->mClickCount == 2) {
+ // We only want inline frames; PeekBackwardAndForward dislikes blocks
+ beginAmount = endAmount = eSelectWord;
+ } else {
+ return NS_OK;
+ }
+
+ nsPoint relPoint =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
+ return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
+ (aControlHeld ? SELECT_ACCUMULATE : 0));
+}
+
+nsresult
+nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
+ nsSelectionAmount aAmountForward,
+ int32_t aStartPos,
+ bool aJumpLines,
+ uint32_t aSelectFlags)
+{
+ nsIFrame* baseFrame = this;
+ int32_t baseOffset = aStartPos;
+ nsresult rv;
+
+ if (aAmountBack == eSelectWord) {
+ // To avoid selecting the previous word when at start of word,
+ // first move one character forward.
+ nsPeekOffsetStruct pos(eSelectCharacter,
+ eDirNext,
+ aStartPos,
+ nsPoint(0, 0),
+ aJumpLines,
+ true, //limit on scrolled views
+ false,
+ false,
+ false);
+ rv = PeekOffset(&pos);
+ if (NS_SUCCEEDED(rv)) {
+ baseFrame = pos.mResultFrame;
+ baseOffset = pos.mContentOffset;
+ }
+ }
+
+ // Use peek offset one way then the other:
+ nsPeekOffsetStruct startpos(aAmountBack,
+ eDirPrevious,
+ baseOffset,
+ nsPoint(0, 0),
+ aJumpLines,
+ true, //limit on scrolled views
+ false,
+ false,
+ false);
+ rv = baseFrame->PeekOffset(&startpos);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsPeekOffsetStruct endpos(aAmountForward,
+ eDirNext,
+ aStartPos,
+ nsPoint(0, 0),
+ aJumpLines,
+ true, //limit on scrolled views
+ false,
+ false,
+ false);
+ rv = PeekOffset(&endpos);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Keep frameSelection alive.
+ RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
+
+ rv = frameSelection->HandleClick(startpos.mResultContent,
+ startpos.mContentOffset, startpos.mContentOffset,
+ false, (aSelectFlags & SELECT_ACCUMULATE),
+ CARET_ASSOCIATE_AFTER);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = frameSelection->HandleClick(endpos.mResultContent,
+ endpos.mContentOffset, endpos.mContentOffset,
+ true, false,
+ CARET_ASSOCIATE_BEFORE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // maintain selection
+ return frameSelection->MaintainSelection(aAmountBack);
+}
+
+NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
+ "HandleDrag can only handle mouse event");
+
+ RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
+ bool mouseDown = frameselection->GetDragState();
+ if (!mouseDown) {
+ return NS_OK;
+ }
+
+ nsIFrame* scrollbar =
+ nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::scrollbarFrame);
+ if (!scrollbar) {
+ // XXX Do we really need to exclude non-selectable content here?
+ // GetContentOffsetsFromPoint can handle it just fine, although some
+ // other stuff might not like it.
+ // NOTE: DisplaySelection() returns SELECTION_OFF for non-selectable frames.
+ if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
+ return NS_OK;
+ }
+ }
+
+ frameselection->StopAutoScrollTimer();
+
+ // Check if we are dragging in a table cell
+ nsCOMPtr<nsIContent> parentContent;
+ int32_t contentOffset;
+ int32_t target;
+ WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+ nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
+ nsresult result;
+ result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
+ getter_AddRefs(parentContent),
+ &contentOffset, &target);
+
+ nsWeakFrame weakThis = this;
+ if (NS_SUCCEEDED(result) && parentContent) {
+ frameselection->HandleTableSelection(parentContent, contentOffset, target,
+ mouseEvent);
+ } else {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
+ frameselection->HandleDrag(this, pt);
+ }
+
+ // The frameselection object notifies selection listeners synchronously above
+ // which might have killed us.
+ if (!weakThis.IsAlive()) {
+ return NS_OK;
+ }
+
+ // get the nearest scrollframe
+ nsIScrollableFrame* scrollFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(this,
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+
+ if (scrollFrame) {
+ nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
+ if (capturingFrame) {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
+ capturingFrame);
+ frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
+ }
+ }
+
+ return NS_OK;
+}
+
+/**
+ * This static method handles part of the nsFrame::HandleRelease in a way
+ * which doesn't rely on the nsFrame object to stay alive.
+ */
+static nsresult
+HandleFrameSelection(nsFrameSelection* aFrameSelection,
+ nsIFrame::ContentOffsets& aOffsets,
+ bool aHandleTableSel,
+ int32_t aContentOffsetForTableSel,
+ int32_t aTargetForTableSel,
+ nsIContent* aParentContentForTableSel,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ if (!aFrameSelection) {
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+
+ if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
+ if (!aHandleTableSel) {
+ if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // We are doing this to simulate what we would have done on HandlePress.
+ // We didn't do it there to give the user an opportunity to drag
+ // the text, but since they didn't drag, we want to place the
+ // caret.
+ // However, we'll use the mouse position from the release, since:
+ // * it's easier
+ // * that's the normal click position to use (although really, in
+ // the normal case, small movements that don't count as a drag
+ // can do selection)
+ aFrameSelection->SetDragState(true);
+
+ rv = aFrameSelection->HandleClick(aOffsets.content,
+ aOffsets.StartOffset(),
+ aOffsets.EndOffset(),
+ aFrameSelection->IsShiftDownInDelayedCaretData(),
+ false,
+ aOffsets.associate);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else if (aParentContentForTableSel) {
+ aFrameSelection->SetDragState(false);
+ rv = aFrameSelection->HandleTableSelection(
+ aParentContentForTableSel,
+ aContentOffsetForTableSel,
+ aTargetForTableSel,
+ aEvent->AsMouseEvent());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ aFrameSelection->SetDelayedCaretData(0);
+ }
+
+ aFrameSelection->SetDragState(false);
+ aFrameSelection->StopAutoScrollTimer();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ if (aEvent->mClass != eMouseEventClass) {
+ return NS_OK;
+ }
+
+ nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
+
+ nsCOMPtr<nsIContent> captureContent = nsIPresShell::GetCapturingContent();
+
+ // We can unconditionally stop capturing because
+ // we should never be capturing when the mouse button is up
+ nsIPresShell::SetCapturingContent(nullptr, 0);
+
+ bool selectionOff =
+ (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF);
+
+ RefPtr<nsFrameSelection> frameselection;
+ ContentOffsets offsets;
+ nsCOMPtr<nsIContent> parentContent;
+ int32_t contentOffsetForTableSel = 0;
+ int32_t targetForTableSel = 0;
+ bool handleTableSelection = true;
+
+ if (!selectionOff) {
+ frameselection = GetFrameSelection();
+ if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
+ // Check if the frameselection recorded the mouse going down.
+ // If not, the user must have clicked in a part of the selection.
+ // Place the caret before continuing!
+
+ if (frameselection->MouseDownRecorded()) {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
+ offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
+ handleTableSelection = false;
+ } else {
+ GetDataForTableSelection(frameselection, PresContext()->PresShell(),
+ aEvent->AsMouseEvent(),
+ getter_AddRefs(parentContent),
+ &contentOffsetForTableSel,
+ &targetForTableSel);
+ }
+ }
+ }
+
+ // We might be capturing in some other document and the event just happened to
+ // trickle down here. Make sure that document's frame selection is notified.
+ // Note, this may cause the current nsFrame object to be deleted, bug 336592.
+ RefPtr<nsFrameSelection> frameSelection;
+ if (activeFrame != this &&
+ static_cast<nsFrame*>(activeFrame)->DisplaySelection(activeFrame->PresContext())
+ != nsISelectionController::SELECTION_OFF) {
+ frameSelection = activeFrame->GetFrameSelection();
+ }
+
+ // Also check the selection of the capturing content which might be in a
+ // different document.
+ if (!frameSelection && captureContent) {
+ nsIDocument* doc = captureContent->GetUncomposedDoc();
+ if (doc) {
+ nsIPresShell* capturingShell = doc->GetShell();
+ if (capturingShell && capturingShell != PresContext()->GetPresShell()) {
+ frameSelection = capturingShell->FrameSelection();
+ }
+ }
+ }
+
+ if (frameSelection) {
+ frameSelection->SetDragState(false);
+ frameSelection->StopAutoScrollTimer();
+ nsIScrollableFrame* scrollFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(this,
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+ if (scrollFrame) {
+ // Perform any additional scrolling needed to maintain CSS snap point
+ // requirements when autoscrolling is over.
+ scrollFrame->ScrollSnap();
+ }
+ }
+
+ // Do not call any methods of the current object after this point!!!
+ // The object is perhaps dead!
+
+ return selectionOff
+ ? NS_OK
+ : HandleFrameSelection(frameselection, offsets, handleTableSelection,
+ contentOffsetForTableSel, targetForTableSel,
+ parentContent, aEvent, aEventStatus);
+}
+
+struct MOZ_STACK_CLASS FrameContentRange {
+ FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd) :
+ content(aContent), start(aStart), end(aEnd) { }
+ nsCOMPtr<nsIContent> content;
+ int32_t start;
+ int32_t end;
+};
+
+// Retrieve the content offsets of a frame
+static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
+ nsCOMPtr<nsIContent> content, parent;
+ content = aFrame->GetContent();
+ if (!content) {
+ NS_WARNING("Frame has no content");
+ return FrameContentRange(nullptr, -1, -1);
+ }
+ nsIAtom* type = aFrame->GetType();
+ if (type == nsGkAtoms::textFrame) {
+ int32_t offset, offsetEnd;
+ aFrame->GetOffsets(offset, offsetEnd);
+ return FrameContentRange(content, offset, offsetEnd);
+ }
+ if (type == nsGkAtoms::brFrame) {
+ parent = content->GetParent();
+ int32_t beginOffset = parent->IndexOf(content);
+ return FrameContentRange(parent, beginOffset, beginOffset);
+ }
+ // Loop to deal with anonymous content, which has no index; this loop
+ // probably won't run more than twice under normal conditions
+ do {
+ parent = content->GetParent();
+ if (parent) {
+ int32_t beginOffset = parent->IndexOf(content);
+ if (beginOffset >= 0)
+ return FrameContentRange(parent, beginOffset, beginOffset + 1);
+ content = parent;
+ }
+ } while (parent);
+
+ // The root content node must act differently
+ return FrameContentRange(content, 0, content->GetChildCount());
+}
+
+// The FrameTarget represents the closest frame to a point that can be selected
+// The frame is the frame represented, frameEdge says whether one end of the
+// frame is the result (in which case different handling is needed), and
+// afterFrame says which end is repersented if frameEdge is true
+struct FrameTarget {
+ FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame,
+ bool aEmptyBlock = false) :
+ frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame),
+ emptyBlock(aEmptyBlock) { }
+ static FrameTarget Null() {
+ return FrameTarget(nullptr, false, false);
+ }
+ bool IsNull() {
+ return !frame;
+ }
+ nsIFrame* frame;
+ bool frameEdge;
+ bool afterFrame;
+ bool emptyBlock;
+};
+
+// See function implementation for information
+static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
+ uint32_t aFlags);
+
+static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags)
+{
+ if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
+ !aFrame->StyleVisibility()->IsVisible()) {
+ return false;
+ }
+ return !aFrame->IsGeneratedContentFrame() &&
+ aFrame->StyleUIReset()->mUserSelect != StyleUserSelect::None;
+}
+
+static bool SelectionDescendToKids(nsIFrame* aFrame) {
+ StyleUserSelect style = aFrame->StyleUIReset()->mUserSelect;
+ nsIFrame* parent = aFrame->GetParent();
+ // If we are only near (not directly over) then don't traverse
+ // frames with independent selection (e.g. text and list controls)
+ // unless we're already inside such a frame (see bug 268497). Note that this
+ // prevents any of the users of this method from entering form controls.
+ // XXX We might want some way to allow using the up-arrow to go into a form
+ // control, but the focus didn't work right anyway; it'd probably be enough
+ // if the left and right arrows could enter textboxes (which I don't believe
+ // they can at the moment)
+ return !aFrame->IsGeneratedContentFrame() &&
+ style != StyleUserSelect::All &&
+ style != StyleUserSelect::None &&
+ ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) ||
+ !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
+}
+
+static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
+ nsPoint aPoint,
+ uint32_t aFlags)
+{
+ nsIFrame* parent = aChild->GetParent();
+ if (SelectionDescendToKids(aChild)) {
+ nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
+ return GetSelectionClosestFrame(aChild, pt, aFlags);
+ }
+ return FrameTarget(aChild, false, false);
+}
+
+// When the cursor needs to be at the beginning of a block, it shouldn't be
+// before the first child. A click on a block whose first child is a block
+// should put the cursor in the child. The cursor shouldn't be between the
+// blocks, because that's not where it's expected.
+// Note that this method is guaranteed to succeed.
+static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame,
+ bool aEndFrame, uint32_t aFlags) {
+ if (SelectionDescendToKids(aFrame)) {
+ nsIFrame* result = nullptr;
+ nsIFrame *frame = aFrame->PrincipalChildList().FirstChild();
+ if (!aEndFrame) {
+ while (frame && (!SelfIsSelectable(frame, aFlags) ||
+ frame->IsEmpty()))
+ frame = frame->GetNextSibling();
+ if (frame)
+ result = frame;
+ } else {
+ // Because the frame tree is singly linked, to find the last frame,
+ // we have to iterate through all the frames
+ // XXX I have a feeling this could be slow for long blocks, although
+ // I can't find any slowdowns
+ while (frame) {
+ if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
+ result = frame;
+ frame = frame->GetNextSibling();
+ }
+ }
+ if (result)
+ return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
+ }
+ // If the current frame has no targetable children, target the current frame
+ return FrameTarget(aFrame, true, aEndFrame);
+}
+
+// This method finds the closest valid FrameTarget on a given line; if there is
+// no valid FrameTarget on the line, it returns a null FrameTarget
+static FrameTarget GetSelectionClosestFrameForLine(
+ nsBlockFrame* aParent,
+ nsBlockFrame::LineIterator aLine,
+ nsPoint aPoint,
+ uint32_t aFlags)
+{
+ nsIFrame *frame = aLine->mFirstChild;
+ // Account for end of lines (any iterator from the block is valid)
+ if (aLine == aParent->LinesEnd())
+ return DrillDownToSelectionFrame(aParent, true, aFlags);
+ nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr;
+ nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
+ WritingMode wm = aLine->mWritingMode;
+ LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
+ bool canSkipBr = false;
+ for (int32_t n = aLine->GetChildCount(); n;
+ --n, frame = frame->GetNextSibling()) {
+ // Skip brFrames. Can only skip if the line contains at least
+ // one selectable and non-empty frame before
+ if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
+ (canSkipBr && frame->GetType() == nsGkAtoms::brFrame)) {
+ continue;
+ }
+ canSkipBr = true;
+ LogicalRect frameRect = LogicalRect(wm, frame->GetRect(),
+ aLine->mContainerSize);
+ if (pt.I(wm) >= frameRect.IStart(wm)) {
+ if (pt.I(wm) < frameRect.IEnd(wm)) {
+ return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
+ }
+ if (frameRect.IEnd(wm) >= closestIStart) {
+ closestFromIStart = frame;
+ closestIStart = frameRect.IEnd(wm);
+ }
+ } else {
+ if (frameRect.IStart(wm) <= closestIEnd) {
+ closestFromIEnd = frame;
+ closestIEnd = frameRect.IStart(wm);
+ }
+ }
+ }
+ if (!closestFromIStart && !closestFromIEnd) {
+ // We should only get here if there are no selectable frames on a line
+ // XXX Do we need more elaborate handling here?
+ return FrameTarget::Null();
+ }
+ if (closestFromIStart &&
+ (!closestFromIEnd ||
+ (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
+ return GetSelectionClosestFrameForChild(closestFromIStart, aPoint,
+ aFlags);
+ }
+ return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
+}
+
+// This method is for the special handling we do for block frames; they're
+// special because they represent paragraphs and because they are organized
+// into lines, which have bounds that are not stored elsewhere in the
+// frame tree. Returns a null FrameTarget for frames which are not
+// blocks or blocks with no lines except editable one.
+static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
+ nsPoint aPoint,
+ uint32_t aFlags)
+{
+ nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI
+ if (!bf)
+ return FrameTarget::Null();
+
+ // This code searches for the correct line
+ nsBlockFrame::LineIterator firstLine = bf->LinesBegin();
+ nsBlockFrame::LineIterator end = bf->LinesEnd();
+ if (firstLine == end) {
+ nsIContent *blockContent = aFrame->GetContent();
+ if (blockContent) {
+ // Return with empty flag true.
+ return FrameTarget(aFrame, false, false, true);
+ }
+ return FrameTarget::Null();
+ }
+ nsBlockFrame::LineIterator curLine = firstLine;
+ nsBlockFrame::LineIterator closestLine = end;
+ // Convert aPoint into a LogicalPoint in the writing-mode of this block
+ WritingMode wm = curLine->mWritingMode;
+ LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
+ while (curLine != end) {
+ // Check to see if our point lies within the line's block-direction bounds
+ nscoord BCoord = pt.B(wm) - curLine->BStart();
+ nscoord BSize = curLine->BSize();
+ if (BCoord >= 0 && BCoord < BSize) {
+ closestLine = curLine;
+ break; // We found the line; stop looking
+ }
+ if (BCoord < 0)
+ break;
+ ++curLine;
+ }
+
+ if (closestLine == end) {
+ nsBlockFrame::LineIterator prevLine = curLine.prev();
+ nsBlockFrame::LineIterator nextLine = curLine;
+ // Avoid empty lines
+ while (nextLine != end && nextLine->IsEmpty())
+ ++nextLine;
+ while (prevLine != end && prevLine->IsEmpty())
+ --prevLine;
+
+ // This hidden pref dictates whether a point above or below all lines comes
+ // up with a line or the beginning or end of the frame; 0 on Windows,
+ // 1 on other platforms by default at the writing of this code
+ int32_t dragOutOfFrame =
+ Preferences::GetInt("browser.drag_out_of_frame_style");
+
+ if (prevLine == end) {
+ if (dragOutOfFrame == 1 || nextLine == end)
+ return DrillDownToSelectionFrame(aFrame, false, aFlags);
+ closestLine = nextLine;
+ } else if (nextLine == end) {
+ if (dragOutOfFrame == 1)
+ return DrillDownToSelectionFrame(aFrame, true, aFlags);
+ closestLine = prevLine;
+ } else { // Figure out which line is closer
+ if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
+ closestLine = prevLine;
+ else
+ closestLine = nextLine;
+ }
+ }
+
+ do {
+ FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine,
+ aPoint, aFlags);
+ if (!target.IsNull())
+ return target;
+ ++closestLine;
+ } while (closestLine != end);
+ // Fall back to just targeting the last targetable place
+ return DrillDownToSelectionFrame(aFrame, true, aFlags);
+}
+
+// GetSelectionClosestFrame is the helper function that calculates the closest
+// frame to the given point.
+// It doesn't completely account for offset styles, so needs to be used in
+// restricted environments.
+// Cannot handle overlapping frames correctly, so it should receive the output
+// of GetFrameForPoint
+// Guaranteed to return a valid FrameTarget
+static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
+ uint32_t aFlags)
+{
+ {
+ // Handle blocks; if the frame isn't a block, the method fails
+ FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags);
+ if (!target.IsNull())
+ return target;
+ }
+
+ nsIFrame *kid = aFrame->PrincipalChildList().FirstChild();
+
+ if (kid) {
+ // Go through all the child frames to find the closest one
+ nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX };
+ for (; kid; kid = kid->GetNextSibling()) {
+ if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty())
+ continue;
+
+ kid->FindCloserFrameForSelection(aPoint, &closest);
+ }
+ if (closest.mFrame) {
+ if (closest.mFrame->IsSVGText())
+ return FrameTarget(closest.mFrame, false, false);
+ return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
+ }
+ }
+ return FrameTarget(aFrame, false, false);
+}
+
+nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint)
+{
+ nsIFrame::ContentOffsets offsets;
+ FrameContentRange range = GetRangeForFrame(aFrame);
+ offsets.content = range.content;
+ // If there are continuations (meaning it's not one rectangle), this is the
+ // best this function can do
+ if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
+ offsets.offset = range.start;
+ offsets.secondaryOffset = range.end;
+ offsets.associate = CARET_ASSOCIATE_AFTER;
+ return offsets;
+ }
+
+ // Figure out whether the offsets should be over, after, or before the frame
+ nsRect rect(nsPoint(0, 0), aFrame->GetSize());
+
+ bool isBlock = aFrame->GetDisplay() != StyleDisplay::Inline;
+ bool isRtl = (aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL);
+ if ((isBlock && rect.y < aPoint.y) ||
+ (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
+ (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
+ offsets.offset = range.end;
+ if (rect.Contains(aPoint))
+ offsets.secondaryOffset = range.start;
+ else
+ offsets.secondaryOffset = range.end;
+ } else {
+ offsets.offset = range.start;
+ if (rect.Contains(aPoint))
+ offsets.secondaryOffset = range.end;
+ else
+ offsets.secondaryOffset = range.start;
+ }
+ offsets.associate =
+ offsets.offset == range.start ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
+ return offsets;
+}
+
+static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
+ nsIFrame* adjustedFrame = aFrame;
+ for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent())
+ {
+ // These are the conditions that make all children not able to handle
+ // a cursor.
+ StyleUserSelect userSelect = frame->StyleUIReset()->mUserSelect;
+ if (userSelect == StyleUserSelect::MozText) {
+ // If we see a -moz-text element, we shouldn't look further up the parent
+ // chain!
+ break;
+ }
+ if (userSelect == StyleUserSelect::All ||
+ frame->IsGeneratedContentFrame()) {
+ adjustedFrame = frame;
+ }
+ }
+ return adjustedFrame;
+}
+
+nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
+ uint32_t aFlags)
+{
+ nsIFrame *adjustedFrame;
+ if (aFlags & IGNORE_SELECTION_STYLE) {
+ adjustedFrame = this;
+ }
+ else {
+ // This section of code deals with special selection styles. Note that
+ // -moz-all exists, even though it doesn't need to be explicitly handled.
+ //
+ // The offset is forced not to end up in generated content; content offsets
+ // cannot represent content outside of the document's content tree.
+
+ adjustedFrame = AdjustFrameForSelectionStyles(this);
+
+ // -moz-user-select: all needs special handling, because clicking on it
+ // should lead to the whole frame being selected
+ if (adjustedFrame && adjustedFrame->StyleUIReset()->mUserSelect ==
+ StyleUserSelect::All) {
+ nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
+ return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
+ }
+
+ // For other cases, try to find a closest frame starting from the parent of
+ // the unselectable frame
+ if (adjustedFrame != this)
+ adjustedFrame = adjustedFrame->GetParent();
+ }
+
+ nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
+
+ FrameTarget closest =
+ GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
+
+ if (closest.emptyBlock) {
+ ContentOffsets offsets;
+ NS_ASSERTION(closest.frame,
+ "closest.frame must not be null when it's empty");
+ offsets.content = closest.frame->GetContent();
+ offsets.offset = 0;
+ offsets.secondaryOffset = 0;
+ offsets.associate = CARET_ASSOCIATE_AFTER;
+ return offsets;
+ }
+
+ // If the correct offset is at one end of a frame, use offset-based
+ // calculation method
+ if (closest.frameEdge) {
+ ContentOffsets offsets;
+ FrameContentRange range = GetRangeForFrame(closest.frame);
+ offsets.content = range.content;
+ if (closest.afterFrame)
+ offsets.offset = range.end;
+ else
+ offsets.offset = range.start;
+ offsets.secondaryOffset = offsets.offset;
+ offsets.associate = offsets.offset == range.start ?
+ CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
+ return offsets;
+ }
+
+ nsPoint pt;
+ if (closest.frame != this) {
+ if (closest.frame->IsSVGText()) {
+ pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame,
+ aPoint, this);
+ } else {
+ pt = aPoint - closest.frame->GetOffsetTo(this);
+ }
+ } else {
+ pt = aPoint;
+ }
+ return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt);
+
+ // XXX should I add some kind of offset standardization?
+ // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
+ // x and first z put the cursor in the same logical position in addition
+ // to the same visual position?
+}
+
+nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
+{
+ return OffsetsForSingleFrame(this, aPoint);
+}
+
+void
+nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext)
+{
+ if (aImage.GetType() != eStyleImageType_Image) {
+ return;
+ }
+
+ imgRequestProxy* req = aImage.GetImageData();
+ if (!req) {
+ return;
+ }
+
+ mozilla::css::ImageLoader* loader =
+ aPresContext->Document()->StyleImageLoader();
+
+ // If this fails there's not much we can do ...
+ loader->AssociateRequestToFrame(req, this);
+}
+
+nsresult
+nsFrame::GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor)
+{
+ FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
+ if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
+ // If this is editable, I-beam cursor is better for most elements.
+ aCursor.mCursor =
+ (mContent && mContent->IsEditable())
+ ? NS_STYLE_CURSOR_TEXT : NS_STYLE_CURSOR_DEFAULT;
+ }
+ if (NS_STYLE_CURSOR_TEXT == aCursor.mCursor &&
+ GetWritingMode().IsVertical()) {
+ // Per CSS UI spec, UA may treat value 'text' as
+ // 'vertical-text' for vertical text.
+ aCursor.mCursor = NS_STYLE_CURSOR_VERTICAL_TEXT;
+ }
+
+ return NS_OK;
+}
+
+// Resize and incremental reflow
+
+/* virtual */ void
+nsFrame::MarkIntrinsicISizesDirty()
+{
+ // This version is meant only for what used to be box-to-block adaptors.
+ // It should not be called by other derived classes.
+ if (::IsXULBoxWrapped(this)) {
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+
+ SizeNeedsRecalc(metrics->mPrefSize);
+ SizeNeedsRecalc(metrics->mMinSize);
+ SizeNeedsRecalc(metrics->mMaxSize);
+ SizeNeedsRecalc(metrics->mBlockPrefSize);
+ SizeNeedsRecalc(metrics->mBlockMinSize);
+ CoordNeedsRecalc(metrics->mFlex);
+ CoordNeedsRecalc(metrics->mAscent);
+ }
+
+ if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
+ nsFontInflationData::MarkFontInflationDataTextDirty(this);
+ }
+}
+
+/* virtual */ nscoord
+nsFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = 0;
+ DISPLAY_MIN_WIDTH(this, result);
+ return result;
+}
+
+/* virtual */ nscoord
+nsFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = 0;
+ DISPLAY_PREF_WIDTH(this, result);
+ return result;
+}
+
+/* virtual */ void
+nsFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlineMinISizeData* aData)
+{
+ nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ this, nsLayoutUtils::MIN_ISIZE);
+ aData->DefaultAddInlineMinISize(this, isize);
+}
+
+/* virtual */ void
+nsFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlinePrefISizeData* aData)
+{
+ nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ this, nsLayoutUtils::PREF_ISIZE);
+ aData->DefaultAddInlinePrefISize(isize);
+}
+
+void
+nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
+ nscoord aISize,
+ bool aAllowBreak)
+{
+ auto parent = aFrame->GetParent();
+ MOZ_ASSERT(parent, "Must have a parent if we get here!");
+ const bool mayBreak = aAllowBreak &&
+ !aFrame->CanContinueTextRun() &&
+ !parent->StyleContext()->ShouldSuppressLineBreak() &&
+ parent->StyleText()->WhiteSpaceCanWrap(parent);
+ if (mayBreak) {
+ OptionallyBreak();
+ }
+ mTrailingWhitespace = 0;
+ mSkipWhitespace = false;
+ mCurrentLine += aISize;
+ mAtStartOfLine = false;
+ if (mayBreak) {
+ OptionallyBreak();
+ }
+}
+
+void
+nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize)
+{
+ mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
+ mTrailingWhitespace = 0;
+ mSkipWhitespace = false;
+}
+
+void
+nsIFrame::InlineMinISizeData::ForceBreak()
+{
+ mCurrentLine -= mTrailingWhitespace;
+ mPrevLines = std::max(mPrevLines, mCurrentLine);
+ mCurrentLine = mTrailingWhitespace = 0;
+
+ for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
+ nscoord float_min = mFloats[i].Width();
+ if (float_min > mPrevLines)
+ mPrevLines = float_min;
+ }
+ mFloats.Clear();
+ mSkipWhitespace = true;
+}
+
+void
+nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth)
+{
+ // If we can fit more content into a smaller width by staying on this
+ // line (because we're still at a negative offset due to negative
+ // text-indent or negative margin), don't break. Otherwise, do the
+ // same as ForceBreak. it doesn't really matter when we accumulate
+ // floats.
+ if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine)
+ return;
+ mCurrentLine += aHyphenWidth;
+ ForceBreak();
+}
+
+void
+nsIFrame::InlinePrefISizeData::ForceBreak()
+{
+ if (mFloats.Length() != 0) {
+ // preferred widths accumulated for floats that have already
+ // been cleared past
+ nscoord floats_done = 0,
+ // preferred widths accumulated for floats that have not yet
+ // been cleared past
+ floats_cur_left = 0,
+ floats_cur_right = 0;
+
+ for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
+ const FloatInfo& floatInfo = mFloats[i];
+ const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
+ StyleClear breakType = floatDisp->PhysicalBreakType(mLineContainerWM);
+ if (breakType == StyleClear::Left ||
+ breakType == StyleClear::Right ||
+ breakType == StyleClear::Both) {
+ nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left,
+ floats_cur_right);
+ if (floats_cur > floats_done) {
+ floats_done = floats_cur;
+ }
+ if (breakType != StyleClear::Right) {
+ floats_cur_left = 0;
+ }
+ if (breakType != StyleClear::Left) {
+ floats_cur_right = 0;
+ }
+ }
+
+ StyleFloat floatStyle = floatDisp->PhysicalFloats(mLineContainerWM);
+ nscoord& floats_cur =
+ floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
+ nscoord floatWidth = floatInfo.Width();
+ // Negative-width floats don't change the available space so they
+ // shouldn't change our intrinsic line width either.
+ floats_cur =
+ NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
+ }
+
+ nscoord floats_cur =
+ NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
+ if (floats_cur > floats_done)
+ floats_done = floats_cur;
+
+ mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done);
+
+ mFloats.Clear();
+ }
+
+ mCurrentLine =
+ NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
+ mPrevLines = std::max(mPrevLines, mCurrentLine);
+ mCurrentLine = mTrailingWhitespace = 0;
+ mSkipWhitespace = true;
+}
+
+static void
+AddCoord(const nsStyleCoord& aStyle,
+ nsIFrame* aFrame,
+ nscoord* aCoord, float* aPercent,
+ bool aClampNegativeToZero)
+{
+ switch (aStyle.GetUnit()) {
+ case eStyleUnit_Coord: {
+ NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0,
+ "unexpected negative value");
+ *aCoord += aStyle.GetCoordValue();
+ return;
+ }
+ case eStyleUnit_Percent: {
+ NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f,
+ "unexpected negative value");
+ *aPercent += aStyle.GetPercentValue();
+ return;
+ }
+ case eStyleUnit_Calc: {
+ const nsStyleCoord::Calc *calc = aStyle.GetCalcValue();
+ if (aClampNegativeToZero) {
+ // This is far from ideal when one is negative and one is positive.
+ *aCoord += std::max(calc->mLength, 0);
+ *aPercent += std::max(calc->mPercent, 0.0f);
+ } else {
+ *aCoord += calc->mLength;
+ *aPercent += calc->mPercent;
+ }
+ return;
+ }
+ default: {
+ return;
+ }
+ }
+}
+
+static nsIFrame::IntrinsicISizeOffsetData
+IntrinsicSizeOffsets(nsIFrame* aFrame, bool aForISize)
+{
+ nsIFrame::IntrinsicISizeOffsetData result;
+ WritingMode wm = aFrame->GetWritingMode();
+ const nsStyleMargin* styleMargin = aFrame->StyleMargin();
+ bool verticalAxis = aForISize == wm.IsVertical();
+ AddCoord(verticalAxis ? styleMargin->mMargin.GetTop()
+ : styleMargin->mMargin.GetLeft(),
+ aFrame, &result.hMargin, &result.hPctMargin,
+ false);
+ AddCoord(verticalAxis ? styleMargin->mMargin.GetBottom()
+ : styleMargin->mMargin.GetRight(),
+ aFrame, &result.hMargin, &result.hPctMargin,
+ false);
+
+ const nsStylePadding* stylePadding = aFrame->StylePadding();
+ AddCoord(verticalAxis ? stylePadding->mPadding.GetTop()
+ : stylePadding->mPadding.GetLeft(),
+ aFrame, &result.hPadding, &result.hPctPadding,
+ true);
+ AddCoord(verticalAxis ? stylePadding->mPadding.GetBottom()
+ : stylePadding->mPadding.GetRight(),
+ aFrame, &result.hPadding, &result.hPctPadding,
+ true);
+
+ const nsStyleBorder* styleBorder = aFrame->StyleBorder();
+ if (verticalAxis) {
+ result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_TOP);
+ result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_BOTTOM);
+ } else {
+ result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_LEFT);
+ result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT);
+ }
+
+ const nsStyleDisplay* disp = aFrame->StyleDisplay();
+ if (aFrame->IsThemed(disp)) {
+ nsPresContext* presContext = aFrame->PresContext();
+
+ nsIntMargin border;
+ presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
+ aFrame, disp->mAppearance,
+ &border);
+ result.hBorder =
+ presContext->DevPixelsToAppUnits(verticalAxis ? border.TopBottom()
+ : border.LeftRight());
+
+ nsIntMargin padding;
+ if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
+ aFrame, disp->mAppearance,
+ &padding)) {
+ result.hPadding =
+ presContext->DevPixelsToAppUnits(verticalAxis ? padding.TopBottom()
+ : padding.LeftRight());
+ result.hPctPadding = 0;
+ }
+ }
+ return result;
+}
+
+/* virtual */ nsIFrame::IntrinsicISizeOffsetData
+nsFrame::IntrinsicISizeOffsets()
+{
+ return IntrinsicSizeOffsets(this, true);
+}
+
+nsIFrame::IntrinsicISizeOffsetData
+nsIFrame::IntrinsicBSizeOffsets()
+{
+ return IntrinsicSizeOffsets(this, false);
+}
+
+/* virtual */ IntrinsicSize
+nsFrame::GetIntrinsicSize()
+{
+ return IntrinsicSize(); // default is width/height set to eStyleUnit_None
+}
+
+/* virtual */ nsSize
+nsFrame::GetIntrinsicRatio()
+{
+ return nsSize(0, 0);
+}
+
+/* virtual */
+LogicalSize
+nsFrame::ComputeSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ MOZ_ASSERT(GetIntrinsicRatio() == nsSize(0,0),
+ "Please override this method and call "
+ "nsFrame::ComputeSizeWithIntrinsicDimensions instead.");
+ LogicalSize result = ComputeAutoSize(aRenderingContext, aWM,
+ aCBSize, aAvailableISize,
+ aMargin, aBorder, aPadding,
+ aFlags);
+ const nsStylePosition *stylePos = StylePosition();
+
+ LogicalSize boxSizingAdjust(aWM);
+ if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
+ boxSizingAdjust = aBorder + aPadding;
+ }
+ nscoord boxSizingToMarginEdgeISize =
+ aMargin.ISize(aWM) + aBorder.ISize(aWM) + aPadding.ISize(aWM) -
+ boxSizingAdjust.ISize(aWM);
+
+ const nsStyleCoord* inlineStyleCoord = &stylePos->ISize(aWM);
+ const nsStyleCoord* blockStyleCoord = &stylePos->BSize(aWM);
+
+ nsIAtom* parentFrameType = GetParent() ? GetParent()->GetType() : nullptr;
+ auto alignCB = GetParent();
+ bool isGridItem = (parentFrameType == nsGkAtoms::gridContainerFrame &&
+ !(GetStateBits() & NS_FRAME_OUT_OF_FLOW));
+ if (parentFrameType == nsGkAtoms::tableWrapperFrame &&
+ GetType() == nsGkAtoms::tableFrame) {
+ // An inner table frame is sized as a grid item if its table wrapper is,
+ // because they actually have the same CB (the wrapper's CB).
+ // @see ReflowInput::InitCBReflowInput
+ auto tableWrapper = GetParent();
+ auto grandParent = tableWrapper->GetParent();
+ isGridItem = (grandParent->GetType() == nsGkAtoms::gridContainerFrame &&
+ !(tableWrapper->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
+ if (isGridItem) {
+ // When resolving justify/align-self below, we want to use the grid
+ // container's justify/align-items value and WritingMode.
+ alignCB = grandParent;
+ }
+ }
+ bool isFlexItem = (parentFrameType == nsGkAtoms::flexContainerFrame &&
+ !(GetStateBits() & NS_FRAME_OUT_OF_FLOW));
+ bool isInlineFlexItem = false;
+ if (isFlexItem) {
+ // Flex items use their "flex-basis" property in place of their main-size
+ // property (e.g. "width") for sizing purposes, *unless* they have
+ // "flex-basis:auto", in which case they use their main-size property after
+ // all.
+ uint32_t flexDirection = GetParent()->StylePosition()->mFlexDirection;
+ isInlineFlexItem =
+ flexDirection == NS_STYLE_FLEX_DIRECTION_ROW ||
+ flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE;
+
+ // NOTE: The logic here should match the similar chunk for determining
+ // inlineStyleCoord and blockStyleCoord in
+ // nsFrame::ComputeSizeWithIntrinsicDimensions().
+ const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
+ if (flexBasis->GetUnit() != eStyleUnit_Auto) {
+ if (isInlineFlexItem) {
+ inlineStyleCoord = flexBasis;
+ } else {
+ // One caveat for vertical flex items: We don't support enumerated
+ // values (e.g. "max-content") for height properties yet. So, if our
+ // computed flex-basis is an enumerated value, we'll just behave as if
+ // it were "auto", which means "use the main-size property after all"
+ // (which is "height", in this case).
+ // NOTE: Once we support intrinsic sizing keywords for "height",
+ // we should remove this check.
+ if (flexBasis->GetUnit() != eStyleUnit_Enumerated) {
+ blockStyleCoord = flexBasis;
+ }
+ }
+ }
+ }
+
+ // Compute inline-axis size
+
+ if (inlineStyleCoord->GetUnit() != eStyleUnit_Auto) {
+ result.ISize(aWM) =
+ ComputeISizeValue(aRenderingContext, aCBSize.ISize(aWM),
+ boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
+ *inlineStyleCoord, aFlags);
+ } else if (MOZ_UNLIKELY(isGridItem) &&
+ !IS_TRUE_OVERFLOW_CONTAINER(this)) {
+ // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
+ // 'normal' and clamp it to the CB if requested:
+ bool stretch = false;
+ if (!(aFlags & nsIFrame::eShrinkWrap) &&
+ !StyleMargin()->HasInlineAxisAuto(aWM)) {
+ auto inlineAxisAlignment =
+ aWM.IsOrthogonalTo(alignCB->GetWritingMode()) ?
+ StylePosition()->UsedAlignSelf(alignCB->StyleContext()) :
+ StylePosition()->UsedJustifySelf(alignCB->StyleContext());
+ stretch = inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
+ inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH;
+ }
+ if (stretch || (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
+ auto iSizeToFillCB = std::max(nscoord(0), aCBSize.ISize(aWM) -
+ aPadding.ISize(aWM) -
+ aBorder.ISize(aWM) -
+ aMargin.ISize(aWM));
+ if (stretch || result.ISize(aWM) > iSizeToFillCB) {
+ result.ISize(aWM) = iSizeToFillCB;
+ }
+ }
+ }
+
+ // Flex items ignore their min & max sizing properties in their
+ // flex container's main-axis. (Those properties get applied later in
+ // the flexbox algorithm.)
+ const nsStyleCoord& maxISizeCoord = stylePos->MaxISize(aWM);
+ nscoord maxISize = NS_UNCONSTRAINEDSIZE;
+ if (maxISizeCoord.GetUnit() != eStyleUnit_None &&
+ !(isFlexItem && isInlineFlexItem)) {
+ maxISize =
+ ComputeISizeValue(aRenderingContext, aCBSize.ISize(aWM),
+ boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
+ maxISizeCoord, aFlags);
+ result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
+ }
+
+ const nsStyleCoord& minISizeCoord = stylePos->MinISize(aWM);
+ nscoord minISize;
+ if (minISizeCoord.GetUnit() != eStyleUnit_Auto &&
+ !(isFlexItem && isInlineFlexItem)) {
+ minISize =
+ ComputeISizeValue(aRenderingContext, aCBSize.ISize(aWM),
+ boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
+ minISizeCoord, aFlags);
+ } else if (MOZ_UNLIKELY(isGridItem)) {
+ // This implements "Implied Minimum Size of Grid Items".
+ // https://drafts.csswg.org/css-grid/#min-size-auto
+ minISize = std::min(maxISize, GetMinISize(aRenderingContext));
+ if (inlineStyleCoord->IsCoordPercentCalcUnit()) {
+ minISize = std::min(minISize, result.ISize(aWM));
+ } else if (aFlags & eIClampMarginBoxMinSize) {
+ // "if the grid item spans only grid tracks that have a fixed max track
+ // sizing function, its automatic minimum size in that dimension is
+ // further clamped to less than or equal to the size necessary to fit
+ // its margin box within the resulting grid area (flooring at zero)"
+ // https://drafts.csswg.org/css-grid/#min-size-auto
+ auto maxMinISize = std::max(nscoord(0), aCBSize.ISize(aWM) -
+ aPadding.ISize(aWM) -
+ aBorder.ISize(aWM) -
+ aMargin.ISize(aWM));
+ minISize = std::min(minISize, maxMinISize);
+ }
+ } else {
+ // Treat "min-width: auto" as 0.
+ // NOTE: Technically, "auto" is supposed to behave like "min-content" on
+ // flex items. However, we don't need to worry about that here, because
+ // flex items' min-sizes are intentionally ignored until the flex
+ // container explicitly considers them during space distribution.
+ minISize = 0;
+ }
+ result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
+
+ // Compute block-axis size
+ // (but not if we have auto bsize or if we recieved the "eUseAutoBSize"
+ // flag -- then, we'll just stick with the bsize that we already calculated
+ // in the initial ComputeAutoSize() call.)
+ if (!(aFlags & nsIFrame::eUseAutoBSize)) {
+ if (!nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM))) {
+ result.BSize(aWM) =
+ nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
+ boxSizingAdjust.BSize(aWM),
+ *blockStyleCoord);
+ } else if (MOZ_UNLIKELY(isGridItem) &&
+ blockStyleCoord->GetUnit() == eStyleUnit_Auto &&
+ !IS_TRUE_OVERFLOW_CONTAINER(this)) {
+ auto cbSize = aCBSize.BSize(aWM);
+ if (cbSize != NS_AUTOHEIGHT) {
+ // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
+ // 'normal' and clamp it to the CB if requested:
+ bool stretch = false;
+ if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
+ auto blockAxisAlignment =
+ !aWM.IsOrthogonalTo(alignCB->GetWritingMode()) ?
+ StylePosition()->UsedAlignSelf(alignCB->StyleContext()) :
+ StylePosition()->UsedJustifySelf(alignCB->StyleContext());
+ stretch = blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
+ blockAxisAlignment == NS_STYLE_ALIGN_STRETCH;
+ }
+ if (stretch || (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
+ auto bSizeToFillCB = std::max(nscoord(0), cbSize -
+ aPadding.BSize(aWM) -
+ aBorder.BSize(aWM) -
+ aMargin.BSize(aWM));
+ if (stretch || (result.BSize(aWM) != NS_AUTOHEIGHT &&
+ result.BSize(aWM) > bSizeToFillCB)) {
+ result.BSize(aWM) = bSizeToFillCB;
+ }
+ }
+ }
+ }
+ }
+
+ const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(aWM);
+
+ if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
+ if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
+ !(isFlexItem && !isInlineFlexItem)) {
+ nscoord maxBSize =
+ nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
+ boxSizingAdjust.BSize(aWM),
+ maxBSizeCoord);
+ result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
+ }
+
+ const nsStyleCoord& minBSizeCoord = stylePos->MinBSize(aWM);
+
+ if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
+ !(isFlexItem && !isInlineFlexItem)) {
+ nscoord minBSize =
+ nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
+ boxSizingAdjust.BSize(aWM),
+ minBSizeCoord);
+ result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
+ }
+ }
+
+ const nsStyleDisplay *disp = StyleDisplay();
+ if (IsThemed(disp)) {
+ LayoutDeviceIntSize widget;
+ bool canOverride = true;
+ nsPresContext *presContext = PresContext();
+ presContext->GetTheme()->
+ GetMinimumWidgetSize(presContext, this, disp->mAppearance,
+ &widget, &canOverride);
+
+ // Convert themed widget's physical dimensions to logical coords
+ LogicalSize size(aWM,
+ nsSize(presContext->DevPixelsToAppUnits(widget.width),
+ presContext->DevPixelsToAppUnits(widget.height)));
+
+ // GMWS() returns border-box; we need content-box
+ size.ISize(aWM) -= aBorder.ISize(aWM) + aPadding.ISize(aWM);
+ size.BSize(aWM) -= aBorder.BSize(aWM) + aPadding.BSize(aWM);
+
+ if (size.BSize(aWM) > result.BSize(aWM) || !canOverride) {
+ result.BSize(aWM) = size.BSize(aWM);
+ }
+ if (size.ISize(aWM) > result.ISize(aWM) || !canOverride) {
+ result.ISize(aWM) = size.ISize(aWM);
+ }
+ }
+
+ result.ISize(aWM) = std::max(0, result.ISize(aWM));
+ result.BSize(aWM) = std::max(0, result.BSize(aWM));
+
+ return result;
+}
+
+LogicalSize
+nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const IntrinsicSize& aIntrinsicSize,
+ nsSize aIntrinsicRatio,
+ const LogicalSize& aCBSize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ const nsStylePosition* stylePos = StylePosition();
+ const nsStyleCoord* inlineStyleCoord = &stylePos->ISize(aWM);
+ const nsStyleCoord* blockStyleCoord = &stylePos->BSize(aWM);
+ const nsIAtom* parentFrameType =
+ GetParent() ? GetParent()->GetType() : nullptr;
+ const bool isGridItem = (parentFrameType == nsGkAtoms::gridContainerFrame &&
+ !(GetStateBits() & NS_FRAME_OUT_OF_FLOW));
+ const bool isFlexItem = (parentFrameType == nsGkAtoms::flexContainerFrame &&
+ !(GetStateBits() & NS_FRAME_OUT_OF_FLOW));
+ bool isInlineFlexItem = false;
+ Maybe<nsStyleCoord> imposedMainSizeStyleCoord;
+
+ // If this is a flex item, and we're measuring its cross size after flexing
+ // to resolve its main size, then we need to use the resolved main size
+ // that the container provides to us *instead of* the main-size coordinate
+ // from our style struct. (Otherwise, we'll be using an irrelevant value in
+ // the aspect-ratio calculations below.)
+ if (isFlexItem) {
+ uint32_t flexDirection =
+ GetParent()->StylePosition()->mFlexDirection;
+ isInlineFlexItem =
+ flexDirection == NS_STYLE_FLEX_DIRECTION_ROW ||
+ flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE;
+
+ // If FlexItemMainSizeOverride frame-property is set, then that means the
+ // flex container is imposing a main-size on this flex item for it to use
+ // as its size in the container's main axis.
+ FrameProperties props = Properties();
+ bool didImposeMainSize;
+ nscoord imposedMainSize =
+ props.Get(nsIFrame::FlexItemMainSizeOverride(), &didImposeMainSize);
+ if (didImposeMainSize) {
+ imposedMainSizeStyleCoord.emplace(imposedMainSize,
+ nsStyleCoord::CoordConstructor);
+ if (isInlineFlexItem) {
+ inlineStyleCoord = imposedMainSizeStyleCoord.ptr();
+ } else {
+ blockStyleCoord = imposedMainSizeStyleCoord.ptr();
+ }
+
+ } else {
+ // Flex items use their "flex-basis" property in place of their main-size
+ // property (e.g. "width") for sizing purposes, *unless* they have
+ // "flex-basis:auto", in which case they use their main-size property
+ // after all.
+ // NOTE: The logic here should match the similar chunk for determining
+ // inlineStyleCoord and blockStyleCoord in nsFrame::ComputeSize().
+ const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
+ if (flexBasis->GetUnit() != eStyleUnit_Auto) {
+ if (isInlineFlexItem) {
+ inlineStyleCoord = flexBasis;
+ } else {
+ // One caveat for vertical flex items: We don't support enumerated
+ // values (e.g. "max-content") for height properties yet. So, if our
+ // computed flex-basis is an enumerated value, we'll just behave as if
+ // it were "auto", which means "use the main-size property after all"
+ // (which is "height", in this case).
+ // NOTE: Once we support intrinsic sizing keywords for "height",
+ // we should remove this check.
+ if (flexBasis->GetUnit() != eStyleUnit_Enumerated) {
+ blockStyleCoord = flexBasis;
+ }
+ }
+ }
+ }
+ }
+
+ // Handle intrinsic sizes and their interaction with
+ // {min-,max-,}{width,height} according to the rules in
+ // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
+
+ // Note: throughout the following section of the function, I avoid
+ // a * (b / c) because of its reduced accuracy relative to a * b / c
+ // or (a * b) / c (which are equivalent).
+
+ const bool isAutoISize = inlineStyleCoord->GetUnit() == eStyleUnit_Auto;
+ const bool isAutoBSize =
+ nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM));
+
+ LogicalSize boxSizingAdjust(aWM);
+ if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
+ boxSizingAdjust = aBorder + aPadding;
+ }
+ nscoord boxSizingToMarginEdgeISize =
+ aMargin.ISize(aWM) + aBorder.ISize(aWM) + aPadding.ISize(aWM) -
+ boxSizingAdjust.ISize(aWM);
+
+ nscoord iSize, minISize, maxISize, bSize, minBSize, maxBSize;
+ enum class Stretch {
+ // stretch to fill the CB (preserving intrinsic ratio) in the relevant axis
+ eStretchPreservingRatio,
+ // stretch to fill the CB in the relevant axis
+ eStretch,
+ // no stretching in the relevant axis
+ eNoStretch,
+ };
+ // just to avoid having to type these out everywhere:
+ const auto eStretchPreservingRatio = Stretch::eStretchPreservingRatio;
+ const auto eStretch = Stretch::eStretch;
+ const auto eNoStretch = Stretch::eNoStretch;
+
+ Stretch stretchI = eNoStretch; // stretch behavior in the inline axis
+ Stretch stretchB = eNoStretch; // stretch behavior in the block axis
+
+ if (!isAutoISize) {
+ iSize = ComputeISizeValue(aRenderingContext,
+ aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
+ boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
+ } else if (MOZ_UNLIKELY(isGridItem)) {
+ MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
+ // 'auto' inline-size for grid-level box - apply 'stretch' as needed:
+ auto cbSize = aCBSize.ISize(aWM);
+ if (cbSize != NS_UNCONSTRAINEDSIZE) {
+ if (!StyleMargin()->HasInlineAxisAuto(aWM)) {
+ auto inlineAxisAlignment =
+ aWM.IsOrthogonalTo(GetParent()->GetWritingMode()) ?
+ stylePos->UsedAlignSelf(GetParent()->StyleContext()) :
+ stylePos->UsedJustifySelf(GetParent()->StyleContext());
+ if (inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL) {
+ stretchI = eStretchPreservingRatio;
+ } else if (inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
+ stretchI = eStretch;
+ }
+ }
+ if (stretchI != eNoStretch ||
+ (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
+ iSize = std::max(nscoord(0), cbSize -
+ aPadding.ISize(aWM) -
+ aBorder.ISize(aWM) -
+ aMargin.ISize(aWM));
+ }
+ } else {
+ // Reset this flag to avoid applying the clamping below.
+ aFlags = ComputeSizeFlags(aFlags &
+ ~ComputeSizeFlags::eIClampMarginBoxMinSize);
+ }
+ }
+
+ const nsStyleCoord& maxISizeCoord = stylePos->MaxISize(aWM);
+
+ if (maxISizeCoord.GetUnit() != eStyleUnit_None &&
+ !(isFlexItem && isInlineFlexItem)) {
+ maxISize = ComputeISizeValue(aRenderingContext,
+ aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
+ boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
+ } else {
+ maxISize = nscoord_MAX;
+ }
+
+ // NOTE: Flex items ignore their min & max sizing properties in their
+ // flex container's main-axis. (Those properties get applied later in
+ // the flexbox algorithm.)
+
+ const nsStyleCoord& minISizeCoord = stylePos->MinISize(aWM);
+
+ if (minISizeCoord.GetUnit() != eStyleUnit_Auto &&
+ !(isFlexItem && isInlineFlexItem)) {
+ minISize = ComputeISizeValue(aRenderingContext,
+ aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
+ boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
+ } else {
+ // Treat "min-width: auto" as 0.
+ // NOTE: Technically, "auto" is supposed to behave like "min-content" on
+ // flex items. However, we don't need to worry about that here, because
+ // flex items' min-sizes are intentionally ignored until the flex
+ // container explicitly considers them during space distribution.
+ minISize = 0;
+ }
+
+ if (!isAutoBSize) {
+ bSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
+ boxSizingAdjust.BSize(aWM),
+ *blockStyleCoord);
+ } else if (MOZ_UNLIKELY(isGridItem)) {
+ MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
+ // 'auto' block-size for grid-level box - apply 'stretch' as needed:
+ auto cbSize = aCBSize.BSize(aWM);
+ if (cbSize != NS_AUTOHEIGHT) {
+ if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
+ auto blockAxisAlignment =
+ !aWM.IsOrthogonalTo(GetParent()->GetWritingMode()) ?
+ stylePos->UsedAlignSelf(GetParent()->StyleContext()) :
+ stylePos->UsedJustifySelf(GetParent()->StyleContext());
+ if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL) {
+ stretchB = eStretchPreservingRatio;
+ } else if (blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
+ stretchB = eStretch;
+ }
+ }
+ if (stretchB != eNoStretch ||
+ (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
+ bSize = std::max(nscoord(0), cbSize -
+ aPadding.BSize(aWM) -
+ aBorder.BSize(aWM) -
+ aMargin.BSize(aWM));
+ }
+ } else {
+ // Reset this flag to avoid applying the clamping below.
+ aFlags = ComputeSizeFlags(aFlags &
+ ~ComputeSizeFlags::eBClampMarginBoxMinSize);
+ }
+ }
+
+ const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(aWM);
+
+ if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
+ !(isFlexItem && !isInlineFlexItem)) {
+ maxBSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
+ boxSizingAdjust.BSize(aWM), maxBSizeCoord);
+ } else {
+ maxBSize = nscoord_MAX;
+ }
+
+ const nsStyleCoord& minBSizeCoord = stylePos->MinBSize(aWM);
+
+ if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
+ !(isFlexItem && !isInlineFlexItem)) {
+ minBSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
+ boxSizingAdjust.BSize(aWM), minBSizeCoord);
+ } else {
+ minBSize = 0;
+ }
+
+ // Resolve percentage intrinsic iSize/bSize as necessary:
+
+ NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
+ "Our containing block must not have unconstrained inline-size!");
+
+ const bool isVertical = aWM.IsVertical();
+ const nsStyleCoord& isizeCoord =
+ isVertical ? aIntrinsicSize.height : aIntrinsicSize.width;
+ const nsStyleCoord& bsizeCoord =
+ isVertical ? aIntrinsicSize.width : aIntrinsicSize.height;
+
+ bool hasIntrinsicISize, hasIntrinsicBSize;
+ nscoord intrinsicISize, intrinsicBSize;
+
+ if (isizeCoord.GetUnit() == eStyleUnit_Coord) {
+ hasIntrinsicISize = true;
+ intrinsicISize = isizeCoord.GetCoordValue();
+ if (intrinsicISize < 0)
+ intrinsicISize = 0;
+ } else {
+ NS_ASSERTION(isizeCoord.GetUnit() == eStyleUnit_None,
+ "unexpected unit");
+ hasIntrinsicISize = false;
+ intrinsicISize = 0;
+ }
+
+ if (bsizeCoord.GetUnit() == eStyleUnit_Coord) {
+ hasIntrinsicBSize = true;
+ intrinsicBSize = bsizeCoord.GetCoordValue();
+ if (intrinsicBSize < 0)
+ intrinsicBSize = 0;
+ } else {
+ NS_ASSERTION(bsizeCoord.GetUnit() == eStyleUnit_None,
+ "unexpected unit");
+ hasIntrinsicBSize = false;
+ intrinsicBSize = 0;
+ }
+
+ NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0,
+ "Intrinsic ratio has a negative component!");
+ LogicalSize logicalRatio(aWM, aIntrinsicRatio);
+
+ // Now calculate the used values for iSize and bSize:
+
+ if (isAutoISize) {
+ if (isAutoBSize) {
+
+ // 'auto' iSize, 'auto' bSize
+
+ // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
+
+ nscoord tentISize, tentBSize;
+
+ if (hasIntrinsicISize) {
+ tentISize = intrinsicISize;
+ } else if (hasIntrinsicBSize && logicalRatio.BSize(aWM) > 0) {
+ tentISize = NSCoordMulDiv(intrinsicBSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM));
+ } else if (logicalRatio.ISize(aWM) > 0) {
+ tentISize = aCBSize.ISize(aWM) - boxSizingToMarginEdgeISize; // XXX scrollbar?
+ if (tentISize < 0) tentISize = 0;
+ } else {
+ tentISize = nsPresContext::CSSPixelsToAppUnits(300);
+ }
+
+ // If we need to clamp the inline size to fit the CB, we use the 'stretch'
+ // or 'normal' codepath. We use the ratio-preserving 'normal' codepath
+ // unless we have 'stretch' in the other axis.
+ if ((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) &&
+ stretchI != eStretch && tentISize > iSize) {
+ stretchI = (stretchB == eStretch ? eStretch : eStretchPreservingRatio);
+ }
+
+ if (hasIntrinsicBSize) {
+ tentBSize = intrinsicBSize;
+ } else if (logicalRatio.ISize(aWM) > 0) {
+ tentBSize = NSCoordMulDiv(tentISize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM));
+ } else {
+ tentBSize = nsPresContext::CSSPixelsToAppUnits(150);
+ }
+
+ // (ditto the comment about clamping the inline size above)
+ if ((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) &&
+ stretchB != eStretch && tentBSize > bSize) {
+ stretchB = (stretchI == eStretch ? eStretch : eStretchPreservingRatio);
+ }
+
+ if (aIntrinsicRatio != nsSize(0, 0)) {
+ if (stretchI == eStretch) {
+ tentISize = iSize; // * / 'stretch'
+ if (stretchB == eStretch) {
+ tentBSize = bSize; // 'stretch' / 'stretch'
+ } else if (stretchB == eStretchPreservingRatio && logicalRatio.ISize(aWM) > 0) {
+ // 'normal' / 'stretch'
+ tentBSize = NSCoordMulDiv(iSize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM));
+ }
+ } else if (stretchB == eStretch) {
+ tentBSize = bSize; // 'stretch' / * (except 'stretch')
+ if (stretchI == eStretchPreservingRatio && logicalRatio.BSize(aWM) > 0) {
+ // 'stretch' / 'normal'
+ tentISize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM));
+ }
+ } else if (stretchI == eStretchPreservingRatio) {
+ tentISize = iSize; // * (except 'stretch') / 'normal'
+ if (logicalRatio.ISize(aWM) > 0) {
+ tentBSize = NSCoordMulDiv(iSize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM));
+ }
+ if (stretchB == eStretchPreservingRatio && tentBSize > bSize) {
+ // Stretch within the CB size with preserved intrinsic ratio.
+ tentBSize = bSize; // 'normal' / 'normal'
+ if (logicalRatio.BSize(aWM) > 0) {
+ tentISize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM));
+ }
+ }
+ } else if (stretchB == eStretchPreservingRatio) {
+ tentBSize = bSize; // 'normal' / * (except 'normal' and 'stretch')
+ if (logicalRatio.BSize(aWM) > 0) {
+ tentISize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM));
+ }
+ }
+ }
+
+ // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when applying
+ // the min/max-size. We don't want that when we have 'stretch' in either
+ // axis because tentISize/tentBSize is likely not according to ratio now.
+ if (aIntrinsicRatio != nsSize(0, 0) &&
+ stretchI != eStretch && stretchB != eStretch) {
+ nsSize autoSize = nsLayoutUtils::
+ ComputeAutoSizeWithIntrinsicDimensions(minISize, minBSize,
+ maxISize, maxBSize,
+ tentISize, tentBSize);
+ // The nsSize that ComputeAutoSizeWithIntrinsicDimensions returns will
+ // actually contain logical values if the parameters passed to it were
+ // logical coordinates, so we do NOT perform a physical-to-logical
+ // conversion here, but just assign the fields directly to our result.
+ iSize = autoSize.width;
+ bSize = autoSize.height;
+ } else {
+ // Not honoring an intrinsic ratio: clamp the dimensions independently.
+ iSize = NS_CSS_MINMAX(tentISize, minISize, maxISize);
+ bSize = NS_CSS_MINMAX(tentBSize, minBSize, maxBSize);
+ }
+ } else {
+
+ // 'auto' iSize, non-'auto' bSize
+ bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
+ if (stretchI != eStretch) {
+ if (logicalRatio.BSize(aWM) > 0) {
+ iSize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM));
+ } else if (hasIntrinsicISize) {
+ if (!((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) &&
+ intrinsicISize > iSize)) {
+ iSize = intrinsicISize;
+ } // else - leave iSize as is to fill the CB
+ } else {
+ iSize = nsPresContext::CSSPixelsToAppUnits(300);
+ }
+ } // else - leave iSize as is to fill the CB
+ iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
+
+ }
+ } else {
+ if (isAutoBSize) {
+
+ // non-'auto' iSize, 'auto' bSize
+ iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
+ if (stretchB != eStretch) {
+ if (logicalRatio.ISize(aWM) > 0) {
+ bSize = NSCoordMulDiv(iSize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM));
+ } else if (hasIntrinsicBSize) {
+ if (!((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) &&
+ intrinsicBSize > bSize)) {
+ bSize = intrinsicBSize;
+ } // else - leave bSize as is to fill the CB
+ } else {
+ bSize = nsPresContext::CSSPixelsToAppUnits(150);
+ }
+ } // else - leave bSize as is to fill the CB
+ bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
+
+ } else {
+
+ // non-'auto' iSize, non-'auto' bSize
+ iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
+ bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
+
+ }
+ }
+
+ return LogicalSize(aWM, iSize, bSize);
+}
+
+nsRect
+nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const
+{
+ return GetVisualOverflowRect();
+}
+
+nsRect
+nsFrame::ComputeSimpleTightBounds(DrawTarget* aDrawTarget) const
+{
+ if (StyleOutline()->mOutlineStyle != NS_STYLE_BORDER_STYLE_NONE ||
+ StyleBorder()->HasBorder() || !StyleBackground()->IsTransparent() ||
+ StyleDisplay()->mAppearance) {
+ // Not necessarily tight, due to clipping, negative
+ // outline-offset, and lots of other issues, but that's OK
+ return GetVisualOverflowRect();
+ }
+
+ nsRect r(0, 0, 0, 0);
+ ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* child = childFrames.get();
+ r.UnionRect(r, child->ComputeTightBounds(aDrawTarget) + child->GetPosition());
+ }
+ }
+ return r;
+}
+
+/* virtual */ nsresult
+nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext,
+ nscoord* aX,
+ nscoord* aXMost)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* virtual */
+LogicalSize
+nsFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ // Use basic shrink-wrapping as a default implementation.
+ LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
+
+ // don't bother setting it if the result won't be used
+ if (StylePosition()->ISize(aWM).GetUnit() == eStyleUnit_Auto) {
+ nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
+ aBorder.ISize(aWM) - aPadding.ISize(aWM);
+ result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
+ }
+ return result;
+}
+
+nscoord
+nsFrame::ShrinkWidthToFit(nsRenderingContext* aRenderingContext,
+ nscoord aISizeInCB,
+ ComputeSizeFlags aFlags)
+{
+ // If we're a container for font size inflation, then shrink
+ // wrapping inside of us should not apply font size inflation.
+ AutoMaybeDisableFontInflation an(this);
+
+ nscoord result;
+ nscoord minISize = GetMinISize(aRenderingContext);
+ if (minISize > aISizeInCB) {
+ const bool clamp = aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize;
+ result = MOZ_UNLIKELY(clamp) ? aISizeInCB : minISize;
+ } else {
+ nscoord prefISize = GetPrefISize(aRenderingContext);
+ if (prefISize > aISizeInCB) {
+ result = aISizeInCB;
+ } else {
+ result = prefISize;
+ }
+ }
+ return result;
+}
+
+nscoord
+nsIFrame::ComputeISizeValue(nsRenderingContext* aRenderingContext,
+ nscoord aContainingBlockISize,
+ nscoord aContentEdgeToBoxSizing,
+ nscoord aBoxSizingToMarginEdge,
+ const nsStyleCoord& aCoord,
+ ComputeSizeFlags aFlags)
+{
+ NS_PRECONDITION(aRenderingContext, "non-null rendering context expected");
+ LAYOUT_WARN_IF_FALSE(aContainingBlockISize != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained inline-size; this should only result from "
+ "very large sizes, not attempts at intrinsic inline-size "
+ "calculation");
+ NS_PRECONDITION(aContainingBlockISize >= 0,
+ "inline-size less than zero");
+
+ nscoord result;
+ if (aCoord.IsCoordPercentCalcUnit()) {
+ result = nsRuleNode::ComputeCoordPercentCalc(aCoord,
+ aContainingBlockISize);
+ // The result of a calc() expression might be less than 0; we
+ // should clamp at runtime (below). (Percentages and coords that
+ // are less than 0 have already been dropped by the parser.)
+ result -= aContentEdgeToBoxSizing;
+ } else {
+ MOZ_ASSERT(eStyleUnit_Enumerated == aCoord.GetUnit());
+ // If 'this' is a container for font size inflation, then shrink
+ // wrapping inside of it should not apply font size inflation.
+ AutoMaybeDisableFontInflation an(this);
+
+ int32_t val = aCoord.GetIntValue();
+ switch (val) {
+ case NS_STYLE_WIDTH_MAX_CONTENT:
+ result = GetPrefISize(aRenderingContext);
+ NS_ASSERTION(result >= 0, "inline-size less than zero");
+ break;
+ case NS_STYLE_WIDTH_MIN_CONTENT:
+ result = GetMinISize(aRenderingContext);
+ NS_ASSERTION(result >= 0, "inline-size less than zero");
+ if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
+ auto available = aContainingBlockISize -
+ (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
+ result = std::min(available, result);
+ }
+ break;
+ case NS_STYLE_WIDTH_FIT_CONTENT:
+ {
+ nscoord pref = GetPrefISize(aRenderingContext),
+ min = GetMinISize(aRenderingContext),
+ fill = aContainingBlockISize -
+ (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
+ if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
+ min = std::min(min, fill);
+ }
+ result = std::max(min, std::min(pref, fill));
+ NS_ASSERTION(result >= 0, "inline-size less than zero");
+ }
+ break;
+ case NS_STYLE_WIDTH_AVAILABLE:
+ result = aContainingBlockISize -
+ (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
+ }
+ }
+
+ return std::max(0, result);
+}
+
+void
+nsFrame::DidReflow(nsPresContext* aPresContext,
+ const ReflowInput* aReflowInput,
+ nsDidReflowStatus aStatus)
+{
+ NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
+ ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus)));
+
+ nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW);
+
+ if (nsDidReflowStatus::FINISHED == aStatus) {
+ mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+
+ // Notify the percent bsize observer if there is a percent bsize.
+ // The observer may be able to initiate another reflow with a computed
+ // bsize. This happens in the case where a table cell has no computed
+ // bsize but can fabricate one when the cell bsize is known.
+ if (aReflowInput && aReflowInput->mPercentBSizeObserver &&
+ !GetPrevInFlow()) {
+ const nsStyleCoord &bsize =
+ aReflowInput->mStylePosition->BSize(aReflowInput->GetWritingMode());
+ if (bsize.HasPercent()) {
+ aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
+ }
+ }
+
+ aPresContext->ReflowedFrame();
+}
+
+void
+nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ bool aConstrainBSize)
+{
+ ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus, aConstrainBSize);
+
+ FinishAndStoreOverflow(&aDesiredSize);
+}
+
+void
+nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ bool aConstrainBSize)
+{
+ if (HasAbsolutelyPositionedChildren()) {
+ nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
+
+ // Let the absolutely positioned container reflow any absolutely positioned
+ // child frames that need to be reflowed
+
+ // The containing block for the abs pos kids is formed by our padding edge.
+ nsMargin usedBorder = GetUsedBorder();
+ nscoord containingBlockWidth =
+ std::max(0, aDesiredSize.Width() - usedBorder.LeftRight());
+ nscoord containingBlockHeight =
+ std::max(0, aDesiredSize.Height() - usedBorder.TopBottom());
+ nsContainerFrame* container = do_QueryFrame(this);
+ NS_ASSERTION(container, "Abs-pos children only supported on container frames for now");
+
+ nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
+ AbsPosReflowFlags flags =
+ AbsPosReflowFlags::eCBWidthAndHeightChanged; // XXX could be optimized
+ if (aConstrainBSize) {
+ flags |= AbsPosReflowFlags::eConstrainHeight;
+ }
+ absoluteContainer->Reflow(container, aPresContext, aReflowInput, aStatus,
+ containingBlock, flags,
+ &aDesiredSize.mOverflowAreas);
+ }
+}
+
+void
+nsFrame::PushDirtyBitToAbsoluteFrames()
+{
+ if (!(GetStateBits() & NS_FRAME_IS_DIRTY)) {
+ return; // No dirty bit to push.
+ }
+ if (!HasAbsolutelyPositionedChildren()) {
+ return; // No absolute children to push to.
+ }
+ GetAbsoluteContainingBlock()->MarkAllFramesDirty();
+}
+
+/* virtual */ bool
+nsFrame::CanContinueTextRun() const
+{
+ // By default, a frame will *not* allow a text run to be continued
+ // through it.
+ return false;
+}
+
+void
+nsFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsFrame");
+ aDesiredSize.ClearSize();
+ aStatus = NS_FRAME_COMPLETE;
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nsresult
+nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
+{
+ NS_NOTREACHED("should only be called for text frames");
+ return NS_OK;
+}
+
+nsresult
+nsFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ return NS_OK;
+}
+
+// Flow member functions
+
+nsSplittableType
+nsFrame::GetSplittableType() const
+{
+ return NS_FRAME_NOT_SPLITTABLE;
+}
+
+nsIFrame* nsFrame::GetPrevContinuation() const
+{
+ return nullptr;
+}
+
+void
+nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation)
+{
+ MOZ_ASSERT(false, "not splittable");
+}
+
+nsIFrame* nsFrame::GetNextContinuation() const
+{
+ return nullptr;
+}
+
+void
+nsFrame::SetNextContinuation(nsIFrame*)
+{
+ MOZ_ASSERT(false, "not splittable");
+}
+
+nsIFrame* nsFrame::GetPrevInFlowVirtual() const
+{
+ return nullptr;
+}
+
+void
+nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow)
+{
+ MOZ_ASSERT(false, "not splittable");
+}
+
+nsIFrame* nsFrame::GetNextInFlowVirtual() const
+{
+ return nullptr;
+}
+
+void
+nsFrame::SetNextInFlow(nsIFrame*)
+{
+ MOZ_ASSERT(false, "not splittable");
+}
+
+nsIFrame* nsIFrame::GetTailContinuation()
+{
+ nsIFrame* frame = this;
+ while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
+ frame = frame->GetPrevContinuation();
+ NS_ASSERTION(frame, "first continuation can't be overflow container");
+ }
+ for (nsIFrame* next = frame->GetNextContinuation();
+ next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
+ next = frame->GetNextContinuation()) {
+ frame = next;
+ }
+ NS_POSTCONDITION(frame, "illegal state in continuation chain.");
+ return frame;
+}
+
+NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(ViewProperty, nsView)
+
+// Associated view object
+nsView*
+nsIFrame::GetView() const
+{
+ // Check the frame state bit and see if the frame has a view
+ if (!(GetStateBits() & NS_FRAME_HAS_VIEW))
+ return nullptr;
+
+ // Check for a property on the frame
+ nsView* value = Properties().Get(ViewProperty());
+ NS_ASSERTION(value, "frame state bit was set but frame has no view");
+ return value;
+}
+
+nsresult
+nsIFrame::SetView(nsView* aView)
+{
+ if (aView) {
+ aView->SetFrame(this);
+
+#ifdef DEBUG
+ nsIAtom* frameType = GetType();
+ NS_ASSERTION(frameType == nsGkAtoms::scrollFrame ||
+ frameType == nsGkAtoms::subDocumentFrame ||
+ frameType == nsGkAtoms::listControlFrame ||
+ frameType == nsGkAtoms::objectFrame ||
+ frameType == nsGkAtoms::viewportFrame ||
+ frameType == nsGkAtoms::menuPopupFrame,
+ "Only specific frame types can have an nsView");
+#endif
+
+ // Set a property on the frame
+ Properties().Set(ViewProperty(), aView);
+
+ // Set the frame state bit that says the frame has a view
+ AddStateBits(NS_FRAME_HAS_VIEW);
+
+ // Let all of the ancestors know they have a descendant with a view.
+ for (nsIFrame* f = GetParent();
+ f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
+ f = f->GetParent())
+ f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
+ }
+
+ return NS_OK;
+}
+
+// Find the first geometric parent that has a view
+nsIFrame* nsIFrame::GetAncestorWithView() const
+{
+ for (nsIFrame* f = GetParent(); nullptr != f; f = f->GetParent()) {
+ if (f->HasView()) {
+ return f;
+ }
+ }
+ return nullptr;
+}
+
+nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const
+{
+ NS_PRECONDITION(aOther,
+ "Must have frame for destination coordinate system!");
+
+ NS_ASSERTION(PresContext() == aOther->PresContext(),
+ "GetOffsetTo called on frames in different documents");
+
+ nsPoint offset(0, 0);
+ const nsIFrame* f;
+ for (f = this; f != aOther && f; f = f->GetParent()) {
+ offset += f->GetPosition();
+ }
+
+ if (f != aOther) {
+ // Looks like aOther wasn't an ancestor of |this|. So now we have
+ // the root-frame-relative position of |this| in |offset|. Convert back
+ // to the coordinates of aOther
+ while (aOther) {
+ offset -= aOther->GetPosition();
+ aOther = aOther->GetParent();
+ }
+ }
+
+ return offset;
+}
+
+nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const
+{
+ return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
+}
+
+nsPoint
+nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const
+{
+ NS_PRECONDITION(aOther,
+ "Must have frame for destination coordinate system!");
+ NS_ASSERTION(PresContext()->GetRootPresContext() ==
+ aOther->PresContext()->GetRootPresContext(),
+ "trying to get the offset between frames in different document "
+ "hierarchies?");
+ if (PresContext()->GetRootPresContext() !=
+ aOther->PresContext()->GetRootPresContext()) {
+ // crash right away, we are almost certainly going to crash anyway.
+ NS_RUNTIMEABORT("trying to get the offset between frames in different "
+ "document hierarchies?");
+ }
+
+ const nsIFrame* root = nullptr;
+ // offset will hold the final offset
+ // docOffset holds the currently accumulated offset at the current APD, it
+ // will be converted and added to offset when the current APD changes.
+ nsPoint offset(0, 0), docOffset(0, 0);
+ const nsIFrame* f = this;
+ int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
+ while (f && f != aOther) {
+ docOffset += f->GetPosition();
+ nsIFrame* parent = f->GetParent();
+ if (parent) {
+ f = parent;
+ } else {
+ nsPoint newOffset(0, 0);
+ root = f;
+ f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset);
+ int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
+ if (!f || newAPD != currAPD) {
+ // Convert docOffset to the right APD and add it to offset.
+ offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
+ docOffset.x = docOffset.y = 0;
+ }
+ currAPD = newAPD;
+ docOffset += newOffset;
+ }
+ }
+ if (f == aOther) {
+ offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
+ } else {
+ // Looks like aOther wasn't an ancestor of |this|. So now we have
+ // the root-document-relative position of |this| in |offset|. Subtract the
+ // root-document-relative position of |aOther| from |offset|.
+ // This call won't try to recurse again because root is an ancestor of
+ // aOther.
+ nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
+ offset -= negOffset;
+ }
+
+ return offset;
+}
+
+nsIntRect nsIFrame::GetScreenRect() const
+{
+ return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel());
+}
+
+nsRect nsIFrame::GetScreenRectInAppUnits() const
+{
+ nsPresContext* presContext = PresContext();
+ nsIFrame* rootFrame =
+ presContext->PresShell()->FrameManager()->GetRootFrame();
+ nsPoint rootScreenPos(0, 0);
+ nsPoint rootFrameOffsetInParent(0, 0);
+ nsIFrame* rootFrameParent =
+ nsLayoutUtils::GetCrossDocParentFrame(rootFrame, &rootFrameOffsetInParent);
+ if (rootFrameParent) {
+ nsRect parentScreenRectAppUnits = rootFrameParent->GetScreenRectInAppUnits();
+ nsPresContext* parentPresContext = rootFrameParent->PresContext();
+ double parentScale = double(presContext->AppUnitsPerDevPixel())/
+ parentPresContext->AppUnitsPerDevPixel();
+ nsPoint rootPt = parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
+ rootScreenPos.x = NS_round(parentScale*rootPt.x);
+ rootScreenPos.y = NS_round(parentScale*rootPt.y);
+ } else {
+ nsCOMPtr<nsIWidget> rootWidget;
+ presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget));
+ if (rootWidget) {
+ LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
+ rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
+ rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
+ }
+ }
+
+ return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
+}
+
+// Returns the offset from this frame to the closest geometric parent that
+// has a view. Also returns the containing view or null in case of error
+void
+nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const
+{
+ NS_PRECONDITION(nullptr != aView, "null OUT parameter pointer");
+ nsIFrame* frame = const_cast<nsIFrame*>(this);
+
+ *aView = nullptr;
+ aOffset.MoveTo(0, 0);
+ do {
+ aOffset += frame->GetPosition();
+ frame = frame->GetParent();
+ } while (frame && !frame->HasView());
+
+ if (frame) {
+ *aView = frame->GetView();
+ }
+}
+
+nsIWidget*
+nsIFrame::GetNearestWidget() const
+{
+ return GetClosestView()->GetNearestWidget(nullptr);
+}
+
+nsIWidget*
+nsIFrame::GetNearestWidget(nsPoint& aOffset) const
+{
+ nsPoint offsetToView;
+ nsPoint offsetToWidget;
+ nsIWidget* widget =
+ GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
+ aOffset = offsetToView + offsetToWidget;
+ return widget;
+}
+
+nsIAtom*
+nsFrame::GetType() const
+{
+ return nullptr;
+}
+
+bool
+nsIFrame::IsLeaf() const
+{
+ return true;
+}
+
+Matrix4x4
+nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
+ nsIFrame** aOutAncestor)
+{
+ NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
+
+ /* If we're transformed, we want to hand back the combination
+ * transform/translate matrix that will apply our current transform, then
+ * shift us to our parent.
+ */
+ if (IsTransformed()) {
+ /* Compute the delta to the parent, which we need because we are converting
+ * coordinates to our parent.
+ */
+ NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
+ "Cannot transform the viewport frame!");
+ int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel();
+
+ Matrix4x4 result = nsDisplayTransform::GetResultingTransformMatrix(this,
+ nsPoint(0,0), scaleFactor,
+ nsDisplayTransform::INCLUDE_PERSPECTIVE|nsDisplayTransform::OFFSET_BY_ORIGIN,
+ nullptr);
+ *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
+ nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
+ /* Combine the raw transform with a translation to our parent. */
+ result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
+ NSAppUnitsToFloatPixels(delta.y, scaleFactor),
+ 0.0f);
+
+ return result;
+ }
+
+ if (nsLayoutUtils::IsPopup(this) &&
+ GetType() == nsGkAtoms::listControlFrame) {
+ nsPresContext* presContext = PresContext();
+ nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame();
+
+ // Compute a matrix that transforms from the popup widget to the toplevel
+ // widget. We use the widgets because they're the simplest and most
+ // accurate approach --- this should work no matter how the widget position
+ // was chosen.
+ nsIWidget* widget = GetView()->GetWidget();
+ nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
+ // Maybe the widget hasn't been created yet? Popups without widgets are
+ // treated as regular frames. That should work since they'll be rendered
+ // as part of the page if they're rendered at all.
+ if (widget && rootPresContext) {
+ nsIWidget* toplevel = rootPresContext->GetNearestWidget();
+ if (toplevel) {
+ LayoutDeviceIntRect screenBounds = widget->GetClientBounds();
+ LayoutDeviceIntRect toplevelScreenBounds = toplevel->GetClientBounds();
+ LayoutDeviceIntPoint translation =
+ screenBounds.TopLeft() - toplevelScreenBounds.TopLeft();
+
+ Matrix4x4 transformToTop;
+ transformToTop._41 = translation.x;
+ transformToTop._42 = translation.y;
+
+ *aOutAncestor = docRootFrame;
+ Matrix4x4 docRootTransformToTop =
+ nsLayoutUtils::GetTransformToAncestor(docRootFrame, nullptr);
+ if (docRootTransformToTop.IsSingular()) {
+ NS_WARNING("Containing document is invisible, we can't compute a valid transform");
+ } else {
+ docRootTransformToTop.Invert();
+ return transformToTop * docRootTransformToTop;
+ }
+ }
+ }
+ }
+
+ *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
+
+ /* Otherwise, we're not transformed. In that case, we'll walk up the frame
+ * tree until we either hit the root frame or something that may be
+ * transformed. We'll then change coordinates into that frame, since we're
+ * guaranteed that nothing in-between can be transformed. First, however,
+ * we have to check to see if we have a parent. If not, we'll set the
+ * outparam to null (indicating that there's nothing left) and will hand back
+ * the identity matrix.
+ */
+ if (!*aOutAncestor)
+ return Matrix4x4();
+
+ /* Keep iterating while the frame can't possibly be transformed. */
+ while (!(*aOutAncestor)->IsTransformed() &&
+ !nsLayoutUtils::IsPopup(*aOutAncestor) &&
+ *aOutAncestor != aStopAtAncestor) {
+ /* If no parent, stop iterating. Otherwise, update the ancestor. */
+ nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
+ if (!parent)
+ break;
+
+ *aOutAncestor = parent;
+ }
+
+ NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
+
+ /* Translate from this frame to our ancestor, if it exists. That's the
+ * entire transform, so we're done.
+ */
+ nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
+ int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel();
+ return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
+ NSAppUnitsToFloatPixels(delta.y, scaleFactor),
+ 0.0f);
+}
+
+static void InvalidateRenderingObservers(nsIFrame* aFrame)
+{
+ nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
+ nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
+ nsIFrame* parent = aFrame;
+ while (parent != displayRoot &&
+ (parent = nsLayoutUtils::GetCrossDocParentFrame(parent)) &&
+ !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
+ nsSVGEffects::InvalidateDirectRenderingObservers(parent);
+ }
+}
+
+void
+SchedulePaintInternal(nsIFrame* aFrame, nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT)
+{
+ nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
+ nsPresContext* pres = displayRoot->PresContext()->GetRootPresContext();
+
+ // No need to schedule a paint for an external document since they aren't
+ // painted directly.
+ if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
+ return;
+ }
+ if (!pres->GetContainerWeak()) {
+ NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
+ return;
+ }
+
+ pres->PresShell()->ScheduleViewManagerFlush(aType == nsIFrame::PAINT_DELAYED_COMPRESS ?
+ nsIPresShell::PAINT_DELAYED_COMPRESS :
+ nsIPresShell::PAINT_DEFAULT);
+
+ if (aType == nsIFrame::PAINT_DELAYED_COMPRESS) {
+ return;
+ }
+
+ if (aType == nsIFrame::PAINT_DEFAULT) {
+ displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
+ }
+ nsIPresShell* shell = aFrame->PresContext()->PresShell();
+ if (shell) {
+ shell->AddInvalidateHiddenPresShellObserver(pres->RefreshDriver());
+ }
+}
+
+static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true)
+{
+ if (aHasDisplayItem) {
+ aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
+ }
+ nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
+ bool needsSchedulePaint = false;
+ if (nsLayoutUtils::IsPopup(aFrame)) {
+ needsSchedulePaint = true;
+ } else {
+ nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
+ while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
+ if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
+ parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
+ }
+ nsSVGEffects::InvalidateDirectRenderingObservers(parent);
+
+ // If we're inside a popup, then we need to make sure that we
+ // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
+ // flag gets added to the popup display root frame.
+ if (nsLayoutUtils::IsPopup(parent)) {
+ needsSchedulePaint = true;
+ break;
+ }
+ parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
+ }
+ if (!parent) {
+ needsSchedulePaint = true;
+ }
+ }
+ if (!aHasDisplayItem) {
+ return;
+ }
+ if (needsSchedulePaint) {
+ SchedulePaintInternal(aFrame);
+ }
+ if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
+ aFrame->Properties().Delete(nsIFrame::InvalidationRect());
+ aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
+ }
+}
+
+void
+nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey)
+{
+ bool hasDisplayItem =
+ !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
+ InvalidateFrame(aDisplayItemKey);
+
+ if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT) || !hasDisplayItem) {
+ return;
+ }
+
+ AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
+
+ AutoTArray<nsIFrame::ChildList,4> childListArray;
+ GetCrossDocChildLists(&childListArray);
+
+ nsIFrame::ChildListArrayIterator lists(childListArray);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ childFrames.get()->InvalidateFrameSubtree();
+ }
+ }
+}
+
+void
+nsIFrame::ClearInvalidationStateBits()
+{
+ if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
+ AutoTArray<nsIFrame::ChildList,4> childListArray;
+ GetCrossDocChildLists(&childListArray);
+
+ nsIFrame::ChildListArrayIterator lists(childListArray);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ childFrames.get()->ClearInvalidationStateBits();
+ }
+ }
+ }
+
+ RemoveStateBits(NS_FRAME_NEEDS_PAINT |
+ NS_FRAME_DESCENDANT_NEEDS_PAINT |
+ NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
+}
+
+void
+nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey)
+{
+ bool hasDisplayItem =
+ !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
+ InvalidateFrameInternal(this, hasDisplayItem);
+}
+
+void
+nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
+{
+ bool hasDisplayItem =
+ !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
+ bool alreadyInvalid = false;
+ if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
+ InvalidateFrameInternal(this, hasDisplayItem);
+ } else {
+ alreadyInvalid = true;
+ }
+
+ if (!hasDisplayItem) {
+ return;
+ }
+
+ nsRect* rect = Properties().Get(InvalidationRect());
+ if (!rect) {
+ if (alreadyInvalid) {
+ return;
+ }
+ rect = new nsRect();
+ Properties().Set(InvalidationRect(), rect);
+ AddStateBits(NS_FRAME_HAS_INVALID_RECT);
+ }
+
+ *rect = rect->Union(aRect);
+}
+
+/*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
+
+static bool
+DoesLayerHaveOutOfDateFrameMetrics(Layer* aLayer)
+{
+ for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
+ const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
+ if (!metrics.IsScrollable()) {
+ continue;
+ }
+ nsIScrollableFrame* scrollableFrame =
+ nsLayoutUtils::FindScrollableFrameFor(metrics.GetScrollId());
+ if (!scrollableFrame) {
+ // This shouldn't happen, so let's do the safe thing and trigger a full
+ // paint if it does.
+ return true;
+ }
+ nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
+ if (metrics.GetScrollOffset() != CSSPoint::FromAppUnits(scrollPosition)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(Layer* aLayer)
+{
+ for (Layer* layer = aLayer; layer; layer = layer->GetParent()) {
+ if (DoesLayerHaveOutOfDateFrameMetrics(layer)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult)
+{
+ Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
+ this, nsDisplayItem::TYPE_TRANSFORM);
+ if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) {
+ // If this layer isn't prerendered or we clip composites to our OS
+ // window, then we can't correctly optimize to an empty
+ // transaction in general.
+ return false;
+ }
+
+ if (DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(layer)) {
+ // At least one scroll frame that can affect the position of this layer
+ // has changed its scroll offset since the last paint. Schedule a full
+ // paint to make sure that this layer's transform and all the frame
+ // metrics that affect it are in sync.
+ return false;
+ }
+
+ gfx::Matrix4x4 transform3d;
+ if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) {
+ // We're not able to compute a layer transform that we know would
+ // be used at the next layers transaction, so we can't only update
+ // the transform and will need to schedule an invalidating paint.
+ return false;
+ }
+ gfx::Matrix transform;
+ gfx::Matrix previousTransform;
+ // FIXME/bug 796690 and 796705: in general, changes to 3D
+ // transforms, or transform changes to properties other than
+ // translation, may lead us to choose a different rendering
+ // resolution for our layer. So if the transform is 3D or has a
+ // non-translation change, bail and schedule an invalidating paint.
+ // (We can often do better than this, for example for scale-down
+ // changes.)
+ static const gfx::Float kError = 0.0001f;
+ if (!transform3d.Is2D(&transform) ||
+ !layer->GetBaseTransform().Is2D(&previousTransform) ||
+ !gfx::FuzzyEqual(transform._11, previousTransform._11, kError) ||
+ !gfx::FuzzyEqual(transform._22, previousTransform._22, kError) ||
+ !gfx::FuzzyEqual(transform._21, previousTransform._21, kError) ||
+ !gfx::FuzzyEqual(transform._12, previousTransform._12, kError)) {
+ return false;
+ }
+ layer->SetBaseTransformForNextTransaction(transform3d);
+ *aLayerResult = layer;
+ return true;
+}
+
+bool
+nsIFrame::IsInvalid(nsRect& aRect)
+{
+ if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
+ return false;
+ }
+
+ if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
+ nsRect* rect = Properties().Get(InvalidationRect());
+ NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
+ aRect = *rect;
+ } else {
+ aRect.SetEmpty();
+ }
+ return true;
+}
+
+void
+nsIFrame::SchedulePaint(PaintType aType)
+{
+ InvalidateRenderingObservers(this);
+ SchedulePaintInternal(this, aType);
+}
+
+Layer*
+nsIFrame::InvalidateLayer(uint32_t aDisplayItemKey,
+ const nsIntRect* aDamageRect,
+ const nsRect* aFrameDamageRect,
+ uint32_t aFlags /* = 0 */)
+{
+ NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
+
+ Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
+
+ InvalidateRenderingObservers(this);
+
+ // If the layer is being updated asynchronously, and it's being forwarded
+ // to a compositor, then we don't need to invalidate.
+ if ((aFlags & UPDATE_IS_ASYNC) && layer &&
+ layer->Manager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
+ return layer;
+ }
+
+ if (!layer) {
+ if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
+ return nullptr;
+ }
+
+ // Plugins can transition from not rendering anything to rendering,
+ // and still only call this. So always invalidate, with specifying
+ // the display item type just in case.
+ //
+ // In the bug 930056, dialer app startup but not shown on the
+ // screen because sometimes we don't have any retainned data
+ // for remote type displayitem and thus Repaint event is not
+ // triggered. So, always invalidate here as well.
+ uint32_t displayItemKey = aDisplayItemKey;
+ if (aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN ||
+ aDisplayItemKey == nsDisplayItem::TYPE_REMOTE) {
+ displayItemKey = 0;
+ }
+
+ if (aFrameDamageRect) {
+ InvalidateFrameWithRect(*aFrameDamageRect, displayItemKey);
+ } else {
+ InvalidateFrame(displayItemKey);
+ }
+
+ return nullptr;
+ }
+
+ if (aDamageRect && aDamageRect->IsEmpty()) {
+ return layer;
+ }
+
+ if (aDamageRect) {
+ layer->AddInvalidRect(*aDamageRect);
+ } else {
+ layer->SetInvalidRectToVisibleRegion();
+ }
+
+ SchedulePaintInternal(this, PAINT_COMPOSITE_ONLY);
+ return layer;
+}
+
+static nsRect
+ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
+ const nsSize& aNewSize)
+{
+ nsRect r = aOverflowRect;
+
+ if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+ // For SVG frames, we only need to account for filters.
+ // TODO: We could also take account of clipPath and mask to reduce the
+ // visual overflow, but that's not essential.
+ if (aFrame->StyleEffects()->HasFilters()) {
+ aFrame->Properties().
+ Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
+ r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
+ }
+ return r;
+ }
+
+ // box-shadow
+ r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
+
+ // border-image-outset.
+ // We need to include border-image-outset because it can cause the
+ // border image to be drawn beyond the border box.
+
+ // (1) It's important we not check whether there's a border-image
+ // since the style hint for a change in border image doesn't cause
+ // reflow, and that's probably more important than optimizing the
+ // overflow areas for the silly case of border-image-outset without
+ // border-image
+ // (2) It's important that we not check whether the border-image
+ // is actually loaded, since that would require us to reflow when
+ // the image loads.
+ const nsStyleBorder* styleBorder = aFrame->StyleBorder();
+ nsMargin outsetMargin = styleBorder->GetImageOutset();
+
+ if (outsetMargin != nsMargin(0, 0, 0, 0)) {
+ nsRect outsetRect(nsPoint(0, 0), aNewSize);
+ outsetRect.Inflate(outsetMargin);
+ r.UnionRect(r, outsetRect);
+ }
+
+ // Note that we don't remove the outlineInnerRect if a frame loses outline
+ // style. That would require an extra property lookup for every frame,
+ // or a new frame state bit to track whether a property had been stored,
+ // or something like that. It's not worth doing that here. At most it's
+ // only one heap-allocated rect per frame and it will be cleaned up when
+ // the frame dies.
+
+ if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
+ aFrame->Properties().
+ Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
+ r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r);
+ }
+
+ return r;
+}
+
+void
+nsIFrame::MovePositionBy(const nsPoint& aTranslation)
+{
+ nsPoint position = GetNormalPosition() + aTranslation;
+
+ const nsMargin* computedOffsets = nullptr;
+ if (IsRelativelyPositioned()) {
+ computedOffsets = Properties().Get(nsIFrame::ComputedOffsetProperty());
+ }
+ ReflowInput::ApplyRelativePositioning(this, computedOffsets ?
+ *computedOffsets : nsMargin(),
+ &position);
+ SetPosition(position);
+}
+
+nsRect
+nsIFrame::GetNormalRect() const
+{
+ // It might be faster to first check
+ // StyleDisplay()->IsRelativelyPositionedStyle().
+ nsPoint* normalPosition = Properties().Get(NormalPositionProperty());
+ if (normalPosition) {
+ return nsRect(*normalPosition, GetSize());
+ }
+ return GetRect();
+}
+
+nsPoint
+nsIFrame::GetNormalPosition() const
+{
+ // It might be faster to first check
+ // StyleDisplay()->IsRelativelyPositionedStyle().
+ nsPoint* normalPosition = Properties().Get(NormalPositionProperty());
+ if (normalPosition) {
+ return *normalPosition;
+ }
+ return GetPosition();
+}
+
+nsPoint
+nsIFrame::GetPositionIgnoringScrolling()
+{
+ return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
+ : GetPosition();
+}
+
+nsRect
+nsIFrame::GetOverflowRect(nsOverflowType aType) const
+{
+ MOZ_ASSERT(aType == eVisualOverflow || aType == eScrollableOverflow,
+ "unexpected type");
+
+ // Note that in some cases the overflow area might not have been
+ // updated (yet) to reflect any outline set on the frame or the area
+ // of child frames. That's OK because any reflow that updates these
+ // areas will invalidate the appropriate area, so any (mis)uses of
+ // this method will be fixed up.
+
+ if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+ // there is an overflow rect, and it's not stored as deltas but as
+ // a separately-allocated rect
+ return static_cast<nsOverflowAreas*>(const_cast<nsIFrame*>(this)->
+ GetOverflowAreasProperty())->Overflow(aType);
+ }
+
+ if (aType == eVisualOverflow &&
+ mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
+ return GetVisualOverflowFromDeltas();
+ }
+
+ return nsRect(nsPoint(0, 0), GetSize());
+}
+
+nsOverflowAreas
+nsIFrame::GetOverflowAreas() const
+{
+ if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+ // there is an overflow rect, and it's not stored as deltas but as
+ // a separately-allocated rect
+ return *const_cast<nsIFrame*>(this)->GetOverflowAreasProperty();
+ }
+
+ return nsOverflowAreas(GetVisualOverflowFromDeltas(),
+ nsRect(nsPoint(0, 0), GetSize()));
+}
+
+nsOverflowAreas
+nsIFrame::GetOverflowAreasRelativeToSelf() const
+{
+ if (IsTransformed()) {
+ nsOverflowAreas* preTransformOverflows =
+ Properties().Get(PreTransformOverflowAreasProperty());
+ if (preTransformOverflows) {
+ return nsOverflowAreas(preTransformOverflows->VisualOverflow(),
+ preTransformOverflows->ScrollableOverflow());
+ }
+ }
+ return nsOverflowAreas(GetVisualOverflowRect(),
+ GetScrollableOverflowRect());
+}
+
+nsRect
+nsIFrame::GetScrollableOverflowRectRelativeToParent() const
+{
+ return GetScrollableOverflowRect() + mRect.TopLeft();
+}
+
+nsRect
+nsIFrame::GetVisualOverflowRectRelativeToParent() const
+{
+ return GetVisualOverflowRect() + mRect.TopLeft();
+}
+
+nsRect
+nsIFrame::GetScrollableOverflowRectRelativeToSelf() const
+{
+ if (IsTransformed()) {
+ nsOverflowAreas* preTransformOverflows =
+ Properties().Get(PreTransformOverflowAreasProperty());
+ if (preTransformOverflows)
+ return preTransformOverflows->ScrollableOverflow();
+ }
+ return GetScrollableOverflowRect();
+}
+
+nsRect
+nsIFrame::GetVisualOverflowRectRelativeToSelf() const
+{
+ if (IsTransformed()) {
+ nsOverflowAreas* preTransformOverflows =
+ Properties().Get(PreTransformOverflowAreasProperty());
+ if (preTransformOverflows)
+ return preTransformOverflows->VisualOverflow();
+ }
+ return GetVisualOverflowRect();
+}
+
+nsRect
+nsIFrame::GetPreEffectsVisualOverflowRect() const
+{
+ nsRect* r = Properties().Get(nsIFrame::PreEffectsBBoxProperty());
+ return r ? *r : GetVisualOverflowRectRelativeToSelf();
+}
+
+bool
+nsIFrame::UpdateOverflow()
+{
+ MOZ_ASSERT(FrameMaintainsOverflow(),
+ "Non-display SVG do not maintain visual overflow rects");
+
+ nsRect rect(nsPoint(0, 0), GetSize());
+ nsOverflowAreas overflowAreas(rect, rect);
+
+ if (!ComputeCustomOverflow(overflowAreas)) {
+ return false;
+ }
+
+ UnionChildOverflow(overflowAreas);
+
+ if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
+ nsView* view = GetView();
+ if (view) {
+ uint32_t flags = GetXULLayoutFlags();
+
+ if ((flags & NS_FRAME_NO_SIZE_VIEW) == 0) {
+ // Make sure the frame's view is properly sized.
+ nsViewManager* vm = view->GetViewManager();
+ vm->ResizeView(view, overflowAreas.VisualOverflow(), true);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/* virtual */ bool
+nsFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ return true;
+}
+
+/* virtual */ void
+nsFrame::UnionChildOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ if (!DoesClipChildren() &&
+ !(IsXULCollapsed() && (IsXULBoxFrame() || ::IsXULBoxWrapped(this)))) {
+ nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
+ }
+}
+
+
+// Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
+// 4 for the frames above the document's frames:
+// the Viewport, GFXScroll, ScrollPort, and Canvas
+#define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4)
+
+bool
+nsFrame::IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus)
+{
+ if (aReflowInput.mReflowDepth > MAX_FRAME_DEPTH) {
+ NS_WARNING("frame tree too deep; setting zero size and returning");
+ mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
+ ClearOverflowRects();
+ aMetrics.ClearSize();
+ aMetrics.SetBlockStartAscent(0);
+ aMetrics.mCarriedOutBEndMargin.Zero();
+ aMetrics.mOverflowAreas.Clear();
+
+ if (GetNextInFlow()) {
+ // Reflow depth might vary between reflows, so we might have
+ // successfully reflowed and split this frame before. If so, we
+ // shouldn't delete its continuations.
+ aStatus = NS_FRAME_NOT_COMPLETE;
+ } else {
+ aStatus = NS_FRAME_COMPLETE;
+ }
+
+ return true;
+ }
+ mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
+ return false;
+}
+
+bool
+nsIFrame::IsBlockWrapper() const
+{
+ nsIAtom *pseudoType = StyleContext()->GetPseudo();
+ return (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
+ pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
+ pseudoType == nsCSSAnonBoxes::buttonContent ||
+ pseudoType == nsCSSAnonBoxes::cellContent);
+}
+
+static nsIFrame*
+GetNearestBlockContainer(nsIFrame* frame)
+{
+ // The block wrappers we use to wrap blocks inside inlines aren't
+ // described in the CSS spec. We need to make them not be containing
+ // blocks.
+ // Since the parent of such a block is either a normal block or
+ // another such pseudo, this shouldn't cause anything bad to happen.
+ // Also the anonymous blocks inside table cells are not containing blocks.
+ while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
+ frame->IsBlockWrapper() ||
+ // Table rows are not containing blocks either
+ frame->GetType() == nsGkAtoms::tableRowFrame) {
+ frame = frame->GetParent();
+ NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?");
+ }
+ return frame;
+}
+
+nsIFrame*
+nsIFrame::GetContainingBlock(uint32_t aFlags) const
+{
+ if (!GetParent()) {
+ return nullptr;
+ }
+ // MathML frames might have absolute positioning style, but they would
+ // still be in-flow. So we have to check to make sure that the frame
+ // is really out-of-flow too.
+ nsIFrame* f;
+ if (IsAbsolutelyPositioned() &&
+ (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ f = GetParent(); // the parent is always the containing block
+ } else {
+ f = GetNearestBlockContainer(GetParent());
+ }
+
+ if (aFlags & SKIP_SCROLLED_FRAME && f &&
+ f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::scrolledContent) {
+ f = f->GetParent();
+ }
+ return f;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+
+int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame)
+{
+ int32_t result = -1;
+
+ nsIContent* content = aFrame->GetContent();
+ if (content) {
+ nsIContent* parentContent = content->GetParent();
+ if (parentContent) {
+ result = parentContent->IndexOf(content);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * List a frame tree to stderr. Meant to be called from gdb.
+ */
+void
+DebugListFrameTree(nsIFrame* aFrame)
+{
+ ((nsFrame*)aFrame)->List(stderr);
+}
+
+void
+nsIFrame::ListTag(nsACString& aTo) const
+{
+ ListTag(aTo, this);
+}
+
+/* static */
+void
+nsIFrame::ListTag(nsACString& aTo, const nsIFrame* aFrame) {
+ nsAutoString tmp;
+ aFrame->GetFrameName(tmp);
+ aTo += NS_ConvertUTF16toUTF8(tmp).get();
+ aTo += nsPrintfCString("@%p", static_cast<const void*>(aFrame));
+}
+
+// Debugging
+void
+nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) const
+{
+ aTo =+ aPrefix;
+ ListTag(aTo);
+ if (HasView()) {
+ aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
+ }
+ if (GetNextSibling()) {
+ aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
+ }
+ if (GetPrevContinuation()) {
+ bool fluid = GetPrevInFlow() == GetPrevContinuation();
+ aTo += nsPrintfCString(" prev-%s=%p", fluid?"in-flow":"continuation",
+ static_cast<void*>(GetPrevContinuation()));
+ }
+ if (GetNextContinuation()) {
+ bool fluid = GetNextInFlow() == GetNextContinuation();
+ aTo += nsPrintfCString(" next-%s=%p", fluid?"in-flow":"continuation",
+ static_cast<void*>(GetNextContinuation()));
+ }
+ void* IBsibling = Properties().Get(IBSplitSibling());
+ if (IBsibling) {
+ aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
+ }
+ void* IBprevsibling = Properties().Get(IBSplitPrevSibling());
+ if (IBprevsibling) {
+ aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
+ }
+ aTo += nsPrintfCString(" {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
+
+ mozilla::WritingMode wm = GetWritingMode();
+ if (wm.IsVertical() || !wm.IsBidiLTR()) {
+ aTo += nsPrintfCString(" wm=%s: logical size={%d,%d}", wm.DebugString(),
+ ISize(), BSize());
+ }
+
+ nsIFrame* parent = GetParent();
+ if (parent) {
+ WritingMode pWM = parent->GetWritingMode();
+ if (pWM.IsVertical() || !pWM.IsBidiLTR()) {
+ nsSize containerSize = parent->mRect.Size();
+ LogicalRect lr(pWM, mRect, containerSize);
+ aTo += nsPrintfCString(" parent wm=%s, cs={%d,%d}, "
+ " logicalRect={%d,%d,%d,%d}",
+ pWM.DebugString(),
+ containerSize.width, containerSize.height,
+ lr.IStart(pWM), lr.BStart(pWM),
+ lr.ISize(pWM), lr.BSize(pWM));
+ }
+ }
+ nsIFrame* f = const_cast<nsIFrame*>(this);
+ if (f->HasOverflowAreas()) {
+ nsRect vo = f->GetVisualOverflowRect();
+ if (!vo.IsEqualEdges(mRect)) {
+ aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height);
+ }
+ nsRect so = f->GetScrollableOverflowRect();
+ if (!so.IsEqualEdges(mRect)) {
+ aTo += nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so.x, so.y, so.width, so.height);
+ }
+ }
+ if (0 != mState) {
+ aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState);
+ }
+ if (IsTransformed()) {
+ aTo += nsPrintfCString(" transformed");
+ }
+ if (ChildrenHavePerspective()) {
+ aTo += nsPrintfCString(" perspective");
+ }
+ if (Extend3DContext()) {
+ aTo += nsPrintfCString(" preserves-3d-children");
+ }
+ if (Combines3DTransformWithAncestors()) {
+ aTo += nsPrintfCString(" preserves-3d");
+ }
+ if (mContent) {
+ aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
+ }
+ aTo += nsPrintfCString(" [sc=%p", static_cast<void*>(mStyleContext));
+ if (mStyleContext) {
+ nsIAtom* pseudoTag = mStyleContext->GetPseudo();
+ if (pseudoTag) {
+ nsAutoString atomString;
+ pseudoTag->ToString(atomString);
+ aTo += nsPrintfCString("%s", NS_LossyConvertUTF16toASCII(atomString).get());
+ }
+ if (!mStyleContext->GetParent() ||
+ (GetParent() && GetParent()->StyleContext() != mStyleContext->GetParent())) {
+ aTo += nsPrintfCString("^%p", mStyleContext->GetParent());
+ if (mStyleContext->GetParent()) {
+ aTo += nsPrintfCString("^%p", mStyleContext->GetParent()->GetParent());
+ if (mStyleContext->GetParent()->GetParent()) {
+ aTo += nsPrintfCString("^%p", mStyleContext->GetParent()->GetParent()->GetParent());
+ }
+ }
+ }
+ }
+ aTo += "]";
+}
+
+void
+nsIFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+ fprintf_stderr(out, "%s\n", str.get());
+}
+
+nsresult
+nsFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult);
+}
+
+nsresult
+nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const
+{
+ aResult = aType;
+ if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) {
+ nsAutoString buf;
+ mContent->NodeInfo()->NameAtom()->ToString(buf);
+ if (GetType() == nsGkAtoms::subDocumentFrame) {
+ nsAutoString src;
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
+ buf.AppendLiteral(" src=");
+ buf.Append(src);
+ }
+ aResult.Append('(');
+ aResult.Append(buf);
+ aResult.Append(')');
+ }
+ char buf[40];
+ SprintfLiteral(buf, "(%d)", ContentIndexInContainer(this));
+ AppendASCIItoUTF16(buf, aResult);
+ return NS_OK;
+}
+
+void
+nsIFrame::DumpFrameTree() const
+{
+ RootFrameList(PresContext(), stderr);
+}
+
+void
+nsIFrame::DumpFrameTreeLimited() const
+{
+ List(stderr);
+}
+
+void
+nsIFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, const char* aPrefix)
+{
+ if (!aPresContext || !out)
+ return;
+
+ nsIPresShell *shell = aPresContext->GetPresShell();
+ if (shell) {
+ nsIFrame* frame = shell->FrameManager()->GetRootFrame();
+ if(frame) {
+ frame->List(out, aPrefix);
+ }
+ }
+}
+#endif
+
+#ifdef DEBUG
+nsFrameState
+nsFrame::GetDebugStateBits() const
+{
+ // We'll ignore these flags for the purposes of comparing frame state:
+ //
+ // NS_FRAME_EXTERNAL_REFERENCE
+ // because this is set by the event state manager or the
+ // caret code when a frame is focused. Depending on whether
+ // or not the regression tests are run as the focused window
+ // will make this value vary randomly.
+#define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE
+
+#define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS))
+
+ return GetStateBits() & FRAME_STATE_MASK;
+}
+
+void
+nsFrame::XMLQuote(nsString& aString)
+{
+ int32_t i, len = aString.Length();
+ for (i = 0; i < len; i++) {
+ char16_t ch = aString.CharAt(i);
+ if (ch == '<') {
+ nsAutoString tmp(NS_LITERAL_STRING("&lt;"));
+ aString.Cut(i, 1);
+ aString.Insert(tmp, i);
+ len += 3;
+ i += 3;
+ }
+ else if (ch == '>') {
+ nsAutoString tmp(NS_LITERAL_STRING("&gt;"));
+ aString.Cut(i, 1);
+ aString.Insert(tmp, i);
+ len += 3;
+ i += 3;
+ }
+ else if (ch == '\"') {
+ nsAutoString tmp(NS_LITERAL_STRING("&quot;"));
+ aString.Cut(i, 1);
+ aString.Insert(tmp, i);
+ len += 5;
+ i += 5;
+ }
+ }
+}
+#endif
+
+bool
+nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) {
+ if (!StyleVisibility()->IsVisible())
+ return false;
+ nsISelection* sel = aBuilder->GetBoundingSelection();
+ return !sel || IsVisibleInSelection(sel);
+}
+
+bool
+nsIFrame::IsVisibleForPainting() {
+ if (!StyleVisibility()->IsVisible())
+ return false;
+
+ nsPresContext* pc = PresContext();
+ if (!pc->IsRenderingOnlySelection())
+ return true;
+
+ nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(pc->PresShell()));
+ if (selcon) {
+ nsCOMPtr<nsISelection> sel;
+ selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+ getter_AddRefs(sel));
+ if (sel)
+ return IsVisibleInSelection(sel);
+ }
+ return true;
+}
+
+bool
+nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) {
+ nsISelection* sel = aBuilder->GetBoundingSelection();
+ return !sel || IsVisibleInSelection(sel);
+}
+
+bool
+nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) {
+ if (!StyleVisibility()->IsVisibleOrCollapsed())
+ return false;
+ nsISelection* sel = aBuilder->GetBoundingSelection();
+ return !sel || IsVisibleInSelection(sel);
+}
+
+bool
+nsIFrame::IsVisibleInSelection(nsISelection* aSelection)
+{
+ if (!GetContent() || !GetContent()->IsSelectionDescendant()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
+ bool vis;
+ nsresult rv = aSelection->ContainsNode(node, true, &vis);
+ return NS_FAILED(rv) || vis;
+}
+
+/* virtual */ bool
+nsFrame::IsEmpty()
+{
+ return false;
+}
+
+bool
+nsIFrame::CachedIsEmpty()
+{
+ NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
+ "Must only be called on reflowed lines");
+ return IsEmpty();
+}
+
+/* virtual */ bool
+nsFrame::IsSelfEmpty()
+{
+ return false;
+}
+
+nsresult
+nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon)
+{
+ if (!aPresContext || !aSelCon)
+ return NS_ERROR_INVALID_ARG;
+
+ nsIFrame *frame = this;
+ while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
+ nsITextControlFrame *tcf = do_QueryFrame(frame);
+ if (tcf) {
+ return tcf->GetOwnedSelectionController(aSelCon);
+ }
+ frame = frame->GetParent();
+ }
+
+ return CallQueryInterface(aPresContext->GetPresShell(), aSelCon);
+}
+
+already_AddRefed<nsFrameSelection>
+nsIFrame::GetFrameSelection()
+{
+ RefPtr<nsFrameSelection> fs =
+ const_cast<nsFrameSelection*>(GetConstFrameSelection());
+ return fs.forget();
+}
+
+const nsFrameSelection*
+nsIFrame::GetConstFrameSelection() const
+{
+ nsIFrame* frame = const_cast<nsIFrame*>(this);
+ while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
+ nsITextControlFrame* tcf = do_QueryFrame(frame);
+ if (tcf) {
+ return tcf->GetOwnedFrameSelection();
+ }
+ frame = frame->GetParent();
+ }
+
+ return PresContext()->PresShell()->ConstFrameSelection();
+}
+
+#ifdef DEBUG
+nsresult
+nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
+{
+ IndentBy(out, aIndent);
+ fprintf(out, "<frame va=\"%p\" type=\"", (void*)this);
+ nsAutoString name;
+ GetFrameName(name);
+ XMLQuote(name);
+ fputs(NS_LossyConvertUTF16toASCII(name).get(), out);
+ fprintf(out, "\" state=\"%016llx\" parent=\"%p\">\n",
+ (unsigned long long)GetDebugStateBits(), (void*)GetParent());
+
+ aIndent++;
+ DumpBaseRegressionData(aPresContext, out, aIndent);
+ aIndent--;
+
+ IndentBy(out, aIndent);
+ fprintf(out, "</frame>\n");
+
+ return NS_OK;
+}
+
+void
+nsFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
+{
+ if (GetNextSibling()) {
+ IndentBy(out, aIndent);
+ fprintf(out, "<next-sibling va=\"%p\"/>\n", (void*)GetNextSibling());
+ }
+
+ if (HasView()) {
+ IndentBy(out, aIndent);
+ fprintf(out, "<view va=\"%p\">\n", (void*)GetView());
+ aIndent++;
+ // XXX add in code to dump out view state too...
+ aIndent--;
+ IndentBy(out, aIndent);
+ fprintf(out, "</view>\n");
+ }
+
+ IndentBy(out, aIndent);
+ fprintf(out, "<bbox x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\"/>\n",
+ mRect.x, mRect.y, mRect.width, mRect.height);
+
+ // Now dump all of the children on all of the child lists
+ ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ IndentBy(out, aIndent);
+ if (lists.CurrentID() != kPrincipalList) {
+ fprintf(out, "<child-list name=\"%s\">\n", mozilla::layout::ChildListName(lists.CurrentID()));
+ }
+ else {
+ fprintf(out, "<child-list>\n");
+ }
+ aIndent++;
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* kid = childFrames.get();
+ kid->DumpRegressionData(aPresContext, out, aIndent);
+ }
+ aIndent--;
+ IndentBy(out, aIndent);
+ fprintf(out, "</child-list>\n");
+ }
+}
+#endif
+
+bool
+nsIFrame::IsFrameSelected() const
+{
+ NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
+ "use the public IsSelected() instead");
+ return nsRange::IsNodeSelected(GetContent(), 0,
+ GetContent()->GetChildCount());
+}
+
+nsresult
+nsFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint)
+{
+ NS_PRECONDITION(outPoint != nullptr, "Null parameter");
+ nsRect contentRect = GetContentRectRelativeToSelf();
+ nsPoint pt = contentRect.TopLeft();
+ if (mContent)
+ {
+ nsIContent* newContent = mContent->GetParent();
+ if (newContent){
+ int32_t newOffset = newContent->IndexOf(mContent);
+
+ // Find the direction of the frame from the EmbeddingLevelProperty,
+ // which is the resolved bidi level set in
+ // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
+ // If the embedding level isn't set, just use the CSS direction
+ // property.
+ bool hasBidiData;
+ FrameBidiData bidiData =
+ Properties().Get(BidiDataProperty(), &hasBidiData);
+ bool isRTL = hasBidiData
+ ? IS_LEVEL_RTL(bidiData.embeddingLevel)
+ : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
+ if ((!isRTL && inOffset > newOffset) ||
+ (isRTL && inOffset <= newOffset)) {
+ pt = contentRect.TopRight();
+ }
+ }
+ }
+ *outPoint = pt;
+ return NS_OK;
+}
+
+nsresult
+nsFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
+ nsTArray<nsRect>& aOutRect)
+{
+ /* no text */
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, int32_t* outFrameContentOffset, nsIFrame **outChildFrame)
+{
+ NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter");
+ *outFrameContentOffset = (int32_t)inHint;
+ //the best frame to reflect any given offset would be a visible frame if possible
+ //i.e. we are looking for a valid frame to place the blinking caret
+ nsRect rect = GetRect();
+ if (!rect.width || !rect.height)
+ {
+ //if we have a 0 width or height then lets look for another frame that possibly has
+ //the same content. If we have no frames in flow then just let us return 'this' frame
+ nsIFrame* nextFlow = GetNextInFlow();
+ if (nextFlow)
+ return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
+ }
+ *outChildFrame = this;
+ return NS_OK;
+}
+
+//
+// What I've pieced together about this routine:
+// Starting with a block frame (from which a line frame can be gotten)
+// and a line number, drill down and get the first/last selectable
+// frame on that line, depending on aPos->mDirection.
+// aOutSideLimit != 0 means ignore aLineStart, instead work from
+// the end (if > 0) or beginning (if < 0).
+//
+nsresult
+nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
+ nsPeekOffsetStruct *aPos,
+ nsIFrame *aBlockFrame,
+ int32_t aLineStart,
+ int8_t aOutSideLimit
+ )
+{
+ //magic numbers aLineStart will be -1 for end of block 0 will be start of block
+ if (!aBlockFrame || !aPos)
+ return NS_ERROR_NULL_POINTER;
+
+ aPos->mResultFrame = nullptr;
+ aPos->mResultContent = nullptr;
+ aPos->mAttach =
+ aPos->mDirection == eDirNext ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
+
+ nsAutoLineIterator it = aBlockFrame->GetLineIterator();
+ if (!it)
+ return NS_ERROR_FAILURE;
+ int32_t searchingLine = aLineStart;
+ int32_t countLines = it->GetNumLines();
+ if (aOutSideLimit > 0) //start at end
+ searchingLine = countLines;
+ else if (aOutSideLimit <0)//start at beginning
+ searchingLine = -1;//"next" will be 0
+ else
+ if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
+ (aPos->mDirection == eDirNext && searchingLine >= (countLines -1) )){
+ //we need to jump to new block frame.
+ return NS_ERROR_FAILURE;
+ }
+ int32_t lineFrameCount;
+ nsIFrame *resultFrame = nullptr;
+ nsIFrame *farStoppingFrame = nullptr; //we keep searching until we find a "this" frame then we go to next line
+ nsIFrame *nearStoppingFrame = nullptr; //if we are backing up from edge, stop here
+ nsIFrame *firstFrame;
+ nsIFrame *lastFrame;
+ nsRect rect;
+ bool isBeforeFirstFrame, isAfterLastFrame;
+ bool found = false;
+
+ nsresult result = NS_OK;
+ while (!found)
+ {
+ if (aPos->mDirection == eDirPrevious)
+ searchingLine --;
+ else
+ searchingLine ++;
+ if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
+ (aPos->mDirection == eDirNext && searchingLine >= countLines ))
+ {
+ //we need to jump to new block frame.
+ return NS_ERROR_FAILURE;
+ }
+ result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount,
+ rect);
+ if (!lineFrameCount)
+ continue;
+ if (NS_SUCCEEDED(result)){
+ lastFrame = firstFrame;
+ for (;lineFrameCount > 1;lineFrameCount --){
+ //result = lastFrame->GetNextSibling(&lastFrame, searchingLine);
+ result = it->GetNextSiblingOnLine(lastFrame, searchingLine);
+ if (NS_FAILED(result) || !lastFrame){
+ NS_ERROR("GetLine promised more frames than could be found");
+ return NS_ERROR_FAILURE;
+ }
+ }
+ GetLastLeaf(aPresContext, &lastFrame);
+
+ if (aPos->mDirection == eDirNext){
+ nearStoppingFrame = firstFrame;
+ farStoppingFrame = lastFrame;
+ }
+ else{
+ nearStoppingFrame = lastFrame;
+ farStoppingFrame = firstFrame;
+ }
+ nsPoint offset;
+ nsView * view; //used for call of get offset from view
+ aBlockFrame->GetOffsetFromView(offset,&view);
+ nsPoint newDesiredPos =
+ aPos->mDesiredPos - offset; //get desired position into blockframe coords
+ result = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
+ &isBeforeFirstFrame, &isAfterLastFrame);
+ if(NS_FAILED(result))
+ continue;
+ }
+
+ if (NS_SUCCEEDED(result) && resultFrame)
+ {
+ //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines
+ nsAutoLineIterator newIt = resultFrame->GetLineIterator();
+ if (newIt)
+ {
+ aPos->mResultFrame = resultFrame;
+ return NS_OK;
+ }
+ //resultFrame is not a block frame
+ result = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFrameEnumerator> frameTraversal;
+ result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
+ aPresContext, resultFrame,
+ ePostOrder,
+ false, // aVisual
+ aPos->mScrollViewStop,
+ false, // aFollowOOFs
+ false // aSkipPopupChecks
+ );
+ if (NS_FAILED(result))
+ return result;
+
+ nsIFrame *storeOldResultFrame = resultFrame;
+ while ( !found ){
+ nsPoint point;
+ nsRect tempRect = resultFrame->GetRect();
+ nsPoint offset;
+ nsView * view; //used for call of get offset from view
+ resultFrame->GetOffsetFromView(offset, &view);
+ if (!view) {
+ return NS_ERROR_FAILURE;
+ }
+ if (resultFrame->GetWritingMode().IsVertical()) {
+ point.y = aPos->mDesiredPos.y;
+ point.x = tempRect.width + offset.x;
+ } else {
+ point.y = tempRect.height + offset.y;
+ point.x = aPos->mDesiredPos.x;
+ }
+
+ //special check. if we allow non-text selection then we can allow a hit location to fall before a table.
+ //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself)
+ nsIPresShell *shell = aPresContext->GetPresShell();
+ if (!shell)
+ return NS_ERROR_FAILURE;
+ int16_t isEditor = shell->GetSelectionFlags();
+ isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
+ if ( isEditor )
+ {
+ if (resultFrame->GetType() == nsGkAtoms::tableWrapperFrame)
+ {
+ if (((point.x - offset.x + tempRect.x)<0) || ((point.x - offset.x+ tempRect.x)>tempRect.width))//off left/right side
+ {
+ nsIContent* content = resultFrame->GetContent();
+ if (content)
+ {
+ nsIContent* parent = content->GetParent();
+ if (parent)
+ {
+ aPos->mResultContent = parent;
+ aPos->mContentOffset = parent->IndexOf(content);
+ aPos->mAttach = CARET_ASSOCIATE_BEFORE;
+ if ((point.x - offset.x+ tempRect.x)>tempRect.width)
+ {
+ aPos->mContentOffset++;//go to end of this frame
+ aPos->mAttach = CARET_ASSOCIATE_AFTER;
+ }
+ //result frame is the result frames parent.
+ aPos->mResultFrame = resultFrame->GetParent();
+ return NS_POSITION_BEFORE_TABLE;
+ }
+ }
+ }
+ }
+ }
+
+ if (!resultFrame->HasView())
+ {
+ nsView* view;
+ nsPoint offset;
+ resultFrame->GetOffsetFromView(offset, &view);
+ ContentOffsets offsets =
+ resultFrame->GetContentOffsetsFromPoint(point - offset);
+ aPos->mResultContent = offsets.content;
+ aPos->mContentOffset = offsets.offset;
+ aPos->mAttach = offsets.associate;
+ if (offsets.content)
+ {
+ bool selectable;
+ resultFrame->IsSelectable(&selectable, nullptr);
+ if (selectable)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame))
+ break;
+ if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
+ break;
+ //always try previous on THAT line if that fails go the other way
+ frameTraversal->Prev();
+ resultFrame = frameTraversal->CurrentItem();
+ if (!resultFrame)
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!found){
+ resultFrame = storeOldResultFrame;
+
+ result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
+ aPresContext, resultFrame,
+ eLeaf,
+ false, // aVisual
+ aPos->mScrollViewStop,
+ false, // aFollowOOFs
+ false // aSkipPopupChecks
+ );
+ }
+ while ( !found ){
+ nsPoint point = aPos->mDesiredPos;
+ nsView* view;
+ nsPoint offset;
+ resultFrame->GetOffsetFromView(offset, &view);
+ ContentOffsets offsets =
+ resultFrame->GetContentOffsetsFromPoint(point - offset);
+ aPos->mResultContent = offsets.content;
+ aPos->mContentOffset = offsets.offset;
+ aPos->mAttach = offsets.associate;
+ if (offsets.content)
+ {
+ bool selectable;
+ resultFrame->IsSelectable(&selectable, nullptr);
+ if (selectable)
+ {
+ found = true;
+ if (resultFrame == farStoppingFrame)
+ aPos->mAttach = CARET_ASSOCIATE_BEFORE;
+ else
+ aPos->mAttach = CARET_ASSOCIATE_AFTER;
+ break;
+ }
+ }
+ if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame))
+ break;
+ if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
+ break;
+ //previous didnt work now we try "next"
+ frameTraversal->Next();
+ nsIFrame *tempFrame = frameTraversal->CurrentItem();
+ if (!tempFrame)
+ break;
+ resultFrame = tempFrame;
+ }
+ aPos->mResultFrame = resultFrame;
+ }
+ else {
+ //we need to jump to new block frame.
+ aPos->mAmount = eSelectLine;
+ aPos->mStartOffset = 0;
+ aPos->mAttach = aPos->mDirection == eDirNext ?
+ CARET_ASSOCIATE_BEFORE : CARET_ASSOCIATE_AFTER;
+ if (aPos->mDirection == eDirPrevious)
+ aPos->mStartOffset = -1;//start from end
+ return aBlockFrame->PeekOffset(aPos);
+ }
+ }
+ return NS_OK;
+}
+
+nsIFrame::CaretPosition
+nsIFrame::GetExtremeCaretPosition(bool aStart)
+{
+ CaretPosition result;
+
+ FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
+ FrameContentRange range = GetRangeForFrame(targetFrame.frame);
+ result.mResultContent = range.content;
+ result.mContentOffset = aStart ? range.start : range.end;
+ return result;
+}
+
+// Find the first (or last) descendant of the given frame
+// which is either a block frame or a BRFrame.
+static nsContentAndOffset
+FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection)
+{
+ nsContentAndOffset result;
+ result.mContent = nullptr;
+ result.mOffset = 0;
+
+ if (aFrame->IsGeneratedContentFrame())
+ return result;
+
+ // Treat form controls as inline leaves
+ // XXX we really need a way to determine whether a frame is inline-level
+ nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
+ if (fcf)
+ return result;
+
+ // Check the frame itself
+ // Fall through block-in-inline split frames because their mContent is
+ // the content of the inline frames they were created from. The
+ // first/last child of such frames is the real block frame we're
+ // looking for.
+ if ((nsLayoutUtils::GetAsBlock(aFrame) &&
+ !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) ||
+ aFrame->GetType() == nsGkAtoms::brFrame) {
+ nsIContent* content = aFrame->GetContent();
+ result.mContent = content->GetParent();
+ // In some cases (bug 310589, bug 370174) we end up here with a null content.
+ // This probably shouldn't ever happen, but since it sometimes does, we want
+ // to avoid crashing here.
+ NS_ASSERTION(result.mContent, "Unexpected orphan content");
+ if (result.mContent)
+ result.mOffset = result.mContent->IndexOf(content) +
+ (aDirection == eDirPrevious ? 1 : 0);
+ return result;
+ }
+
+ // If this is a preformatted text frame, see if it ends with a newline
+ if (aFrame->HasSignificantTerminalNewline()) {
+ int32_t startOffset, endOffset;
+ aFrame->GetOffsets(startOffset, endOffset);
+ result.mContent = aFrame->GetContent();
+ result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
+ return result;
+ }
+
+ // Iterate over children and call ourselves recursively
+ if (aDirection == eDirPrevious) {
+ nsIFrame* child = aFrame->GetChildList(nsIFrame::kPrincipalList).LastChild();
+ while(child && !result.mContent) {
+ result = FindBlockFrameOrBR(child, aDirection);
+ child = child->GetPrevSibling();
+ }
+ } else { // eDirNext
+ nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
+ while(child && !result.mContent) {
+ result = FindBlockFrameOrBR(child, aDirection);
+ child = child->GetNextSibling();
+ }
+ }
+ return result;
+}
+
+nsresult
+nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos)
+{
+ nsIFrame* frame = this;
+ nsContentAndOffset blockFrameOrBR;
+ blockFrameOrBR.mContent = nullptr;
+ bool reachedBlockAncestor = false;
+
+ // Go through containing frames until reaching a block frame.
+ // In each step, search the previous (or next) siblings for the closest
+ // "stop frame" (a block frame or a BRFrame).
+ // If found, set it to be the selection boundray and abort.
+
+ if (aPos->mDirection == eDirPrevious) {
+ while (!reachedBlockAncestor) {
+ nsIFrame* parent = frame->GetParent();
+ // Treat a frame associated with the root content as if it were a block frame.
+ if (!frame->mContent || !frame->mContent->GetParent()) {
+ reachedBlockAncestor = true;
+ break;
+ }
+ nsIFrame* sibling = frame->GetPrevSibling();
+ while (sibling && !blockFrameOrBR.mContent) {
+ blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirPrevious);
+ sibling = sibling->GetPrevSibling();
+ }
+ if (blockFrameOrBR.mContent) {
+ aPos->mResultContent = blockFrameOrBR.mContent;
+ aPos->mContentOffset = blockFrameOrBR.mOffset;
+ break;
+ }
+ frame = parent;
+ reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr);
+ }
+ if (reachedBlockAncestor) { // no "stop frame" found
+ aPos->mResultContent = frame->GetContent();
+ aPos->mContentOffset = 0;
+ }
+ } else { // eDirNext
+ while (!reachedBlockAncestor) {
+ nsIFrame* parent = frame->GetParent();
+ // Treat a frame associated with the root content as if it were a block frame.
+ if (!frame->mContent || !frame->mContent->GetParent()) {
+ reachedBlockAncestor = true;
+ break;
+ }
+ nsIFrame* sibling = frame;
+ while (sibling && !blockFrameOrBR.mContent) {
+ blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirNext);
+ sibling = sibling->GetNextSibling();
+ }
+ if (blockFrameOrBR.mContent) {
+ aPos->mResultContent = blockFrameOrBR.mContent;
+ aPos->mContentOffset = blockFrameOrBR.mOffset;
+ break;
+ }
+ frame = parent;
+ reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr);
+ }
+ if (reachedBlockAncestor) { // no "stop frame" found
+ aPos->mResultContent = frame->GetContent();
+ if (aPos->mResultContent)
+ aPos->mContentOffset = aPos->mResultContent->GetChildCount();
+ }
+ }
+ return NS_OK;
+}
+
+// Determine movement direction relative to frame
+static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, bool aVisual)
+{
+ bool isReverseDirection = aVisual && IsReversedDirectionFrame(frame);
+ return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
+}
+
+nsresult
+nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
+{
+ if (!aPos)
+ return NS_ERROR_NULL_POINTER;
+ nsresult result = NS_ERROR_FAILURE;
+
+ if (mState & NS_FRAME_IS_DIRTY)
+ return NS_ERROR_UNEXPECTED;
+
+ // Translate content offset to be relative to frame
+ FrameContentRange range = GetRangeForFrame(this);
+ int32_t offset = aPos->mStartOffset - range.start;
+ nsIFrame* current = this;
+
+ switch (aPos->mAmount) {
+ case eSelectCharacter:
+ case eSelectCluster:
+ {
+ bool eatingNonRenderableWS = false;
+ nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
+ bool jumpedLine = false;
+ bool movedOverNonSelectableText = false;
+
+ while (peekSearchState != FOUND) {
+ bool movingInFrameDirection =
+ IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
+
+ if (eatingNonRenderableWS)
+ peekSearchState = current->PeekOffsetNoAmount(movingInFrameDirection, &offset);
+ else
+ peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection, &offset,
+ aPos->mAmount == eSelectCluster);
+
+ movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE);
+
+ if (peekSearchState != FOUND) {
+ bool movedOverNonSelectable = false;
+ result =
+ current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
+ aPos->mJumpLines, aPos->mScrollViewStop,
+ &current, &offset, &jumpedLine,
+ &movedOverNonSelectable);
+ if (NS_FAILED(result))
+ return result;
+
+ // If we jumped lines, it's as if we found a character, but we still need
+ // to eat non-renderable content on the new line.
+ if (jumpedLine)
+ eatingNonRenderableWS = true;
+
+ // Remember if we moved over non-selectable text when finding another frame.
+ if (movedOverNonSelectable) {
+ movedOverNonSelectableText = true;
+ }
+ }
+
+ // Found frame, but because we moved over non selectable text we want the offset
+ // to be at the frame edge. Note that if we are extending the selection, this
+ // doesn't matter.
+ if (peekSearchState == FOUND && movedOverNonSelectableText &&
+ !aPos->mExtend)
+ {
+ int32_t start, end;
+ current->GetOffsets(start, end);
+ offset = aPos->mDirection == eDirNext ? 0 : end - start;
+ }
+ }
+
+ // Set outputs
+ range = GetRangeForFrame(current);
+ aPos->mResultFrame = current;
+ aPos->mResultContent = range.content;
+ // Output offset is relative to content, not frame
+ aPos->mContentOffset = offset < 0 ? range.end : range.start + offset;
+ // If we're dealing with a text frame and moving backward positions us at
+ // the end of that line, decrease the offset by one to make sure that
+ // we're placed before the linefeed character on the previous line.
+ if (offset < 0 && jumpedLine &&
+ aPos->mDirection == eDirPrevious &&
+ current->HasSignificantTerminalNewline()) {
+ --aPos->mContentOffset;
+ }
+
+ break;
+ }
+ case eSelectWordNoSpace:
+ // eSelectWordNoSpace means that we should not be eating any whitespace when
+ // moving to the adjacent word. This means that we should set aPos->
+ // mWordMovementType to eEndWord if we're moving forwards, and to eStartWord
+ // if we're moving backwards.
+ if (aPos->mDirection == eDirPrevious) {
+ aPos->mWordMovementType = eStartWord;
+ } else {
+ aPos->mWordMovementType = eEndWord;
+ }
+ // Intentionally fall through the eSelectWord case.
+ MOZ_FALLTHROUGH;
+ case eSelectWord:
+ {
+ // wordSelectEatSpace means "are we looking for a boundary between whitespace
+ // and non-whitespace (in the direction we're moving in)".
+ // It is true when moving forward and looking for a beginning of a word, or
+ // when moving backwards and looking for an end of a word.
+ bool wordSelectEatSpace;
+ if (aPos->mWordMovementType != eDefaultBehavior) {
+ // aPos->mWordMovementType possible values:
+ // eEndWord: eat the space if we're moving backwards
+ // eStartWord: eat the space if we're moving forwards
+ wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious));
+ }
+ else {
+ // Use the hidden preference which is based on operating system behavior.
+ // This pref only affects whether moving forward by word should go to the end of this word or start of the next word.
+ // When going backwards, the start of the word is always used, on every operating system.
+ wordSelectEatSpace = aPos->mDirection == eDirNext &&
+ Preferences::GetBool("layout.word_select.eat_space_to_next_word");
+ }
+
+ // mSawBeforeType means "we already saw characters of the type
+ // before the boundary we're looking for". Examples:
+ // 1. If we're moving forward, looking for a word beginning (i.e. a boundary
+ // between whitespace and non-whitespace), then eatingWS==true means
+ // "we already saw some whitespace".
+ // 2. If we're moving backward, looking for a word beginning (i.e. a boundary
+ // between non-whitespace and whitespace), then eatingWS==true means
+ // "we already saw some non-whitespace".
+ PeekWordState state;
+ int32_t offsetAdjustment = 0;
+ bool done = false;
+ while (!done) {
+ bool movingInFrameDirection =
+ IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
+
+ done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
+ aPos->mIsKeyboardSelect, &offset, &state) == FOUND;
+
+ if (!done) {
+ nsIFrame* nextFrame;
+ int32_t nextFrameOffset;
+ bool jumpedLine, movedOverNonSelectableText;
+ result =
+ current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
+ aPos->mJumpLines, aPos->mScrollViewStop,
+ &nextFrame, &nextFrameOffset, &jumpedLine,
+ &movedOverNonSelectableText);
+ // We can't jump lines if we're looking for whitespace following
+ // non-whitespace, and we already encountered non-whitespace.
+ if (NS_FAILED(result) ||
+ (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) {
+ done = true;
+ // If we've crossed the line boundary, check to make sure that we
+ // have not consumed a trailing newline as whitesapce if it's significant.
+ if (jumpedLine && wordSelectEatSpace &&
+ current->HasSignificantTerminalNewline()) {
+ offsetAdjustment = -1;
+ }
+ } else {
+ if (jumpedLine) {
+ state.mContext.Truncate();
+ }
+ current = nextFrame;
+ offset = nextFrameOffset;
+ // Jumping a line is equivalent to encountering whitespace
+ if (wordSelectEatSpace && jumpedLine)
+ state.SetSawBeforeType();
+ }
+ }
+ }
+
+ // Set outputs
+ range = GetRangeForFrame(current);
+ aPos->mResultFrame = current;
+ aPos->mResultContent = range.content;
+ // Output offset is relative to content, not frame
+ aPos->mContentOffset = (offset < 0 ? range.end : range.start + offset) + offsetAdjustment;
+ break;
+ }
+ case eSelectLine :
+ {
+ nsAutoLineIterator iter;
+ nsIFrame *blockFrame = this;
+
+ while (NS_FAILED(result)){
+ int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
+ if (thisLine < 0)
+ return NS_ERROR_FAILURE;
+ iter = blockFrame->GetLineIterator();
+ NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?");
+ result = NS_OK;
+
+ int edgeCase = 0; // no edge case. this should look at thisLine
+
+ bool doneLooping = false; // tells us when no more block frames hit.
+ // this part will find a frame or a block frame. if it's a block frame
+ // it will "drill down" to find a viable frame or it will return an error.
+ nsIFrame *lastFrame = this;
+ do {
+ result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(),
+ aPos,
+ blockFrame,
+ thisLine,
+ edgeCase); // start from thisLine
+
+ // we came back to same spot! keep going
+ if (NS_SUCCEEDED(result) &&
+ (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
+ aPos->mResultFrame = nullptr;
+ if (aPos->mDirection == eDirPrevious)
+ thisLine--;
+ else
+ thisLine++;
+ } else // if failure or success with different frame.
+ doneLooping = true; // do not continue with while loop
+
+ lastFrame = aPos->mResultFrame; // set last frame
+
+ // make sure block element is not the same as the one we had before
+ if (NS_SUCCEEDED(result) &&
+ aPos->mResultFrame &&
+ blockFrame != aPos->mResultFrame) {
+ /* SPECIAL CHECK FOR TABLE NAVIGATION
+ tables need to navigate also and the frame that supports it is
+ nsTableRowGroupFrame which is INSIDE nsTableWrapperFrame.
+ If we have stumbled onto an nsTableWrapperFrame we need to drill
+ into nsTableRowGroup if we hit a header or footer that's ok just
+ go into them.
+ */
+ bool searchTableBool = false;
+ if (aPos->mResultFrame->GetType() == nsGkAtoms::tableWrapperFrame ||
+ aPos->mResultFrame->GetType() == nsGkAtoms::tableCellFrame) {
+ nsIFrame* frame = aPos->mResultFrame->PrincipalChildList().FirstChild();
+ // got the table frame now
+ // ok time to drill down to find iterator
+ while (frame) {
+ iter = frame->GetLineIterator();
+ if (iter) {
+ aPos->mResultFrame = frame;
+ searchTableBool = true;
+ result = NS_OK;
+ break; // while(frame)
+ }
+ result = NS_ERROR_FAILURE;
+ frame = frame->PrincipalChildList().FirstChild();
+ }
+ }
+
+ if (!searchTableBool) {
+ iter = aPos->mResultFrame->GetLineIterator();
+ result = iter ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ // we've struck another block element!
+ if (NS_SUCCEEDED(result) && iter) {
+ doneLooping = false;
+ if (aPos->mDirection == eDirPrevious)
+ edgeCase = 1; // far edge, search from end backwards
+ else
+ edgeCase = -1; // near edge search from beginning onwards
+ thisLine = 0; // this line means nothing now.
+ // everything else means something so keep looking "inside" the block
+ blockFrame = aPos->mResultFrame;
+ } else {
+ // THIS is to mean that everything is ok to the containing while loop
+ result = NS_OK;
+ break;
+ }
+ }
+ } while (!doneLooping);
+ }
+ return result;
+ }
+
+ case eSelectParagraph:
+ return PeekOffsetParagraph(aPos);
+
+ case eSelectBeginLine:
+ case eSelectEndLine:
+ {
+ // Adjusted so that the caret can't get confused when content changes
+ nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this);
+ int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
+ if (thisLine < 0)
+ return NS_ERROR_FAILURE;
+ nsAutoLineIterator it = blockFrame->GetLineIterator();
+ NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
+
+ int32_t lineFrameCount;
+ nsIFrame *firstFrame;
+ nsRect usedRect;
+ nsIFrame* baseFrame = nullptr;
+ bool endOfLine = (eSelectEndLine == aPos->mAmount);
+
+ if (aPos->mVisual && PresContext()->BidiEnabled()) {
+ bool lineIsRTL = it->GetDirection();
+ bool isReordered;
+ nsIFrame *lastFrame;
+ result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
+ baseFrame = endOfLine ? lastFrame : firstFrame;
+ if (baseFrame) {
+ bool frameIsRTL =
+ (nsBidiPresUtils::FrameDirection(baseFrame) == NSBIDI_RTL);
+ // If the direction of the frame on the edge is opposite to
+ // that of the line, we'll need to drill down to its opposite
+ // end, so reverse endOfLine.
+ if (frameIsRTL != lineIsRTL) {
+ endOfLine = !endOfLine;
+ }
+ }
+ } else {
+ it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect);
+
+ nsIFrame* frame = firstFrame;
+ for (int32_t count = lineFrameCount; count;
+ --count, frame = frame->GetNextSibling()) {
+ if (!frame->IsGeneratedContentFrame()) {
+ // When jumping to the end of the line with the "end" key,
+ // skip over brFrames
+ if (endOfLine && lineFrameCount > 1 &&
+ frame->GetType() == nsGkAtoms::brFrame) {
+ continue;
+ }
+ baseFrame = frame;
+ if (!endOfLine)
+ break;
+ }
+ }
+ }
+ if (!baseFrame)
+ return NS_ERROR_FAILURE;
+ FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame,
+ endOfLine, 0);
+ FrameContentRange range = GetRangeForFrame(targetFrame.frame);
+ aPos->mResultContent = range.content;
+ aPos->mContentOffset = endOfLine ? range.end : range.start;
+ if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
+ // Do not position the caret after the terminating newline if we're
+ // trying to move to the end of line (see bug 596506)
+ --aPos->mContentOffset;
+ }
+ aPos->mResultFrame = targetFrame.frame;
+ aPos->mAttach = aPos->mContentOffset == range.start ?
+ CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
+ if (!range.content)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+ }
+
+ default:
+ {
+ NS_ASSERTION(false, "Invalid amount");
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+nsIFrame::FrameSearchResult
+nsFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ // Sure, we can stop right here.
+ return FOUND;
+}
+
+nsIFrame::FrameSearchResult
+nsFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ int32_t startOffset = *aOffset;
+ // A negative offset means "end of frame", which in our case means offset 1.
+ if (startOffset < 0)
+ startOffset = 1;
+ if (aForward == (startOffset == 0)) {
+ // We're before the frame and moving forward, or after it and moving backwards:
+ // skip to the other side and we're done.
+ *aOffset = 1 - startOffset;
+ return FOUND;
+ }
+ return CONTINUE;
+}
+
+nsIFrame::FrameSearchResult
+nsFrame::PeekOffsetWord(bool aForward,
+ bool aWordSelectEatSpace,
+ bool aIsKeyboardSelect,
+ int32_t* aOffset,
+ PeekWordState* aState)
+{
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ int32_t startOffset = *aOffset;
+ // This isn't text, so truncate the context
+ aState->mContext.Truncate();
+ if (startOffset < 0)
+ startOffset = 1;
+ if (aForward == (startOffset == 0)) {
+ // We're before the frame and moving forward, or after it and moving backwards.
+ // If we're looking for non-whitespace, we found it (without skipping this frame).
+ if (!aState->mAtStart) {
+ if (aState->mLastCharWasPunctuation) {
+ // We're not punctuation, so this is a punctuation boundary.
+ if (BreakWordBetweenPunctuation(aState, aForward, false, false, aIsKeyboardSelect))
+ return FOUND;
+ } else {
+ // This is not a punctuation boundary.
+ if (aWordSelectEatSpace && aState->mSawBeforeType)
+ return FOUND;
+ }
+ }
+ // Otherwise skip to the other side and note that we encountered non-whitespace.
+ *aOffset = 1 - startOffset;
+ aState->Update(false, // not punctuation
+ false // not whitespace
+ );
+ if (!aWordSelectEatSpace)
+ aState->SetSawBeforeType();
+ }
+ return CONTINUE;
+}
+
+bool
+nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
+ bool aForward,
+ bool aPunctAfter, bool aWhitespaceAfter,
+ bool aIsKeyboardSelect)
+{
+ NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
+ "Call this only at punctuation boundaries");
+ if (aState->mLastCharWasWhitespace) {
+ // We always stop between whitespace and punctuation
+ return true;
+ }
+ if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) {
+ // When this pref is false, we never stop at a punctuation boundary unless
+ // it's followed by whitespace (in the relevant direction).
+ return aWhitespaceAfter;
+ }
+ if (!aIsKeyboardSelect) {
+ // mouse caret movement (e.g. word selection) always stops at every punctuation boundary
+ return true;
+ }
+ bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
+ if (!afterPunct) {
+ // keyboard caret movement only stops after punctuation (in content order)
+ return false;
+ }
+ // Stop only if we've seen some non-punctuation since the last whitespace;
+ // don't stop after punctuation that follows whitespace.
+ return aState->mSeenNonPunctuationSinceWhitespace;
+}
+
+nsresult
+nsFrame::CheckVisibility(nsPresContext* , int32_t , int32_t , bool , bool *, bool *)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+int32_t
+nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainingBlock)
+{
+ NS_ASSERTION(aFrame, "null aFrame");
+ nsFrameManager* frameManager = aFrame->PresContext()->FrameManager();
+ nsIFrame *blockFrame = aFrame;
+ nsIFrame *thisBlock;
+ nsAutoLineIterator it;
+ nsresult result = NS_ERROR_FAILURE;
+ while (NS_FAILED(result) && blockFrame)
+ {
+ thisBlock = blockFrame;
+ if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+ //if we are searching for a frame that is not in flow we will not find it.
+ //we must instead look for its placeholder
+ if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
+ // abspos continuations don't have placeholders, get the fif
+ thisBlock = thisBlock->FirstInFlow();
+ }
+ thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock);
+ if (!thisBlock)
+ return -1;
+ }
+ blockFrame = thisBlock->GetParent();
+ result = NS_OK;
+ if (blockFrame) {
+ if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame)
+ return -1;
+ it = blockFrame->GetLineIterator();
+ if (!it)
+ result = NS_ERROR_FAILURE;
+ }
+ }
+ if (!blockFrame || !it)
+ return -1;
+
+ if (aContainingBlock)
+ *aContainingBlock = blockFrame;
+ return it->FindLineContaining(thisBlock);
+}
+
+nsresult
+nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
+ bool aJumpLines, bool aScrollViewStop,
+ nsIFrame** aOutFrame, int32_t* aOutOffset,
+ bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText)
+{
+ nsresult result;
+
+ if (!aOutFrame || !aOutOffset || !aOutJumpedLine)
+ return NS_ERROR_NULL_POINTER;
+
+ nsPresContext* presContext = PresContext();
+ *aOutFrame = nullptr;
+ *aOutOffset = 0;
+ *aOutJumpedLine = false;
+ *aOutMovedOverNonSelectableText = false;
+
+ // Find the prev/next selectable frame
+ bool selectable = false;
+ nsIFrame *traversedFrame = this;
+ while (!selectable) {
+ nsIFrame *blockFrame;
+
+ int32_t thisLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame);
+ if (thisLine < 0)
+ return NS_ERROR_FAILURE;
+
+ nsAutoLineIterator it = blockFrame->GetLineIterator();
+ NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
+
+ bool atLineEdge;
+ nsIFrame *firstFrame;
+ nsIFrame *lastFrame;
+ if (aVisual && presContext->BidiEnabled()) {
+ bool lineIsRTL = it->GetDirection();
+ bool isReordered;
+ result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
+ nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
+ if (*framePtr) {
+ bool frameIsRTL =
+ (nsBidiPresUtils::FrameDirection(*framePtr) == NSBIDI_RTL);
+ if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
+ nsFrame::GetFirstLeaf(presContext, framePtr);
+ } else {
+ nsFrame::GetLastLeaf(presContext, framePtr);
+ }
+ atLineEdge = *framePtr == traversedFrame;
+ } else {
+ atLineEdge = true;
+ }
+ } else {
+ nsRect nonUsedRect;
+ int32_t lineFrameCount;
+ result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,
+ nonUsedRect);
+ if (NS_FAILED(result))
+ return result;
+
+ if (aDirection == eDirPrevious) {
+ nsFrame::GetFirstLeaf(presContext, &firstFrame);
+ atLineEdge = firstFrame == traversedFrame;
+ } else { // eDirNext
+ lastFrame = firstFrame;
+ for (;lineFrameCount > 1;lineFrameCount --){
+ result = it->GetNextSiblingOnLine(lastFrame, thisLine);
+ if (NS_FAILED(result) || !lastFrame){
+ NS_ERROR("should not be reached nsFrame");
+ return NS_ERROR_FAILURE;
+ }
+ }
+ nsFrame::GetLastLeaf(presContext, &lastFrame);
+ atLineEdge = lastFrame == traversedFrame;
+ }
+ }
+
+ if (atLineEdge) {
+ *aOutJumpedLine = true;
+ if (!aJumpLines)
+ return NS_ERROR_FAILURE; //we are done. cannot jump lines
+ }
+
+ nsCOMPtr<nsIFrameEnumerator> frameTraversal;
+ result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
+ presContext, traversedFrame,
+ eLeaf,
+ aVisual && presContext->BidiEnabled(),
+ aScrollViewStop,
+ true, // aFollowOOFs
+ false // aSkipPopupChecks
+ );
+ if (NS_FAILED(result))
+ return result;
+
+ if (aDirection == eDirNext)
+ frameTraversal->Next();
+ else
+ frameTraversal->Prev();
+
+ traversedFrame = frameTraversal->CurrentItem();
+
+ // Skip anonymous elements, but watch out for generated content
+ if (!traversedFrame ||
+ (!traversedFrame->IsGeneratedContentFrame() &&
+ traversedFrame->GetContent()->IsRootOfNativeAnonymousSubtree())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Skip brFrames, but only if they are not the only frame in the line
+ if (atLineEdge && aDirection == eDirPrevious &&
+ traversedFrame->GetType() == nsGkAtoms::brFrame) {
+ int32_t lineFrameCount;
+ nsIFrame *currentBlockFrame, *currentFirstFrame;
+ nsRect usedRect;
+ int32_t currentLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &currentBlockFrame);
+ nsAutoLineIterator iter = currentBlockFrame->GetLineIterator();
+ result = iter->GetLine(currentLine, &currentFirstFrame, &lineFrameCount, usedRect);
+ if (NS_FAILED(result)) {
+ return result;
+ }
+ if (lineFrameCount > 1) {
+ continue;
+ }
+ }
+
+ traversedFrame->IsSelectable(&selectable, nullptr);
+ if (!selectable) {
+ *aOutMovedOverNonSelectableText = true;
+ }
+ } // while (!selectable)
+
+ *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
+
+ if (aVisual && IsReversedDirectionFrame(traversedFrame)) {
+ // The new frame is reverse-direction, go to the other end
+ *aOutOffset = -1 - *aOutOffset;
+ }
+ *aOutFrame = traversedFrame;
+ return NS_OK;
+}
+
+nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const
+{
+ nsPoint offset(0,0);
+ for (const nsIFrame *f = this; f; f = f->GetParent()) {
+ if (f->HasView()) {
+ if (aOffset)
+ *aOffset = offset;
+ return f->GetView();
+ }
+ offset += f->GetPosition();
+ }
+
+ NS_NOTREACHED("No view on any parent? How did that happen?");
+ return nullptr;
+}
+
+
+/* virtual */ void
+nsFrame::ChildIsDirty(nsIFrame* aChild)
+{
+ NS_NOTREACHED("should never be called on a frame that doesn't inherit from "
+ "nsContainerFrame");
+}
+
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsFrame::AccessibleType()
+{
+ if (IsTableCaption() && !GetRect().IsEmpty()) {
+ return a11y::eHTMLCaptionType;
+ }
+ return a11y::eNoType;
+}
+#endif
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(OverflowAreasProperty, nsOverflowAreas)
+
+bool
+nsIFrame::ClearOverflowRects()
+{
+ if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
+ return false;
+ }
+ if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+ Properties().Delete(OverflowAreasProperty());
+ }
+ mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
+ return true;
+}
+
+/** Create or retrieve the previously stored overflow area, if the frame does
+ * not overflow and no creation is required return nullptr.
+ * @return pointer to the overflow area rectangle
+ */
+nsOverflowAreas*
+nsIFrame::GetOverflowAreasProperty()
+{
+ FrameProperties props = Properties();
+ nsOverflowAreas* overflow = props.Get(OverflowAreasProperty());
+
+ if (overflow) {
+ return overflow; // the property already exists
+ }
+
+ // The property isn't set yet, so allocate a new rect, set the property,
+ // and return the newly allocated rect
+ overflow = new nsOverflowAreas;
+ props.Set(OverflowAreasProperty(), overflow);
+ return overflow;
+}
+
+/** Set the overflowArea rect, storing it as deltas or a separate rect
+ * depending on its size in relation to the primary frame rect.
+ */
+bool
+nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
+{
+ if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+ nsOverflowAreas* overflow = Properties().Get(OverflowAreasProperty());
+ bool changed = *overflow != aOverflowAreas;
+ *overflow = aOverflowAreas;
+
+ // Don't bother with converting to the deltas form if we already
+ // have a property.
+ return changed;
+ }
+
+ const nsRect& vis = aOverflowAreas.VisualOverflow();
+ uint32_t l = -vis.x, // left edge: positive delta is leftwards
+ t = -vis.y, // top: positive is upwards
+ r = vis.XMost() - mRect.width, // right: positive is rightwards
+ b = vis.YMost() - mRect.height; // bottom: positive is downwards
+ if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) &&
+ l <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+ t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+ r <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+ b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+ // we have to check these against zero because we *never* want to
+ // set a frame as having no overflow in this function. This is
+ // because FinishAndStoreOverflow calls this function prior to
+ // SetRect based on whether the overflow areas match aNewSize.
+ // In the case where the overflow areas exactly match mRect but
+ // do not match aNewSize, we need to store overflow in a property
+ // so that our eventual SetRect/SetSize will know that it has to
+ // reset our overflow areas.
+ (l | t | r | b) != 0) {
+ VisualDeltas oldDeltas = mOverflow.mVisualDeltas;
+ // It's a "small" overflow area so we store the deltas for each edge
+ // directly in the frame, rather than allocating a separate rect.
+ // If they're all zero, that's fine; we're setting things to
+ // no-overflow.
+ mOverflow.mVisualDeltas.mLeft = l;
+ mOverflow.mVisualDeltas.mTop = t;
+ mOverflow.mVisualDeltas.mRight = r;
+ mOverflow.mVisualDeltas.mBottom = b;
+ // There was no scrollable overflow before, and there isn't now.
+ return oldDeltas != mOverflow.mVisualDeltas;
+ } else {
+ bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) ||
+ !aOverflowAreas.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas());
+
+ // it's a large overflow area that we need to store as a property
+ mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
+ nsOverflowAreas* overflow = GetOverflowAreasProperty();
+ NS_ASSERTION(overflow, "should have created areas");
+ *overflow = aOverflowAreas;
+ return changed;
+ }
+}
+
+inline bool
+IsInlineFrame(nsIFrame *aFrame)
+{
+ nsIAtom *type = aFrame->GetType();
+ return type == nsGkAtoms::inlineFrame;
+}
+
+/**
+ * Compute the union of the border boxes of aFrame and its descendants,
+ * in aFrame's coordinate space (if aApplyTransform is false) or its
+ * post-transform coordinate space (if aApplyTransform is true).
+ */
+static nsRect
+UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform,
+ bool& aOutValid,
+ const nsSize* aSizeOverride = nullptr,
+ const nsOverflowAreas* aOverflowOverride = nullptr)
+{
+ const nsRect bounds(nsPoint(0, 0),
+ aSizeOverride ? *aSizeOverride : aFrame->GetSize());
+
+ // The SVG container frames do not maintain an accurate mRect.
+ // It will make the outline be larger than we expect, we need
+ // to make them narrow to their children's outline.
+ // aOutValid is set to false if the returned nsRect is not valid
+ // and should not be included in the outline rectangle.
+ aOutValid = !aFrame->IsFrameOfType(nsIFrame::eSVGContainer);
+
+ // Start from our border-box, transformed. See comment below about
+ // transform of children.
+ nsRect u;
+ bool doTransform = aApplyTransform && aFrame->IsTransformed();
+ if (doTransform) {
+ u = nsDisplayTransform::TransformRect(bounds, aFrame, &bounds);
+ } else {
+ u = bounds;
+ }
+
+ // Only iterate through the children if the overflow areas suggest
+ // that we might need to, and if the frame doesn't clip its overflow
+ // anyway.
+ if (aOverflowOverride) {
+ if (!doTransform &&
+ bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) &&
+ bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
+ return u;
+ }
+ } else {
+ if (!doTransform &&
+ bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) &&
+ bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
+ return u;
+ }
+ }
+ const nsStyleDisplay* disp = aFrame->StyleDisplay();
+ nsIAtom* fType = aFrame->GetType();
+ if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
+ fType == nsGkAtoms::scrollFrame ||
+ fType == nsGkAtoms::listControlFrame ||
+ fType == nsGkAtoms::svgOuterSVGFrame) {
+ return u;
+ }
+
+ const nsStyleEffects* effects = aFrame->StyleEffects();
+ Maybe<nsRect> clipPropClipRect =
+ aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
+
+ // Iterate over all children except pop-ups.
+ const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
+ nsIFrame::kSelectPopupList);
+ for (nsIFrame::ChildListIterator childLists(aFrame);
+ !childLists.IsDone(); childLists.Next()) {
+ if (skip.Contains(childLists.CurrentID())) {
+ continue;
+ }
+
+ nsFrameList children = childLists.CurrentList();
+ for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+ nsIFrame* child = e.get();
+ // Note that passing |true| for aApplyTransform when
+ // child->Combines3DTransformWithAncestors() is incorrect if our
+ // aApplyTransform is false... but the opposite would be as
+ // well. This is because elements within a preserve-3d scene
+ // are always transformed up to the top of the scene. This
+ // means we don't have a mechanism for getting a transform up to
+ // an intermediate point within the scene. We choose to
+ // over-transform rather than under-transform because this is
+ // consistent with other overflow areas.
+ bool validRect = true;
+ nsRect childRect = UnionBorderBoxes(child, true, validRect) +
+ child->GetPosition();
+
+ if (!validRect) {
+ continue;
+ }
+
+ if (clipPropClipRect) {
+ // Intersect with the clip before transforming.
+ childRect.IntersectRect(childRect, *clipPropClipRect);
+ }
+
+ // Note that we transform each child separately according to
+ // aFrame's transform, and then union, which gives a different
+ // (smaller) result from unioning and then transforming the
+ // union. This doesn't match the way we handle overflow areas
+ // with 2-D transforms, though it does match the way we handle
+ // overflow areas in preserve-3d 3-D scenes.
+ if (doTransform && !child->Combines3DTransformWithAncestors()) {
+ childRect = nsDisplayTransform::TransformRect(childRect, aFrame, &bounds);
+ }
+
+ // If a SVGContainer has a non-SVGContainer child, we assign
+ // its child's outline to this SVGContainer directly.
+ if (!aOutValid && validRect) {
+ u = childRect;
+ aOutValid = true;
+ } else {
+ u.UnionRectEdges(u, childRect);
+ }
+ }
+ }
+
+ return u;
+}
+
+static void
+ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas,
+ const nsSize& aNewSize)
+{
+ const nsStyleOutline* outline = aFrame->StyleOutline();
+ const uint8_t outlineStyle = outline->mOutlineStyle;
+ if (outlineStyle == NS_STYLE_BORDER_STYLE_NONE) {
+ return;
+ }
+
+ nscoord width = outline->GetOutlineWidth();
+ if (width <= 0 && outlineStyle != NS_STYLE_BORDER_STYLE_AUTO) {
+ return;
+ }
+
+ // When the outline property is set on :-moz-anonymous-block or
+ // :-moz-anonymous-positioned-block pseudo-elements, it inherited
+ // that outline from the inline that was broken because it
+ // contained a block. In that case, we don't want a really wide
+ // outline if the block inside the inline is narrow, so union the
+ // actual contents of the anonymous blocks.
+ nsIFrame *frameForArea = aFrame;
+ do {
+ nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo();
+ if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
+ pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
+ break;
+ // If we're done, we really want it and all its later siblings.
+ frameForArea = frameForArea->PrincipalChildList().FirstChild();
+ NS_ASSERTION(frameForArea, "anonymous block with no children?");
+ } while (frameForArea);
+
+ // Find the union of the border boxes of all descendants, or in
+ // the block-in-inline case, all descendants we care about.
+ //
+ // Note that the interesting perspective-related cases are taken
+ // care of by the code that handles those issues for overflow
+ // calling FinishAndStoreOverflow again, which in turn calls this
+ // function again. We still need to deal with preserve-3d a bit.
+ nsRect innerRect;
+ bool validRect;
+ if (frameForArea == aFrame) {
+ innerRect = UnionBorderBoxes(aFrame, false, validRect, &aNewSize, &aOverflowAreas);
+ } else {
+ for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
+ nsRect r(UnionBorderBoxes(frameForArea, true, validRect));
+
+ // Adjust for offsets transforms up to aFrame's pre-transform
+ // (i.e., normal) coordinate space; see comments in
+ // UnionBorderBoxes for some of the subtlety here.
+ for (nsIFrame *f = frameForArea, *parent = f->GetParent();
+ /* see middle of loop */;
+ f = parent, parent = f->GetParent()) {
+ r += f->GetPosition();
+ if (parent == aFrame) {
+ break;
+ }
+ if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
+ r = nsDisplayTransform::TransformRect(r, parent);
+ }
+ }
+
+ innerRect.UnionRect(innerRect, r);
+ }
+ }
+
+ // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
+ aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(),
+ new nsRect(innerRect));
+ const nscoord offset = outline->mOutlineOffset;
+ nsRect outerRect(innerRect);
+ bool useOutlineAuto = false;
+ if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
+ useOutlineAuto = outlineStyle == NS_STYLE_BORDER_STYLE_AUTO;
+ if (MOZ_UNLIKELY(useOutlineAuto)) {
+ nsPresContext* presContext = aFrame->PresContext();
+ nsITheme* theme = presContext->GetTheme();
+ if (theme && theme->ThemeSupportsWidget(presContext, aFrame,
+ NS_THEME_FOCUS_OUTLINE)) {
+ outerRect.Inflate(offset);
+ theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
+ NS_THEME_FOCUS_OUTLINE, &outerRect);
+ } else {
+ useOutlineAuto = false;
+ }
+ }
+ }
+ if (MOZ_LIKELY(!useOutlineAuto)) {
+ outerRect.Inflate(width + offset);
+ }
+
+ nsRect& vo = aOverflowAreas.VisualOverflow();
+ vo.UnionRectEdges(vo, innerRect.Union(outerRect));
+}
+
+bool
+nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
+ nsSize aNewSize, nsSize* aOldSize)
+{
+ NS_ASSERTION(FrameMaintainsOverflow(),
+ "Don't call - overflow rects not maintained on these SVG frames");
+
+ nsRect bounds(nsPoint(0, 0), aNewSize);
+ // Store the passed in overflow area if we are a preserve-3d frame or we have
+ // a transform, and it's not just the frame bounds.
+ if (Combines3DTransformWithAncestors() || IsTransformed()) {
+ if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
+ !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
+ nsOverflowAreas* initial =
+ Properties().Get(nsIFrame::InitialOverflowProperty());
+ if (!initial) {
+ Properties().Set(nsIFrame::InitialOverflowProperty(),
+ new nsOverflowAreas(aOverflowAreas));
+ } else if (initial != &aOverflowAreas) {
+ *initial = aOverflowAreas;
+ }
+ } else {
+ Properties().Delete(nsIFrame::InitialOverflowProperty());
+ }
+#ifdef DEBUG
+ Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
+#endif
+ } else {
+#ifdef DEBUG
+ Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied());
+#endif
+ }
+
+ // This is now called FinishAndStoreOverflow() instead of
+ // StoreOverflow() because frame-generic ways of adding overflow
+ // can happen here, e.g. CSS2 outline and native theme.
+ // If the overflow area width or height is nscoord_MAX, then a
+ // saturating union may have encounted an overflow, so the overflow may not
+ // contain the frame border-box. Don't warn in that case.
+ // Don't warn for SVG either, since SVG doesn't need the overflow area
+ // to contain the frame bounds.
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
+ NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
+ r->width == nscoord_MAX || r->height == nscoord_MAX ||
+ (mState & NS_FRAME_SVG_LAYOUT) ||
+ r->Contains(nsRect(nsPoint(0,0), aNewSize)),
+ "Computed overflow area must contain frame bounds");
+ }
+
+ // If we clip our children, clear accumulated overflow area. The
+ // children are actually clipped to the padding-box, but since the
+ // overflow area should include the entire border-box, just set it to
+ // the border-box here.
+ const nsStyleDisplay* disp = StyleDisplay();
+ NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) ==
+ (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP),
+ "If one overflow is clip, the other should be too");
+ if (nsFrame::ShouldApplyOverflowClipping(this, disp)) {
+ // The contents are actually clipped to the padding area
+ aOverflowAreas.SetAllTo(bounds);
+ }
+
+ // Overflow area must always include the frame's top-left and bottom-right,
+ // even if the frame rect is empty (so we can scroll to those positions).
+ // Pending a real fix for bug 426879, don't do this for inline frames
+ // with zero width.
+ // Do not do this for SVG either, since it will usually massively increase
+ // the area unnecessarily.
+ if ((aNewSize.width != 0 || !IsInlineFrame(this)) &&
+ !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = aOverflowAreas.Overflow(otype);
+ o.UnionRectEdges(o, bounds);
+ }
+ }
+
+ // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background,
+ // so we add theme background overflow here so it's not clipped.
+ if (!::IsXULBoxWrapped(this) && IsThemed(disp)) {
+ nsRect r(bounds);
+ nsPresContext *presContext = PresContext();
+ if (presContext->GetTheme()->
+ GetWidgetOverflow(presContext->DeviceContext(), this,
+ disp->mAppearance, &r)) {
+ nsRect& vo = aOverflowAreas.VisualOverflow();
+ vo.UnionRectEdges(vo, r);
+ }
+ }
+
+ ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
+
+ // Nothing in here should affect scrollable overflow.
+ aOverflowAreas.VisualOverflow() =
+ ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize);
+
+ // Absolute position clipping
+ const nsStyleEffects* effects = StyleEffects();
+ Maybe<nsRect> clipPropClipRect =
+ GetClipPropClipRect(disp, effects, aNewSize);
+ if (clipPropClipRect) {
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = aOverflowAreas.Overflow(otype);
+ o.IntersectRect(o, *clipPropClipRect);
+ }
+ }
+
+ /* If we're transformed, transform the overflow rect by the current transformation. */
+ bool hasTransform = IsTransformed();
+ nsSize oldSize = mRect.Size();
+ bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
+
+ /* Since our size might not actually have been computed yet, we need to make sure that we use the
+ * correct dimensions by overriding the stored bounding rectangle with the value the caller has
+ * ensured us we'll use.
+ */
+ SetSize(aNewSize);
+
+ if (ChildrenHavePerspective() && sizeChanged) {
+ nsRect newBounds(nsPoint(0, 0), aNewSize);
+ RecomputePerspectiveChildrenOverflow(this);
+ }
+
+ if (hasTransform) {
+ Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
+ new nsOverflowAreas(aOverflowAreas));
+
+ if (Combines3DTransformWithAncestors()) {
+ /* If we're a preserve-3d leaf frame, then our pre-transform overflow should be correct. Our
+ * post-transform overflow is empty though, because we only contribute to the overflow area
+ * of the preserve-3d root frame.
+ * If we're an intermediate frame then the pre-transform overflow should contain all our
+ * non-preserve-3d children, which is what we want. Again we have no post-transform overflow.
+ */
+ aOverflowAreas.SetAllTo(nsRect());
+ } else {
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = aOverflowAreas.Overflow(otype);
+ o = nsDisplayTransform::TransformRect(o, this);
+ }
+
+ /* If we're the root of the 3d context, then we want to include the overflow areas of all
+ * the participants. This won't have happened yet as the code above set their overflow
+ * area to empty. Manually collect these overflow areas now.
+ */
+ if (Extend3DContext()) {
+ ComputePreserve3DChildrenOverflow(aOverflowAreas);
+ }
+ }
+ } else {
+ Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
+ }
+
+ /* Revert the size change in case some caller is depending on this. */
+ SetSize(oldSize);
+
+ bool anyOverflowChanged;
+ if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
+ anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
+ } else {
+ anyOverflowChanged = ClearOverflowRects();
+ }
+
+ if (anyOverflowChanged) {
+ nsSVGEffects::InvalidateDirectRenderingObservers(this);
+ }
+ return anyOverflowChanged;
+}
+
+void
+nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame)
+{
+ nsIFrame::ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* child = childFrames.get();
+ if (!child->FrameMaintainsOverflow()) {
+ continue; // frame does not maintain overflow rects
+ }
+ if (child->HasPerspective()) {
+ nsOverflowAreas* overflow =
+ child->Properties().Get(nsIFrame::InitialOverflowProperty());
+ nsRect bounds(nsPoint(0, 0), child->GetSize());
+ if (overflow) {
+ nsOverflowAreas overflowCopy = *overflow;
+ child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
+ } else {
+ nsOverflowAreas boundsOverflow;
+ boundsOverflow.SetAllTo(bounds);
+ child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
+ }
+ } else if (child->GetContainingBlock(SKIP_SCROLLED_FRAME) == aStartFrame) {
+ // If a frame is using perspective, then the size used to compute
+ // perspective-origin is the size of the frame belonging to its parent
+ // style context. We must find any descendant frames using our size
+ // (by recursing into frames that have the same containing block)
+ // to update their overflow rects too.
+ child->RecomputePerspectiveChildrenOverflow(aStartFrame);
+ }
+ }
+ }
+}
+
+void
+nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ // Find all descendants that participate in the 3d context, and include their overflow.
+ // These descendants have an empty overflow, so won't have been included in the normal
+ // overflow calculation. Any children that don't participate have normal overflow,
+ // so will have been included already.
+
+ nsRect childVisual;
+ nsRect childScrollable;
+ nsIFrame::ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* child = childFrames.get();
+
+ // If this child participates in the 3d context, then take the pre-transform
+ // region (which contains all descendants that aren't participating in the 3d context)
+ // and transform it into the 3d context root coordinate space.
+ if (child->Combines3DTransformWithAncestors()) {
+ nsOverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
+
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ nsRect& o = childOverflow.Overflow(otype);
+ o = nsDisplayTransform::TransformRect(o, child);
+ }
+
+ aOverflowAreas.UnionWith(childOverflow);
+
+ // If this child also extends the 3d context, then recurse into it
+ // looking for more participants.
+ if (child->Extend3DContext()) {
+ child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
+ }
+ }
+ }
+ }
+}
+
+uint32_t
+nsIFrame::GetDepthInFrameTree() const
+{
+ uint32_t result = 0;
+ for (nsContainerFrame* ancestor = GetParent(); ancestor;
+ ancestor = ancestor->GetParent()) {
+ result++;
+ }
+ return result;
+}
+
+void
+nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
+ nsIFrame* aChildFrame)
+{
+ aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
+ aChildFrame->GetPosition());
+}
+
+/**
+ * This function takes a frame that is part of a block-in-inline split,
+ * and _if_ that frame is an anonymous block created by an ib split it
+ * returns the block's preceding inline. This is needed because the
+ * split inline's style context is the parent of the anonymous block's
+ * style context.
+ *
+ * If aFrame is not an anonymous block, null is returned.
+ */
+static nsIFrame*
+GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame)
+{
+ NS_PRECONDITION(aFrame, "Must have a non-null frame!");
+ NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
+ "GetIBSplitSibling should only be called on ib-split frames");
+
+ nsIAtom* type = aFrame->StyleContext()->GetPseudo();
+ if (type != nsCSSAnonBoxes::mozAnonymousBlock &&
+ type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) {
+ // it's not an anonymous block
+ return nullptr;
+ }
+
+ // Find the first continuation of the frame. (Ugh. This ends up
+ // being O(N^2) when it is called O(N) times.)
+ aFrame = aFrame->FirstContinuation();
+
+ /*
+ * Now look up the nsGkAtoms::IBSplitPrevSibling
+ * property.
+ */
+ nsIFrame *ibSplitSibling =
+ aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling());
+ NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
+ return ibSplitSibling;
+}
+
+/**
+ * Get the parent, corrected for the mangled frame tree resulting from
+ * having a block within an inline. The result only differs from the
+ * result of |GetParent| when |GetParent| returns an anonymous block
+ * that was created for an element that was 'display: inline' because
+ * that element contained a block.
+ *
+ * Also skip anonymous scrolled-content parents; inherit directly from the
+ * outer scroll frame.
+ */
+static nsIFrame*
+GetCorrectedParent(const nsIFrame* aFrame)
+{
+ nsIFrame* parent = aFrame->GetParent();
+ if (!parent) {
+ return nullptr;
+ }
+
+ // For a table caption we want the _inner_ table frame (unless it's anonymous)
+ // as the style parent.
+ if (aFrame->IsTableCaption()) {
+ nsIFrame* innerTable = parent->PrincipalChildList().FirstChild();
+ if (!innerTable->StyleContext()->GetPseudo()) {
+ return innerTable;
+ }
+ }
+
+ // Table wrappers are always anon boxes; if we're in here for an outer
+ // table, that actually means its the _inner_ table that wants to
+ // know its parent. So get the pseudo of the inner in that case.
+ nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
+ if (pseudo == nsCSSAnonBoxes::tableWrapper) {
+ pseudo = aFrame->PrincipalChildList().FirstChild()->StyleContext()->GetPseudo();
+ }
+ return nsFrame::CorrectStyleParentFrame(parent, pseudo);
+}
+
+/* static */
+nsIFrame*
+nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
+ nsIAtom* aChildPseudo)
+{
+ NS_PRECONDITION(aProspectiveParent, "Must have a prospective parent");
+
+ // Anon boxes are parented to their actual parent already, except
+ // for non-elements. Those should not be treated as an anon box.
+ if (aChildPseudo && !nsCSSAnonBoxes::IsNonElement(aChildPseudo) &&
+ nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) {
+ NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock &&
+ aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock,
+ "Should have dealt with kids that have "
+ "NS_FRAME_PART_OF_IBSPLIT elsewhere");
+ return aProspectiveParent;
+ }
+
+ // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
+ // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause
+ // style data to be out of sync with the frame tree.
+ nsIFrame* parent = aProspectiveParent;
+ do {
+ if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
+ nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
+
+ if (sibling) {
+ // |parent| was a block in an {ib} split; use the inline as
+ // |the style parent.
+ parent = sibling;
+ }
+ }
+
+ nsIAtom* parentPseudo = parent->StyleContext()->GetPseudo();
+ if (!parentPseudo ||
+ (!nsCSSAnonBoxes::IsAnonBox(parentPseudo) &&
+ // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for
+ // aChildPseudo (even though that's not a valid pseudo-type) just to
+ // trigger this behavior of walking up to the nearest non-pseudo
+ // ancestor.
+ aChildPseudo != nsGkAtoms::placeholderFrame)) {
+ return parent;
+ }
+
+ parent = parent->GetParent();
+ } while (parent);
+
+ if (aProspectiveParent->StyleContext()->GetPseudo() ==
+ nsCSSAnonBoxes::viewportScroll) {
+ // aProspectiveParent is the scrollframe for a viewport
+ // and the kids are the anonymous scrollbars
+ return aProspectiveParent;
+ }
+
+ // We can get here if the root element is absolutely positioned.
+ // We can't test for this very accurately, but it can only happen
+ // when the prospective parent is a canvas frame.
+ NS_ASSERTION(aProspectiveParent->GetType() == nsGkAtoms::canvasFrame,
+ "Should have found a parent before this");
+ return nullptr;
+}
+
+nsStyleContext*
+nsFrame::DoGetParentStyleContext(nsIFrame** aProviderFrame) const
+{
+ *aProviderFrame = nullptr;
+ nsFrameManager* fm = PresContext()->FrameManager();
+ if (MOZ_LIKELY(mContent)) {
+ nsIContent* parentContent = mContent->GetFlattenedTreeParent();
+ if (MOZ_LIKELY(parentContent)) {
+ nsIAtom* pseudo = StyleContext()->GetPseudo();
+ if (!pseudo || !mContent->IsElement() ||
+ (!nsCSSAnonBoxes::IsAnonBox(pseudo) &&
+ // Ensure that we don't return the display:contents style
+ // of the parent content for pseudos that have the same content
+ // as their primary frame (like -moz-list-bullets do):
+ mContent->GetPrimaryFrame() == this) ||
+ /* if next is true then it's really a request for the table frame's
+ parent context, see nsTable[Outer]Frame::GetParentStyleContext. */
+ pseudo == nsCSSAnonBoxes::tableWrapper) {
+ nsStyleContext* sc = fm->GetDisplayContentsStyleFor(parentContent);
+ if (MOZ_UNLIKELY(sc)) {
+ return sc;
+ }
+ }
+ } else {
+ if (!StyleContext()->GetPseudo()) {
+ // we're a frame for the root. We have no style context parent.
+ return nullptr;
+ }
+ }
+ }
+
+ if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
+ /*
+ * If this frame is an anonymous block created when an inline with a block
+ * inside it got split, then the parent style context is on its preceding
+ * inline. We can get to it using GetIBSplitSiblingForAnonymousBlock.
+ */
+ if (mState & NS_FRAME_PART_OF_IBSPLIT) {
+ nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
+ if (ibSplitSibling) {
+ return (*aProviderFrame = ibSplitSibling)->StyleContext();
+ }
+ }
+
+ // If this frame is one of the blocks that split an inline, we must
+ // return the "special" inline parent, i.e., the parent that this
+ // frame would have if we didn't mangle the frame structure.
+ *aProviderFrame = GetCorrectedParent(this);
+ return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
+ }
+
+ // We're an out-of-flow frame. For out-of-flow frames, we must
+ // resolve underneath the placeholder's parent. The placeholder is
+ // reached from the first-in-flow.
+ nsIFrame* placeholder = fm->GetPlaceholderFrameFor(FirstInFlow());
+ if (!placeholder) {
+ NS_NOTREACHED("no placeholder frame for out-of-flow frame");
+ *aProviderFrame = GetCorrectedParent(this);
+ return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
+ }
+ return placeholder->GetParentStyleContext(aProviderFrame);
+}
+
+void
+nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
+{
+ if (!aFrame || !*aFrame)
+ return;
+ nsIFrame *child = *aFrame;
+ //if we are a block frame then go for the last line of 'this'
+ while (1){
+ child = child->PrincipalChildList().FirstChild();
+ if (!child)
+ return;//nothing to do
+ nsIFrame* siblingFrame;
+ nsIContent* content;
+ //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
+ //see bug 278197 comment #12 #13 for details
+ while ((siblingFrame = child->GetNextSibling()) &&
+ (content = siblingFrame->GetContent()) &&
+ !content->IsRootOfNativeAnonymousSubtree())
+ child = siblingFrame;
+ *aFrame = child;
+ }
+}
+
+void
+nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
+{
+ if (!aFrame || !*aFrame)
+ return;
+ nsIFrame *child = *aFrame;
+ while (1){
+ child = child->PrincipalChildList().FirstChild();
+ if (!child)
+ return;//nothing to do
+ *aFrame = child;
+ }
+}
+
+/* virtual */ bool
+nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
+{
+ int32_t tabIndex = -1;
+ if (aTabIndex) {
+ *aTabIndex = -1; // Default for early return is not focusable
+ }
+ bool isFocusable = false;
+
+ if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors() &&
+ StyleContext()->GetPseudo() != nsCSSAnonBoxes::anonymousFlexItem &&
+ StyleContext()->GetPseudo() != nsCSSAnonBoxes::anonymousGridItem) {
+ const nsStyleUserInterface* ui = StyleUserInterface();
+ if (ui->mUserFocus != StyleUserFocus::Ignore &&
+ ui->mUserFocus != StyleUserFocus::None) {
+ // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
+ tabIndex = 0;
+ }
+ isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse);
+ if (!isFocusable && !aWithMouse &&
+ GetType() == nsGkAtoms::scrollFrame &&
+ mContent->IsHTMLElement() &&
+ !mContent->IsRootOfNativeAnonymousSubtree() &&
+ mContent->GetParent() &&
+ !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
+ // Elements with scrollable view are focusable with script & tabbable
+ // Otherwise you couldn't scroll them with keyboard, which is
+ // an accessibility issue (e.g. Section 508 rules)
+ // However, we don't make them to be focusable with the mouse,
+ // because the extra focus outlines are considered unnecessarily ugly.
+ // When clicked on, the selection position within the element
+ // will be enough to make them keyboard scrollable.
+ nsIScrollableFrame *scrollFrame = do_QueryFrame(this);
+ if (scrollFrame &&
+ !scrollFrame->GetScrollbarStyles().IsHiddenInBothDirections() &&
+ !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
+ // Scroll bars will be used for overflow
+ isFocusable = true;
+ tabIndex = 0;
+ }
+ }
+ }
+
+ if (aTabIndex) {
+ *aTabIndex = tabIndex;
+ }
+ return isFocusable;
+}
+
+/**
+ * @return true if this text frame ends with a newline character which is
+ * treated as preformatted. It should return false if this is not a text frame.
+ */
+bool
+nsIFrame::HasSignificantTerminalNewline() const
+{
+ return false;
+}
+
+static uint8_t
+ConvertSVGDominantBaselineToVerticalAlign(uint8_t aDominantBaseline)
+{
+ // Most of these are approximate mappings.
+ switch (aDominantBaseline) {
+ case NS_STYLE_DOMINANT_BASELINE_HANGING:
+ case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
+ return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP;
+ case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
+ case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
+ return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM;
+ case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
+ case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
+ case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL:
+ return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
+ case NS_STYLE_DOMINANT_BASELINE_AUTO:
+ case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
+ return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+ case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT:
+ case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE:
+ case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE:
+ // These three should not simply map to 'baseline', but we don't
+ // support the complex baseline model that SVG 1.1 has and which
+ // css3-linebox now defines.
+ return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+ default:
+ NS_NOTREACHED("unexpected aDominantBaseline value");
+ return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+ }
+}
+
+uint8_t
+nsIFrame::VerticalAlignEnum() const
+{
+ if (IsSVGText()) {
+ uint8_t dominantBaseline;
+ for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) {
+ dominantBaseline = frame->StyleSVGReset()->mDominantBaseline;
+ if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO ||
+ frame->GetType() == nsGkAtoms::svgTextFrame) {
+ break;
+ }
+ }
+ return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline);
+ }
+
+ const nsStyleCoord& verticalAlign = StyleDisplay()->mVerticalAlign;
+ if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
+ return verticalAlign.GetIntValue();
+ }
+
+ return eInvalidVerticalAlign;
+}
+
+/* static */
+void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
+ nsIFrame::Cursor& aCursor)
+{
+ aCursor.mCursor = ui->mCursor;
+ aCursor.mHaveHotspot = false;
+ aCursor.mLoading = false;
+ aCursor.mHotspotX = aCursor.mHotspotY = 0.0f;
+
+ for (const nsCursorImage& item : ui->mCursorImages) {
+ uint32_t status;
+ nsresult rv = item.GetImage()->GetImageStatus(&status);
+ if (NS_SUCCEEDED(rv)) {
+ if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
+ // If we are falling back because any cursor before is loading,
+ // let the consumer know.
+ aCursor.mLoading = true;
+ } else if (!(status & imgIRequest::STATUS_ERROR)) {
+ // This is the one we want
+ item.GetImage()->GetImage(getter_AddRefs(aCursor.mContainer));
+ aCursor.mHaveHotspot = item.mHaveHotspot;
+ aCursor.mHotspotX = item.mHotspotX;
+ aCursor.mHotspotY = item.mHotspotY;
+ break;
+ }
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsFrame::RefreshSizeCache(nsBoxLayoutState& aState)
+{
+ // XXXbz this comment needs some rewriting to make sense in the
+ // post-reflow-branch world.
+
+ // Ok we need to compute our minimum, preferred, and maximum sizes.
+ // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
+ // 2) Preferred size. This is a little harder. This is the size the block would be
+ // if it were laid out on an infinite canvas. So we can get this by reflowing
+ // the block with and INTRINSIC width and height. We can also do a nice optimization
+ // for incremental reflow. If the reflow is incremental then we can pass a flag to
+ // have the block compute the preferred width for us! Preferred height can just be
+ // the minimum height;
+ // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element
+ // size. That would give us the width. Unfortunately you can only ask for a maxElementSize
+ // during an incremental reflow. So on other reflows we will just have to use 0.
+ // The min height on the other hand is fairly easy we need to get the largest
+ // line height. This can be done with the line iterator.
+
+ // if we do have a rendering context
+ nsRenderingContext* rendContext = aState.GetRenderingContext();
+ if (rendContext) {
+ nsPresContext* presContext = aState.PresContext();
+
+ // If we don't have any HTML constraints and it's a resize, then nothing in the block
+ // could have changed, so no refresh is necessary.
+ nsBoxLayoutMetrics* metrics = BoxMetrics();
+ if (!DoesNeedRecalc(metrics->mBlockPrefSize))
+ return NS_OK;
+
+ // the rect we plan to size to.
+ nsRect rect = GetRect();
+
+ nsMargin bp(0,0,0,0);
+ GetXULBorderAndPadding(bp);
+
+ {
+ // If we're a container for font size inflation, then shrink
+ // wrapping inside of us should not apply font size inflation.
+ AutoMaybeDisableFontInflation an(this);
+
+ metrics->mBlockPrefSize.width =
+ GetPrefISize(rendContext) + bp.LeftRight();
+ metrics->mBlockMinSize.width =
+ GetMinISize(rendContext) + bp.LeftRight();
+ }
+
+ // do the nasty.
+ const WritingMode wm = aState.OuterReflowInput() ?
+ aState.OuterReflowInput()->GetWritingMode() : GetWritingMode();
+ ReflowOutput desiredSize(wm);
+ BoxReflow(aState, presContext, desiredSize, rendContext,
+ rect.x, rect.y,
+ metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
+
+ metrics->mBlockMinSize.height = 0;
+ // ok we need the max ascent of the items on the line. So to do this
+ // ask the block for its line iterator. Get the max ascent.
+ nsAutoLineIterator lines = GetLineIterator();
+ if (lines)
+ {
+ metrics->mBlockMinSize.height = 0;
+ int count = 0;
+ nsIFrame* firstFrame = nullptr;
+ int32_t framesOnLine;
+ nsRect lineBounds;
+
+ do {
+ lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds);
+
+ if (lineBounds.height > metrics->mBlockMinSize.height)
+ metrics->mBlockMinSize.height = lineBounds.height;
+
+ count++;
+ } while(firstFrame);
+ } else {
+ metrics->mBlockMinSize.height = desiredSize.Height();
+ }
+
+ metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
+
+ if (desiredSize.BlockStartAscent() ==
+ ReflowOutput::ASK_FOR_BASELINE) {
+ if (!nsLayoutUtils::GetFirstLineBaseline(wm, this,
+ &metrics->mBlockAscent))
+ metrics->mBlockAscent = GetLogicalBaseline(wm);
+ } else {
+ metrics->mBlockAscent = desiredSize.BlockStartAscent();
+ }
+
+#ifdef DEBUG_adaptor
+ printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics->mBlockMinSize.width,
+ metrics->mBlockMinSize.height,
+ metrics->mBlockPrefSize.width,
+ metrics->mBlockPrefSize.height,
+ metrics->mBlockAscent);
+#endif
+ }
+
+ return NS_OK;
+}
+
+/* virtual */ nsILineIterator*
+nsFrame::GetLineIterator()
+{
+ return nullptr;
+}
+
+nsSize
+nsFrame::GetXULPrefSize(nsBoxLayoutState& aState)
+{
+ nsSize size(0,0);
+ DISPLAY_PREF_SIZE(this, size);
+ // If the size is cached, and there are no HTML constraints that we might
+ // be depending on, then we just return the cached size.
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+ if (!DoesNeedRecalc(metrics->mPrefSize)) {
+ return metrics->mPrefSize;
+ }
+
+ if (IsXULCollapsed())
+ return size;
+
+ // get our size in CSS.
+ bool widthSet, heightSet;
+ bool completelyRedefined = nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
+
+ // Refresh our caches with new sizes.
+ if (!completelyRedefined) {
+ RefreshSizeCache(aState);
+ nsSize blockSize = metrics->mBlockPrefSize;
+
+ // notice we don't need to add our borders or padding
+ // in. That's because the block did it for us.
+ if (!widthSet)
+ size.width = blockSize.width;
+ if (!heightSet)
+ size.height = blockSize.height;
+ }
+
+ metrics->mPrefSize = size;
+ return size;
+}
+
+nsSize
+nsFrame::GetXULMinSize(nsBoxLayoutState& aState)
+{
+ nsSize size(0,0);
+ DISPLAY_MIN_SIZE(this, size);
+ // Don't use the cache if we have HTMLReflowInput constraints --- they might have changed
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+ if (!DoesNeedRecalc(metrics->mMinSize)) {
+ size = metrics->mMinSize;
+ return size;
+ }
+
+ if (IsXULCollapsed())
+ return size;
+
+ // get our size in CSS.
+ bool widthSet, heightSet;
+ bool completelyRedefined =
+ nsIFrame::AddXULMinSize(aState, this, size, widthSet, heightSet);
+
+ // Refresh our caches with new sizes.
+ if (!completelyRedefined) {
+ RefreshSizeCache(aState);
+ nsSize blockSize = metrics->mBlockMinSize;
+
+ if (!widthSet)
+ size.width = blockSize.width;
+ if (!heightSet)
+ size.height = blockSize.height;
+ }
+
+ metrics->mMinSize = size;
+ return size;
+}
+
+nsSize
+nsFrame::GetXULMaxSize(nsBoxLayoutState& aState)
+{
+ nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
+ DISPLAY_MAX_SIZE(this, size);
+ // Don't use the cache if we have HTMLReflowInput constraints --- they might have changed
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+ if (!DoesNeedRecalc(metrics->mMaxSize)) {
+ size = metrics->mMaxSize;
+ return size;
+ }
+
+ if (IsXULCollapsed())
+ return size;
+
+ size = nsBox::GetXULMaxSize(aState);
+ metrics->mMaxSize = size;
+
+ return size;
+}
+
+nscoord
+nsFrame::GetXULFlex()
+{
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+ if (!DoesNeedRecalc(metrics->mFlex))
+ return metrics->mFlex;
+
+ metrics->mFlex = nsBox::GetXULFlex();
+
+ return metrics->mFlex;
+}
+
+nscoord
+nsFrame::GetXULBoxAscent(nsBoxLayoutState& aState)
+{
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+ if (!DoesNeedRecalc(metrics->mAscent))
+ return metrics->mAscent;
+
+ if (IsXULCollapsed()) {
+ metrics->mAscent = 0;
+ } else {
+ // Refresh our caches with new sizes.
+ RefreshSizeCache(aState);
+ metrics->mAscent = metrics->mBlockAscent;
+ }
+
+ return metrics->mAscent;
+}
+
+nsresult
+nsFrame::DoXULLayout(nsBoxLayoutState& aState)
+{
+ nsRect ourRect(mRect);
+
+ nsRenderingContext* rendContext = aState.GetRenderingContext();
+ nsPresContext* presContext = aState.PresContext();
+ WritingMode ourWM = GetWritingMode();
+ const WritingMode outerWM = aState.OuterReflowInput() ?
+ aState.OuterReflowInput()->GetWritingMode() : ourWM;
+ ReflowOutput desiredSize(outerWM);
+ LogicalSize ourSize = GetLogicalSize(outerWM);
+
+ if (rendContext) {
+
+ BoxReflow(aState, presContext, desiredSize, rendContext,
+ ourRect.x, ourRect.y, ourRect.width, ourRect.height);
+
+ if (IsXULCollapsed()) {
+ SetSize(nsSize(0, 0));
+ } else {
+
+ // if our child needs to be bigger. This might happend with
+ // wrapping text. There is no way to predict its height until we
+ // reflow it. Now that we know the height reshuffle upward.
+ if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM) ||
+ desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
+
+#ifdef DEBUG_GROW
+ XULDumpBox(stdout);
+ printf(" GREW from (%d,%d) -> (%d,%d)\n",
+ ourSize.ISize(outerWM), ourSize.BSize(outerWM),
+ desiredSize.ISize(outerWM), desiredSize.BSize(outerWM));
+#endif
+
+ if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM)) {
+ ourSize.ISize(outerWM) = desiredSize.ISize(outerWM);
+ }
+
+ if (desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
+ ourSize.BSize(outerWM) = desiredSize.BSize(outerWM);
+ }
+ }
+
+ // ensure our size is what we think is should be. Someone could have
+ // reset the frame to be smaller or something dumb like that.
+ SetSize(ourSize.ConvertTo(ourWM, outerWM));
+ }
+ }
+
+ // Should we do this if IsXULCollapsed() is true?
+ LogicalSize size(GetLogicalSize(outerWM));
+ desiredSize.ISize(outerWM) = size.ISize(outerWM);
+ desiredSize.BSize(outerWM) = size.BSize(outerWM);
+ desiredSize.UnionOverflowAreasWithDesiredBounds();
+
+ if (HasAbsolutelyPositionedChildren()) {
+ // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
+ ReflowInput reflowInput(aState.PresContext(), this,
+ aState.GetRenderingContext(),
+ LogicalSize(ourWM, ISize(),
+ NS_UNCONSTRAINEDSIZE),
+ ReflowInput::DUMMY_PARENT_REFLOW_STATE);
+
+ AddStateBits(NS_FRAME_IN_REFLOW);
+ // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
+ // (just a dummy value; hopefully that's OK)
+ nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
+ ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
+ reflowInput, reflowStatus);
+ RemoveStateBits(NS_FRAME_IN_REFLOW);
+ }
+
+ nsSize oldSize(ourRect.Size());
+ FinishAndStoreOverflow(desiredSize.mOverflowAreas,
+ size.GetPhysicalSize(outerWM), &oldSize);
+
+ SyncLayout(aState);
+
+ return NS_OK;
+}
+
+void
+nsFrame::BoxReflow(nsBoxLayoutState& aState,
+ nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ nsRenderingContext* aRenderingContext,
+ nscoord aX,
+ nscoord aY,
+ nscoord aWidth,
+ nscoord aHeight,
+ bool aMoveFrame)
+{
+ DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
+
+#ifdef DEBUG_REFLOW
+ nsAdaptorAddIndents();
+ printf("Reflowing: ");
+ nsFrame::ListTag(stdout, mFrame);
+ printf("\n");
+ gIndent2++;
+#endif
+
+ nsBoxLayoutMetrics *metrics = BoxMetrics();
+ nsReflowStatus status = NS_FRAME_COMPLETE;
+ WritingMode wm = aDesiredSize.GetWritingMode();
+
+ bool needsReflow = NS_SUBTREE_DIRTY(this);
+
+ // if we don't need a reflow then
+ // lets see if we are already that size. Yes? then don't even reflow. We are done.
+ if (!needsReflow) {
+
+ if (aWidth != NS_INTRINSICSIZE && aHeight != NS_INTRINSICSIZE) {
+
+ // if the new calculated size has a 0 width or a 0 height
+ if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) && (aWidth == 0 || aHeight == 0)) {
+ needsReflow = false;
+ aDesiredSize.Width() = aWidth;
+ aDesiredSize.Height() = aHeight;
+ SetSize(aDesiredSize.Size(wm).ConvertTo(GetWritingMode(), wm));
+ } else {
+ aDesiredSize.Width() = metrics->mLastSize.width;
+ aDesiredSize.Height() = metrics->mLastSize.height;
+
+ // remove the margin. The rect of our child does not include it but our calculated size does.
+ // don't reflow if we are already the right size
+ if (metrics->mLastSize.width == aWidth && metrics->mLastSize.height == aHeight)
+ needsReflow = false;
+ else
+ needsReflow = true;
+
+ }
+ } else {
+ // if the width or height are intrinsic alway reflow because
+ // we don't know what it should be.
+ needsReflow = true;
+ }
+ }
+
+ // ok now reflow the child into the spacers calculated space
+ if (needsReflow) {
+
+ aDesiredSize.ClearSize();
+
+ // create a reflow state to tell our child to flow at the given size.
+
+ // Construct a bogus parent reflow state so that there's a usable
+ // containing block reflow state.
+ nsMargin margin(0,0,0,0);
+ GetXULMargin(margin);
+
+ nsSize parentSize(aWidth, aHeight);
+ if (parentSize.height != NS_INTRINSICSIZE)
+ parentSize.height += margin.TopBottom();
+ if (parentSize.width != NS_INTRINSICSIZE)
+ parentSize.width += margin.LeftRight();
+
+ nsIFrame *parentFrame = GetParent();
+ nsFrameState savedState = parentFrame->GetStateBits();
+ WritingMode parentWM = parentFrame->GetWritingMode();
+ ReflowInput
+ parentReflowInput(aPresContext, parentFrame, aRenderingContext,
+ LogicalSize(parentWM, parentSize),
+ ReflowInput::DUMMY_PARENT_REFLOW_STATE);
+ parentFrame->RemoveStateBits(~nsFrameState(0));
+ parentFrame->AddStateBits(savedState);
+
+ // This may not do very much useful, but it's probably worth trying.
+ if (parentSize.width != NS_INTRINSICSIZE)
+ parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
+ if (parentSize.height != NS_INTRINSICSIZE)
+ parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
+ parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
+ // XXX use box methods
+ parentFrame->GetXULPadding(parentReflowInput.ComputedPhysicalPadding());
+ parentFrame->GetXULBorder(parentReflowInput.ComputedPhysicalBorderPadding());
+ parentReflowInput.ComputedPhysicalBorderPadding() +=
+ parentReflowInput.ComputedPhysicalPadding();
+
+ // Construct the parent chain manually since constructing it normally
+ // messes up dimensions.
+ const ReflowInput *outerReflowInput = aState.OuterReflowInput();
+ NS_ASSERTION(!outerReflowInput || outerReflowInput->mFrame != this,
+ "in and out of XUL on a single frame?");
+ const ReflowInput* parentRI;
+ if (outerReflowInput && outerReflowInput->mFrame == parentFrame) {
+ // We're a frame (such as a text control frame) that jumps into
+ // box reflow and then straight out of it on the child frame.
+ // This means we actually have a real parent reflow state.
+ // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
+ // linked up correctly for text control frames, so do so here).
+ parentRI = outerReflowInput;
+ } else {
+ parentRI = &parentReflowInput;
+ }
+
+ // XXX Is it OK that this reflow state has only one ancestor?
+ // (It used to have a bogus parent, skipping all the boxes).
+ WritingMode wm = GetWritingMode();
+ LogicalSize logicalSize(wm, nsSize(aWidth, aHeight));
+ logicalSize.BSize(wm) = NS_INTRINSICSIZE;
+ ReflowInput reflowInput(aPresContext, *parentRI, this,
+ logicalSize, nullptr,
+ ReflowInput::DUMMY_PARENT_REFLOW_STATE);
+
+ // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
+ // here (which it might be), then we should make sure that it's
+ // correct the first time around, rather than changing it later.
+ reflowInput.mCBReflowInput = parentRI;
+
+ reflowInput.mReflowDepth = aState.GetReflowDepth();
+
+ // mComputedWidth and mComputedHeight are content-box, not
+ // border-box
+ if (aWidth != NS_INTRINSICSIZE) {
+ nscoord computedWidth =
+ aWidth - reflowInput.ComputedPhysicalBorderPadding().LeftRight();
+ computedWidth = std::max(computedWidth, 0);
+ reflowInput.SetComputedWidth(computedWidth);
+ }
+
+ // Most child frames of box frames (e.g. subdocument or scroll frames)
+ // need to be constrained to the provided size and overflow as necessary.
+ // The one exception are block frames, because we need to know their
+ // natural height excluding any overflow area which may be caused by
+ // various CSS effects such as shadow or outline.
+ if (!IsFrameOfType(eBlockFrame)) {
+ if (aHeight != NS_INTRINSICSIZE) {
+ nscoord computedHeight =
+ aHeight - reflowInput.ComputedPhysicalBorderPadding().TopBottom();
+ computedHeight = std::max(computedHeight, 0);
+ reflowInput.SetComputedHeight(computedHeight);
+ } else {
+ reflowInput.SetComputedHeight(
+ ComputeSize(aRenderingContext, wm,
+ logicalSize,
+ logicalSize.ISize(wm),
+ reflowInput.ComputedLogicalMargin().Size(wm),
+ reflowInput.ComputedLogicalBorderPadding().Size(wm) -
+ reflowInput.ComputedLogicalPadding().Size(wm),
+ reflowInput.ComputedLogicalPadding().Size(wm),
+ ComputeSizeFlags::eDefault).Height(wm));
+ }
+ }
+
+ // Box layout calls SetRect before XULLayout, whereas non-box layout
+ // calls SetRect after Reflow.
+ // XXX Perhaps we should be doing this by twiddling the rect back to
+ // mLastSize before calling Reflow and then switching it back, but
+ // However, mLastSize can also be the size passed to BoxReflow by
+ // RefreshSizeCache, so that doesn't really make sense.
+ if (metrics->mLastSize.width != aWidth) {
+ reflowInput.SetHResize(true);
+
+ // When font size inflation is enabled, a horizontal resize
+ // requires a full reflow. See ReflowInput::InitResizeFlags
+ // for more details.
+ if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
+ AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+ }
+ if (metrics->mLastSize.height != aHeight) {
+ reflowInput.SetVResize(true);
+ }
+
+ #ifdef DEBUG_REFLOW
+ nsAdaptorAddIndents();
+ printf("Size=(%d,%d)\n",reflowInput.ComputedWidth(),
+ reflowInput.ComputedHeight());
+ nsAdaptorAddIndents();
+ nsAdaptorPrintReason(reflowInput);
+ printf("\n");
+ #endif
+
+ // place the child and reflow
+
+ Reflow(aPresContext, aDesiredSize, reflowInput, status);
+
+ NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
+
+ uint32_t layoutFlags = aState.LayoutFlags();
+ nsContainerFrame::FinishReflowChild(this, aPresContext, aDesiredSize,
+ &reflowInput, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME);
+
+ // Save the ascent. (bug 103925)
+ if (IsXULCollapsed()) {
+ metrics->mAscent = 0;
+ } else {
+ if (aDesiredSize.BlockStartAscent() ==
+ ReflowOutput::ASK_FOR_BASELINE) {
+ if (!nsLayoutUtils::GetFirstLineBaseline(wm, this, &metrics->mAscent))
+ metrics->mAscent = GetLogicalBaseline(wm);
+ } else
+ metrics->mAscent = aDesiredSize.BlockStartAscent();
+ }
+
+ } else {
+ aDesiredSize.SetBlockStartAscent(metrics->mBlockAscent);
+ }
+
+#ifdef DEBUG_REFLOW
+ if (aHeight != NS_INTRINSICSIZE && aDesiredSize.Height() != aHeight)
+ {
+ nsAdaptorAddIndents();
+ printf("*****got taller!*****\n");
+
+ }
+ if (aWidth != NS_INTRINSICSIZE && aDesiredSize.Width() != aWidth)
+ {
+ nsAdaptorAddIndents();
+ printf("*****got wider!******\n");
+
+ }
+#endif
+
+ if (aWidth == NS_INTRINSICSIZE)
+ aWidth = aDesiredSize.Width();
+
+ if (aHeight == NS_INTRINSICSIZE)
+ aHeight = aDesiredSize.Height();
+
+ metrics->mLastSize.width = aDesiredSize.Width();
+ metrics->mLastSize.height = aDesiredSize.Height();
+
+#ifdef DEBUG_REFLOW
+ gIndent2--;
+#endif
+}
+
+nsBoxLayoutMetrics*
+nsFrame::BoxMetrics() const
+{
+ nsBoxLayoutMetrics* metrics = Properties().Get(BoxMetricsProperty());
+ NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
+ return metrics;
+}
+
+/* static */ void
+nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame)
+{
+ if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
+ aFrame->TrackingVisibility()) {
+ // Assume all frames in popups are visible.
+ aFrame->IncApproximateVisibleCount();
+ }
+
+ aFrame->AddStateBits(NS_FRAME_IN_POPUP);
+
+ AutoTArray<nsIFrame::ChildList,4> childListArray;
+ aFrame->GetCrossDocChildLists(&childListArray);
+
+ nsIFrame::ChildListArrayIterator lists(childListArray);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ AddInPopupStateBitToDescendants(childFrames.get());
+ }
+ }
+}
+
+/* static */ void
+nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame)
+{
+ if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
+ nsLayoutUtils::IsPopup(aFrame)) {
+ return;
+ }
+
+ aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
+
+ if (aFrame->TrackingVisibility()) {
+ // We assume all frames in popups are visible, so this decrement balances
+ // out the increment in AddInPopupStateBitToDescendants above.
+ aFrame->DecApproximateVisibleCount();
+ }
+
+ AutoTArray<nsIFrame::ChildList,4> childListArray;
+ aFrame->GetCrossDocChildLists(&childListArray);
+
+ nsIFrame::ChildListArrayIterator lists(childListArray);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ RemoveInPopupStateBitFromDescendants(childFrames.get());
+ }
+ }
+}
+
+void
+nsIFrame::SetParent(nsContainerFrame* aParent)
+{
+ // Note that the current mParent may already be destroyed at this point.
+ mParent = aParent;
+ if (::IsXULBoxWrapped(this)) {
+ ::InitBoxMetrics(this, true);
+ } else {
+ // We could call Properties().Delete(BoxMetricsProperty()); here but
+ // that's kind of slow and re-parenting in such a way that we were
+ // IsXULBoxWrapped() before but not now should be very rare, so we'll just
+ // keep this unused frame property until this frame dies instead.
+ }
+
+ if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
+ for (nsIFrame* f = aParent;
+ f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
+ f = f->GetParent()) {
+ f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
+ }
+ }
+
+ if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
+ for (nsIFrame* f = aParent; f; f = f->GetParent()) {
+ if (f->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
+ break;
+ }
+ f->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ }
+ }
+
+ if (HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
+ for (nsIFrame* f = aParent; f; f = f->GetParent()) {
+ if (f->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
+ break;
+ }
+ f->AddStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
+ }
+ }
+
+ if (HasInvalidFrameInSubtree()) {
+ for (nsIFrame* f = aParent;
+ f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT | NS_FRAME_IS_NONDISPLAY);
+ f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
+ f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
+ }
+ }
+
+ if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
+ AddInPopupStateBitToDescendants(this);
+ } else {
+ RemoveInPopupStateBitFromDescendants(this);
+ }
+
+ // If our new parent only has invalid children, then we just invalidate
+ // ourselves too. This is probably faster than clearing the flag all
+ // the way up the frame tree.
+ if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
+ InvalidateFrame();
+ }
+}
+
+void
+nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
+ nsDisplayList* aList)
+{
+ if (GetContent() &&
+ GetContent()->IsXULElement() &&
+ GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
+ aList->AppendNewToTop(new (aBuilder)
+ nsDisplayOwnLayer(aBuilder, this, aList));
+ }
+}
+
+bool
+nsIFrame::IsSelected() const
+{
+ return (GetContent() && GetContent()->IsSelectionDescendant()) ?
+ IsFrameSelected() : false;
+}
+
+/*static*/ void
+nsIFrame::DestroyContentArray(ContentArray* aArray)
+{
+ for (nsIContent* content : *aArray) {
+ content->UnbindFromTree();
+ NS_RELEASE(content);
+ }
+ delete aArray;
+}
+
+bool
+nsIFrame::IsPseudoStackingContextFromStyle() {
+ // If you change this, also change the computation of pseudoStackingContext
+ // in BuildDisplayListForChild()
+ if (StyleEffects()->mOpacity != 1.0f) {
+ return true;
+ }
+ const nsStyleDisplay* disp = StyleDisplay();
+ return disp->IsAbsPosContainingBlock(this) ||
+ disp->IsFloating(this) ||
+ (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT);
+}
+
+Element*
+nsIFrame::GetPseudoElement(CSSPseudoElementType aType)
+{
+ nsIFrame* frame = nullptr;
+
+ if (aType == CSSPseudoElementType::before) {
+ frame = nsLayoutUtils::GetBeforeFrame(this);
+ } else if (aType == CSSPseudoElementType::after) {
+ frame = nsLayoutUtils::GetAfterFrame(this);
+ }
+
+ if (frame) {
+ nsIContent* content = frame->GetContent();
+ if (content->IsElement()) {
+ return content->AsElement();
+ }
+ }
+
+ return nullptr;
+}
+
+static bool
+IsFrameScrolledOutOfView(nsIFrame *aFrame)
+{
+ nsIScrollableFrame* scrollableFrame =
+ nsLayoutUtils::GetNearestScrollableFrame(aFrame,
+ nsLayoutUtils::SCROLLABLE_SAME_DOC |
+ nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+ if (!scrollableFrame) {
+ return false;
+ }
+
+ nsIFrame *scrollableParent = do_QueryFrame(scrollableFrame);
+ nsRect rect = aFrame->GetVisualOverflowRect();
+
+ nsRect transformedRect =
+ nsLayoutUtils::TransformFrameRectToAncestor(aFrame,
+ rect,
+ scrollableParent);
+
+ nsRect scrollableRect = scrollableParent->GetVisualOverflowRect();
+ if (!transformedRect.Intersects(scrollableRect)) {
+ return true;
+ }
+
+ nsIFrame* parent = scrollableParent->GetParent();
+ if (!parent) {
+ return false;
+ }
+
+ return IsFrameScrolledOutOfView(parent);
+}
+
+bool
+nsIFrame::IsScrolledOutOfView()
+{
+ return IsFrameScrolledOutOfView(this);
+}
+
+nsIFrame::CaretPosition::CaretPosition()
+ : mContentOffset(0)
+{
+}
+
+nsIFrame::CaretPosition::~CaretPosition()
+{
+}
+
+bool
+nsFrame::HasCSSAnimations()
+{
+ auto collection =
+ AnimationCollection<CSSAnimation>::GetAnimationCollection(this);
+ return collection && collection->mAnimations.Length() > 0;
+}
+
+bool
+nsFrame::HasCSSTransitions()
+{
+ auto collection =
+ AnimationCollection<CSSTransition>::GetAnimationCollection(this);
+ return collection && collection->mAnimations.Length() > 0;
+}
+
+// Box layout debugging
+#ifdef DEBUG_REFLOW
+int32_t gIndent2 = 0;
+
+void
+nsAdaptorAddIndents()
+{
+ for(int32_t i=0; i < gIndent2; i++)
+ {
+ printf(" ");
+ }
+}
+
+void
+nsAdaptorPrintReason(ReflowInput& aReflowInput)
+{
+ char* reflowReasonString;
+
+ switch(aReflowInput.reason)
+ {
+ case eReflowReason_Initial:
+ reflowReasonString = "initial";
+ break;
+
+ case eReflowReason_Resize:
+ reflowReasonString = "resize";
+ break;
+ case eReflowReason_Dirty:
+ reflowReasonString = "dirty";
+ break;
+ case eReflowReason_StyleChange:
+ reflowReasonString = "stylechange";
+ break;
+ case eReflowReason_Incremental:
+ {
+ switch (aReflowInput.reflowCommand->Type()) {
+ case eReflowType_StyleChanged:
+ reflowReasonString = "incremental (StyleChanged)";
+ break;
+ case eReflowType_ReflowDirty:
+ reflowReasonString = "incremental (ReflowDirty)";
+ break;
+ default:
+ reflowReasonString = "incremental (Unknown)";
+ }
+ }
+ break;
+ default:
+ reflowReasonString = "unknown";
+ break;
+ }
+
+ printf("%s",reflowReasonString);
+}
+
+#endif
+#ifdef DEBUG_LAYOUT
+void
+nsFrame::GetBoxName(nsAutoString& aName)
+{
+ GetFrameName(aName);
+}
+#endif
+
+#ifdef DEBUG
+static void
+GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
+ char* aResult)
+{
+ if (aContent) {
+ snprintf(aResult, aResultSize, "%s@%p",
+ nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
+ }
+ else {
+ snprintf(aResult, aResultSize, "@%p", aFrame);
+ }
+}
+
+void
+nsFrame::Trace(const char* aMethod, bool aEnter)
+{
+ if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
+ char tagbuf[40];
+ GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
+ PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
+ }
+}
+
+void
+nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
+{
+ if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
+ char tagbuf[40];
+ GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
+ PR_LogPrint("%s: %s %s, status=%scomplete%s",
+ tagbuf, aEnter ? "enter" : "exit", aMethod,
+ NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "",
+ (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : "");
+ }
+}
+
+void
+nsFrame::TraceMsg(const char* aFormatString, ...)
+{
+ if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
+ // Format arguments into a buffer
+ char argbuf[200];
+ va_list ap;
+ va_start(ap, aFormatString);
+ PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap);
+ va_end(ap);
+
+ char tagbuf[40];
+ GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
+ PR_LogPrint("%s: %s", tagbuf, argbuf);
+ }
+}
+
+void
+nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList)
+{
+ for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
+ NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY,
+ "dirty bit not set");
+ }
+}
+
+// Start Display Reflow
+#ifdef DEBUG
+
+DR_cookie::DR_cookie(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ const ReflowInput& aReflowInput,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus)
+ :mPresContext(aPresContext), mFrame(aFrame), mReflowInput(aReflowInput), mMetrics(aMetrics), mStatus(aStatus)
+{
+ MOZ_COUNT_CTOR(DR_cookie);
+ mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowInput);
+}
+
+DR_cookie::~DR_cookie()
+{
+ MOZ_COUNT_DTOR(DR_cookie);
+ nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
+}
+
+DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame)
+ : mFrame(aFrame)
+{
+ MOZ_COUNT_CTOR(DR_layout_cookie);
+ mValue = nsFrame::DisplayLayoutEnter(mFrame);
+}
+
+DR_layout_cookie::~DR_layout_cookie()
+{
+ MOZ_COUNT_DTOR(DR_layout_cookie);
+ nsFrame::DisplayLayoutExit(mFrame, mValue);
+}
+
+DR_intrinsic_width_cookie::DR_intrinsic_width_cookie(
+ nsIFrame* aFrame,
+ const char* aType,
+ nscoord& aResult)
+ : mFrame(aFrame)
+ , mType(aType)
+ , mResult(aResult)
+{
+ MOZ_COUNT_CTOR(DR_intrinsic_width_cookie);
+ mValue = nsFrame::DisplayIntrinsicISizeEnter(mFrame, mType);
+}
+
+DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie()
+{
+ MOZ_COUNT_DTOR(DR_intrinsic_width_cookie);
+ nsFrame::DisplayIntrinsicISizeExit(mFrame, mType, mResult, mValue);
+}
+
+DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(
+ nsIFrame* aFrame,
+ const char* aType,
+ nsSize& aResult)
+ : mFrame(aFrame)
+ , mType(aType)
+ , mResult(aResult)
+{
+ MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
+ mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
+}
+
+DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie()
+{
+ MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
+ nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
+}
+
+DR_init_constraints_cookie::DR_init_constraints_cookie(
+ nsIFrame* aFrame,
+ ReflowInput* aState,
+ nscoord aCBWidth,
+ nscoord aCBHeight,
+ const nsMargin* aMargin,
+ const nsMargin* aPadding)
+ : mFrame(aFrame)
+ , mState(aState)
+{
+ MOZ_COUNT_CTOR(DR_init_constraints_cookie);
+ mValue = ReflowInput::DisplayInitConstraintsEnter(mFrame, mState,
+ aCBWidth, aCBHeight,
+ aMargin, aPadding);
+}
+
+DR_init_constraints_cookie::~DR_init_constraints_cookie()
+{
+ MOZ_COUNT_DTOR(DR_init_constraints_cookie);
+ ReflowInput::DisplayInitConstraintsExit(mFrame, mState, mValue);
+}
+
+DR_init_offsets_cookie::DR_init_offsets_cookie(
+ nsIFrame* aFrame,
+ SizeComputationInput* aState,
+ const LogicalSize& aPercentBasis,
+ const nsMargin* aMargin,
+ const nsMargin* aPadding)
+ : mFrame(aFrame)
+ , mState(aState)
+{
+ MOZ_COUNT_CTOR(DR_init_offsets_cookie);
+ mValue = SizeComputationInput::DisplayInitOffsetsEnter(mFrame, mState,
+ aPercentBasis,
+ aMargin, aPadding);
+}
+
+DR_init_offsets_cookie::~DR_init_offsets_cookie()
+{
+ MOZ_COUNT_DTOR(DR_init_offsets_cookie);
+ SizeComputationInput::DisplayInitOffsetsExit(mFrame, mState, mValue);
+}
+
+DR_init_type_cookie::DR_init_type_cookie(
+ nsIFrame* aFrame,
+ ReflowInput* aState)
+ : mFrame(aFrame)
+ , mState(aState)
+{
+ MOZ_COUNT_CTOR(DR_init_type_cookie);
+ mValue = ReflowInput::DisplayInitFrameTypeEnter(mFrame, mState);
+}
+
+DR_init_type_cookie::~DR_init_type_cookie()
+{
+ MOZ_COUNT_DTOR(DR_init_type_cookie);
+ ReflowInput::DisplayInitFrameTypeExit(mFrame, mState, mValue);
+}
+
+struct DR_FrameTypeInfo;
+struct DR_FrameTreeNode;
+struct DR_Rule;
+
+struct DR_State
+{
+ DR_State();
+ ~DR_State();
+ void Init();
+ void AddFrameTypeInfo(nsIAtom* aFrameType,
+ const char* aFrameNameAbbrev,
+ const char* aFrameName);
+ DR_FrameTypeInfo* GetFrameTypeInfo(nsIAtom* aFrameType);
+ DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
+ void InitFrameTypeTable();
+ DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
+ const ReflowInput* aReflowInput);
+ void FindMatchingRule(DR_FrameTreeNode& aNode);
+ bool RuleMatches(DR_Rule& aRule,
+ DR_FrameTreeNode& aNode);
+ bool GetToken(FILE* aFile,
+ char* aBuf,
+ size_t aBufSize);
+ DR_Rule* ParseRule(FILE* aFile);
+ void ParseRulesFile();
+ void AddRule(nsTArray<DR_Rule*>& aRules,
+ DR_Rule& aRule);
+ bool IsWhiteSpace(int c);
+ bool GetNumber(char* aBuf,
+ int32_t& aNumber);
+ void PrettyUC(nscoord aSize,
+ char* aBuf,
+ int aBufSize);
+ void PrintMargin(const char* tag, const nsMargin* aMargin);
+ void DisplayFrameTypeInfo(nsIFrame* aFrame,
+ int32_t aIndent);
+ void DeleteTreeNode(DR_FrameTreeNode& aNode);
+
+ bool mInited;
+ bool mActive;
+ int32_t mCount;
+ int32_t mAssert;
+ int32_t mIndent;
+ bool mIndentUndisplayedFrames;
+ bool mDisplayPixelErrors;
+ nsTArray<DR_Rule*> mWildRules;
+ nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
+ // reflow specific state
+ nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
+};
+
+static DR_State *DR_state; // the one and only DR_State
+
+struct DR_RulePart
+{
+ explicit DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {}
+ void Destroy();
+
+ nsIAtom* mFrameType;
+ DR_RulePart* mNext;
+};
+
+void DR_RulePart::Destroy()
+{
+ if (mNext) {
+ mNext->Destroy();
+ }
+ delete this;
+}
+
+struct DR_Rule
+{
+ DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
+ MOZ_COUNT_CTOR(DR_Rule);
+ }
+ ~DR_Rule() {
+ if (mTarget) mTarget->Destroy();
+ MOZ_COUNT_DTOR(DR_Rule);
+ }
+ void AddPart(nsIAtom* aFrameType);
+
+ uint32_t mLength;
+ DR_RulePart* mTarget;
+ bool mDisplay;
+};
+
+void DR_Rule::AddPart(nsIAtom* aFrameType)
+{
+ DR_RulePart* newPart = new DR_RulePart(aFrameType);
+ newPart->mNext = mTarget;
+ mTarget = newPart;
+ mLength++;
+}
+
+struct DR_FrameTypeInfo
+{
+ DR_FrameTypeInfo(nsIAtom* aFrmeType, const char* aFrameNameAbbrev, const char* aFrameName);
+ ~DR_FrameTypeInfo() {
+ int32_t numElements;
+ numElements = mRules.Length();
+ for (int32_t i = numElements - 1; i >= 0; i--) {
+ delete mRules.ElementAt(i);
+ }
+ }
+
+ nsIAtom* mType;
+ char mNameAbbrev[16];
+ char mName[32];
+ nsTArray<DR_Rule*> mRules;
+private:
+ DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) = delete;
+};
+
+DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType,
+ const char* aFrameNameAbbrev,
+ const char* aFrameName)
+{
+ mType = aFrameType;
+ PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev));
+ PL_strncpyz(mName, aFrameName, sizeof(mName));
+}
+
+struct DR_FrameTreeNode
+{
+ DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0)
+ {
+ MOZ_COUNT_CTOR(DR_FrameTreeNode);
+ }
+
+ ~DR_FrameTreeNode()
+ {
+ MOZ_COUNT_DTOR(DR_FrameTreeNode);
+ }
+
+ nsIFrame* mFrame;
+ DR_FrameTreeNode* mParent;
+ bool mDisplay;
+ uint32_t mIndent;
+};
+
+// DR_State implementation
+
+DR_State::DR_State()
+: mInited(false), mActive(false), mCount(0), mAssert(-1), mIndent(0),
+ mIndentUndisplayedFrames(false), mDisplayPixelErrors(false)
+{
+ MOZ_COUNT_CTOR(DR_State);
+}
+
+void DR_State::Init()
+{
+ char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
+ int32_t num;
+ if (env) {
+ if (GetNumber(env, num))
+ mAssert = num;
+ else
+ printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
+ }
+
+ env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
+ if (env) {
+ if (GetNumber(env, num))
+ mIndent = num;
+ else
+ printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
+ }
+
+ env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
+ if (env) {
+ if (GetNumber(env, num))
+ mIndentUndisplayedFrames = num;
+ else
+ printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env);
+ }
+
+ env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
+ if (env) {
+ if (GetNumber(env, num))
+ mDisplayPixelErrors = num;
+ else
+ printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env);
+ }
+
+ InitFrameTypeTable();
+ ParseRulesFile();
+ mInited = true;
+}
+
+DR_State::~DR_State()
+{
+ MOZ_COUNT_DTOR(DR_State);
+ int32_t numElements, i;
+ numElements = mWildRules.Length();
+ for (i = numElements - 1; i >= 0; i--) {
+ delete mWildRules.ElementAt(i);
+ }
+ numElements = mFrameTreeLeaves.Length();
+ for (i = numElements - 1; i >= 0; i--) {
+ delete mFrameTreeLeaves.ElementAt(i);
+ }
+}
+
+bool DR_State::GetNumber(char* aBuf,
+ int32_t& aNumber)
+{
+ if (sscanf(aBuf, "%d", &aNumber) > 0)
+ return true;
+ else
+ return false;
+}
+
+bool DR_State::IsWhiteSpace(int c) {
+ return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
+}
+
+bool DR_State::GetToken(FILE* aFile,
+ char* aBuf,
+ size_t aBufSize)
+{
+ bool haveToken = false;
+ aBuf[0] = 0;
+ // get the 1st non whitespace char
+ int c = -1;
+ for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
+ }
+
+ if (c > 0) {
+ haveToken = true;
+ aBuf[0] = c;
+ // get everything up to the next whitespace char
+ size_t cX;
+ for (cX = 1; cX + 1 < aBufSize ; cX++) {
+ c = getc(aFile);
+ if (c < 0) { // EOF
+ ungetc(' ', aFile);
+ break;
+ }
+ else {
+ if (IsWhiteSpace(c)) {
+ break;
+ }
+ else {
+ aBuf[cX] = c;
+ }
+ }
+ }
+ aBuf[cX] = 0;
+ }
+ return haveToken;
+}
+
+DR_Rule* DR_State::ParseRule(FILE* aFile)
+{
+ char buf[128];
+ int32_t doDisplay;
+ DR_Rule* rule = nullptr;
+ while (GetToken(aFile, buf, sizeof(buf))) {
+ if (GetNumber(buf, doDisplay)) {
+ if (rule) {
+ rule->mDisplay = !!doDisplay;
+ break;
+ }
+ else {
+ printf("unexpected token - %s \n", buf);
+ }
+ }
+ else {
+ if (!rule) {
+ rule = new DR_Rule;
+ }
+ if (strcmp(buf, "*") == 0) {
+ rule->AddPart(nullptr);
+ }
+ else {
+ DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
+ if (info) {
+ rule->AddPart(info->mType);
+ }
+ else {
+ printf("invalid frame type - %s \n", buf);
+ }
+ }
+ }
+ }
+ return rule;
+}
+
+void DR_State::AddRule(nsTArray<DR_Rule*>& aRules,
+ DR_Rule& aRule)
+{
+ int32_t numRules = aRules.Length();
+ for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
+ DR_Rule* rule = aRules.ElementAt(ruleX);
+ NS_ASSERTION(rule, "program error");
+ if (aRule.mLength > rule->mLength) {
+ aRules.InsertElementAt(ruleX, &aRule);
+ return;
+ }
+ }
+ aRules.AppendElement(&aRule);
+}
+
+void DR_State::ParseRulesFile()
+{
+ char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
+ if (path) {
+ FILE* inFile = fopen(path, "r");
+ if (inFile) {
+ for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
+ if (rule->mTarget) {
+ nsIAtom* fType = rule->mTarget->mFrameType;
+ if (fType) {
+ DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
+ if (info) {
+ AddRule(info->mRules, *rule);
+ }
+ }
+ else {
+ AddRule(mWildRules, *rule);
+ }
+ mActive = true;
+ }
+ }
+
+ fclose(inFile);
+ }
+ }
+}
+
+
+void DR_State::AddFrameTypeInfo(nsIAtom* aFrameType,
+ const char* aFrameNameAbbrev,
+ const char* aFrameName)
+{
+ mFrameTypeTable.AppendElement(DR_FrameTypeInfo(aFrameType, aFrameNameAbbrev, aFrameName));
+}
+
+DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(nsIAtom* aFrameType)
+{
+ int32_t numEntries = mFrameTypeTable.Length();
+ NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
+ for (int32_t i = 0; i < numEntries; i++) {
+ DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
+ if (info.mType == aFrameType) {
+ return &info;
+ }
+ }
+ return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type
+}
+
+DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName)
+{
+ int32_t numEntries = mFrameTypeTable.Length();
+ NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
+ for (int32_t i = 0; i < numEntries; i++) {
+ DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
+ if ((strcmp(aFrameName, info.mName) == 0) || (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
+ return &info;
+ }
+ }
+ return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type
+}
+
+void DR_State::InitFrameTypeTable()
+{
+ AddFrameTypeInfo(nsGkAtoms::blockFrame, "block", "block");
+ AddFrameTypeInfo(nsGkAtoms::brFrame, "br", "br");
+ AddFrameTypeInfo(nsGkAtoms::bulletFrame, "bullet", "bullet");
+ AddFrameTypeInfo(nsGkAtoms::colorControlFrame, "color", "colorControl");
+ AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame, "button", "gfxButtonControl");
+ AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame, "HTMLbutton", "HTMLButtonControl");
+ AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame, "HTMLCanvas","HTMLCanvas");
+ AddFrameTypeInfo(nsGkAtoms::subDocumentFrame, "subdoc", "subDocument");
+ AddFrameTypeInfo(nsGkAtoms::imageFrame, "img", "image");
+ AddFrameTypeInfo(nsGkAtoms::inlineFrame, "inline", "inline");
+ AddFrameTypeInfo(nsGkAtoms::letterFrame, "letter", "letter");
+ AddFrameTypeInfo(nsGkAtoms::lineFrame, "line", "line");
+ AddFrameTypeInfo(nsGkAtoms::listControlFrame, "select", "select");
+ AddFrameTypeInfo(nsGkAtoms::objectFrame, "obj", "object");
+ AddFrameTypeInfo(nsGkAtoms::pageFrame, "page", "page");
+ AddFrameTypeInfo(nsGkAtoms::placeholderFrame, "place", "placeholder");
+ AddFrameTypeInfo(nsGkAtoms::canvasFrame, "canvas", "canvas");
+ AddFrameTypeInfo(nsGkAtoms::rootFrame, "root", "root");
+ AddFrameTypeInfo(nsGkAtoms::scrollFrame, "scroll", "scroll");
+ AddFrameTypeInfo(nsGkAtoms::tableCellFrame, "cell", "tableCell");
+ AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame, "bcCell", "bcTableCell");
+ AddFrameTypeInfo(nsGkAtoms::tableColFrame, "col", "tableCol");
+ AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame, "colG", "tableColGroup");
+ AddFrameTypeInfo(nsGkAtoms::tableFrame, "tbl", "table");
+ AddFrameTypeInfo(nsGkAtoms::tableWrapperFrame, "tblW", "tableWrapper");
+ AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame, "rowG", "tableRowGroup");
+ AddFrameTypeInfo(nsGkAtoms::tableRowFrame, "row", "tableRow");
+ AddFrameTypeInfo(nsGkAtoms::textInputFrame, "textCtl", "textInput");
+ AddFrameTypeInfo(nsGkAtoms::textFrame, "text", "text");
+ AddFrameTypeInfo(nsGkAtoms::viewportFrame, "VP", "viewport");
+#ifdef MOZ_XUL
+ AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel");
+ AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box");
+ AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider");
+ AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet");
+#endif
+ AddFrameTypeInfo(nullptr, "unknown", "unknown");
+}
+
+
+void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame,
+ int32_t aIndent)
+{
+ DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType());
+ if (frameTypeInfo) {
+ for (int32_t i = 0; i < aIndent; i++) {
+ printf(" ");
+ }
+ if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
+ if (aFrame) {
+ nsAutoString name;
+ aFrame->GetFrameName(name);
+ printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame);
+ }
+ else {
+ printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
+ }
+ }
+ else {
+ printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
+ }
+ }
+}
+
+bool DR_State::RuleMatches(DR_Rule& aRule,
+ DR_FrameTreeNode& aNode)
+{
+ NS_ASSERTION(aRule.mTarget, "program error");
+
+ DR_RulePart* rulePart;
+ DR_FrameTreeNode* parentNode;
+ for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
+ rulePart && parentNode;
+ rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
+ if (rulePart->mFrameType) {
+ if (parentNode->mFrame) {
+ if (rulePart->mFrameType != parentNode->mFrame->GetType()) {
+ return false;
+ }
+ }
+ else NS_ASSERTION(false, "program error");
+ }
+ // else wild card match
+ }
+ return true;
+}
+
+void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode)
+{
+ if (!aNode.mFrame) {
+ NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
+ return;
+ }
+
+ bool matchingRule = false;
+
+ DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->GetType());
+ NS_ASSERTION(info, "program error");
+ int32_t numRules = info->mRules.Length();
+ for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
+ DR_Rule* rule = info->mRules.ElementAt(ruleX);
+ if (rule && RuleMatches(*rule, aNode)) {
+ aNode.mDisplay = rule->mDisplay;
+ matchingRule = true;
+ break;
+ }
+ }
+ if (!matchingRule) {
+ int32_t numWildRules = mWildRules.Length();
+ for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) {
+ DR_Rule* rule = mWildRules.ElementAt(ruleX);
+ if (rule && RuleMatches(*rule, aNode)) {
+ aNode.mDisplay = rule->mDisplay;
+ break;
+ }
+ }
+ }
+}
+
+DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
+ const ReflowInput* aReflowInput)
+{
+ // find the frame of the parent reflow state (usually just the parent of aFrame)
+ nsIFrame* parentFrame;
+ if (aReflowInput) {
+ const ReflowInput* parentRI = aReflowInput->mParentReflowInput;
+ parentFrame = (parentRI) ? parentRI->mFrame : nullptr;
+ } else {
+ parentFrame = aFrame->GetParent();
+ }
+
+ // find the parent tree node leaf
+ DR_FrameTreeNode* parentNode = nullptr;
+
+ DR_FrameTreeNode* lastLeaf = nullptr;
+ if(mFrameTreeLeaves.Length())
+ lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
+ if (lastLeaf) {
+ for (parentNode = lastLeaf; parentNode && (parentNode->mFrame != parentFrame); parentNode = parentNode->mParent) {
+ }
+ }
+ DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
+ FindMatchingRule(*newNode);
+
+ newNode->mIndent = mIndent;
+ if (newNode->mDisplay || mIndentUndisplayedFrames) {
+ ++mIndent;
+ }
+
+ if (lastLeaf && (lastLeaf == parentNode)) {
+ mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1);
+ }
+ mFrameTreeLeaves.AppendElement(newNode);
+ mCount++;
+
+ return newNode;
+}
+
+void DR_State::PrettyUC(nscoord aSize,
+ char* aBuf,
+ int aBufSize)
+{
+ if (NS_UNCONSTRAINEDSIZE == aSize) {
+ strcpy(aBuf, "UC");
+ }
+ else {
+ if ((nscoord)0xdeadbeefU == aSize)
+ {
+ strcpy(aBuf, "deadbeef");
+ }
+ else {
+ snprintf(aBuf, aBufSize, "%d", aSize);
+ }
+ }
+}
+
+void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin)
+{
+ if (aMargin) {
+ char t[16], r[16], b[16], l[16];
+ PrettyUC(aMargin->top, t, 16);
+ PrettyUC(aMargin->right, r, 16);
+ PrettyUC(aMargin->bottom, b, 16);
+ PrettyUC(aMargin->left, l, 16);
+ printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
+ } else {
+ // use %p here for consistency with other null-pointer printouts
+ printf(" %s=%p", tag, (void*)aMargin);
+ }
+}
+
+void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode)
+{
+ mFrameTreeLeaves.RemoveElement(&aNode);
+ int32_t numLeaves = mFrameTreeLeaves.Length();
+ if ((0 == numLeaves) || (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
+ mFrameTreeLeaves.AppendElement(aNode.mParent);
+ }
+
+ if (aNode.mDisplay || mIndentUndisplayedFrames) {
+ --mIndent;
+ }
+ // delete the tree node
+ delete &aNode;
+}
+
+static void
+CheckPixelError(nscoord aSize,
+ int32_t aPixelToTwips)
+{
+ if (NS_UNCONSTRAINEDSIZE != aSize) {
+ if ((aSize % aPixelToTwips) > 0) {
+ printf("VALUE %d is not a whole pixel \n", aSize);
+ }
+ }
+}
+
+static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ const ReflowInput& aReflowInput,
+ DR_FrameTreeNode& aTreeNode,
+ bool aChanged)
+{
+ if (aTreeNode.mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
+
+ char width[16];
+ char height[16];
+
+ DR_state->PrettyUC(aReflowInput.AvailableWidth(), width, 16);
+ DR_state->PrettyUC(aReflowInput.AvailableHeight(), height, 16);
+ printf("Reflow a=%s,%s ", width, height);
+
+ DR_state->PrettyUC(aReflowInput.ComputedWidth(), width, 16);
+ DR_state->PrettyUC(aReflowInput.ComputedHeight(), height, 16);
+ printf("c=%s,%s ", width, height);
+
+ if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY)
+ printf("dirty ");
+
+ if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)
+ printf("dirty-children ");
+
+ if (aReflowInput.mFlags.mSpecialBSizeReflow)
+ printf("special-bsize ");
+
+ if (aReflowInput.IsHResize())
+ printf("h-resize ");
+
+ if (aReflowInput.IsVResize())
+ printf("v-resize ");
+
+ nsIFrame* inFlow = aFrame->GetPrevInFlow();
+ if (inFlow) {
+ printf("pif=%p ", (void*)inFlow);
+ }
+ inFlow = aFrame->GetNextInFlow();
+ if (inFlow) {
+ printf("nif=%p ", (void*)inFlow);
+ }
+ if (aChanged)
+ printf("CHANGED \n");
+ else
+ printf("cnt=%d \n", DR_state->mCount);
+ if (DR_state->mDisplayPixelErrors) {
+ int32_t p2t = aPresContext->AppUnitsPerDevPixel();
+ CheckPixelError(aReflowInput.AvailableWidth(), p2t);
+ CheckPixelError(aReflowInput.AvailableHeight(), p2t);
+ CheckPixelError(aReflowInput.ComputedWidth(), p2t);
+ CheckPixelError(aReflowInput.ComputedHeight(), p2t);
+ }
+ }
+}
+
+void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ const ReflowInput& aReflowInput)
+{
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ NS_ASSERTION(aFrame, "invalid call");
+
+ DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowInput);
+ if (treeNode) {
+ DisplayReflowEnterPrint(aPresContext, aFrame, aReflowInput, *treeNode, false);
+ }
+ return treeNode;
+}
+
+void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame)
+{
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ NS_ASSERTION(aFrame, "invalid call");
+
+ DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
+ if (treeNode && treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ printf("XULLayout\n");
+ }
+ return treeNode;
+}
+
+void* nsFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame,
+ const char* aType)
+{
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ NS_ASSERTION(aFrame, "invalid call");
+
+ DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
+ if (treeNode && treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ printf("Get%sWidth\n", aType);
+ }
+ return treeNode;
+}
+
+void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame,
+ const char* aType)
+{
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ NS_ASSERTION(aFrame, "invalid call");
+
+ DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
+ if (treeNode && treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ printf("Get%sSize\n", aType);
+ }
+ return treeNode;
+}
+
+void nsFrame::DisplayReflowExit(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ ReflowOutput& aMetrics,
+ nsReflowStatus aStatus,
+ void* aFrameTreeNode)
+{
+ if (!DR_state->mActive) return;
+
+ NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
+ if (!aFrameTreeNode) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+
+ char width[16];
+ char height[16];
+ char x[16];
+ char y[16];
+ DR_state->PrettyUC(aMetrics.Width(), width, 16);
+ DR_state->PrettyUC(aMetrics.Height(), height, 16);
+ printf("Reflow d=%s,%s", width, height);
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
+ printf(" status=0x%x", aStatus);
+ }
+ if (aFrame->HasOverflowAreas()) {
+ DR_state->PrettyUC(aMetrics.VisualOverflow().x, x, 16);
+ DR_state->PrettyUC(aMetrics.VisualOverflow().y, y, 16);
+ DR_state->PrettyUC(aMetrics.VisualOverflow().width, width, 16);
+ DR_state->PrettyUC(aMetrics.VisualOverflow().height, height, 16);
+ printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
+
+ nsRect storedOverflow = aFrame->GetVisualOverflowRect();
+ DR_state->PrettyUC(storedOverflow.x, x, 16);
+ DR_state->PrettyUC(storedOverflow.y, y, 16);
+ DR_state->PrettyUC(storedOverflow.width, width, 16);
+ DR_state->PrettyUC(storedOverflow.height, height, 16);
+ printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
+
+ DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16);
+ DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16);
+ DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16);
+ DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16);
+ printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
+
+ storedOverflow = aFrame->GetScrollableOverflowRect();
+ DR_state->PrettyUC(storedOverflow.x, x, 16);
+ DR_state->PrettyUC(storedOverflow.y, y, 16);
+ DR_state->PrettyUC(storedOverflow.width, width, 16);
+ DR_state->PrettyUC(storedOverflow.height, height, 16);
+ printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
+ }
+ printf("\n");
+ if (DR_state->mDisplayPixelErrors) {
+ int32_t p2t = aPresContext->AppUnitsPerDevPixel();
+ CheckPixelError(aMetrics.Width(), p2t);
+ CheckPixelError(aMetrics.Height(), p2t);
+ }
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+void nsFrame::DisplayLayoutExit(nsIFrame* aFrame,
+ void* aFrameTreeNode)
+{
+ if (!DR_state->mActive) return;
+
+ NS_ASSERTION(aFrame, "non-null frame required");
+ if (!aFrameTreeNode) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ nsRect rect = aFrame->GetRect();
+ printf("XULLayout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+void nsFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame,
+ const char* aType,
+ nscoord aResult,
+ void* aFrameTreeNode)
+{
+ if (!DR_state->mActive) return;
+
+ NS_ASSERTION(aFrame, "non-null frame required");
+ if (!aFrameTreeNode) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ char width[16];
+ DR_state->PrettyUC(aResult, width, 16);
+ printf("Get%sWidth=%s\n", aType, width);
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame,
+ const char* aType,
+ nsSize aResult,
+ void* aFrameTreeNode)
+{
+ if (!DR_state->mActive) return;
+
+ NS_ASSERTION(aFrame, "non-null frame required");
+ if (!aFrameTreeNode) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+
+ char width[16];
+ char height[16];
+ DR_state->PrettyUC(aResult.width, width, 16);
+ DR_state->PrettyUC(aResult.height, height, 16);
+ printf("Get%sSize=%s,%s\n", aType, width, height);
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+/* static */ void
+nsFrame::DisplayReflowStartup()
+{
+ DR_state = new DR_State();
+}
+
+/* static */ void
+nsFrame::DisplayReflowShutdown()
+{
+ delete DR_state;
+ DR_state = nullptr;
+}
+
+void DR_cookie::Change() const
+{
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
+ if (treeNode && treeNode->mDisplay) {
+ DisplayReflowEnterPrint(mPresContext, mFrame, mReflowInput, *treeNode, true);
+ }
+}
+
+/* static */ void*
+ReflowInput::DisplayInitConstraintsEnter(nsIFrame* aFrame,
+ ReflowInput* aState,
+ nscoord aContainingBlockWidth,
+ nscoord aContainingBlockHeight,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding)
+{
+ NS_PRECONDITION(aFrame, "non-null frame required");
+ NS_PRECONDITION(aState, "non-null state required");
+
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
+ if (treeNode && treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+
+ printf("InitConstraints parent=%p",
+ (void*)aState->mParentReflowInput);
+
+ char width[16];
+ char height[16];
+
+ DR_state->PrettyUC(aContainingBlockWidth, width, 16);
+ DR_state->PrettyUC(aContainingBlockHeight, height, 16);
+ printf(" cb=%s,%s", width, height);
+
+ DR_state->PrettyUC(aState->AvailableWidth(), width, 16);
+ DR_state->PrettyUC(aState->AvailableHeight(), height, 16);
+ printf(" as=%s,%s", width, height);
+
+ DR_state->PrintMargin("b", aBorder);
+ DR_state->PrintMargin("p", aPadding);
+ putchar('\n');
+ }
+ return treeNode;
+}
+
+/* static */ void
+ReflowInput::DisplayInitConstraintsExit(nsIFrame* aFrame,
+ ReflowInput* aState,
+ void* aValue)
+{
+ NS_PRECONDITION(aFrame, "non-null frame required");
+ NS_PRECONDITION(aState, "non-null state required");
+
+ if (!DR_state->mActive) return;
+ if (!aValue) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
+ DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16);
+ DR_state->PrettyUC(aState->ComputedWidth(), cw, 16);
+ DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16);
+ DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16);
+ DR_state->PrettyUC(aState->ComputedHeight(), ch, 16);
+ DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16);
+ printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)",
+ cmiw, cw, cmxw, cmih, ch, cmxh);
+ DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets());
+ putchar('\n');
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+
+/* static */ void*
+SizeComputationInput::DisplayInitOffsetsEnter(nsIFrame* aFrame,
+ SizeComputationInput* aState,
+ const LogicalSize& aPercentBasis,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding)
+{
+ NS_PRECONDITION(aFrame, "non-null frame required");
+ NS_PRECONDITION(aState, "non-null state required");
+
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ // aState is not necessarily a ReflowInput
+ DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
+ if (treeNode && treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+
+ char horizPctBasisStr[16];
+ char vertPctBasisStr[16];
+ WritingMode wm = aState->GetWritingMode();
+ DR_state->PrettyUC(aPercentBasis.ISize(wm), horizPctBasisStr, 16);
+ DR_state->PrettyUC(aPercentBasis.BSize(wm), vertPctBasisStr, 16);
+ printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr);
+
+ DR_state->PrintMargin("b", aBorder);
+ DR_state->PrintMargin("p", aPadding);
+ putchar('\n');
+ }
+ return treeNode;
+}
+
+/* static */ void
+SizeComputationInput::DisplayInitOffsetsExit(nsIFrame* aFrame,
+ SizeComputationInput* aState,
+ void* aValue)
+{
+ NS_PRECONDITION(aFrame, "non-null frame required");
+ NS_PRECONDITION(aState, "non-null state required");
+
+ if (!DR_state->mActive) return;
+ if (!aValue) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ printf("InitOffsets=");
+ DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin());
+ DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding());
+ DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding());
+ putchar('\n');
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+/* static */ void*
+ReflowInput::DisplayInitFrameTypeEnter(nsIFrame* aFrame,
+ ReflowInput* aState)
+{
+ NS_PRECONDITION(aFrame, "non-null frame required");
+ NS_PRECONDITION(aState, "non-null state required");
+
+ if (!DR_state->mInited) DR_state->Init();
+ if (!DR_state->mActive) return nullptr;
+
+ // we don't print anything here
+ return DR_state->CreateTreeNode(aFrame, aState);
+}
+
+/* static */ void
+ReflowInput::DisplayInitFrameTypeExit(nsIFrame* aFrame,
+ ReflowInput* aState,
+ void* aValue)
+{
+ NS_PRECONDITION(aFrame, "non-null frame required");
+ NS_PRECONDITION(aState, "non-null state required");
+
+ if (!DR_state->mActive) return;
+ if (!aValue) return;
+
+ DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
+ if (treeNode->mDisplay) {
+ DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
+ printf("InitFrameType");
+
+ const nsStyleDisplay *disp = aState->mStyleDisplay;
+
+ if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
+ printf(" out-of-flow");
+ if (aFrame->GetPrevInFlow())
+ printf(" prev-in-flow");
+ if (aFrame->IsAbsolutelyPositioned())
+ printf(" abspos");
+ if (aFrame->IsFloating())
+ printf(" float");
+
+ // This array must exactly match the StyleDisplay enum.
+ const char *const displayTypes[] = {
+ "none", "block", "inline", "inline-block", "list-item", "table",
+ "inline-table", "table-row-group", "table-column", "table-column",
+ "table-column-group", "table-header-group", "table-footer-group",
+ "table-row", "table-cell", "table-caption", "flex", "inline-flex",
+ "grid", "inline-grid", "ruby", "ruby-base", "ruby-base-container",
+ "ruby-text", "ruby-text-container", "contents", "-webkit-box",
+ "-webkit-inline-box", "box", "inline-box",
+#ifdef MOZ_XUL
+ "grid", "inline-grid", "grid-group", "grid-line", "stack",
+ "inline-stack", "deck", "groupbox", "popup",
+#endif
+ };
+ const uint32_t display = static_cast<uint32_t>(disp->mDisplay);
+ if (display >= ArrayLength(displayTypes))
+ printf(" display=%u", display);
+ else
+ printf(" display=%s", displayTypes[display]);
+
+ // This array must exactly match the NS_CSS_FRAME_TYPE constants.
+ const char *const cssFrameTypes[] = {
+ "unknown", "inline", "block", "floating", "absolute", "internal-table"
+ };
+ nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType);
+ bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType);
+ bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType);
+
+ if (bareType >= ArrayLength(cssFrameTypes)) {
+ printf(" result=type %u", bareType);
+ } else {
+ printf(" result=%s", cssFrameTypes[bareType]);
+ }
+ printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
+ }
+ DR_state->DeleteTreeNode(*treeNode);
+}
+
+#endif
+// End Display Reflow
+
+#endif
diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h
new file mode 100644
index 000000000..f996f57d7
--- /dev/null
+++ b/layout/generic/nsFrame.h
@@ -0,0 +1,920 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class of all rendering objects */
+
+#ifndef nsFrame_h___
+#define nsFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Likely.h"
+#include "nsBox.h"
+#include "mozilla/Logging.h"
+
+#include "nsIPresShell.h"
+#include "mozilla/ReflowInput.h"
+#include "nsHTMLParts.h"
+#include "nsISelectionDisplay.h"
+
+/**
+ * nsFrame logging constants. We redefine the nspr
+ * PRLogModuleInfo.level field to be a bitfield. Each bit controls a
+ * specific type of logging. Each logging operation has associated
+ * inline methods defined below.
+ */
+#define NS_FRAME_TRACE_CALLS 0x1
+#define NS_FRAME_TRACE_PUSH_PULL 0x2
+#define NS_FRAME_TRACE_CHILD_REFLOW 0x4
+#define NS_FRAME_TRACE_NEW_FRAMES 0x8
+
+#define NS_FRAME_LOG_TEST(_lm,_bit) (int(((mozilla::LogModule*)_lm)->Level()) & (_bit))
+
+#ifdef DEBUG
+#define NS_FRAME_LOG(_bit,_args) \
+ PR_BEGIN_MACRO \
+ if (NS_FRAME_LOG_TEST(nsFrame::sFrameLogModule,_bit)) { \
+ PR_LogPrint _args; \
+ } \
+ PR_END_MACRO
+#else
+#define NS_FRAME_LOG(_bit,_args)
+#endif
+
+// XXX Need to rework this so that logging is free when it's off
+#ifdef DEBUG
+#define NS_FRAME_TRACE_IN(_method) Trace(_method, true)
+
+#define NS_FRAME_TRACE_OUT(_method) Trace(_method, false)
+
+// XXX remove me
+#define NS_FRAME_TRACE_MSG(_bit,_args) \
+ PR_BEGIN_MACRO \
+ if (NS_FRAME_LOG_TEST(nsFrame::sFrameLogModule,_bit)) { \
+ TraceMsg _args; \
+ } \
+ PR_END_MACRO
+
+#define NS_FRAME_TRACE(_bit,_args) \
+ PR_BEGIN_MACRO \
+ if (NS_FRAME_LOG_TEST(nsFrame::sFrameLogModule,_bit)) { \
+ TraceMsg _args; \
+ } \
+ PR_END_MACRO
+
+#define NS_FRAME_TRACE_REFLOW_IN(_method) Trace(_method, true)
+
+#define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) \
+ Trace(_method, false, _status)
+
+#else
+#define NS_FRAME_TRACE(_bits,_args)
+#define NS_FRAME_TRACE_IN(_method)
+#define NS_FRAME_TRACE_OUT(_method)
+#define NS_FRAME_TRACE_MSG(_bits,_args)
+#define NS_FRAME_TRACE_REFLOW_IN(_method)
+#define NS_FRAME_TRACE_REFLOW_OUT(_method, _status)
+#endif
+
+// Frame allocation boilerplate macros. Every subclass of nsFrame must
+// either use NS_{DECL,IMPL}_FRAMEARENA_HELPERS pair for allocating
+// memory correctly, or use NS_DECL_ABSTRACT_FRAME to declare a frame
+// class abstract and stop it from being instantiated. If a frame class
+// without its own operator new and GetFrameId gets instantiated, the
+// per-frame recycler lists in nsPresArena will not work correctly,
+// with potentially catastrophic consequences (not enough memory is
+// allocated for a frame object).
+
+#define NS_DECL_FRAMEARENA_HELPERS \
+ void* operator new(size_t, nsIPresShell*) MOZ_MUST_OVERRIDE; \
+ virtual nsQueryFrame::FrameIID GetFrameId() override MOZ_MUST_OVERRIDE;
+
+#define NS_IMPL_FRAMEARENA_HELPERS(class) \
+ void* class::operator new(size_t sz, nsIPresShell* aShell) \
+ { return aShell->AllocateFrame(nsQueryFrame::class##_id, sz); } \
+ nsQueryFrame::FrameIID class::GetFrameId() \
+ { return nsQueryFrame::class##_id; }
+
+#define NS_DECL_ABSTRACT_FRAME(class) \
+ void* operator new(size_t, nsIPresShell*) MOZ_MUST_OVERRIDE = delete; \
+ virtual nsQueryFrame::FrameIID GetFrameId() override MOZ_MUST_OVERRIDE = 0;
+
+//----------------------------------------------------------------------
+
+struct nsBoxLayoutMetrics;
+struct nsRect;
+
+/**
+ * Implementation of a simple frame that's not splittable and has no
+ * child frames.
+ *
+ * Sets the NS_FRAME_SYNCHRONIZE_FRAME_AND_VIEW bit, so the default
+ * behavior is to keep the frame and view position and size in sync.
+ */
+class nsFrame : public nsBox
+{
+public:
+ /**
+ * Create a new "empty" frame that maps a given piece of content into a
+ * 0,0 area.
+ */
+ friend nsIFrame* NS_NewEmptyFrame(nsIPresShell* aShell,
+ nsStyleContext* aContext);
+
+private:
+ // Left undefined; nsFrame objects are never allocated from the heap.
+ void* operator new(size_t sz) CPP_THROW_NEW;
+
+protected:
+ // Overridden to prevent the global delete from being called, since
+ // the memory came out of an arena instead of the heap.
+ //
+ // Ideally this would be private and undefined, like the normal
+ // operator new. Unfortunately, the C++ standard requires an
+ // overridden operator delete to be accessible to any subclass that
+ // defines a virtual destructor, so we can only make it protected;
+ // worse, some C++ compilers will synthesize calls to this function
+ // from the "deleting destructors" that they emit in case of
+ // delete-expressions, so it can't even be undefined.
+ void operator delete(void* aPtr, size_t sz);
+
+public:
+
+ // nsQueryFrame
+ NS_DECL_QUERYFRAME
+ void* operator new(size_t, nsIPresShell*) MOZ_MUST_OVERRIDE;
+ virtual nsQueryFrame::FrameIID GetFrameId() MOZ_MUST_OVERRIDE;
+
+ // nsIFrame
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual nsStyleContext* GetAdditionalStyleContext(int32_t aIndex) const override;
+ virtual void SetAdditionalStyleContext(int32_t aIndex,
+ nsStyleContext* aStyleContext) override;
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+ virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
+ virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
+
+ virtual nsresult HandleEvent(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) override;
+ virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
+ nsIContent** aContent) override;
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor) override;
+
+ virtual nsresult GetPointFromOffset(int32_t inOffset,
+ nsPoint* outPoint) override;
+ virtual nsresult GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aOutRect) override;
+
+ virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
+ bool inHint,
+ int32_t* outFrameContentOffset,
+ nsIFrame** outChildFrame) override;
+
+ static nsresult GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
+ nsPeekOffsetStruct *aPos,
+ nsIFrame *aBlockFrame,
+ int32_t aLineStart,
+ int8_t aOutSideLimit
+ );
+
+ virtual nsresult CharacterDataChanged(CharacterDataChangeInfo* aInfo) override;
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+ virtual nsSplittableType GetSplittableType() const override;
+ virtual nsIFrame* GetPrevContinuation() const override;
+ virtual void SetPrevContinuation(nsIFrame*) override;
+ virtual nsIFrame* GetNextContinuation() const override;
+ virtual void SetNextContinuation(nsIFrame*) override;
+ virtual nsIFrame* GetPrevInFlowVirtual() const override;
+ virtual void SetPrevInFlow(nsIFrame*) override;
+ virtual nsIFrame* GetNextInFlowVirtual() const override;
+ virtual void SetNextInFlow(nsIFrame*) override;
+ virtual nsIAtom* GetType() const override;
+
+ virtual nsresult IsSelectable(bool* aIsSelectable,
+ mozilla::StyleUserSelect* aSelectStyle) const override;
+
+ virtual nsresult GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) override;
+
+ virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override;
+ virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) override;
+ virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
+ int32_t* aOffset, PeekWordState *aState) override;
+ /**
+ * Check whether we should break at a boundary between punctuation and
+ * non-punctuation. Only call it at a punctuation boundary
+ * (i.e. exactly one of the previous and next characters are punctuation).
+ * @param aForward true if we're moving forward in content order
+ * @param aPunctAfter true if the next character is punctuation
+ * @param aWhitespaceAfter true if the next character is whitespace
+ */
+ bool BreakWordBetweenPunctuation(const PeekWordState* aState,
+ bool aForward,
+ bool aPunctAfter, bool aWhitespaceAfter,
+ bool aIsKeyboardSelect);
+
+ virtual nsresult CheckVisibility(nsPresContext* aContext, int32_t aStartIndex, int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *_retval) override;
+
+ virtual nsresult GetOffsets(int32_t &aStart, int32_t &aEnd) const override;
+ virtual void ChildIsDirty(nsIFrame* aChild) override;
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ virtual nsStyleContext* GetParentStyleContext(nsIFrame** aProviderFrame) const override {
+ return DoGetParentStyleContext(aProviderFrame);
+ }
+
+ /**
+ * Do the work for getting the parent style context frame so that
+ * other frame's |GetParentStyleContext| methods can call this
+ * method on *another* frame. (This function handles out-of-flow
+ * frames by using the frame manager's placeholder map and it also
+ * handles block-within-inline and generated content wrappers.)
+ *
+ * @param aProviderFrame (out) the frame associated with the returned value
+ * or null if the style context is for display:contents content.
+ * @return The style context that should be the parent of this frame's
+ * style context. Null is permitted, and means that this frame's
+ * style context should be the root of the style context tree.
+ */
+ nsStyleContext* DoGetParentStyleContext(nsIFrame** aProviderFrame) const;
+
+ virtual bool IsEmpty() override;
+ virtual bool IsSelfEmpty() override;
+
+ virtual void MarkIntrinsicISizesDirty() override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+ virtual IntrinsicISizeOffsetData IntrinsicISizeOffsets() override;
+ virtual mozilla::IntrinsicSize GetIntrinsicSize() override;
+ virtual nsSize GetIntrinsicRatio() override;
+
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ /**
+ * Calculate the used values for 'width' and 'height' for a replaced element.
+ * http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
+ */
+ mozilla::LogicalSize
+ ComputeSizeWithIntrinsicDimensions(
+ nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWM,
+ const mozilla::IntrinsicSize& aIntrinsicSize,
+ nsSize aIntrinsicRatio,
+ const mozilla::LogicalSize& aCBSize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags);
+
+ // Compute tight bounds assuming this frame honours its border, background
+ // and outline, its children's tight bounds, and nothing else.
+ nsRect ComputeSimpleTightBounds(mozilla::gfx::DrawTarget* aDrawTarget) const;
+
+ /**
+ * A helper, used by |nsFrame::ComputeSize| (for frames that need to
+ * override only this part of ComputeSize), that computes the size
+ * that should be returned when 'width', 'height', and
+ * min/max-width/height are all 'auto' or equivalent.
+ *
+ * In general, frames that can accept any computed width/height should
+ * override only ComputeAutoSize, and frames that cannot do so need to
+ * override ComputeSize to enforce their width/height invariants.
+ *
+ * Implementations may optimize by returning a garbage width if
+ * StylePosition()->mWidth.GetUnit() != eStyleUnit_Auto, and
+ * likewise for height, since in such cases the result is guaranteed
+ * to be unused.
+ */
+ virtual mozilla::LogicalSize
+ ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags);
+
+ /**
+ * Utility function for ComputeAutoSize implementations. Return
+ * max(GetMinISize(), min(aISizeInCB, GetPrefISize()))
+ */
+ nscoord ShrinkWidthToFit(nsRenderingContext* aRenderingContext,
+ nscoord aISizeInCB,
+ ComputeSizeFlags aFlags);
+
+ /**
+ * Calculates the size of this frame after reflowing (calling Reflow on, and
+ * updating the size and position of) its children, as necessary. The
+ * calculated size is returned to the caller via the ReflowOutput
+ * outparam. (The caller is responsible for setting the actual size and
+ * position of this frame.)
+ *
+ * A frame's children must _all_ be reflowed if the frame is dirty (the
+ * NS_FRAME_IS_DIRTY bit is set on it). Otherwise, individual children
+ * must be reflowed if they are dirty or have the NS_FRAME_HAS_DIRTY_CHILDREN
+ * bit set on them. Otherwise, whether children need to be reflowed depends
+ * on the frame's type (it's up to individual Reflow methods), and on what
+ * has changed. For example, a change in the width of the frame may require
+ * all of its children to be reflowed (even those without dirty bits set on
+ * them), whereas a change in its height might not.
+ * (ReflowInput::ShouldReflowAllKids may be helpful in deciding whether
+ * to reflow all the children, but for some frame types it might result in
+ * over-reflow.)
+ *
+ * Note: if it's only the overflow rect(s) of a frame that need to be
+ * updated, then UpdateOverflow should be called instead of Reflow.
+ */
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ virtual void DidReflow(nsPresContext* aPresContext,
+ const ReflowInput* aReflowInput,
+ nsDidReflowStatus aStatus) override;
+
+ /**
+ * NOTE: aStatus is assumed to be already-initialized. The reflow statuses of
+ * any reflowed absolute children will be merged into aStatus; aside from
+ * that, this method won't modify aStatus.
+ */
+ void ReflowAbsoluteFrames(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ bool aConstrainBSize = true);
+ void FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ bool aConstrainBSize = true);
+
+ /*
+ * If this frame is dirty, marks all absolutely-positioned children of this
+ * frame dirty. If this frame isn't dirty, or if there are no
+ * absolutely-positioned children, does nothing.
+ *
+ * It's necessary to use PushDirtyBitToAbsoluteFrames() when you plan to
+ * reflow this frame's absolutely-positioned children after the dirty bit on
+ * this frame has already been cleared, which prevents ReflowInput from
+ * propagating the dirty bit normally. This situation generally only arises
+ * when a multipass layout algorithm is used.
+ */
+ void PushDirtyBitToAbsoluteFrames();
+
+ virtual bool CanContinueTextRun() const override;
+
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
+
+ virtual void UnionChildOverflow(nsOverflowAreas& aOverflowAreas) override;
+
+ // Selection Methods
+
+ NS_IMETHOD HandlePress(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus);
+
+ NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus,
+ bool aControlHeld);
+
+ NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus);
+
+ NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus);
+
+ enum { SELECT_ACCUMULATE = 0x01 };
+
+ nsresult PeekBackwardAndForward(nsSelectionAmount aAmountBack,
+ nsSelectionAmount aAmountForward,
+ int32_t aStartPos,
+ bool aJumpLines,
+ uint32_t aSelectFlags);
+
+ nsresult SelectByTypeAtPoint(nsPresContext* aPresContext,
+ const nsPoint& aPoint,
+ nsSelectionAmount aBeginAmountType,
+ nsSelectionAmount aEndAmountType,
+ uint32_t aSelectFlags);
+
+ // Helper for GetContentAndOffsetsFromPoint; calculation of content offsets
+ // in this function assumes there is no child frame that can be targeted.
+ virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint);
+
+ // Box layout methods
+ virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsSize GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nscoord GetXULFlex() override;
+ virtual nscoord GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState) override;
+
+ // We compute and store the HTML content's overflow area. So don't
+ // try to compute it in the box code.
+ virtual bool ComputesOwnOverflowArea() override { return true; }
+
+ //--------------------------------------------------
+ // Additional methods
+
+ // Helper function that tests if the frame tree is too deep; if it is
+ // it marks the frame as "unflowable", zeroes out the metrics, sets
+ // the reflow status, and returns true. Otherwise, the frame is
+ // unmarked "unflowable" and the metrics and reflow status are not
+ // touched and false is returned.
+ bool IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus);
+
+ // Incorporate the child overflow areas into aOverflowAreas.
+ // If the child does not have a overflow, use the child area.
+ void ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
+ nsIFrame* aChildFrame);
+
+ /**
+ * @return true if we should avoid a page/column break in this frame.
+ */
+ bool ShouldAvoidBreakInside(const ReflowInput& aReflowInput) const {
+ return !aReflowInput.mFlags.mIsTopOfPage &&
+ NS_STYLE_PAGE_BREAK_AVOID == StyleDisplay()->mBreakInside &&
+ !GetPrevInFlow();
+ }
+
+#ifdef DEBUG
+ /**
+ * Tracing method that writes a method enter/exit routine to the
+ * nspr log using the nsIFrame log module. The tracing is only
+ * done when the NS_FRAME_TRACE_CALLS bit is set in the log module's
+ * level field.
+ */
+ void Trace(const char* aMethod, bool aEnter);
+ void Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus);
+ void TraceMsg(const char* fmt, ...);
+
+ // Helper function that verifies that each frame in the list has the
+ // NS_FRAME_IS_DIRTY bit set
+ static void VerifyDirtyBitSet(const nsFrameList& aFrameList);
+
+ static void XMLQuote(nsString& aString);
+
+ /**
+ * Dump out the "base classes" regression data. This should dump
+ * out the interior data, not the "frame" XML container. And it
+ * should call the base classes same named method before doing
+ * anything specific in a derived class. This means that derived
+ * classes need not override DumpRegressionData unless they need
+ * some custom behavior that requires changing how the outer "frame"
+ * XML container is dumped.
+ */
+ virtual void DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent);
+
+ // Display Reflow Debugging
+ static void* DisplayReflowEnter(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ const ReflowInput& aReflowInput);
+ static void* DisplayLayoutEnter(nsIFrame* aFrame);
+ static void* DisplayIntrinsicISizeEnter(nsIFrame* aFrame,
+ const char* aType);
+ static void* DisplayIntrinsicSizeEnter(nsIFrame* aFrame,
+ const char* aType);
+ static void DisplayReflowExit(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ ReflowOutput& aMetrics,
+ uint32_t aStatus,
+ void* aFrameTreeNode);
+ static void DisplayLayoutExit(nsIFrame* aFrame,
+ void* aFrameTreeNode);
+ static void DisplayIntrinsicISizeExit(nsIFrame* aFrame,
+ const char* aType,
+ nscoord aResult,
+ void* aFrameTreeNode);
+ static void DisplayIntrinsicSizeExit(nsIFrame* aFrame,
+ const char* aType,
+ nsSize aResult,
+ void* aFrameTreeNode);
+
+ static void DisplayReflowStartup();
+ static void DisplayReflowShutdown();
+#endif
+
+ /**
+ * Adds display items for standard CSS background if necessary.
+ * Does not check IsVisibleForPainting.
+ * @param aForceBackground draw the background even if the frame
+ * background style appears to have no background --- this is useful
+ * for frames that might receive a propagated background via
+ * nsCSSRendering::FindBackground
+ * @return whether a themed background item was created.
+ */
+ bool DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists,
+ bool aForceBackground);
+ /**
+ * Adds display items for standard CSS borders, background and outline for
+ * for this frame, as necessary. Checks IsVisibleForPainting and won't
+ * display anything if the frame is not visible.
+ * @param aForceBackground draw the background even if the frame
+ * background style appears to have no background --- this is useful
+ * for frames that might receive a propagated background via
+ * nsCSSRendering::FindBackground
+ */
+ void DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists,
+ bool aForceBackground = false);
+ /**
+ * Add a display item for the CSS outline. Does not check visibility.
+ */
+ void DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists);
+ /**
+ * Add a display item for the CSS outline, after calling
+ * IsVisibleForPainting to confirm we are visible.
+ */
+ void DisplayOutline(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists);
+
+ /**
+ * Adjust the given parent frame to the right style context parent frame for
+ * the child, given the pseudo-type of the prospective child. This handles
+ * things like walking out of table pseudos and so forth.
+ *
+ * @param aProspectiveParent what GetParent() on the child returns.
+ * Must not be null.
+ * @param aChildPseudo the child's pseudo type, if any.
+ */
+ static nsIFrame*
+ CorrectStyleParentFrame(nsIFrame* aProspectiveParent, nsIAtom* aChildPseudo);
+
+protected:
+ // Protected constructor and destructor
+ explicit nsFrame(nsStyleContext* aContext);
+ virtual ~nsFrame();
+
+ /**
+ * To be called by |BuildDisplayLists| of this class or derived classes to add
+ * a translucent overlay if this frame's content is selected.
+ * @param aContentType an nsISelectionDisplay DISPLAY_ constant identifying
+ * which kind of content this is for
+ */
+ void DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
+ nsDisplayList* aList, uint16_t aContentType = nsISelectionDisplay::DISPLAY_FRAMES);
+
+ int16_t DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn = false);
+
+ // Style post processing hook
+ virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
+
+public:
+ //given a frame five me the first/last leaf available
+ //XXX Robert O'Callahan wants to move these elsewhere
+ static void GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame);
+ static void GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame);
+
+ // Return the line number of the aFrame, and (optionally) the containing block
+ // frame.
+ // If aScrollLock is true, don't break outside scrollframes when looking for a
+ // containing block frame.
+ static int32_t GetLineNumber(nsIFrame *aFrame,
+ bool aLockScroll,
+ nsIFrame** aContainingBlock = nullptr);
+
+ /**
+ * Returns true if aFrame should apply overflow clipping.
+ */
+ static bool ShouldApplyOverflowClipping(const nsIFrame* aFrame,
+ const nsStyleDisplay* aDisp)
+ {
+ // clip overflow:-moz-hidden-unscrollable, except for nsListControlFrame,
+ // which is an nsHTMLScrollFrame.
+ if (MOZ_UNLIKELY(aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP &&
+ aFrame->GetType() != nsGkAtoms::listControlFrame)) {
+ return true;
+ }
+
+ // and overflow:hidden that we should interpret as -moz-hidden-unscrollable
+ if (aDisp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN &&
+ aDisp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
+ // REVIEW: these are the frame types that set up clipping.
+ nsIAtom* type = aFrame->GetType();
+ if (type == nsGkAtoms::tableFrame ||
+ type == nsGkAtoms::tableCellFrame ||
+ type == nsGkAtoms::bcTableCellFrame ||
+ type == nsGkAtoms::svgOuterSVGFrame ||
+ type == nsGkAtoms::svgInnerSVGFrame ||
+ type == nsGkAtoms::svgForeignObjectFrame) {
+ return true;
+ }
+ if (aFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
+ if (type == nsGkAtoms::textInputFrame) {
+ // It always has an anonymous scroll frame that handles any overflow.
+ return false;
+ }
+ return true;
+ }
+ }
+
+ if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
+ return false;
+ }
+
+ // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW
+ // set, then we want to clip our overflow.
+ return
+ (aFrame->GetStateBits() & NS_BLOCK_CLIP_PAGINATED_OVERFLOW) != 0 &&
+ aFrame->PresContext()->IsPaginated() &&
+ aFrame->GetType() == nsGkAtoms::blockFrame;
+ }
+
+ virtual nsILineIterator* GetLineIterator() override;
+
+protected:
+
+ // Test if we are selecting a table object:
+ // Most table/cell selection requires that Ctrl (Cmd on Mac) key is down
+ // during a mouse click or drag. Exception is using Shift+click when
+ // already in "table/cell selection mode" to extend a block selection
+ // Get the parent content node and offset of the frame
+ // of the enclosing cell or table (if not inside a cell)
+ // aTarget tells us what table element to select (currently only cell and table supported)
+ // (enums for this are defined in nsIFrame.h)
+ NS_IMETHOD GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
+ nsIPresShell* aPresShell,
+ mozilla::WidgetMouseEvent* aMouseEvent,
+ nsIContent** aParentContent,
+ int32_t* aContentOffset,
+ int32_t* aTarget);
+
+ // Fills aCursor with the appropriate information from ui
+ static void FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
+ nsIFrame::Cursor& aCursor);
+ NS_IMETHOD DoXULLayout(nsBoxLayoutState& aBoxLayoutState) override;
+
+#ifdef DEBUG_LAYOUT
+ virtual void GetBoxName(nsAutoString& aName) override;
+#endif
+
+ nsBoxLayoutMetrics* BoxMetrics() const;
+
+ // Fire DOM event. If no aContent argument use frame's mContent.
+ void FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent = nullptr);
+
+private:
+ void BoxReflow(nsBoxLayoutState& aState,
+ nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ nsRenderingContext* aRenderingContext,
+ nscoord aX,
+ nscoord aY,
+ nscoord aWidth,
+ nscoord aHeight,
+ bool aMoveFrame = true);
+
+ NS_IMETHODIMP RefreshSizeCache(nsBoxLayoutState& aState);
+
+ // Returns true if this frame has any kind of CSS animations.
+ bool HasCSSAnimations();
+
+ // Returns true if this frame has any kind of CSS transitions.
+ bool HasCSSTransitions();
+
+#ifdef DEBUG_FRAME_DUMP
+public:
+ /**
+ * Get a printable from of the name of the frame type.
+ * XXX This should be eliminated and we use GetType() instead...
+ */
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+ nsresult MakeFrameName(const nsAString& aKind, nsAString& aResult) const;
+ // Helper function to return the index in parent of the frame's content
+ // object. Returns -1 on error or if the frame doesn't have a content object
+ static int32_t ContentIndexInContainer(const nsIFrame* aFrame);
+#endif
+
+#ifdef DEBUG
+public:
+ /**
+ * Return the state bits that are relevant to regression tests (that
+ * is, those bits which indicate a real difference when they differ
+ */
+ virtual nsFrameState GetDebugStateBits() const override;
+ /**
+ * Called to dump out regression data that describes the layout
+ * of the frame and its children, and so on. The format of the
+ * data is dictated to be XML (using a specific DTD); the
+ * specific kind of data dumped is up to the frame itself, with
+ * the caveat that some base types are defined.
+ * For more information, see XXX.
+ */
+ virtual nsresult DumpRegressionData(nsPresContext* aPresContext,
+ FILE* out, int32_t aIndent) override;
+
+ /**
+ * See if style tree verification is enabled. To enable style tree
+ * verification add "styleverifytree:1" to your MOZ_LOG
+ * environment variable (any non-zero debug level will work). Or,
+ * call SetVerifyStyleTreeEnable with true.
+ */
+ static bool GetVerifyStyleTreeEnable();
+
+ /**
+ * Set the verify-style-tree enable flag.
+ */
+ static void SetVerifyStyleTreeEnable(bool aEnabled);
+
+ static mozilla::LazyLogModule sFrameLogModule;
+
+ // Show frame borders when rendering
+ static void ShowFrameBorders(bool aEnable);
+ static bool GetShowFrameBorders();
+
+ // Show frame border of event target
+ static void ShowEventTargetFrameBorder(bool aEnable);
+ static bool GetShowEventTargetFrameBorder();
+
+#endif
+
+public:
+
+ static void PrintDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsDisplayList& aList,
+ bool aDumpHtml = false)
+ {
+ std::stringstream ss;
+ PrintDisplayList(aBuilder, aList, ss, aDumpHtml);
+ fprintf_stderr(stderr, "%s", ss.str().c_str());
+ }
+ static void PrintDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsDisplayList& aList,
+ std::stringstream& aStream,
+ bool aDumpHtml = false);
+ static void PrintDisplayListSet(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aList,
+ std::stringstream& aStream,
+ bool aDumpHtml = false);
+
+};
+
+// Start Display Reflow Debugging
+#ifdef DEBUG
+
+ struct DR_cookie {
+ DR_cookie(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ const mozilla::ReflowInput& aReflowInput,
+ mozilla::ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus);
+ ~DR_cookie();
+ void Change() const;
+
+ nsPresContext* mPresContext;
+ nsIFrame* mFrame;
+ const mozilla::ReflowInput& mReflowInput;
+ mozilla::ReflowOutput& mMetrics;
+ nsReflowStatus& mStatus;
+ void* mValue;
+ };
+
+ struct DR_layout_cookie {
+ explicit DR_layout_cookie(nsIFrame* aFrame);
+ ~DR_layout_cookie();
+
+ nsIFrame* mFrame;
+ void* mValue;
+ };
+
+ struct DR_intrinsic_width_cookie {
+ DR_intrinsic_width_cookie(nsIFrame* aFrame, const char* aType,
+ nscoord& aResult);
+ ~DR_intrinsic_width_cookie();
+
+ nsIFrame* mFrame;
+ const char* mType;
+ nscoord& mResult;
+ void* mValue;
+ };
+
+ struct DR_intrinsic_size_cookie {
+ DR_intrinsic_size_cookie(nsIFrame* aFrame, const char* aType,
+ nsSize& aResult);
+ ~DR_intrinsic_size_cookie();
+
+ nsIFrame* mFrame;
+ const char* mType;
+ nsSize& mResult;
+ void* mValue;
+ };
+
+ struct DR_init_constraints_cookie {
+ DR_init_constraints_cookie(nsIFrame* aFrame, mozilla::ReflowInput* aState,
+ nscoord aCBWidth, nscoord aCBHeight,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding);
+ ~DR_init_constraints_cookie();
+
+ nsIFrame* mFrame;
+ mozilla::ReflowInput* mState;
+ void* mValue;
+ };
+
+ struct DR_init_offsets_cookie {
+ DR_init_offsets_cookie(nsIFrame* aFrame, mozilla::SizeComputationInput* aState,
+ const mozilla::LogicalSize& aPercentBasis,
+ const nsMargin* aBorder,
+ const nsMargin* aPadding);
+ ~DR_init_offsets_cookie();
+
+ nsIFrame* mFrame;
+ mozilla::SizeComputationInput* mState;
+ void* mValue;
+ };
+
+ struct DR_init_type_cookie {
+ DR_init_type_cookie(nsIFrame* aFrame, mozilla::ReflowInput* aState);
+ ~DR_init_type_cookie();
+
+ nsIFrame* mFrame;
+ mozilla::ReflowInput* mState;
+ void* mValue;
+ };
+
+#define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status) \
+ DR_cookie dr_cookie(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status);
+#define DISPLAY_REFLOW_CHANGE() \
+ dr_cookie.Change();
+#define DISPLAY_LAYOUT(dr_frame) \
+ DR_layout_cookie dr_cookie(dr_frame);
+#define DISPLAY_MIN_WIDTH(dr_frame, dr_result) \
+ DR_intrinsic_width_cookie dr_cookie(dr_frame, "Min", dr_result)
+#define DISPLAY_PREF_WIDTH(dr_frame, dr_result) \
+ DR_intrinsic_width_cookie dr_cookie(dr_frame, "Pref", dr_result)
+#define DISPLAY_PREF_SIZE(dr_frame, dr_result) \
+ DR_intrinsic_size_cookie dr_cookie(dr_frame, "Pref", dr_result)
+#define DISPLAY_MIN_SIZE(dr_frame, dr_result) \
+ DR_intrinsic_size_cookie dr_cookie(dr_frame, "Min", dr_result)
+#define DISPLAY_MAX_SIZE(dr_frame, dr_result) \
+ DR_intrinsic_size_cookie dr_cookie(dr_frame, "Max", dr_result)
+#define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, \
+ dr_bdr, dr_pad) \
+ DR_init_constraints_cookie dr_cookie(dr_frame, dr_state, dr_cbw, dr_cbh, \
+ dr_bdr, dr_pad)
+#define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_bdr, dr_pad) \
+ DR_init_offsets_cookie dr_cookie(dr_frame, dr_state, dr_pb, dr_bdr, dr_pad)
+#define DISPLAY_INIT_TYPE(dr_frame, dr_result) \
+ DR_init_type_cookie dr_cookie(dr_frame, dr_result)
+
+#else
+
+#define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status)
+#define DISPLAY_REFLOW_CHANGE()
+#define DISPLAY_LAYOUT(dr_frame) PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_MIN_WIDTH(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_PREF_WIDTH(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_PREF_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_MIN_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_MAX_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, \
+ dr_bdr, dr_pad) \
+ PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_bdr, dr_pad) \
+ PR_BEGIN_MACRO PR_END_MACRO
+#define DISPLAY_INIT_TYPE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
+
+#endif
+// End Display Reflow Debugging
+
+// similar to NS_ENSURE_TRUE but with no return value
+#define ENSURE_TRUE(x) \
+ PR_BEGIN_MACRO \
+ if (!(x)) { \
+ NS_WARNING("ENSURE_TRUE(" #x ") failed"); \
+ return; \
+ } \
+ PR_END_MACRO
+#endif /* nsFrame_h___ */
diff --git a/layout/generic/nsFrameIdList.h b/layout/generic/nsFrameIdList.h
new file mode 100644
index 000000000..6be4309b8
--- /dev/null
+++ b/layout/generic/nsFrameIdList.h
@@ -0,0 +1,180 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+FRAME_ID(BRFrame)
+FRAME_ID(DetailsFrame)
+FRAME_ID(nsAutoRepeatBoxFrame)
+FRAME_ID(nsBCTableCellFrame)
+FRAME_ID(nsBackdropFrame)
+FRAME_ID(nsBlockFrame)
+FRAME_ID(nsBox)
+FRAME_ID(nsBoxFrame)
+FRAME_ID(nsBulletFrame)
+FRAME_ID(nsButtonBoxFrame)
+FRAME_ID(nsCanvasFrame)
+FRAME_ID(nsColorControlFrame)
+FRAME_ID(nsColumnSetFrame)
+FRAME_ID(nsComboboxControlFrame)
+FRAME_ID(nsComboboxDisplayFrame)
+FRAME_ID(nsContainerFrame)
+FRAME_ID(nsContinuingTextFrame)
+FRAME_ID(nsDateTimeControlFrame)
+FRAME_ID(nsDeckFrame)
+FRAME_ID(nsDocElementBoxFrame)
+FRAME_ID(nsFieldSetFrame)
+FRAME_ID(nsFileControlFrame)
+FRAME_ID(nsFirstLetterFrame)
+FRAME_ID(nsFirstLineFrame)
+FRAME_ID(nsFlexContainerFrame)
+FRAME_ID(nsFormControlFrame)
+FRAME_ID(nsFrame)
+FRAME_ID(nsGfxButtonControlFrame)
+FRAME_ID(nsGfxCheckboxControlFrame)
+FRAME_ID(nsGfxRadioControlFrame)
+FRAME_ID(nsGridContainerFrame)
+FRAME_ID(nsGridRowGroupFrame)
+FRAME_ID(nsGridRowLeafFrame)
+FRAME_ID(nsGroupBoxFrame)
+FRAME_ID(nsHTMLButtonControlFrame)
+FRAME_ID(nsHTMLCanvasFrame)
+FRAME_ID(nsHTMLFramesetBlankFrame)
+FRAME_ID(nsHTMLFramesetBorderFrame)
+FRAME_ID(nsHTMLFramesetFrame)
+FRAME_ID(nsHTMLScrollFrame)
+FRAME_ID(nsIAnonymousContentCreator)
+FRAME_ID(nsIComboboxControlFrame)
+FRAME_ID(nsIFormControlFrame)
+FRAME_ID(nsIFrame)
+FRAME_ID(nsIFrameFrame)
+FRAME_ID(nsIListControlFrame)
+FRAME_ID(nsIMathMLFrame)
+FRAME_ID(nsIMenuFrame)
+FRAME_ID(nsIObjectFrame)
+FRAME_ID(nsIPageSequenceFrame)
+FRAME_ID(nsIPercentBSizeObserver)
+FRAME_ID(nsIRootBox)
+FRAME_ID(nsISVGChildFrame)
+FRAME_ID(nsISVGSVGFrame)
+FRAME_ID(nsIScrollableFrame)
+FRAME_ID(nsIScrollbarMediator)
+FRAME_ID(nsISelectControlFrame)
+FRAME_ID(nsIStatefulFrame)
+FRAME_ID(nsITableCellLayout)
+FRAME_ID(nsITableLayout)
+FRAME_ID(nsITextControlFrame)
+FRAME_ID(nsITreeBoxObject)
+FRAME_ID(nsImageBoxFrame)
+FRAME_ID(nsImageControlFrame)
+FRAME_ID(nsImageFrame)
+FRAME_ID(nsInlineFrame)
+FRAME_ID(nsLeafBoxFrame)
+FRAME_ID(nsLeafFrame)
+FRAME_ID(nsLegendFrame)
+FRAME_ID(nsListBoxBodyFrame)
+FRAME_ID(nsListControlFrame)
+FRAME_ID(nsListItemFrame)
+FRAME_ID(nsMathMLContainerFrame)
+FRAME_ID(nsMathMLFrame)
+FRAME_ID(nsMathMLmactionFrame)
+FRAME_ID(nsMathMLmathBlockFrame)
+FRAME_ID(nsMathMLmathInlineFrame)
+FRAME_ID(nsMathMLmencloseFrame)
+FRAME_ID(nsMathMLmfencedFrame)
+FRAME_ID(nsMathMLmfracFrame)
+FRAME_ID(nsMathMLmmultiscriptsFrame)
+FRAME_ID(nsMathMLmoFrame)
+FRAME_ID(nsMathMLmoverFrame)
+FRAME_ID(nsMathMLmpaddedFrame)
+FRAME_ID(nsMathMLmrootFrame)
+FRAME_ID(nsMathMLmrowFrame)
+FRAME_ID(nsMathMLmspaceFrame)
+FRAME_ID(nsMathMLmsqrtFrame)
+FRAME_ID(nsMathMLmstyleFrame)
+FRAME_ID(nsMathMLmtableFrame)
+FRAME_ID(nsMathMLmtableWrapperFrame)
+FRAME_ID(nsMathMLmtdFrame)
+FRAME_ID(nsMathMLmtdInnerFrame)
+FRAME_ID(nsMathMLmtrFrame)
+FRAME_ID(nsMathMLmunderFrame)
+FRAME_ID(nsMathMLmunderoverFrame)
+FRAME_ID(nsMathMLsemanticsFrame)
+FRAME_ID(nsMathMLTokenFrame)
+FRAME_ID(nsMenuBarFrame)
+FRAME_ID(nsMenuFrame)
+FRAME_ID(nsMenuPopupFrame)
+FRAME_ID(nsMeterFrame)
+FRAME_ID(nsNumberControlFrame)
+FRAME_ID(nsPluginFrame)
+FRAME_ID(nsPageBreakFrame)
+FRAME_ID(nsPageContentFrame)
+FRAME_ID(nsPageFrame)
+FRAME_ID(nsPlaceholderFrame)
+FRAME_ID(nsPopupSetFrame)
+FRAME_ID(nsProgressFrame)
+FRAME_ID(nsProgressMeterFrame)
+FRAME_ID(nsRangeFrame)
+FRAME_ID(nsResizerFrame)
+FRAME_ID(nsRootBoxFrame)
+FRAME_ID(nsRubyBaseContainerFrame)
+FRAME_ID(nsRubyBaseFrame)
+FRAME_ID(nsRubyContentFrame)
+FRAME_ID(nsRubyFrame)
+FRAME_ID(nsRubyTextContainerFrame)
+FRAME_ID(nsRubyTextFrame)
+FRAME_ID(nsScrollbarButtonFrame)
+FRAME_ID(nsScrollbarFrame)
+FRAME_ID(nsSelectsAreaFrame)
+FRAME_ID(nsSimplePageSequenceFrame)
+FRAME_ID(nsSliderFrame)
+FRAME_ID(nsSplittableFrame)
+FRAME_ID(nsSplitterFrame)
+FRAME_ID(nsStackFrame)
+FRAME_ID(nsSubDocumentFrame)
+FRAME_ID(nsSVGAFrame)
+FRAME_ID(nsSVGClipPathFrame)
+FRAME_ID(nsSVGContainerFrame)
+FRAME_ID(nsSVGDisplayContainerFrame)
+FRAME_ID(SVGFEContainerFrame)
+FRAME_ID(SVGFEImageFrame)
+FRAME_ID(SVGFELeafFrame)
+FRAME_ID(SVGFEUnstyledLeafFrame)
+FRAME_ID(nsSVGFilterFrame)
+FRAME_ID(nsSVGForeignObjectFrame)
+FRAME_ID(nsSVGGenericContainerFrame)
+FRAME_ID(nsSVGGFrame)
+FRAME_ID(nsSVGGradientFrame)
+FRAME_ID(nsSVGImageFrame)
+FRAME_ID(nsSVGInnerSVGFrame)
+FRAME_ID(nsSVGLinearGradientFrame)
+FRAME_ID(nsSVGMarkerFrame)
+FRAME_ID(nsSVGMarkerAnonChildFrame)
+FRAME_ID(nsSVGMaskFrame)
+FRAME_ID(nsSVGOuterSVGFrame)
+FRAME_ID(nsSVGOuterSVGAnonChildFrame)
+FRAME_ID(nsSVGPaintServerFrame)
+FRAME_ID(nsSVGPathGeometryFrame)
+FRAME_ID(nsSVGPatternFrame)
+FRAME_ID(nsSVGRadialGradientFrame)
+FRAME_ID(nsSVGStopFrame)
+FRAME_ID(nsSVGSwitchFrame)
+FRAME_ID(SVGTextFrame)
+FRAME_ID(nsSVGUseFrame)
+FRAME_ID(SVGViewFrame)
+FRAME_ID(nsTableCellFrame)
+FRAME_ID(nsTableColFrame)
+FRAME_ID(nsTableColGroupFrame)
+FRAME_ID(nsTableFrame)
+FRAME_ID(nsTableWrapperFrame)
+FRAME_ID(nsTableRowFrame)
+FRAME_ID(nsTableRowGroupFrame)
+FRAME_ID(nsTextBoxFrame)
+FRAME_ID(nsTextControlFrame)
+FRAME_ID(nsTextFrame)
+FRAME_ID(nsTitleBarFrame)
+FRAME_ID(nsTreeBodyFrame)
+FRAME_ID(nsTreeColFrame)
+FRAME_ID(nsVideoFrame)
+FRAME_ID(nsXULLabelFrame)
+FRAME_ID(nsXULScrollFrame)
+FRAME_ID(ViewportFrame)
diff --git a/layout/generic/nsFrameList.cpp b/layout/generic/nsFrameList.cpp
new file mode 100644
index 000000000..2981ee4ec
--- /dev/null
+++ b/layout/generic/nsFrameList.cpp
@@ -0,0 +1,549 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsFrameList.h"
+
+#include "mozilla/ArenaObjectID.h"
+#include "nsBidiPresUtils.h"
+#include "nsContainerFrame.h"
+#include "nsGkAtoms.h"
+#include "nsILineIterator.h"
+#include "nsIPresShell.h"
+#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+namespace layout {
+namespace detail {
+const AlignedFrameListBytes gEmptyFrameListBytes = { 0 };
+} // namespace detail
+} // namespace layout
+} // namespace mozilla
+
+void*
+nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell)
+{
+ return aPresShell->AllocateByObjectID(eArenaObjectID_nsFrameList, sz);
+}
+
+void
+nsFrameList::Delete(nsIPresShell* aPresShell)
+{
+ NS_PRECONDITION(this != &EmptyList(), "Shouldn't Delete() this list");
+ NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
+
+ aPresShell->FreeByObjectID(eArenaObjectID_nsFrameList, this);
+}
+
+void
+nsFrameList::DestroyFrames()
+{
+ while (nsIFrame* frame = RemoveFirstChild()) {
+ frame->Destroy();
+ }
+ mLastChild = nullptr;
+}
+
+void
+nsFrameList::DestroyFramesFrom(nsIFrame* aDestructRoot)
+{
+ NS_PRECONDITION(aDestructRoot, "Missing destruct root");
+
+ while (nsIFrame* frame = RemoveFirstChild()) {
+ frame->DestroyFrom(aDestructRoot);
+ }
+ mLastChild = nullptr;
+}
+
+void
+nsFrameList::SetFrames(nsIFrame* aFrameList)
+{
+ NS_PRECONDITION(!mFirstChild, "Losing frames");
+
+ mFirstChild = aFrameList;
+ mLastChild = nsLayoutUtils::GetLastSibling(mFirstChild);
+}
+
+void
+nsFrameList::RemoveFrame(nsIFrame* aFrame)
+{
+ NS_PRECONDITION(aFrame, "null ptr");
+#ifdef DEBUG_FRAME_LIST
+ // ContainsFrame is O(N)
+ NS_PRECONDITION(ContainsFrame(aFrame), "wrong list");
+#endif
+
+ nsIFrame* nextFrame = aFrame->GetNextSibling();
+ if (aFrame == mFirstChild) {
+ mFirstChild = nextFrame;
+ aFrame->SetNextSibling(nullptr);
+ if (!nextFrame) {
+ mLastChild = nullptr;
+ }
+ }
+ else {
+ nsIFrame* prevSibling = aFrame->GetPrevSibling();
+ NS_ASSERTION(prevSibling && prevSibling->GetNextSibling() == aFrame,
+ "Broken frame linkage");
+ prevSibling->SetNextSibling(nextFrame);
+ aFrame->SetNextSibling(nullptr);
+ if (!nextFrame) {
+ mLastChild = prevSibling;
+ }
+ }
+}
+
+nsFrameList
+nsFrameList::RemoveFramesAfter(nsIFrame* aAfterFrame)
+{
+ if (!aAfterFrame) {
+ nsFrameList result;
+ result.InsertFrames(nullptr, nullptr, *this);
+ return result;
+ }
+
+ NS_PRECONDITION(NotEmpty(), "illegal operation on empty list");
+#ifdef DEBUG_FRAME_LIST
+ NS_PRECONDITION(ContainsFrame(aAfterFrame), "wrong list");
+#endif
+
+ nsIFrame* tail = aAfterFrame->GetNextSibling();
+ // if (!tail) return EmptyList(); -- worth optimizing this case?
+ nsIFrame* oldLastChild = mLastChild;
+ mLastChild = aAfterFrame;
+ aAfterFrame->SetNextSibling(nullptr);
+ return nsFrameList(tail, tail ? oldLastChild : nullptr);
+}
+
+nsIFrame*
+nsFrameList::RemoveFirstChild()
+{
+ if (mFirstChild) {
+ nsIFrame* firstChild = mFirstChild;
+ RemoveFrame(firstChild);
+ return firstChild;
+ }
+ return nullptr;
+}
+
+void
+nsFrameList::DestroyFrame(nsIFrame* aFrame)
+{
+ NS_PRECONDITION(aFrame, "null ptr");
+ RemoveFrame(aFrame);
+ aFrame->Destroy();
+}
+
+nsFrameList::Slice
+nsFrameList::InsertFrames(nsContainerFrame* aParent, nsIFrame* aPrevSibling,
+ nsFrameList& aFrameList)
+{
+ NS_PRECONDITION(aFrameList.NotEmpty(), "Unexpected empty list");
+
+ if (aParent) {
+ aFrameList.ApplySetParent(aParent);
+ }
+
+ NS_ASSERTION(IsEmpty() ||
+ FirstChild()->GetParent() == aFrameList.FirstChild()->GetParent(),
+ "frame to add has different parent");
+ NS_ASSERTION(!aPrevSibling ||
+ aPrevSibling->GetParent() == aFrameList.FirstChild()->GetParent(),
+ "prev sibling has different parent");
+#ifdef DEBUG_FRAME_LIST
+ // ContainsFrame is O(N)
+ NS_ASSERTION(!aPrevSibling || ContainsFrame(aPrevSibling),
+ "prev sibling is not on this list");
+#endif
+
+ nsIFrame* firstNewFrame = aFrameList.FirstChild();
+ nsIFrame* nextSibling;
+ if (aPrevSibling) {
+ nextSibling = aPrevSibling->GetNextSibling();
+ aPrevSibling->SetNextSibling(firstNewFrame);
+ }
+ else {
+ nextSibling = mFirstChild;
+ mFirstChild = firstNewFrame;
+ }
+
+ nsIFrame* lastNewFrame = aFrameList.LastChild();
+ lastNewFrame->SetNextSibling(nextSibling);
+ if (!nextSibling) {
+ mLastChild = lastNewFrame;
+ }
+
+ VerifyList();
+
+ aFrameList.Clear();
+ return Slice(*this, firstNewFrame, nextSibling);
+}
+
+nsFrameList
+nsFrameList::ExtractHead(FrameLinkEnumerator& aLink)
+{
+ NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
+ NS_PRECONDITION(!aLink.PrevFrame() ||
+ aLink.PrevFrame()->GetNextSibling() ==
+ aLink.NextFrame(),
+ "Unexpected PrevFrame()");
+ NS_PRECONDITION(aLink.PrevFrame() ||
+ aLink.NextFrame() == FirstChild(),
+ "Unexpected NextFrame()");
+ NS_PRECONDITION(!aLink.PrevFrame() ||
+ aLink.NextFrame() != FirstChild(),
+ "Unexpected NextFrame()");
+ NS_PRECONDITION(aLink.mEnd == nullptr,
+ "Unexpected mEnd for frame link enumerator");
+
+ nsIFrame* prev = aLink.PrevFrame();
+ nsIFrame* newFirstFrame = nullptr;
+ if (prev) {
+ // Truncate the list after |prev| and hand the first part to our new list.
+ prev->SetNextSibling(nullptr);
+ newFirstFrame = mFirstChild;
+ mFirstChild = aLink.NextFrame();
+ if (!mFirstChild) { // we handed over the whole list
+ mLastChild = nullptr;
+ }
+
+ // Now make sure aLink doesn't point to a frame we no longer have.
+ aLink.mPrev = nullptr;
+ }
+ // else aLink is pointing to before our first frame. Nothing to do.
+
+ return nsFrameList(newFirstFrame, prev);
+}
+
+nsFrameList
+nsFrameList::ExtractTail(FrameLinkEnumerator& aLink)
+{
+ NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
+ NS_PRECONDITION(!aLink.PrevFrame() ||
+ aLink.PrevFrame()->GetNextSibling() ==
+ aLink.NextFrame(),
+ "Unexpected PrevFrame()");
+ NS_PRECONDITION(aLink.PrevFrame() ||
+ aLink.NextFrame() == FirstChild(),
+ "Unexpected NextFrame()");
+ NS_PRECONDITION(!aLink.PrevFrame() ||
+ aLink.NextFrame() != FirstChild(),
+ "Unexpected NextFrame()");
+ NS_PRECONDITION(aLink.mEnd == nullptr,
+ "Unexpected mEnd for frame link enumerator");
+
+ nsIFrame* prev = aLink.PrevFrame();
+ nsIFrame* newFirstFrame;
+ nsIFrame* newLastFrame;
+ if (prev) {
+ // Truncate the list after |prev| and hand the second part to our new list
+ prev->SetNextSibling(nullptr);
+ newFirstFrame = aLink.NextFrame();
+ newLastFrame = newFirstFrame ? mLastChild : nullptr;
+ mLastChild = prev;
+ } else {
+ // Hand the whole list over to our new list
+ newFirstFrame = mFirstChild;
+ newLastFrame = mLastChild;
+ Clear();
+ }
+
+ // Now make sure aLink doesn't point to a frame we no longer have.
+ aLink.mFrame = nullptr;
+
+ NS_POSTCONDITION(aLink.AtEnd(), "What's going on here?");
+
+ return nsFrameList(newFirstFrame, newLastFrame);
+}
+
+nsIFrame*
+nsFrameList::FrameAt(int32_t aIndex) const
+{
+ NS_PRECONDITION(aIndex >= 0, "invalid arg");
+ if (aIndex < 0) return nullptr;
+ nsIFrame* frame = mFirstChild;
+ while ((aIndex-- > 0) && frame) {
+ frame = frame->GetNextSibling();
+ }
+ return frame;
+}
+
+int32_t
+nsFrameList::IndexOf(nsIFrame* aFrame) const
+{
+ int32_t count = 0;
+ for (nsIFrame* f = mFirstChild; f; f = f->GetNextSibling()) {
+ if (f == aFrame)
+ return count;
+ ++count;
+ }
+ return -1;
+}
+
+bool
+nsFrameList::ContainsFrame(const nsIFrame* aFrame) const
+{
+ NS_PRECONDITION(aFrame, "null ptr");
+
+ nsIFrame* frame = mFirstChild;
+ while (frame) {
+ if (frame == aFrame) {
+ return true;
+ }
+ frame = frame->GetNextSibling();
+ }
+ return false;
+}
+
+int32_t
+nsFrameList::GetLength() const
+{
+ int32_t count = 0;
+ nsIFrame* frame = mFirstChild;
+ while (frame) {
+ count++;
+ frame = frame->GetNextSibling();
+ }
+ return count;
+}
+
+void
+nsFrameList::ApplySetParent(nsContainerFrame* aParent) const
+{
+ NS_ASSERTION(aParent, "null ptr");
+
+ for (nsIFrame* f = FirstChild(); f; f = f->GetNextSibling()) {
+ f->SetParent(aParent);
+ }
+}
+
+/* static */ void
+nsFrameList::UnhookFrameFromSiblings(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(aFrame->GetPrevSibling() && aFrame->GetNextSibling());
+ nsIFrame* const nextSibling = aFrame->GetNextSibling();
+ nsIFrame* const prevSibling = aFrame->GetPrevSibling();
+ aFrame->SetNextSibling(nullptr);
+ prevSibling->SetNextSibling(nextSibling);
+ MOZ_ASSERT(!aFrame->GetPrevSibling() && !aFrame->GetNextSibling());
+}
+
+#ifdef DEBUG_FRAME_DUMP
+void
+nsFrameList::List(FILE* out) const
+{
+ fprintf_stderr(out, "<\n");
+ for (nsIFrame* frame = mFirstChild; frame;
+ frame = frame->GetNextSibling()) {
+ frame->List(out, " ");
+ }
+ fprintf_stderr(out, ">\n");
+}
+#endif
+
+nsIFrame*
+nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const
+{
+ if (!mFirstChild)
+ return nullptr;
+
+ nsIFrame* parent = mFirstChild->GetParent();
+ if (!parent)
+ return aFrame ? aFrame->GetPrevSibling() : LastChild();
+
+ nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(mFirstChild);
+
+ nsAutoLineIterator iter = parent->GetLineIterator();
+ if (!iter) {
+ // Parent is not a block Frame
+ if (parent->GetType() == nsGkAtoms::lineFrame) {
+ // Line frames are not bidi-splittable, so need to consider bidi reordering
+ if (paraDir == NSBIDI_LTR) {
+ return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
+ } else { // RTL
+ return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
+ }
+ } else {
+ // Just get the next or prev sibling, depending on block and frame direction.
+ if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
+ return aFrame ? aFrame->GetPrevSibling() : LastChild();
+ } else {
+ return aFrame ? aFrame->GetNextSibling() : mFirstChild;
+ }
+ }
+ }
+
+ // Parent is a block frame, so use the LineIterator to find the previous visual
+ // sibling on this line, or the last one on the previous line.
+
+ int32_t thisLine;
+ if (aFrame) {
+ thisLine = iter->FindLineContaining(aFrame);
+ if (thisLine < 0)
+ return nullptr;
+ } else {
+ thisLine = iter->GetNumLines();
+ }
+
+ nsIFrame* frame = nullptr;
+ nsIFrame* firstFrameOnLine;
+ int32_t numFramesOnLine;
+ nsRect lineBounds;
+
+ if (aFrame) {
+ iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds);
+
+ if (paraDir == NSBIDI_LTR) {
+ frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
+ } else { // RTL
+ frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
+ }
+ }
+
+ if (!frame && thisLine > 0) {
+ // Get the last frame of the previous line
+ iter->GetLine(thisLine - 1, &firstFrameOnLine, &numFramesOnLine, lineBounds);
+
+ if (paraDir == NSBIDI_LTR) {
+ frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine, numFramesOnLine);
+ } else { // RTL
+ frame = nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine, numFramesOnLine);
+ }
+ }
+ return frame;
+}
+
+nsIFrame*
+nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const
+{
+ if (!mFirstChild)
+ return nullptr;
+
+ nsIFrame* parent = mFirstChild->GetParent();
+ if (!parent)
+ return aFrame ? aFrame->GetPrevSibling() : mFirstChild;
+
+ nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(mFirstChild);
+
+ nsAutoLineIterator iter = parent->GetLineIterator();
+ if (!iter) {
+ // Parent is not a block Frame
+ if (parent->GetType() == nsGkAtoms::lineFrame) {
+ // Line frames are not bidi-splittable, so need to consider bidi reordering
+ if (paraDir == NSBIDI_LTR) {
+ return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
+ } else { // RTL
+ return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
+ }
+ } else {
+ // Just get the next or prev sibling, depending on block and frame direction.
+ if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
+ return aFrame ? aFrame->GetNextSibling() : mFirstChild;
+ } else {
+ return aFrame ? aFrame->GetPrevSibling() : LastChild();
+ }
+ }
+ }
+
+ // Parent is a block frame, so use the LineIterator to find the next visual
+ // sibling on this line, or the first one on the next line.
+
+ int32_t thisLine;
+ if (aFrame) {
+ thisLine = iter->FindLineContaining(aFrame);
+ if (thisLine < 0)
+ return nullptr;
+ } else {
+ thisLine = -1;
+ }
+
+ nsIFrame* frame = nullptr;
+ nsIFrame* firstFrameOnLine;
+ int32_t numFramesOnLine;
+ nsRect lineBounds;
+
+ if (aFrame) {
+ iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds);
+
+ if (paraDir == NSBIDI_LTR) {
+ frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
+ } else { // RTL
+ frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
+ }
+ }
+
+ int32_t numLines = iter->GetNumLines();
+ if (!frame && thisLine < numLines - 1) {
+ // Get the first frame of the next line
+ iter->GetLine(thisLine + 1, &firstFrameOnLine, &numFramesOnLine, lineBounds);
+
+ if (paraDir == NSBIDI_LTR) {
+ frame = nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine, numFramesOnLine);
+ } else { // RTL
+ frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine, numFramesOnLine);
+ }
+ }
+ return frame;
+}
+
+#ifdef DEBUG_FRAME_LIST
+void
+nsFrameList::VerifyList() const
+{
+ NS_ASSERTION((mFirstChild == nullptr) == (mLastChild == nullptr),
+ "bad list state");
+
+ if (IsEmpty()) {
+ return;
+ }
+
+ // Simple algorithm to find a loop in a linked list -- advance pointers
+ // through it at speeds of 1 and 2, and if they ever get to be equal bail
+ NS_ASSERTION(!mFirstChild->GetPrevSibling(), "bad prev sibling pointer");
+ nsIFrame *first = mFirstChild, *second = mFirstChild;
+ for (;;) {
+ first = first->GetNextSibling();
+ second = second->GetNextSibling();
+ if (!second) {
+ break;
+ }
+ NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
+ "bad prev sibling pointer");
+ second = second->GetNextSibling();
+ if (first == second) {
+ // Loop detected! Since second advances faster, they can't both be null;
+ // we would have broken out of the loop long ago.
+ NS_ERROR("loop in frame list. This will probably hang soon.");
+ return;
+ }
+ if (!second) {
+ break;
+ }
+ NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
+ "bad prev sibling pointer");
+ }
+
+ NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
+ "bogus mLastChild");
+ // XXX we should also assert that all GetParent() are either null or
+ // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
+ // prevents that, e.g. table captions.
+}
+#endif
+
+namespace mozilla {
+namespace layout {
+
+AutoFrameListPtr::~AutoFrameListPtr()
+{
+ if (mFrameList) {
+ mFrameList->Delete(mPresContext->PresShell());
+ }
+}
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/generic/nsFrameList.h b/layout/generic/nsFrameList.h
new file mode 100644
index 000000000..7ee45a1ee
--- /dev/null
+++ b/layout/generic/nsFrameList.h
@@ -0,0 +1,579 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsFrameList_h___
+#define nsFrameList_h___
+
+#include <stdio.h> /* for FILE* */
+#include "nsDebug.h"
+#include "nsTArrayForwardDeclare.h"
+#include "mozilla/ReverseIterator.h"
+
+#if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
+// DEBUG_FRAME_DUMP enables nsIFrame::List and related methods.
+// You can also define this in a non-DEBUG build if you need frame dumps.
+#define DEBUG_FRAME_DUMP 1
+#endif
+
+class nsContainerFrame;
+class nsIFrame;
+class nsIPresShell;
+class nsPresContext;
+
+namespace mozilla {
+namespace layout {
+ class FrameChildList;
+ enum FrameChildListID {
+ // The individual concrete child lists.
+ kPrincipalList = 0x1,
+ kPopupList = 0x2,
+ kCaptionList = 0x4,
+ kColGroupList = 0x8,
+ kSelectPopupList = 0x10,
+ kAbsoluteList = 0x20,
+ kFixedList = 0x40,
+ kOverflowList = 0x80,
+ kOverflowContainersList = 0x100,
+ kExcessOverflowContainersList = 0x200,
+ kOverflowOutOfFlowList = 0x400,
+ kFloatList = 0x800,
+ kBulletList = 0x1000,
+ kPushedFloatsList = 0x2000,
+ kBackdropList = 0x4000,
+ // A special alias for kPrincipalList that suppress the reflow request that
+ // is normally done when manipulating child lists.
+ kNoReflowPrincipalList = 0x8000
+ };
+} // namespace layout
+} // namespace mozilla
+
+// Uncomment this to enable expensive frame-list integrity checking
+// #define DEBUG_FRAME_LIST
+
+/**
+ * A class for managing a list of frames.
+ */
+class nsFrameList {
+public:
+ nsFrameList() :
+ mFirstChild(nullptr), mLastChild(nullptr)
+ {
+ }
+
+ nsFrameList(nsIFrame* aFirstFrame, nsIFrame* aLastFrame) :
+ mFirstChild(aFirstFrame), mLastChild(aLastFrame)
+ {
+ VerifyList();
+ }
+
+ nsFrameList(const nsFrameList& aOther) :
+ mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild)
+ {
+ }
+
+ /**
+ * Infallibly allocate a nsFrameList from the shell arena.
+ */
+ void* operator new(size_t sz, nsIPresShell* aPresShell);
+
+ /**
+ * Deallocate this list that was allocated from the shell arena.
+ * The list is required to be empty.
+ */
+ void Delete(nsIPresShell* aPresShell);
+
+ /**
+ * For each frame in this list: remove it from the list then call
+ * Destroy() on it.
+ */
+ void DestroyFrames();
+
+ /**
+ * For each frame in this list: remove it from the list then call
+ * DestroyFrom(aDestructRoot) on it.
+ */
+ void DestroyFramesFrom(nsIFrame* aDestructRoot);
+
+ void Clear() { mFirstChild = mLastChild = nullptr; }
+
+ void SetFrames(nsIFrame* aFrameList);
+
+ void SetFrames(nsFrameList& aFrameList) {
+ NS_PRECONDITION(!mFirstChild, "Losing frames");
+
+ mFirstChild = aFrameList.FirstChild();
+ mLastChild = aFrameList.LastChild();
+ aFrameList.Clear();
+ }
+
+ class Slice;
+
+ /**
+ * Append aFrameList to this list. If aParent is not null,
+ * reparents the newly added frames. Clears out aFrameList and
+ * returns a list slice represening the newly-appended frames.
+ */
+ Slice AppendFrames(nsContainerFrame* aParent, nsFrameList& aFrameList) {
+ return InsertFrames(aParent, LastChild(), aFrameList);
+ }
+
+
+ /**
+ * Append aFrame to this list. If aParent is not null,
+ * reparents the newly added frame.
+ */
+ void AppendFrame(nsContainerFrame* aParent, nsIFrame* aFrame) {
+ nsFrameList temp(aFrame, aFrame);
+ AppendFrames(aParent, temp);
+ }
+
+ /**
+ * Take aFrame out of the frame list. This also disconnects aFrame
+ * from the sibling list. The frame must be non-null and present on
+ * this list.
+ */
+ void RemoveFrame(nsIFrame* aFrame);
+
+ /**
+ * Take the frames after aAfterFrame out of the frame list. If
+ * aAfterFrame is null, removes the entire list.
+ * @param aAfterFrame a frame in this list, or null
+ * @return the removed frames, if any
+ */
+ nsFrameList RemoveFramesAfter(nsIFrame* aAfterFrame);
+
+ /**
+ * Take the first frame (if any) out of the frame list.
+ * @return the first child, or nullptr if the list is empty
+ */
+ nsIFrame* RemoveFirstChild();
+
+ /**
+ * The following two functions are intended to be used in concert for
+ * removing a frame from its frame list when the set of possible frame
+ * lists is known in advance, but the exact frame list is unknown.
+ * aFrame must be non-null.
+ * Example use:
+ * bool removed = frameList1.StartRemoveFrame(aFrame) ||
+ * frameList2.ContinueRemoveFrame(aFrame) ||
+ * frameList3.ContinueRemoveFrame(aFrame);
+ * MOZ_ASSERT(removed);
+ *
+ * @note One of the frame lists MUST contain aFrame, if it's on some other
+ * frame list then the example above will likely lead to crashes.
+ * This function is O(1).
+ * @return true iff aFrame was removed from /some/ list, not necessarily
+ * this one. If it was removed from a different list then it is
+ * guaranteed that that list is still non-empty.
+ * (this method is implemented in nsIFrame.h to be able to inline)
+ */
+ inline bool StartRemoveFrame(nsIFrame* aFrame);
+
+ /**
+ * Precondition: StartRemoveFrame MUST be called before this.
+ * This function is O(1).
+ * @see StartRemoveFrame
+ * @return true iff aFrame was removed from this list
+ * (this method is implemented in nsIFrame.h to be able to inline)
+ */
+ inline bool ContinueRemoveFrame(nsIFrame* aFrame);
+
+ /**
+ * Take aFrame out of the frame list and then destroy it.
+ * The frame must be non-null and present on this list.
+ */
+ void DestroyFrame(nsIFrame* aFrame);
+
+ /**
+ * Insert aFrame right after aPrevSibling, or prepend it to this
+ * list if aPrevSibling is null. If aParent is not null, also
+ * reparents newly-added frame. Note that this method always
+ * sets the frame's nextSibling pointer.
+ */
+ void InsertFrame(nsContainerFrame* aParent, nsIFrame* aPrevSibling,
+ nsIFrame* aFrame) {
+ nsFrameList temp(aFrame, aFrame);
+ InsertFrames(aParent, aPrevSibling, temp);
+ }
+
+
+ /**
+ * Inserts aFrameList into this list after aPrevSibling (at the beginning if
+ * aPrevSibling is null). If aParent is not null, reparents the newly added
+ * frames. Clears out aFrameList and returns a list slice representing the
+ * newly-inserted frames.
+ */
+ Slice InsertFrames(nsContainerFrame* aParent, nsIFrame* aPrevSibling,
+ nsFrameList& aFrameList);
+
+ class FrameLinkEnumerator;
+
+ /**
+ * Split this frame list such that all the frames before the link pointed to
+ * by aLink end up in the returned list, while the remaining frames stay in
+ * this list. After this call, aLink points to the beginning of this list.
+ */
+ nsFrameList ExtractHead(FrameLinkEnumerator& aLink);
+
+ /**
+ * Split this frame list such that all the frames coming after the link
+ * pointed to by aLink end up in the returned list, while the frames before
+ * that link stay in this list. After this call, aLink is at end.
+ */
+ nsFrameList ExtractTail(FrameLinkEnumerator& aLink);
+
+ nsIFrame* FirstChild() const {
+ return mFirstChild;
+ }
+
+ nsIFrame* LastChild() const {
+ return mLastChild;
+ }
+
+ nsIFrame* FrameAt(int32_t aIndex) const;
+ int32_t IndexOf(nsIFrame* aFrame) const;
+
+ bool IsEmpty() const {
+ return nullptr == mFirstChild;
+ }
+
+ bool NotEmpty() const {
+ return nullptr != mFirstChild;
+ }
+
+ bool ContainsFrame(const nsIFrame* aFrame) const;
+
+ /**
+ * Get the number of frames in this list. Note that currently the
+ * implementation has O(n) time complexity. Do not call it repeatedly in hot
+ * code.
+ */
+ int32_t GetLength() const;
+
+ /**
+ * If this frame list has only one frame, return that frame.
+ * Otherwise, return null.
+ */
+ nsIFrame* OnlyChild() const {
+ if (FirstChild() == LastChild()) {
+ return FirstChild();
+ }
+ return nullptr;
+ }
+
+ /**
+ * Call SetParent(aParent) for each frame in this list.
+ * @param aParent the new parent frame, must be non-null
+ */
+ void ApplySetParent(nsContainerFrame* aParent) const;
+
+ /**
+ * If this frame list is non-empty then append it to aLists as the
+ * aListID child list.
+ * (this method is implemented in FrameChildList.h for dependency reasons)
+ */
+ inline void AppendIfNonempty(nsTArray<mozilla::layout::FrameChildList>* aLists,
+ mozilla::layout::FrameChildListID aListID) const;
+
+ /**
+ * Return the frame before this frame in visual order (after Bidi reordering).
+ * If aFrame is null, return the last frame in visual order.
+ */
+ nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const;
+
+ /**
+ * Return the frame after this frame in visual order (after Bidi reordering).
+ * If aFrame is null, return the first frame in visual order.
+ */
+ nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const;
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out) const;
+#endif
+
+ static inline const nsFrameList& EmptyList();
+
+ class Enumerator;
+
+ /**
+ * A class representing a slice of a frame list.
+ */
+ class Slice {
+ friend class Enumerator;
+
+ public:
+ // Implicit on purpose, so that we can easily create enumerators from
+ // nsFrameList via this impicit constructor.
+ MOZ_IMPLICIT Slice(const nsFrameList& aList) :
+#ifdef DEBUG
+ mList(aList),
+#endif
+ mStart(aList.FirstChild()),
+ mEnd(nullptr)
+ {}
+
+ Slice(const nsFrameList& aList, nsIFrame* aStart, nsIFrame* aEnd) :
+#ifdef DEBUG
+ mList(aList),
+#endif
+ mStart(aStart),
+ mEnd(aEnd)
+ {}
+
+ Slice(const Slice& aOther) :
+#ifdef DEBUG
+ mList(aOther.mList),
+#endif
+ mStart(aOther.mStart),
+ mEnd(aOther.mEnd)
+ {}
+
+ private:
+#ifdef DEBUG
+ const nsFrameList& mList;
+#endif
+ nsIFrame* const mStart; // our starting frame
+ const nsIFrame* const mEnd; // The first frame that is NOT in the slice.
+ // May be null.
+ };
+
+ class Enumerator {
+ public:
+ explicit Enumerator(const Slice& aSlice) :
+#ifdef DEBUG
+ mSlice(aSlice),
+#endif
+ mFrame(aSlice.mStart),
+ mEnd(aSlice.mEnd)
+ {}
+
+ Enumerator(const Enumerator& aOther) :
+#ifdef DEBUG
+ mSlice(aOther.mSlice),
+#endif
+ mFrame(aOther.mFrame),
+ mEnd(aOther.mEnd)
+ {}
+
+ bool AtEnd() const {
+ // Can't just check mEnd, because some table code goes and destroys the
+ // tail of the frame list (including mEnd!) while iterating over the
+ // frame list.
+ return !mFrame || mFrame == mEnd;
+ }
+
+ /* Next() needs to know about nsIFrame, and nsIFrame will need to
+ know about nsFrameList methods, so in order to inline this put
+ the implementation in nsIFrame.h */
+ inline void Next();
+
+ /**
+ * Get the current frame we're pointing to. Do not call this on an
+ * iterator that is at end!
+ */
+ nsIFrame* get() const {
+ NS_PRECONDITION(!AtEnd(), "Enumerator is at end");
+ return mFrame;
+ }
+
+ /**
+ * Get an enumerator that is just like this one, but not limited in terms of
+ * the part of the list it will traverse.
+ */
+ Enumerator GetUnlimitedEnumerator() const {
+ return Enumerator(*this, nullptr);
+ }
+
+#ifdef DEBUG
+ const nsFrameList& List() const { return mSlice.mList; }
+#endif
+
+ protected:
+ Enumerator(const Enumerator& aOther, const nsIFrame* const aNewEnd):
+#ifdef DEBUG
+ mSlice(aOther.mSlice),
+#endif
+ mFrame(aOther.mFrame),
+ mEnd(aNewEnd)
+ {}
+
+#ifdef DEBUG
+ /* Has to be an object, not a reference, since the slice could
+ well be a temporary constructed from an nsFrameList */
+ const Slice mSlice;
+#endif
+ nsIFrame* mFrame; // our current frame.
+ const nsIFrame* const mEnd; // The first frame we should NOT enumerate.
+ // May be null.
+ };
+
+ /**
+ * A class that can be used to enumerate links between frames. When created
+ * from an nsFrameList, it points to the "link" immediately before the first
+ * frame. It can then be advanced until it points to the "link" immediately
+ * after the last frame. At any position, PrevFrame() and NextFrame() are
+ * the frames before and after the given link. This means PrevFrame() is
+ * null when the enumerator is at the beginning of the list and NextFrame()
+ * is null when it's AtEnd().
+ */
+ class FrameLinkEnumerator : private Enumerator {
+ public:
+ friend class nsFrameList;
+
+ explicit FrameLinkEnumerator(const nsFrameList& aList) :
+ Enumerator(aList),
+ mPrev(nullptr)
+ {}
+
+ FrameLinkEnumerator(const FrameLinkEnumerator& aOther) :
+ Enumerator(aOther),
+ mPrev(aOther.mPrev)
+ {}
+
+ /* This constructor needs to know about nsIFrame, and nsIFrame will need to
+ know about nsFrameList methods, so in order to inline this put
+ the implementation in nsIFrame.h */
+ inline FrameLinkEnumerator(const nsFrameList& aList, nsIFrame* aPrevFrame);
+
+ void operator=(const FrameLinkEnumerator& aOther) {
+ NS_PRECONDITION(&List() == &aOther.List(), "Different lists?");
+ mFrame = aOther.mFrame;
+ mPrev = aOther.mPrev;
+ }
+
+ inline void Next();
+
+ bool AtEnd() const { return Enumerator::AtEnd(); }
+
+ nsIFrame* PrevFrame() const { return mPrev; }
+ nsIFrame* NextFrame() const { return mFrame; }
+
+ protected:
+ nsIFrame* mPrev;
+ };
+
+ class Iterator
+ {
+ public:
+ Iterator(const nsFrameList& aList, nsIFrame* aCurrent)
+ : mList(aList)
+ , mCurrent(aCurrent)
+ {}
+
+ Iterator(const Iterator& aOther)
+ : mList(aOther.mList)
+ , mCurrent(aOther.mCurrent)
+ {}
+
+ nsIFrame* operator*() const { return mCurrent; }
+
+ // The operators need to know about nsIFrame, hence the
+ // implementations are in nsIFrame.h
+ Iterator& operator++();
+ Iterator& operator--();
+
+ Iterator operator++(int) { auto ret = *this; ++*this; return ret; }
+ Iterator operator--(int) { auto ret = *this; --*this; return ret; }
+
+ friend bool operator==(const Iterator& aIter1, const Iterator& aIter2);
+ friend bool operator!=(const Iterator& aIter1, const Iterator& aIter2);
+
+ private:
+ const nsFrameList& mList;
+ nsIFrame* mCurrent;
+ };
+
+ typedef Iterator iterator;
+ typedef Iterator const_iterator;
+ typedef mozilla::ReverseIterator<Iterator> reverse_iterator;
+ typedef mozilla::ReverseIterator<Iterator> const_reverse_iterator;
+
+ iterator begin() const { return iterator(*this, mFirstChild); }
+ const_iterator cbegin() const { return begin(); }
+ iterator end() const { return iterator(*this, nullptr); }
+ const_iterator cend() const { return end(); }
+ reverse_iterator rbegin() const { return reverse_iterator(end()); }
+ const_reverse_iterator crbegin() const { return rbegin(); }
+ reverse_iterator rend() const { return reverse_iterator(begin()); }
+ const_reverse_iterator crend() const { return rend(); }
+
+private:
+ void operator delete(void*) = delete;
+
+#ifdef DEBUG_FRAME_LIST
+ void VerifyList() const;
+#else
+ void VerifyList() const {}
+#endif
+
+protected:
+ /**
+ * Disconnect aFrame from its siblings. This must only be called if aFrame
+ * is NOT the first or last sibling, because otherwise its nsFrameList will
+ * have a stale mFirst/LastChild pointer. This precondition is asserted.
+ * This function is O(1).
+ */
+ static void UnhookFrameFromSiblings(nsIFrame* aFrame);
+
+ nsIFrame* mFirstChild;
+ nsIFrame* mLastChild;
+};
+
+inline bool
+operator==(const nsFrameList::Iterator& aIter1,
+ const nsFrameList::Iterator& aIter2)
+{
+ MOZ_ASSERT(&aIter1.mList == &aIter2.mList,
+ "must not compare iterator from different list");
+ return aIter1.mCurrent == aIter2.mCurrent;
+}
+
+inline bool
+operator!=(const nsFrameList::Iterator& aIter1,
+ const nsFrameList::Iterator& aIter2)
+{
+ MOZ_ASSERT(&aIter1.mList == &aIter2.mList,
+ "Must not compare iterator from different list");
+ return aIter1.mCurrent != aIter2.mCurrent;
+}
+
+namespace mozilla {
+namespace layout {
+
+/**
+ * Simple "auto_ptr" for nsFrameLists allocated from the shell arena.
+ * The frame list given to the constructor will be deallocated (if non-null)
+ * in the destructor. The frame list must then be empty.
+ */
+class AutoFrameListPtr {
+public:
+ AutoFrameListPtr(nsPresContext* aPresContext, nsFrameList* aFrameList)
+ : mPresContext(aPresContext), mFrameList(aFrameList) {}
+ ~AutoFrameListPtr();
+ operator nsFrameList*() const { return mFrameList; }
+ nsFrameList* operator->() const { return mFrameList; }
+private:
+ nsPresContext* mPresContext;
+ nsFrameList* mFrameList;
+};
+
+namespace detail {
+union AlignedFrameListBytes {
+ void* ptr;
+ char bytes[sizeof(nsFrameList)];
+};
+extern const AlignedFrameListBytes gEmptyFrameListBytes;
+} // namespace detail
+
+} // namespace layout
+} // namespace mozilla
+
+/* static */ inline const nsFrameList&
+nsFrameList::EmptyList()
+{
+ return *reinterpret_cast<const nsFrameList*>(&mozilla::layout::detail::gEmptyFrameListBytes);
+}
+
+#endif /* nsFrameList_h___ */
diff --git a/layout/generic/nsFrameSelection.h b/layout/generic/nsFrameSelection.h
new file mode 100644
index 000000000..adb24d321
--- /dev/null
+++ b/layout/generic/nsFrameSelection.h
@@ -0,0 +1,768 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsFrameSelection_h___
+#define nsFrameSelection_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/dom/Selection.h"
+#include "mozilla/TextRange.h"
+#include "nsIFrame.h"
+#include "nsIContent.h"
+#include "nsISelectionController.h"
+#include "nsISelectionListener.h"
+#include "nsITableCellLayout.h"
+#include "nsIDOMElement.h"
+#include "WordMovementType.h"
+#include "CaretAssociationHint.h"
+#include "nsBidiPresUtils.h"
+
+class nsRange;
+
+// IID for the nsFrameSelection interface
+// 3c6ae2d0-4cf1-44a1-9e9d-2411867f19c6
+#define NS_FRAME_SELECTION_IID \
+{ 0x3c6ae2d0, 0x4cf1, 0x44a1, \
+ { 0x9e, 0x9d, 0x24, 0x11, 0x86, 0x7f, 0x19, 0xc6 } }
+
+#define BIDI_LEVEL_UNDEFINED 0x80
+
+//----------------------------------------------------------------------
+
+// Selection interface
+
+struct SelectionDetails
+{
+#ifdef NS_BUILD_REFCNT_LOGGING
+ SelectionDetails() {
+ MOZ_COUNT_CTOR(SelectionDetails);
+ }
+ ~SelectionDetails() {
+ MOZ_COUNT_DTOR(SelectionDetails);
+ }
+#endif
+ int32_t mStart;
+ int32_t mEnd;
+ mozilla::SelectionType mSelectionType;
+ mozilla::TextRangeStyle mTextRangeStyle;
+ SelectionDetails *mNext;
+};
+
+class nsIPresShell;
+class nsIScrollableFrame;
+
+/** PeekOffsetStruct is used to group various arguments (both input and output)
+ * that are passed to nsFrame::PeekOffset(). See below for the description of
+ * individual arguments.
+ */
+struct MOZ_STACK_CLASS nsPeekOffsetStruct
+{
+ nsPeekOffsetStruct(nsSelectionAmount aAmount,
+ nsDirection aDirection,
+ int32_t aStartOffset,
+ nsPoint aDesiredPos,
+ bool aJumpLines,
+ bool aScrollViewStop,
+ bool aIsKeyboardSelect,
+ bool aVisual,
+ bool aExtend,
+ mozilla::EWordMovementType aWordMovementType = mozilla::eDefaultBehavior);
+
+ // Note: Most arguments (input and output) are only used with certain values
+ // of mAmount. These values are indicated for each argument below.
+ // Arguments with no such indication are used with all values of mAmount.
+
+ /*** Input arguments ***/
+ // Note: The value of some of the input arguments may be changed upon exit.
+
+ // mAmount: The type of movement requested (by character, word, line, etc.)
+ nsSelectionAmount mAmount;
+
+ // mDirection: eDirPrevious or eDirNext.
+ // * Note for visual bidi movement:
+ // eDirPrevious means 'left-then-up' if the containing block is LTR,
+ // 'right-then-up' if it is RTL.
+ // eDirNext means 'right-then-down' if the containing block is LTR,
+ // 'left-then-down' if it is RTL.
+ // Between paragraphs, eDirPrevious means "go to the visual end of the
+ // previous paragraph", and eDirNext means "go to the visual beginning
+ // of the next paragraph".
+ // Used with: eSelectCharacter, eSelectWord, eSelectLine, eSelectParagraph.
+ nsDirection mDirection;
+
+ // mStartOffset: Offset into the content of the current frame where the peek starts.
+ // Used with: eSelectCharacter, eSelectWord
+ int32_t mStartOffset;
+
+ // mDesiredPos: The desired inline coordinate for the caret
+ // (one of .x or .y will be used, depending on line's writing mode)
+ // Used with: eSelectLine.
+ nsPoint mDesiredPos;
+
+ // mWordMovementType: An enum that determines whether to prefer the start or end of a word
+ // or to use the default beahvior, which is a combination of
+ // direction and the platform-based pref
+ // "layout.word_select.eat_space_to_next_word"
+ mozilla::EWordMovementType mWordMovementType;
+
+ // mJumpLines: Whether to allow jumping across line boundaries.
+ // Used with: eSelectCharacter, eSelectWord.
+ bool mJumpLines;
+
+ // mScrollViewStop: Whether to stop when reaching a scroll view boundary.
+ // Used with: eSelectCharacter, eSelectWord, eSelectLine.
+ bool mScrollViewStop;
+
+ // mIsKeyboardSelect: Whether the peeking is done in response to a keyboard action.
+ // Used with: eSelectWord.
+ bool mIsKeyboardSelect;
+
+ // mVisual: Whether bidi caret behavior is visual (true) or logical (false).
+ // Used with: eSelectCharacter, eSelectWord, eSelectBeginLine, eSelectEndLine.
+ bool mVisual;
+
+ // mExtend: Whether the selection is being extended or moved.
+ bool mExtend;
+
+ /*** Output arguments ***/
+
+ // mResultContent: Content reached as a result of the peek.
+ nsCOMPtr<nsIContent> mResultContent;
+
+ // mResultFrame: Frame reached as a result of the peek.
+ // Used with: eSelectCharacter, eSelectWord.
+ nsIFrame *mResultFrame;
+
+ // mContentOffset: Offset into content reached as a result of the peek.
+ int32_t mContentOffset;
+
+ // mAttachForward: When the result position is between two frames,
+ // indicates which of the two frames the caret should be painted in.
+ // false means "the end of the frame logically before the caret",
+ // true means "the beginning of the frame logically after the caret".
+ // Used with: eSelectLine, eSelectBeginLine, eSelectEndLine.
+ mozilla::CaretAssociationHint mAttach;
+};
+
+struct nsPrevNextBidiLevels
+{
+ void SetData(nsIFrame* aFrameBefore,
+ nsIFrame* aFrameAfter,
+ nsBidiLevel aLevelBefore,
+ nsBidiLevel aLevelAfter)
+ {
+ mFrameBefore = aFrameBefore;
+ mFrameAfter = aFrameAfter;
+ mLevelBefore = aLevelBefore;
+ mLevelAfter = aLevelAfter;
+ }
+ nsIFrame* mFrameBefore;
+ nsIFrame* mFrameAfter;
+ nsBidiLevel mLevelBefore;
+ nsBidiLevel mLevelAfter;
+};
+
+namespace mozilla {
+namespace dom {
+class Selection;
+class SelectionChangeListener;
+} // namespace dom
+} // namespace mozilla
+class nsIScrollableFrame;
+
+/**
+ * Methods which are marked with *unsafe* should be handled with special care.
+ * They may cause nsFrameSelection to be deleted, if strong pointer isn't used,
+ * or they may cause other objects to be deleted.
+ */
+
+class nsFrameSelection final {
+public:
+ typedef mozilla::CaretAssociationHint CaretAssociateHint;
+
+ /*interfaces for addref and release and queryinterface*/
+
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsFrameSelection)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsFrameSelection)
+
+ /** Init will initialize the frame selector with the necessary pres shell to
+ * be used by most of the methods
+ * @param aShell is the parameter to be used for most of the other calls for callbacks etc
+ * @param aLimiter limits the selection to nodes with aLimiter parents
+ */
+ void Init(nsIPresShell *aShell, nsIContent *aLimiter);
+
+ /** HandleClick will take the focus to the new frame at the new offset and
+ * will either extend the selection from the old anchor, or replace the old anchor.
+ * the old anchor and focus position may also be used to deselect things
+ * @param aNewfocus is the content that wants the focus
+ * @param aContentOffset is the content offset of the parent aNewFocus
+ * @param aContentOffsetEnd is the content offset of the parent aNewFocus and is specified different
+ * when you need to select to and include both start and end points
+ * @param aContinueSelection is the flag that tells the selection to keep the old anchor point or not.
+ * @param aMultipleSelection will tell the frame selector to replace /or not the old selection.
+ * cannot coexist with aContinueSelection
+ * @param aHint will tell the selection which direction geometrically to actually show the caret on.
+ * 1 = end of this line 0 = beginning of this line
+ */
+ /*unsafe*/
+ nsresult HandleClick(nsIContent *aNewFocus,
+ uint32_t aContentOffset,
+ uint32_t aContentEndOffset,
+ bool aContinueSelection,
+ bool aMultipleSelection,
+ CaretAssociateHint aHint);
+
+ /** HandleDrag extends the selection to contain the frame closest to aPoint.
+ * @param aPresContext is the context to use when figuring out what frame contains the point.
+ * @param aFrame is the parent of all frames to use when searching for the closest frame to the point.
+ * @param aPoint is relative to aFrame
+ */
+ /*unsafe*/
+ void HandleDrag(nsIFrame *aFrame, nsPoint aPoint);
+
+ /** HandleTableSelection will set selection to a table, cell, etc
+ * depending on information contained in aFlags
+ * @param aParentContent is the paretent of either a table or cell that user clicked or dragged the mouse in
+ * @param aContentOffset is the offset of the table or cell
+ * @param aTarget indicates what to select (defined in nsISelectionPrivate.idl/nsISelectionPrivate.h):
+ * TABLESELECTION_CELL We should select a cell (content points to the cell)
+ * TABLESELECTION_ROW We should select a row (content points to any cell in row)
+ * TABLESELECTION_COLUMN We should select a row (content points to any cell in column)
+ * TABLESELECTION_TABLE We should select a table (content points to the table)
+ * TABLESELECTION_ALLCELLS We should select all cells (content points to any cell in table)
+ * @param aMouseEvent passed in so we can get where event occurred and what keys are pressed
+ */
+ /*unsafe*/
+ nsresult HandleTableSelection(nsINode* aParentContent,
+ int32_t aContentOffset,
+ int32_t aTarget,
+ mozilla::WidgetMouseEvent* aMouseEvent);
+
+ /**
+ * Add cell to the selection.
+ *
+ * @param aCell [in] HTML td element.
+ */
+ virtual nsresult SelectCellElement(nsIContent *aCell);
+
+ /**
+ * Add cells to the selection inside of the given cells range.
+ *
+ * @param aTable [in] HTML table element
+ * @param aStartRowIndex [in] row index where the cells range starts
+ * @param aStartColumnIndex [in] column index where the cells range starts
+ * @param aEndRowIndex [in] row index where the cells range ends
+ * @param aEndColumnIndex [in] column index where the cells range ends
+ */
+ virtual nsresult AddCellsToSelection(nsIContent *aTable,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex);
+
+ /**
+ * Remove cells from selection inside of the given cell range.
+ *
+ * @param aTable [in] HTML table element
+ * @param aStartRowIndex [in] row index where the cells range starts
+ * @param aStartColumnIndex [in] column index where the cells range starts
+ * @param aEndRowIndex [in] row index where the cells range ends
+ * @param aEndColumnIndex [in] column index where the cells range ends
+ */
+ virtual nsresult RemoveCellsFromSelection(nsIContent *aTable,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex);
+
+ /**
+ * Remove cells from selection outside of the given cell range.
+ *
+ * @param aTable [in] HTML table element
+ * @param aStartRowIndex [in] row index where the cells range starts
+ * @param aStartColumnIndex [in] column index where the cells range starts
+ * @param aEndRowIndex [in] row index where the cells range ends
+ * @param aEndColumnIndex [in] column index where the cells range ends
+ */
+ virtual nsresult RestrictCellsToSelection(nsIContent *aTable,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex);
+
+ /** StartAutoScrollTimer is responsible for scrolling frames so that
+ * aPoint is always visible, and for selecting any frame that contains
+ * aPoint. The timer will also reset itself to fire again if we have
+ * not scrolled to the end of the document.
+ * @param aFrame is the outermost frame to use when searching for
+ * the closest frame for the point, i.e. the frame that is capturing
+ * the mouse
+ * @param aPoint is relative to aFrame.
+ * @param aDelay is the timer's interval.
+ */
+ /*unsafe*/
+ nsresult StartAutoScrollTimer(nsIFrame *aFrame,
+ nsPoint aPoint,
+ uint32_t aDelay);
+
+ /** StopAutoScrollTimer stops any active auto scroll timer.
+ */
+ void StopAutoScrollTimer();
+
+ /** Lookup Selection
+ * returns in frame coordinates the selection beginning and ending with the type of selection given
+ * @param aContent is the content asking
+ * @param aContentOffset is the starting content boundary
+ * @param aContentLength is the length of the content piece asking
+ * @param aReturnDetails linkedlist of return values for the selection.
+ * @param aSlowCheck will check using slow method with no shortcuts
+ */
+ SelectionDetails* LookUpSelection(nsIContent *aContent,
+ int32_t aContentOffset,
+ int32_t aContentLength,
+ bool aSlowCheck) const;
+
+ /** SetDragState(bool);
+ * sets the drag state to aState for resons of drag state.
+ * @param aState is the new state of drag
+ */
+ /*unsafe*/
+ void SetDragState(bool aState);
+
+ /** GetDragState(bool *);
+ * gets the drag state to aState for resons of drag state.
+ * @param aState will hold the state of drag
+ */
+ bool GetDragState() const { return mDragState; }
+
+ /**
+ if we are in table cell selection mode. aka ctrl click in table cell
+ */
+ bool GetTableCellSelection() const { return mSelectingTableCellMode != 0; }
+ void ClearTableCellSelection() { mSelectingTableCellMode = 0; }
+
+ /** GetSelection
+ * no query interface for selection. must use this method now.
+ * @param aSelectionType The selection type what you want.
+ */
+ mozilla::dom::Selection*
+ GetSelection(mozilla::SelectionType aSelectionType) const;
+
+ /**
+ * ScrollSelectionIntoView scrolls a region of the selection,
+ * so that it is visible in the scrolled view.
+ *
+ * @param aSelectionType the selection to scroll into view.
+ * @param aRegion the region inside the selection to scroll into view.
+ * @param aFlags the scroll flags. Valid bits include:
+ * SCROLL_SYNCHRONOUS: when set, scrolls the selection into view
+ * before returning. If not set, posts a request which is processed
+ * at some point after the method returns.
+ * SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be scrolled
+ * into view.
+ *
+ */
+ /*unsafe*/
+ nsresult ScrollSelectionIntoView(mozilla::SelectionType aSelectionType,
+ SelectionRegion aRegion,
+ int16_t aFlags) const;
+
+ /** RepaintSelection repaints the selected frames that are inside the selection
+ * specified by aSelectionType.
+ * @param aSelectionType The selection type what you want to repaint.
+ */
+ nsresult RepaintSelection(mozilla::SelectionType aSelectionType);
+
+ /** GetFrameForNodeOffset given a node and its child offset, return the nsIFrame and
+ * the offset into that frame.
+ * @param aNode input parameter for the node to look at
+ * @param aOffset offset into above node.
+ * @param aReturnOffset will contain offset into frame.
+ */
+ virtual nsIFrame* GetFrameForNodeOffset(nsIContent* aNode,
+ int32_t aOffset,
+ CaretAssociateHint aHint,
+ int32_t* aReturnOffset) const;
+
+ /**
+ * Scrolling then moving caret placement code in common to text areas and
+ * content areas should be located in the implementer
+ * This method will accept the following parameters and perform the scroll
+ * and caret movement. It remains for the caller to call the final
+ * ScrollCaretIntoView if that called wants to be sure the caret is always
+ * visible.
+ *
+ * @param aForward if true, scroll forward if not scroll backward
+ * @param aExtend if true, extend selection to the new point
+ * @param aScrollableFrame the frame to scroll
+ */
+ /*unsafe*/
+ void CommonPageMove(bool aForward,
+ bool aExtend,
+ nsIScrollableFrame* aScrollableFrame);
+
+ void SetHint(CaretAssociateHint aHintRight) { mHint = aHintRight; }
+ CaretAssociateHint GetHint() const { return mHint; }
+
+ /** SetCaretBidiLevel sets the caret bidi level
+ * @param aLevel the caret bidi level
+ * This method is virtual since it gets called from outside of layout.
+ */
+ virtual void SetCaretBidiLevel(nsBidiLevel aLevel);
+ /** GetCaretBidiLevel gets the caret bidi level
+ * This method is virtual since it gets called from outside of layout.
+ */
+ virtual nsBidiLevel GetCaretBidiLevel() const;
+ /** UndefineCaretBidiLevel sets the caret bidi level to "undefined"
+ * This method is virtual since it gets called from outside of layout.
+ */
+ virtual void UndefineCaretBidiLevel();
+
+ /** PhysicalMove will generally be called from the nsiselectioncontroller implementations.
+ * the effect being the selection will move one unit 'aAmount' in the
+ * given aDirection.
+ * @param aDirection the direction to move the selection
+ * @param aAmount amount of movement (char/line; word/page; eol/doc)
+ * @param aExtend continue selection
+ */
+ /*unsafe*/
+ nsresult PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend);
+
+ /** CharacterMove will generally be called from the nsiselectioncontroller implementations.
+ * the effect being the selection will move one character left or right.
+ * @param aForward move forward in document.
+ * @param aExtend continue selection
+ */
+ /*unsafe*/
+ nsresult CharacterMove(bool aForward, bool aExtend);
+
+ /** CharacterExtendForDelete extends the selection forward (logically) to
+ * the next character cell, so that the selected cell can be deleted.
+ */
+ /*unsafe*/
+ nsresult CharacterExtendForDelete();
+
+ /** CharacterExtendForBackspace extends the selection backward (logically) to
+ * the previous character cell, so that the selected cell can be deleted.
+ */
+ /*unsafe*/
+ nsresult CharacterExtendForBackspace();
+
+ /** WordMove will generally be called from the nsiselectioncontroller implementations.
+ * the effect being the selection will move one word left or right.
+ * @param aForward move forward in document.
+ * @param aExtend continue selection
+ */
+ /*unsafe*/
+ nsresult WordMove(bool aForward, bool aExtend);
+
+ /** WordExtendForDelete extends the selection backward or forward (logically) to the
+ * next word boundary, so that the selected word can be deleted.
+ * @param aForward select forward in document.
+ */
+ /*unsafe*/
+ nsresult WordExtendForDelete(bool aForward);
+
+ /** LineMove will generally be called from the nsiselectioncontroller implementations.
+ * the effect being the selection will move one line up or down.
+ * @param aForward move forward in document.
+ * @param aExtend continue selection
+ */
+ /*unsafe*/
+ nsresult LineMove(bool aForward, bool aExtend);
+
+ /** IntraLineMove will generally be called from the nsiselectioncontroller implementations.
+ * the effect being the selection will move to beginning or end of line
+ * @param aForward move forward in document.
+ * @param aExtend continue selection
+ */
+ /*unsafe*/
+ nsresult IntraLineMove(bool aForward, bool aExtend);
+
+ /** Select All will generally be called from the nsiselectioncontroller implementations.
+ * it will select the whole doc
+ */
+ /*unsafe*/
+ nsresult SelectAll();
+
+ /** Sets/Gets The display selection enum.
+ */
+ void SetDisplaySelection(int16_t aState) { mDisplaySelection = aState; }
+ int16_t GetDisplaySelection() const { return mDisplaySelection; }
+
+ /** This method can be used to store the data received during a MouseDown
+ * event so that we can place the caret during the MouseUp event.
+ * @aMouseEvent the event received by the selection MouseDown
+ * handling method. A nullptr value can be use to tell this method
+ * that any data is storing is no longer valid.
+ */
+ void SetDelayedCaretData(mozilla::WidgetMouseEvent* aMouseEvent);
+
+ /** Get the delayed MouseDown event data necessary to place the
+ * caret during MouseUp processing.
+ * @return a pointer to the event received
+ * by the selection during MouseDown processing. It can be nullptr
+ * if the data is no longer valid.
+ */
+ bool HasDelayedCaretData() { return mDelayedMouseEventValid; }
+ bool IsShiftDownInDelayedCaretData()
+ {
+ NS_ASSERTION(mDelayedMouseEventValid, "No valid delayed caret data");
+ return mDelayedMouseEventIsShift;
+ }
+ uint32_t GetClickCountInDelayedCaretData()
+ {
+ NS_ASSERTION(mDelayedMouseEventValid, "No valid delayed caret data");
+ return mDelayedMouseEventClickCount;
+ }
+
+ bool MouseDownRecorded()
+ {
+ return !GetDragState() &&
+ HasDelayedCaretData() &&
+ GetClickCountInDelayedCaretData() < 2;
+ }
+
+ /** Get the content node that limits the selection
+ * When searching up a nodes for parents, as in a text edit field
+ * in an browser page, we must stop at this node else we reach into the
+ * parent page, which is very bad!
+ */
+ nsIContent* GetLimiter() const { return mLimiter; }
+
+ nsIContent* GetAncestorLimiter() const { return mAncestorLimiter; }
+ /*unsafe*/
+ void SetAncestorLimiter(nsIContent *aLimiter);
+
+ /** This will tell the frame selection that a double click has been pressed
+ * so it can track abort future drags if inside the same selection
+ * @aDoubleDown has the double click down happened
+ */
+ void SetMouseDoubleDown(bool aDoubleDown) { mMouseDoubleDownState = aDoubleDown; }
+
+ /** This will return whether the double down flag was set.
+ * @return whether the double down flag was set
+ */
+ bool GetMouseDoubleDown() const { return mMouseDoubleDownState; }
+
+ /** GetPrevNextBidiLevels will return the frames and associated Bidi levels of the characters
+ * logically before and after a (collapsed) selection.
+ * @param aNode is the node containing the selection
+ * @param aContentOffset is the offset of the selection in the node
+ * @param aJumpLines If true, look across line boundaries.
+ * If false, behave as if there were base-level frames at line edges.
+ *
+ * @return A struct holding the before/after frame and the before/after level.
+ *
+ * At the beginning and end of each line there is assumed to be a frame with
+ * Bidi level equal to the paragraph embedding level.
+ * In these cases the before frame and after frame respectively will be
+ * nullptr.
+ *
+ * This method is virtual since it gets called from outside of layout.
+ */
+ virtual nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent *aNode,
+ uint32_t aContentOffset,
+ bool aJumpLines) const;
+
+ /** GetFrameFromLevel will scan in a given direction
+ * until it finds a frame with a Bidi level less than or equal to a given level.
+ * It will return the last frame before this.
+ * @param aPresContext is the context to use
+ * @param aFrameIn is the frame to start from
+ * @param aDirection is the direction to scan
+ * @param aBidiLevel is the level to search for
+ * @param aFrameOut will hold the frame returned
+ */
+ nsresult GetFrameFromLevel(nsIFrame *aFrameIn,
+ nsDirection aDirection,
+ nsBidiLevel aBidiLevel,
+ nsIFrame **aFrameOut) const;
+
+ /**
+ * MaintainSelection will track the current selection as being "sticky".
+ * Dragging or extending selection will never allow for a subset
+ * (or the whole) of the maintained selection to become unselected.
+ * Primary use: double click selecting then dragging on second click
+ * @param aAmount the initial amount of text selected (word, line or paragraph).
+ * For "line", use eSelectBeginLine.
+ */
+ nsresult MaintainSelection(nsSelectionAmount aAmount = eSelectNoAmount);
+
+ nsresult ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame,
+ nsPoint& aPoint,
+ nsIFrame **aRetFrame,
+ nsPoint& aRetPoint);
+
+ nsFrameSelection();
+
+ void StartBatchChanges();
+ void EndBatchChanges(int16_t aReason = nsISelectionListener::NO_REASON);
+
+ /*unsafe*/
+ nsresult DeleteFromDocument();
+
+ nsIPresShell *GetShell()const { return mShell; }
+
+ void DisconnectFromPresShell();
+ nsresult ClearNormalSelection();
+
+private:
+ ~nsFrameSelection();
+
+ nsresult TakeFocus(nsIContent *aNewFocus,
+ uint32_t aContentOffset,
+ uint32_t aContentEndOffset,
+ CaretAssociateHint aHint,
+ bool aContinueSelection,
+ bool aMultipleSelection);
+
+ void BidiLevelFromMove(nsIPresShell* aPresShell,
+ nsIContent *aNode,
+ uint32_t aContentOffset,
+ nsSelectionAmount aAmount,
+ CaretAssociateHint aHint);
+ void BidiLevelFromClick(nsIContent *aNewFocus, uint32_t aContentOffset);
+ nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent *aNode,
+ uint32_t aContentOffset,
+ CaretAssociateHint aHint,
+ bool aJumpLines) const;
+
+ bool AdjustForMaintainedSelection(nsIContent *aContent, int32_t aOffset);
+
+// post and pop reasons for notifications. we may stack these later
+ void PostReason(int16_t aReason) { mSelectionChangeReason = aReason; }
+ int16_t PopReason()
+ {
+ int16_t retval = mSelectionChangeReason;
+ mSelectionChangeReason = nsISelectionListener::NO_REASON;
+ return retval;
+ }
+ bool IsUserSelectionReason() const
+ {
+ return (mSelectionChangeReason &
+ (nsISelectionListener::DRAG_REASON |
+ nsISelectionListener::MOUSEDOWN_REASON |
+ nsISelectionListener::MOUSEUP_REASON |
+ nsISelectionListener::KEYPRESS_REASON)) !=
+ nsISelectionListener::NO_REASON;
+ }
+
+ friend class mozilla::dom::Selection;
+ friend class mozilla::dom::SelectionChangeListener;
+ friend struct mozilla::AutoPrepareFocusRange;
+#ifdef DEBUG
+ void printSelection(); // for debugging
+#endif /* DEBUG */
+
+ void ResizeBuffer(uint32_t aNewBufSize);
+
+/*HELPER METHODS*/
+ // Whether MoveCaret should use logical or visual movement,
+ // or follow the bidi.edit.caret_movement_style preference.
+ enum CaretMovementStyle {
+ eLogical,
+ eVisual,
+ eUsePrefStyle
+ };
+ nsresult MoveCaret(nsDirection aDirection, bool aContinueSelection,
+ nsSelectionAmount aAmount,
+ CaretMovementStyle aMovementStyle);
+
+ nsresult FetchDesiredPos(nsPoint &aDesiredPos); //the position requested by the Key Handling for up down
+ void InvalidateDesiredPos(); //do not listen to mDesiredPos you must get another.
+ void SetDesiredPos(nsPoint aPos); //set the mDesiredPos
+
+ uint32_t GetBatching() const {return mBatching; }
+ bool GetNotifyFrames() const { return mNotifyFrames; }
+ void SetDirty(bool aDirty=true){if (mBatching) mChangesDuringBatching = aDirty;}
+
+ // nsFrameSelection may get deleted when calling this,
+ // so remember to use nsCOMPtr when needed.
+ nsresult NotifySelectionListeners(mozilla::SelectionType aSelectionType);
+ // Update the selection cache on repaint when the
+ // selection being repainted is not empty.
+ nsresult UpdateSelectionCacheOnRepaintSelection(mozilla::dom::
+ Selection* aSel);
+
+ RefPtr<mozilla::dom::Selection>
+ mDomSelections[mozilla::kPresentSelectionTypeCount];
+
+ // Table selection support.
+ nsITableCellLayout* GetCellLayout(nsIContent *aCellContent) const;
+
+ nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
+ nsresult SelectRowOrColumn(nsIContent *aCellContent, uint32_t aTarget);
+ nsresult UnselectCells(nsIContent *aTable,
+ int32_t aStartRowIndex, int32_t aStartColumnIndex,
+ int32_t aEndRowIndex, int32_t aEndColumnIndex,
+ bool aRemoveOutsideOfCellRange);
+
+ nsresult GetCellIndexes(nsIContent *aCell, int32_t &aRowIndex, int32_t &aColIndex);
+
+ // Get our first range, if its first selected node is a cell. If this does
+ // not return null, then the first node in the returned range is a cell
+ // (according to GetFirstCellNodeInRange).
+ nsRange* GetFirstCellRange();
+ // Get our next range, if its first selected node is a cell. If this does
+ // not return null, then the first node in the returned range is a cell
+ // (according to GetFirstCellNodeInRange).
+ nsRange* GetNextCellRange();
+ nsIContent* GetFirstCellNodeInRange(nsRange *aRange) const;
+ // Returns non-null table if in same table, null otherwise
+ nsIContent* IsInSameTable(nsIContent *aContent1, nsIContent *aContent2) const;
+ // Might return null
+ nsIContent* GetParentTable(nsIContent *aCellNode) const;
+ nsresult CreateAndAddRange(nsINode *aParentNode, int32_t aOffset);
+
+ nsCOMPtr<nsINode> mCellParent; //used to snap to table selection
+ nsCOMPtr<nsIContent> mStartSelectedCell;
+ nsCOMPtr<nsIContent> mEndSelectedCell;
+ nsCOMPtr<nsIContent> mAppendStartSelectedCell;
+ nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
+ int32_t mSelectingTableCellMode;
+ int32_t mSelectedCellIndex;
+
+ // maintain selection
+ RefPtr<nsRange> mMaintainRange;
+ nsSelectionAmount mMaintainedAmount;
+
+ //batching
+ int32_t mBatching;
+
+ // Limit selection navigation to a child of this node.
+ nsCOMPtr<nsIContent> mLimiter;
+ // Limit selection navigation to a descendant of this node.
+ nsCOMPtr<nsIContent> mAncestorLimiter;
+
+ nsIPresShell *mShell;
+
+ int16_t mSelectionChangeReason; // reason for notifications of selection changing
+ int16_t mDisplaySelection; //for visual display purposes.
+
+ CaretAssociateHint mHint; //hint to tell if the selection is at the end of this line or beginning of next
+ nsBidiLevel mCaretBidiLevel;
+ nsBidiLevel mKbdBidiLevel;
+
+ nsPoint mDesiredPos;
+ uint32_t mDelayedMouseEventClickCount;
+ bool mDelayedMouseEventIsShift;
+ bool mDelayedMouseEventValid;
+
+ bool mChangesDuringBatching;
+ bool mNotifyFrames;
+ bool mDragSelectingCells;
+ bool mDragState; //for drag purposes
+ bool mMouseDoubleDownState; //has the doubleclick down happened
+ bool mDesiredPosSet;
+
+ int8_t mCaretMovementStyle;
+
+ static bool sSelectionEventsEnabled;
+ static bool sSelectionEventsOnTextControlsEnabled;
+};
+
+#endif /* nsFrameSelection_h___ */
diff --git a/layout/generic/nsFrameSetFrame.cpp b/layout/generic/nsFrameSetFrame.cpp
new file mode 100644
index 000000000..87cb31fd0
--- /dev/null
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -0,0 +1,1648 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for HTML <frameset> elements */
+
+#include "nsFrameSetFrame.h"
+
+#include "gfxContext.h"
+#include "gfxUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/Likely.h"
+
+#include "nsGenericHTMLElement.h"
+#include "nsAttrValueInlines.h"
+#include "nsLeafFrame.h"
+#include "nsContainerFrame.h"
+#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsStyleContext.h"
+#include "nsHTMLParts.h"
+#include "nsRenderingContext.h"
+#include "nsIDOMMutationEvent.h"
+#include "nsNameSpaceManager.h"
+#include "nsCSSAnonBoxes.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/dom/Element.h"
+#include "nsDisplayList.h"
+#include "nsNodeUtils.h"
+#include "mozAutoDocUpdate.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/HTMLFrameSetElement.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MouseEvents.h"
+#include "nsSubDocumentFrame.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+
+// masks for mEdgeVisibility
+#define LEFT_VIS 0x0001
+#define RIGHT_VIS 0x0002
+#define TOP_VIS 0x0004
+#define BOTTOM_VIS 0x0008
+#define ALL_VIS 0x000F
+#define NONE_VIS 0x0000
+
+/*******************************************************************************
+ * nsFramesetDrag
+ ******************************************************************************/
+nsFramesetDrag::nsFramesetDrag()
+{
+ UnSet();
+}
+
+void nsFramesetDrag::Reset(bool aVertical,
+ int32_t aIndex,
+ int32_t aChange,
+ nsHTMLFramesetFrame* aSource)
+{
+ mVertical = aVertical;
+ mIndex = aIndex;
+ mChange = aChange;
+ mSource = aSource;
+}
+
+void nsFramesetDrag::UnSet()
+{
+ mVertical = true;
+ mIndex = -1;
+ mChange = 0;
+ mSource = nullptr;
+}
+
+/*******************************************************************************
+ * nsHTMLFramesetBorderFrame
+ ******************************************************************************/
+class nsHTMLFramesetBorderFrame : public nsLeafFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ virtual nsresult HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) override;
+
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ bool GetVisibility() { return mVisibility; }
+ void SetVisibility(bool aVisibility);
+ void SetColor(nscolor aColor);
+
+ void PaintBorder(DrawTarget* aDrawTarget, nsPoint aPt);
+
+protected:
+ nsHTMLFramesetBorderFrame(nsStyleContext* aContext, int32_t aWidth, bool aVertical, bool aVisible);
+ virtual ~nsHTMLFramesetBorderFrame();
+ virtual nscoord GetIntrinsicISize() override;
+ virtual nscoord GetIntrinsicBSize() override;
+
+ // the prev and next neighbors are indexes into the row (for a horizontal border) or col (for
+ // a vertical border) of nsHTMLFramesetFrames or nsHTMLFrames
+ int32_t mPrevNeighbor;
+ int32_t mNextNeighbor;
+ nscolor mColor;
+ int32_t mWidth;
+ bool mVertical;
+ bool mVisibility;
+ bool mCanResize;
+ friend class nsHTMLFramesetFrame;
+};
+/*******************************************************************************
+ * nsHTMLFramesetBlankFrame
+ ******************************************************************************/
+class nsHTMLFramesetBlankFrame : public nsLeafFrame
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsHTMLFramesetBlankFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override
+ {
+ return MakeFrameName(NS_LITERAL_STRING("FramesetBlank"), aResult);
+ }
+#endif
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+protected:
+ explicit nsHTMLFramesetBlankFrame(nsStyleContext* aContext) : nsLeafFrame(aContext) {}
+ virtual ~nsHTMLFramesetBlankFrame();
+ virtual nscoord GetIntrinsicISize() override;
+ virtual nscoord GetIntrinsicBSize() override;
+
+ friend class nsHTMLFramesetFrame;
+ friend class nsHTMLFrameset;
+};
+
+/*******************************************************************************
+ * nsHTMLFramesetFrame
+ ******************************************************************************/
+bool nsHTMLFramesetFrame::gDragInProgress = false;
+#define DEFAULT_BORDER_WIDTH_PX 6
+
+nsHTMLFramesetFrame::nsHTMLFramesetFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+{
+ mNumRows = 0;
+ mNumCols = 0;
+ mEdgeVisibility = 0;
+ mParentFrameborder = eFrameborder_Yes; // default
+ mParentBorderWidth = -1; // default not set
+ mParentBorderColor = NO_COLOR; // default not set
+ mFirstDragPoint.x = mFirstDragPoint.y = 0;
+ mMinDrag = nsPresContext::CSSPixelsToAppUnits(2);
+ mNonBorderChildCount = 0;
+ mNonBlankChildCount = 0;
+ mDragger = nullptr;
+ mChildCount = 0;
+ mTopLevelFrameset = nullptr;
+ mEdgeColors.Set(NO_COLOR);
+}
+
+nsHTMLFramesetFrame::~nsHTMLFramesetFrame()
+{
+}
+
+NS_QUERYFRAME_HEAD(nsHTMLFramesetFrame)
+ NS_QUERYFRAME_ENTRY(nsHTMLFramesetFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+void
+nsHTMLFramesetFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+ // find the highest ancestor that is a frameset
+ nsIFrame* parentFrame = GetParent();
+ mTopLevelFrameset = this;
+ while (parentFrame) {
+ nsHTMLFramesetFrame* frameset = do_QueryFrame(parentFrame);
+ if (frameset) {
+ mTopLevelFrameset = frameset;
+ parentFrame = parentFrame->GetParent();
+ } else {
+ break;
+ }
+ }
+
+ nsPresContext* presContext = PresContext();
+ nsIPresShell* shell = presContext->PresShell();
+
+ nsFrameborder frameborder = GetFrameBorder();
+ int32_t borderWidth = GetBorderWidth(presContext, false);
+ nscolor borderColor = GetBorderColor();
+
+ // Get the rows= cols= data
+ HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
+ NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
+ const nsFramesetSpec* rowSpecs = nullptr;
+ const nsFramesetSpec* colSpecs = nullptr;
+ // GetRowSpec and GetColSpec can fail, but when they do they set
+ // mNumRows and mNumCols respectively to 0, so we deal with it fine.
+ ourContent->GetRowSpec(&mNumRows, &rowSpecs);
+ ourContent->GetColSpec(&mNumCols, &colSpecs);
+
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscoord),
+ "Maximum value of mNumRows and mNumCols is NS_MAX_FRAMESET_SPEC_COUNT");
+ mRowSizes = MakeUnique<nscoord[]>(mNumRows);
+ mColSizes = MakeUnique<nscoord[]>(mNumCols);
+
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT,
+ "Should not overflow numCells");
+ int32_t numCells = mNumRows*mNumCols;
+
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nsHTMLFramesetBorderFrame*),
+ "Should not overflow nsHTMLFramesetBorderFrame");
+ mVerBorders = MakeUnique<nsHTMLFramesetBorderFrame*[]>(mNumCols); // 1 more than number of ver borders
+
+ for (int verX = 0; verX < mNumCols; verX++)
+ mVerBorders[verX] = nullptr;
+
+ mHorBorders = MakeUnique<nsHTMLFramesetBorderFrame*[]>(mNumRows); // 1 more than number of hor borders
+
+ for (int horX = 0; horX < mNumRows; horX++)
+ mHorBorders[horX] = nullptr;
+
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT
+ < UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT,
+ "Should not overflow numCells");
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT
+ < UINT_MAX / sizeof(nsFrameborder) / NS_MAX_FRAMESET_SPEC_COUNT,
+ "Should not overflow numCells");
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT
+ < UINT_MAX / sizeof(nsBorderColor) / NS_MAX_FRAMESET_SPEC_COUNT,
+ "Should not overflow numCells");
+ mChildFrameborder = MakeUnique<nsFrameborder[]>(numCells);
+ mChildBorderColors = MakeUnique<nsBorderColor[]>(numCells);
+
+ // create the children frames; skip content which isn't <frameset> or <frame>
+ mChildCount = 0; // number of <frame> or <frameset> children
+ nsIFrame* frame;
+
+ // number of any type of children
+ uint32_t numChildren = mContent->GetChildCount();
+
+ for (uint32_t childX = 0; childX < numChildren; childX++) {
+ if (mChildCount == numCells) { // we have more <frame> or <frameset> than cells
+ // Clear the lazy bits in the remaining children. Also clear
+ // the restyle flags, like nsCSSFrameConstructor::ProcessChildren does.
+ for (uint32_t i = childX; i < numChildren; i++) {
+ nsIContent *child = mContent->GetChildAt(i);
+ child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
+ child->UnsetRestyleFlagsIfGecko();
+ }
+ break;
+ }
+ nsIContent *child = mContent->GetChildAt(childX);
+ child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
+ // Also clear the restyle flags in the child like
+ // nsCSSFrameConstructor::ProcessChildren does.
+ child->UnsetRestyleFlagsIfGecko();
+
+ // IMPORTANT: This must match the conditions in
+ // nsCSSFrameConstructor::ContentAppended/Inserted/Removed
+ if (!child->IsHTMLElement())
+ continue;
+
+ if (child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) {
+ RefPtr<nsStyleContext> kidSC;
+
+ kidSC = shell->StyleSet()->ResolveStyleFor(child->AsElement(),
+ mStyleContext);
+ if (child->IsHTMLElement(nsGkAtoms::frameset)) {
+ frame = NS_NewHTMLFramesetFrame(shell, kidSC);
+
+ nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame;
+ childFrame->SetParentFrameborder(frameborder);
+ childFrame->SetParentBorderWidth(borderWidth);
+ childFrame->SetParentBorderColor(borderColor);
+ frame->Init(child, this, nullptr);
+
+ mChildBorderColors[mChildCount].Set(childFrame->GetBorderColor());
+ } else { // frame
+ frame = NS_NewSubDocumentFrame(shell, kidSC);
+
+ frame->Init(child, this, nullptr);
+
+ mChildFrameborder[mChildCount] = GetFrameBorder(child);
+ mChildBorderColors[mChildCount].Set(GetBorderColor(child));
+ }
+ child->SetPrimaryFrame(frame);
+
+ mFrames.AppendFrame(nullptr, frame);
+
+ mChildCount++;
+ }
+ }
+
+ mNonBlankChildCount = mChildCount;
+ // add blank frames for frameset cells that had no content provided
+ for (int blankX = mChildCount; blankX < numCells; blankX++) {
+ RefPtr<nsStyleContext> pseudoStyleContext;
+ pseudoStyleContext = shell->StyleSet()->
+ ResolveAnonymousBoxStyle(nsCSSAnonBoxes::framesetBlank, mStyleContext);
+
+ // XXX the blank frame is using the content of its parent - at some point it
+ // should just have null content, if we support that
+ nsHTMLFramesetBlankFrame* blankFrame = new (shell) nsHTMLFramesetBlankFrame(pseudoStyleContext);
+
+ blankFrame->Init(mContent, this, nullptr);
+
+ mFrames.AppendFrame(nullptr, blankFrame);
+
+ mChildBorderColors[mChildCount].Set(NO_COLOR);
+ mChildCount++;
+ }
+
+ mNonBorderChildCount = mChildCount;
+}
+
+void
+nsHTMLFramesetFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ // We do this weirdness where we create our child frames in Init(). On the
+ // other hand, we're going to get a SetInitialChildList() with an empty list
+ // and null list name after the frame constructor is done creating us. So
+ // just ignore that call.
+ if (aListID == kPrincipalList && aChildList.IsEmpty()) {
+ return;
+ }
+
+ nsContainerFrame::SetInitialChildList(aListID, aChildList);
+}
+
+// XXX should this try to allocate twips based on an even pixel boundary?
+void nsHTMLFramesetFrame::Scale(nscoord aDesired,
+ int32_t aNumIndicies,
+ int32_t* aIndicies,
+ int32_t aNumItems,
+ int32_t* aItems)
+{
+ int32_t actual = 0;
+ int32_t i, j;
+ // get the actual total
+ for (i = 0; i < aNumIndicies; i++) {
+ j = aIndicies[i];
+ actual += aItems[j];
+ }
+
+ if (actual > 0) {
+ float factor = (float)aDesired / (float)actual;
+ actual = 0;
+ // scale the items up or down
+ for (i = 0; i < aNumIndicies; i++) {
+ j = aIndicies[i];
+ aItems[j] = NSToCoordRound((float)aItems[j] * factor);
+ actual += aItems[j];
+ }
+ } else if (aNumIndicies != 0) {
+ // All the specs say zero width, but we have to fill up space
+ // somehow. Distribute it equally.
+ nscoord width = NSToCoordRound((float)aDesired / (float)aNumIndicies);
+ actual = width * aNumIndicies;
+ for (i = 0; i < aNumIndicies; i++) {
+ aItems[aIndicies[i]] = width;
+ }
+ }
+
+ if (aNumIndicies > 0 && aDesired != actual) {
+ int32_t unit = (aDesired > actual) ? 1 : -1;
+ for (i=0; (i < aNumIndicies) && (aDesired != actual); i++) {
+ j = aIndicies[i];
+ if (j < aNumItems) {
+ aItems[j] += unit;
+ actual += unit;
+ }
+ }
+ }
+}
+
+
+/**
+ * Translate the rows/cols specs into an array of integer sizes for
+ * each cell in the frameset. Sizes are allocated based on the priorities of the
+ * specifier - fixed sizes have the highest priority, percentage sizes have the next
+ * highest priority and relative sizes have the lowest.
+ */
+void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
+ nscoord aSize,
+ int32_t aNumSpecs,
+ const nsFramesetSpec* aSpecs,
+ nscoord* aValues)
+{
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(int32_t),
+ "aNumSpecs maximum value is NS_MAX_FRAMESET_SPEC_COUNT");
+
+ int32_t fixedTotal = 0;
+ int32_t numFixed = 0;
+ auto fixed = MakeUnique<int32_t[]>(aNumSpecs);
+ int32_t numPercent = 0;
+ auto percent = MakeUnique<int32_t[]>(aNumSpecs);
+ int32_t relativeSums = 0;
+ int32_t numRelative = 0;
+ auto relative = MakeUnique<int32_t[]>(aNumSpecs);
+
+ if (MOZ_UNLIKELY(!fixed || !percent || !relative)) {
+ return; // NS_ERROR_OUT_OF_MEMORY
+ }
+
+ int32_t i, j;
+
+ // initialize the fixed, percent, relative indices, allocate the fixed sizes and zero the others
+ for (i = 0; i < aNumSpecs; i++) {
+ aValues[i] = 0;
+ switch (aSpecs[i].mUnit) {
+ case eFramesetUnit_Fixed:
+ aValues[i] = nsPresContext::CSSPixelsToAppUnits(aSpecs[i].mValue);
+ fixedTotal += aValues[i];
+ fixed[numFixed] = i;
+ numFixed++;
+ break;
+ case eFramesetUnit_Percent:
+ percent[numPercent] = i;
+ numPercent++;
+ break;
+ case eFramesetUnit_Relative:
+ relative[numRelative] = i;
+ numRelative++;
+ relativeSums += aSpecs[i].mValue;
+ break;
+ }
+ }
+
+ // scale the fixed sizes if they total too much (or too little and there aren't any percent or relative)
+ if ((fixedTotal > aSize) || ((fixedTotal < aSize) && (0 == numPercent) && (0 == numRelative))) {
+ Scale(aSize, numFixed, fixed.get(), aNumSpecs, aValues);
+ return;
+ }
+
+ int32_t percentMax = aSize - fixedTotal;
+ int32_t percentTotal = 0;
+ // allocate the percentage sizes from what is left over from the fixed allocation
+ for (i = 0; i < numPercent; i++) {
+ j = percent[i];
+ aValues[j] = NSToCoordRound((float)aSpecs[j].mValue * (float)aSize / 100.0f);
+ percentTotal += aValues[j];
+ }
+
+ // scale the percent sizes if they total too much (or too little and there aren't any relative)
+ if ((percentTotal > percentMax) || ((percentTotal < percentMax) && (0 == numRelative))) {
+ Scale(percentMax, numPercent, percent.get(), aNumSpecs, aValues);
+ return;
+ }
+
+ int32_t relativeMax = percentMax - percentTotal;
+ int32_t relativeTotal = 0;
+ // allocate the relative sizes from what is left over from the percent allocation
+ for (i = 0; i < numRelative; i++) {
+ j = relative[i];
+ aValues[j] = NSToCoordRound((float)aSpecs[j].mValue * (float)relativeMax / (float)relativeSums);
+ relativeTotal += aValues[j];
+ }
+
+ // scale the relative sizes if they take up too much or too little
+ if (relativeTotal != relativeMax) {
+ Scale(relativeMax, numRelative, relative.get(), aNumSpecs, aValues);
+ }
+}
+
+
+/**
+ * Translate the rows/cols integer sizes into an array of specs for
+ * each cell in the frameset. Reverse of CalculateRowCol() behaviour.
+ * This allows us to maintain the user size info through reflows.
+ */
+void nsHTMLFramesetFrame::GenerateRowCol(nsPresContext* aPresContext,
+ nscoord aSize,
+ int32_t aNumSpecs,
+ const nsFramesetSpec* aSpecs,
+ nscoord* aValues,
+ nsString& aNewAttr)
+{
+ int32_t i;
+
+ for (i = 0; i < aNumSpecs; i++) {
+ if (!aNewAttr.IsEmpty())
+ aNewAttr.Append(char16_t(','));
+
+ switch (aSpecs[i].mUnit) {
+ case eFramesetUnit_Fixed:
+ aNewAttr.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(aValues[i]));
+ break;
+ case eFramesetUnit_Percent: // XXX Only accurate to 1%, need 1 pixel
+ case eFramesetUnit_Relative:
+ // Add 0.5 to the percentage to make rounding work right.
+ aNewAttr.AppendInt(uint32_t((100.0*aValues[i])/aSize + 0.5));
+ aNewAttr.Append(char16_t('%'));
+ break;
+ }
+ }
+}
+
+int32_t nsHTMLFramesetFrame::GetBorderWidth(nsPresContext* aPresContext,
+ bool aTakeForcingIntoAccount)
+{
+ nsFrameborder frameborder = GetFrameBorder();
+ if (frameborder == eFrameborder_No) {
+ return 0;
+ }
+ nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
+
+ if (content) {
+ const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::border);
+ if (attr) {
+ int32_t intVal = 0;
+ if (attr->Type() == nsAttrValue::eInteger) {
+ intVal = attr->GetIntegerValue();
+ if (intVal < 0) {
+ intVal = 0;
+ }
+ }
+
+ return nsPresContext::CSSPixelsToAppUnits(intVal);
+ }
+ }
+
+ if (mParentBorderWidth >= 0) {
+ return mParentBorderWidth;
+ }
+
+ return nsPresContext::CSSPixelsToAppUnits(DEFAULT_BORDER_WIDTH_PX);
+}
+
+void
+nsHTMLFramesetFrame::GetDesiredSize(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ ReflowOutput& aDesiredSize)
+{
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalSize desiredSize(wm);
+ nsHTMLFramesetFrame* framesetParent = do_QueryFrame(GetParent());
+ if (nullptr == framesetParent) {
+ if (aPresContext->IsPaginated()) {
+ // XXX This needs to be changed when framesets paginate properly
+ desiredSize.ISize(wm) = aReflowInput.AvailableISize();
+ desiredSize.BSize(wm) = aReflowInput.AvailableBSize();
+ } else {
+ LogicalSize area(wm, aPresContext->GetVisibleArea().Size());
+
+ desiredSize.ISize(wm) = area.ISize(wm);
+ desiredSize.BSize(wm) = area.BSize(wm);
+ }
+ } else {
+ LogicalSize size(wm);
+ framesetParent->GetSizeOfChild(this, wm, size);
+ desiredSize.ISize(wm) = size.ISize(wm);
+ desiredSize.BSize(wm) = size.BSize(wm);
+ }
+ aDesiredSize.SetSize(wm, desiredSize);
+}
+
+// only valid for non border children
+void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t aIndexInParent,
+ WritingMode aWM,
+ LogicalSize& aSize,
+ nsIntPoint& aCellIndex)
+{
+ int32_t row = aIndexInParent / mNumCols;
+ int32_t col = aIndexInParent - (row * mNumCols); // remainder from dividing index by mNumCols
+ if ((row < mNumRows) && (col < mNumCols)) {
+ aSize.ISize(aWM) = mColSizes[col];
+ aSize.BSize(aWM) = mRowSizes[row];
+ aCellIndex.x = col;
+ aCellIndex.y = row;
+ } else {
+ aSize.SizeTo(aWM, 0, 0);
+ aCellIndex.x = aCellIndex.y = 0;
+ }
+}
+
+// only valid for non border children
+void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild,
+ WritingMode aWM,
+ LogicalSize& aSize)
+{
+ // Reflow only creates children frames for <frameset> and <frame> content.
+ // this assumption is used here
+ int i = 0;
+ for (nsIFrame* child : mFrames) {
+ if (aChild == child) {
+ nsIntPoint ignore;
+ GetSizeOfChildAt(i, aWM, aSize, ignore);
+ return;
+ }
+ i++;
+ }
+ aSize.SizeTo(aWM, 0, 0);
+}
+
+
+nsresult nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+ if (mDragger) {
+ // the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
+ switch (aEvent->mMessage) {
+ case eMouseMove:
+ MouseDrag(aPresContext, aEvent);
+ break;
+ case eMouseUp:
+ if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+ EndMouseDrag(aPresContext);
+ }
+ break;
+ default:
+ break;
+ }
+ *aEventStatus = nsEventStatus_eConsumeNoDefault;
+ } else {
+ *aEventStatus = nsEventStatus_eIgnore;
+ }
+ return NS_OK;
+}
+
+nsresult
+nsHTMLFramesetFrame::GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor)
+{
+ if (mDragger) {
+ aCursor.mCursor = (mDragger->mVertical) ? NS_STYLE_CURSOR_EW_RESIZE : NS_STYLE_CURSOR_NS_RESIZE;
+ } else {
+ aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
+ }
+ return NS_OK;
+}
+
+void
+nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
+
+ if (mDragger && aBuilder->IsForEventDelivery()) {
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayEventReceiver(aBuilder, this));
+ }
+}
+
+void
+nsHTMLFramesetFrame::ReflowPlaceChild(nsIFrame* aChild,
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsPoint& aOffset,
+ nsSize& aSize,
+ nsIntPoint* aCellIndex)
+{
+ // reflow the child
+ ReflowInput reflowInput(aPresContext, aReflowInput, aChild,
+ LogicalSize(aChild->GetWritingMode(), aSize));
+ reflowInput.SetComputedWidth(std::max(0, aSize.width - reflowInput.ComputedPhysicalBorderPadding().LeftRight()));
+ reflowInput.SetComputedHeight(std::max(0, aSize.height - reflowInput.ComputedPhysicalBorderPadding().TopBottom()));
+ ReflowOutput reflowOutput(aReflowInput);
+ reflowOutput.Width() = aSize.width;
+ reflowOutput.Height() = aSize.height;
+ nsReflowStatus status;
+
+ ReflowChild(aChild, aPresContext, reflowOutput, reflowInput, aOffset.x,
+ aOffset.y, 0, status);
+ NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
+
+ // Place and size the child
+ reflowOutput.Width() = aSize.width;
+ reflowOutput.Height() = aSize.height;
+ FinishReflowChild(aChild, aPresContext, reflowOutput, nullptr, aOffset.x, aOffset.y, 0);
+}
+
+static
+nsFrameborder GetFrameBorderHelper(nsGenericHTMLElement* aContent)
+{
+ if (nullptr != aContent) {
+ const nsAttrValue* attr = aContent->GetParsedAttr(nsGkAtoms::frameborder);
+ if (attr && attr->Type() == nsAttrValue::eEnum) {
+ switch (attr->GetEnumValue())
+ {
+ case NS_STYLE_FRAME_YES:
+ case NS_STYLE_FRAME_1:
+ return eFrameborder_Yes;
+
+ case NS_STYLE_FRAME_NO:
+ case NS_STYLE_FRAME_0:
+ return eFrameborder_No;
+ }
+ }
+ }
+ return eFrameborder_Notset;
+}
+
+nsFrameborder nsHTMLFramesetFrame::GetFrameBorder()
+{
+ nsFrameborder result = eFrameborder_Notset;
+ nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
+
+ if (content) {
+ result = GetFrameBorderHelper(content);
+ }
+ if (eFrameborder_Notset == result) {
+ return mParentFrameborder;
+ }
+ return result;
+}
+
+nsFrameborder nsHTMLFramesetFrame::GetFrameBorder(nsIContent* aContent)
+{
+ nsFrameborder result = eFrameborder_Notset;
+
+ nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(aContent);
+
+ if (content) {
+ result = GetFrameBorderHelper(content);
+ }
+ if (eFrameborder_Notset == result) {
+ return GetFrameBorder();
+ }
+ return result;
+}
+
+nscolor nsHTMLFramesetFrame::GetBorderColor()
+{
+ nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
+
+ if (content) {
+ const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor);
+ if (attr) {
+ nscolor color;
+ if (attr->GetColorValue(color)) {
+ return color;
+ }
+ }
+ }
+
+ return mParentBorderColor;
+}
+
+nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent)
+{
+ nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(aContent);
+
+ if (content) {
+ const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor);
+ if (attr) {
+ nscolor color;
+ if (attr->GetColorValue(color)) {
+ return color;
+ }
+ }
+ }
+ return GetBorderColor();
+}
+
+void
+nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ nsIPresShell *shell = aPresContext->PresShell();
+ StyleSetHandle styleSet = shell->StyleSet();
+
+ GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+
+ //printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight());
+ // Always get the size so that the caller knows how big we are
+ GetDesiredSize(aPresContext, aReflowInput, aDesiredSize);
+
+ nscoord width = (aDesiredSize.Width() <= aReflowInput.AvailableWidth())
+ ? aDesiredSize.Width() : aReflowInput.AvailableWidth();
+ nscoord height = (aDesiredSize.Height() <= aReflowInput.AvailableHeight())
+ ? aDesiredSize.Height() : aReflowInput.AvailableHeight();
+
+ // We might be reflowed more than once with NS_FRAME_FIRST_REFLOW;
+ // that's allowed. (Though it will only happen for misuse of frameset
+ // that includes it within other content.) So measure firstTime by
+ // what we care about, which is whether we've processed the data we
+ // process below if firstTime is true.
+ MOZ_ASSERT(!mChildFrameborder == !mChildBorderColors);
+ bool firstTime = !!mChildFrameborder;
+
+ // subtract out the width of all of the potential borders. There are
+ // only borders between <frame>s. There are none on the edges (e.g the
+ // leftmost <frame> has no left border).
+ int32_t borderWidth = GetBorderWidth(aPresContext, true);
+
+ width -= (mNumCols - 1) * borderWidth;
+ if (width < 0) width = 0;
+
+ height -= (mNumRows - 1) * borderWidth;
+ if (height < 0) height = 0;
+
+ HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
+ NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
+ const nsFramesetSpec* rowSpecs = nullptr;
+ const nsFramesetSpec* colSpecs = nullptr;
+ int32_t rows = 0;
+ int32_t cols = 0;
+ ourContent->GetRowSpec(&rows, &rowSpecs);
+ ourContent->GetColSpec(&cols, &colSpecs);
+ // If the number of cols or rows has changed, the frame for the frameset
+ // will be re-created.
+ if (mNumRows != rows || mNumCols != cols) {
+ aStatus = NS_FRAME_COMPLETE;
+ mDrag.UnSet();
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+ return;
+ }
+
+ CalculateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes.get());
+ CalculateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes.get());
+
+ UniquePtr<bool[]> verBordersVis; // vertical borders visibility
+ UniquePtr<nscolor[]> verBorderColors;
+ UniquePtr<bool[]> horBordersVis; // horizontal borders visibility
+ UniquePtr<nscolor[]> horBorderColors;
+ nscolor borderColor = GetBorderColor();
+ nsFrameborder frameborder = GetFrameBorder();
+
+ if (firstTime) {
+ // Check for overflow in memory allocations using mNumCols and mNumRows
+ // which have a maxium value of NS_MAX_FRAMESET_SPEC_COUNT.
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(bool),
+ "Check for overflow");
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscolor),
+ "Check for overflow");
+
+ verBordersVis = MakeUnique<bool[]>(mNumCols);
+ verBorderColors = MakeUnique<nscolor[]>(mNumCols);
+ for (int verX = 0; verX < mNumCols; verX++) {
+ verBordersVis[verX] = false;
+ verBorderColors[verX] = NO_COLOR;
+ }
+
+ horBordersVis = MakeUnique<bool[]>(mNumRows);
+ horBorderColors = MakeUnique<nscolor[]>(mNumRows);
+ for (int horX = 0; horX < mNumRows; horX++) {
+ horBordersVis[horX] = false;
+ horBorderColors[horX] = NO_COLOR;
+ }
+ }
+
+ // reflow the children
+ int32_t lastRow = 0;
+ int32_t lastCol = 0;
+ int32_t borderChildX = mNonBorderChildCount; // index of border children
+ nsHTMLFramesetBorderFrame* borderFrame = nullptr;
+ nsPoint offset(0,0);
+ nsSize size, lastSize;
+ WritingMode wm = GetWritingMode();
+ LogicalSize logicalSize(wm);
+ nsIFrame* child = mFrames.FirstChild();
+
+ for (int32_t childX = 0; childX < mNonBorderChildCount; childX++) {
+ nsIntPoint cellIndex;
+ GetSizeOfChildAt(childX, wm, logicalSize, cellIndex);
+ size = logicalSize.GetPhysicalSize(wm);
+
+ if (lastRow != cellIndex.y) { // changed to next row
+ offset.x = 0;
+ offset.y += lastSize.height;
+ if (firstTime) { // create horizontal border
+
+ RefPtr<nsStyleContext> pseudoStyleContext;
+ pseudoStyleContext = styleSet->
+ ResolveAnonymousBoxStyle(nsCSSAnonBoxes::horizontalFramesetBorder,
+ mStyleContext);
+
+ borderFrame = new (shell) nsHTMLFramesetBorderFrame(pseudoStyleContext,
+ borderWidth,
+ false,
+ false);
+ borderFrame->Init(mContent, this, nullptr);
+ mChildCount++;
+ mFrames.AppendFrame(nullptr, borderFrame);
+ mHorBorders[cellIndex.y-1] = borderFrame;
+ // set the neighbors for determining drag boundaries
+ borderFrame->mPrevNeighbor = lastRow;
+ borderFrame->mNextNeighbor = cellIndex.y;
+ } else {
+ borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
+ borderFrame->mWidth = borderWidth;
+ borderChildX++;
+ }
+ nsSize borderSize(aDesiredSize.Width(), borderWidth);
+ ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset, borderSize);
+ borderFrame = nullptr;
+ offset.y += borderWidth;
+ } else {
+ if (cellIndex.x > 0) { // moved to next col in same row
+ if (0 == cellIndex.y) { // in 1st row
+ if (firstTime) { // create vertical border
+
+ RefPtr<nsStyleContext> pseudoStyleContext;
+ pseudoStyleContext = styleSet->
+ ResolveAnonymousBoxStyle(nsCSSAnonBoxes::verticalFramesetBorder,
+ mStyleContext);
+
+ borderFrame = new (shell) nsHTMLFramesetBorderFrame(pseudoStyleContext,
+ borderWidth,
+ true,
+ false);
+ borderFrame->Init(mContent, this, nullptr);
+ mChildCount++;
+ mFrames.AppendFrame(nullptr, borderFrame);
+ mVerBorders[cellIndex.x-1] = borderFrame;
+ // set the neighbors for determining drag boundaries
+ borderFrame->mPrevNeighbor = lastCol;
+ borderFrame->mNextNeighbor = cellIndex.x;
+ } else {
+ borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
+ borderFrame->mWidth = borderWidth;
+ borderChildX++;
+ }
+ nsSize borderSize(borderWidth, aDesiredSize.Height());
+ ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset, borderSize);
+ borderFrame = nullptr;
+ }
+ offset.x += borderWidth;
+ }
+ }
+
+ ReflowPlaceChild(child, aPresContext, aReflowInput, offset, size, &cellIndex);
+
+ if (firstTime) {
+ int32_t childVis;
+ nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(child);
+ nsSubDocumentFrame* subdocFrame;
+ if (framesetFrame) {
+ childVis = framesetFrame->mEdgeVisibility;
+ mChildBorderColors[childX] = framesetFrame->mEdgeColors;
+ } else if ((subdocFrame = do_QueryFrame(child))) {
+ if (eFrameborder_Yes == mChildFrameborder[childX]) {
+ childVis = ALL_VIS;
+ } else if (eFrameborder_No == mChildFrameborder[childX]) {
+ childVis = NONE_VIS;
+ } else { // notset
+ childVis = (eFrameborder_No == frameborder) ? NONE_VIS : ALL_VIS;
+ }
+ } else { // blank
+#ifdef DEBUG
+ nsHTMLFramesetBlankFrame* blank = do_QueryFrame(child);
+ MOZ_ASSERT(blank, "unexpected child frame type");
+#endif
+ childVis = NONE_VIS;
+ }
+ nsBorderColor childColors = mChildBorderColors[childX];
+ // set the visibility, color of our edge borders based on children
+ if (0 == cellIndex.x) {
+ if (!(mEdgeVisibility & LEFT_VIS)) {
+ mEdgeVisibility |= (LEFT_VIS & childVis);
+ }
+ if (NO_COLOR == mEdgeColors.mLeft) {
+ mEdgeColors.mLeft = childColors.mLeft;
+ }
+ }
+ if (0 == cellIndex.y) {
+ if (!(mEdgeVisibility & TOP_VIS)) {
+ mEdgeVisibility |= (TOP_VIS & childVis);
+ }
+ if (NO_COLOR == mEdgeColors.mTop) {
+ mEdgeColors.mTop = childColors.mTop;
+ }
+ }
+ if (mNumCols-1 == cellIndex.x) {
+ if (!(mEdgeVisibility & RIGHT_VIS)) {
+ mEdgeVisibility |= (RIGHT_VIS & childVis);
+ }
+ if (NO_COLOR == mEdgeColors.mRight) {
+ mEdgeColors.mRight = childColors.mRight;
+ }
+ }
+ if (mNumRows-1 == cellIndex.y) {
+ if (!(mEdgeVisibility & BOTTOM_VIS)) {
+ mEdgeVisibility |= (BOTTOM_VIS & childVis);
+ }
+ if (NO_COLOR == mEdgeColors.mBottom) {
+ mEdgeColors.mBottom = childColors.mBottom;
+ }
+ }
+ // set the visibility of borders that the child may affect
+ if (childVis & RIGHT_VIS) {
+ verBordersVis[cellIndex.x] = true;
+ }
+ if (childVis & BOTTOM_VIS) {
+ horBordersVis[cellIndex.y] = true;
+ }
+ if ((cellIndex.x > 0) && (childVis & LEFT_VIS)) {
+ verBordersVis[cellIndex.x-1] = true;
+ }
+ if ((cellIndex.y > 0) && (childVis & TOP_VIS)) {
+ horBordersVis[cellIndex.y-1] = true;
+ }
+ // set the colors of borders that the child may affect
+ if (NO_COLOR == verBorderColors[cellIndex.x]) {
+ verBorderColors[cellIndex.x] = mChildBorderColors[childX].mRight;
+ }
+ if (NO_COLOR == horBorderColors[cellIndex.y]) {
+ horBorderColors[cellIndex.y] = mChildBorderColors[childX].mBottom;
+ }
+ if ((cellIndex.x > 0) && (NO_COLOR == verBorderColors[cellIndex.x-1])) {
+ verBorderColors[cellIndex.x-1] = mChildBorderColors[childX].mLeft;
+ }
+ if ((cellIndex.y > 0) && (NO_COLOR == horBorderColors[cellIndex.y-1])) {
+ horBorderColors[cellIndex.y-1] = mChildBorderColors[childX].mTop;
+ }
+ }
+ lastRow = cellIndex.y;
+ lastCol = cellIndex.x;
+ lastSize = size;
+ offset.x += size.width;
+ child = child->GetNextSibling();
+ }
+
+ if (firstTime) {
+ nscolor childColor;
+ // set the visibility, color, mouse sensitivity of borders
+ for (int verX = 0; verX < mNumCols-1; verX++) {
+ if (mVerBorders[verX]) {
+ mVerBorders[verX]->SetVisibility(verBordersVis[verX]);
+ SetBorderResize(mVerBorders[verX]);
+ childColor = (NO_COLOR == verBorderColors[verX]) ? borderColor : verBorderColors[verX];
+ mVerBorders[verX]->SetColor(childColor);
+ }
+ }
+ for (int horX = 0; horX < mNumRows-1; horX++) {
+ if (mHorBorders[horX]) {
+ mHorBorders[horX]->SetVisibility(horBordersVis[horX]);
+ SetBorderResize(mHorBorders[horX]);
+ childColor = (NO_COLOR == horBorderColors[horX]) ? borderColor : horBorderColors[horX];
+ mHorBorders[horX]->SetColor(childColor);
+ }
+ }
+
+ mChildFrameborder.reset();
+ mChildBorderColors.reset();
+ }
+
+ aStatus = NS_FRAME_COMPLETE;
+ mDrag.UnSet();
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nsIAtom*
+nsHTMLFramesetFrame::GetType() const
+{
+ return nsGkAtoms::frameSetFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsHTMLFramesetFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Frameset"), aResult);
+}
+#endif
+
+bool
+nsHTMLFramesetFrame::IsLeaf() const
+{
+ // We handle constructing our kids manually
+ return true;
+}
+
+bool
+nsHTMLFramesetFrame::CanResize(bool aVertical,
+ bool aLeft)
+{
+ int32_t childX;
+ int32_t startX;
+ if (aVertical) {
+ startX = (aLeft) ? 0 : mNumCols-1;
+ for (childX = startX; childX < mNonBorderChildCount; childX += mNumCols) {
+ if (!CanChildResize(aVertical, aLeft, childX)) {
+ return false;
+ }
+ }
+ } else {
+ startX = (aLeft) ? 0 : (mNumRows - 1) * mNumCols;
+ int32_t endX = startX + mNumCols;
+ for (childX = startX; childX < endX; childX++) {
+ if (!CanChildResize(aVertical, aLeft, childX)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame)
+{
+ nsIContent* content = aChildFrame->GetContent();
+
+ return content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::noresize);
+}
+
+bool
+nsHTMLFramesetFrame::CanChildResize(bool aVertical, bool aLeft, int32_t aChildX)
+{
+ nsIFrame* child = mFrames.FrameAt(aChildX);
+ nsHTMLFramesetFrame* frameset = do_QueryFrame(child);
+ return frameset ? frameset->CanResize(aVertical, aLeft) : !GetNoResize(child);
+}
+
+// This calculates and sets the resizability of all border frames
+
+void
+nsHTMLFramesetFrame::RecalculateBorderResize()
+{
+ if (!mContent) {
+ return;
+ }
+
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT,
+ "Check for overflow");
+ static_assert(NS_MAX_FRAMESET_SPEC_COUNT
+ < UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT,
+ "Check for overflow");
+ // set the visibility and mouse sensitivity of borders
+ int32_t verX;
+ for (verX = 0; verX < mNumCols-1; verX++) {
+ if (mVerBorders[verX]) {
+ mVerBorders[verX]->mCanResize = true;
+ SetBorderResize(mVerBorders[verX]);
+ }
+ }
+ int32_t horX;
+ for (horX = 0; horX < mNumRows-1; horX++) {
+ if (mHorBorders[horX]) {
+ mHorBorders[horX]->mCanResize = true;
+ SetBorderResize(mHorBorders[horX]);
+ }
+ }
+}
+
+void
+nsHTMLFramesetFrame::SetBorderResize(nsHTMLFramesetBorderFrame* aBorderFrame)
+{
+ if (aBorderFrame->mVertical) {
+ for (int rowX = 0; rowX < mNumRows; rowX++) {
+ int32_t childX = aBorderFrame->mPrevNeighbor + (rowX * mNumCols);
+ if (!CanChildResize(true, false, childX) ||
+ !CanChildResize(true, true, childX+1)) {
+ aBorderFrame->mCanResize = false;
+ }
+ }
+ } else {
+ int32_t childX = aBorderFrame->mPrevNeighbor * mNumCols;
+ int32_t endX = childX + mNumCols;
+ for (; childX < endX; childX++) {
+ if (!CanChildResize(false, false, childX)) {
+ aBorderFrame->mCanResize = false;
+ }
+ }
+ endX = endX + mNumCols;
+ for (; childX < endX; childX++) {
+ if (!CanChildResize(false, true, childX)) {
+ aBorderFrame->mCanResize = false;
+ }
+ }
+ }
+}
+
+void
+nsHTMLFramesetFrame::StartMouseDrag(nsPresContext* aPresContext,
+ nsHTMLFramesetBorderFrame* aBorder,
+ WidgetGUIEvent* aEvent)
+{
+#if 0
+ int32_t index;
+ IndexOf(aBorder, index);
+ NS_ASSERTION((nullptr != aBorder) && (index >= 0), "invalid dragger");
+#endif
+
+ nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
+
+ mDragger = aBorder;
+
+ mFirstDragPoint = aEvent->mRefPoint;
+
+ // Store the original frame sizes
+ if (mDragger->mVertical) {
+ mPrevNeighborOrigSize = mColSizes[mDragger->mPrevNeighbor];
+ mNextNeighborOrigSize = mColSizes[mDragger->mNextNeighbor];
+ } else {
+ mPrevNeighborOrigSize = mRowSizes[mDragger->mPrevNeighbor];
+ mNextNeighborOrigSize = mRowSizes[mDragger->mNextNeighbor];
+ }
+
+ gDragInProgress = true;
+}
+
+
+void
+nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent)
+{
+ // if the capture ended, reset the drag state
+ if (nsIPresShell::GetCapturingContent() != GetContent()) {
+ mDragger = nullptr;
+ gDragInProgress = false;
+ return;
+ }
+
+ int32_t change; // measured positive from left-to-right or top-to-bottom
+ nsWeakFrame weakFrame(this);
+ if (mDragger->mVertical) {
+ change = aPresContext->DevPixelsToAppUnits(
+ aEvent->mRefPoint.x - mFirstDragPoint.x);
+ if (change > mNextNeighborOrigSize - mMinDrag) {
+ change = mNextNeighborOrigSize - mMinDrag;
+ } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
+ change = mMinDrag - mPrevNeighborOrigSize;
+ }
+ mColSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
+ mColSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
+
+ if (change != 0) {
+ // Recompute the specs from the new sizes.
+ nscoord width = mRect.width - (mNumCols - 1) * GetBorderWidth(aPresContext, true);
+ HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
+ NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
+ const nsFramesetSpec* colSpecs = nullptr;
+ ourContent->GetColSpec(&mNumCols, &colSpecs);
+ nsAutoString newColAttr;
+ GenerateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes.get(),
+ newColAttr);
+ // Setting the attr will trigger a reflow
+ mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::cols, newColAttr, true);
+ }
+ } else {
+ change = aPresContext->DevPixelsToAppUnits(
+ aEvent->mRefPoint.y - mFirstDragPoint.y);
+ if (change > mNextNeighborOrigSize - mMinDrag) {
+ change = mNextNeighborOrigSize - mMinDrag;
+ } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
+ change = mMinDrag - mPrevNeighborOrigSize;
+ }
+ mRowSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
+ mRowSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
+
+ if (change != 0) {
+ // Recompute the specs from the new sizes.
+ nscoord height = mRect.height - (mNumRows - 1) * GetBorderWidth(aPresContext, true);
+ HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
+ NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
+ const nsFramesetSpec* rowSpecs = nullptr;
+ ourContent->GetRowSpec(&mNumRows, &rowSpecs);
+ nsAutoString newRowAttr;
+ GenerateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes.get(),
+ newRowAttr);
+ // Setting the attr will trigger a reflow
+ mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::rows, newRowAttr, true);
+ }
+ }
+
+ ENSURE_TRUE(weakFrame.IsAlive());
+ if (change != 0) {
+ mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this);
+ }
+}
+
+void
+nsHTMLFramesetFrame::EndMouseDrag(nsPresContext* aPresContext)
+{
+ nsIPresShell::SetCapturingContent(nullptr, 0);
+ mDragger = nullptr;
+ gDragInProgress = false;
+}
+
+nsIFrame*
+NS_NewHTMLFramesetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+#ifdef DEBUG
+ const nsStyleDisplay* disp = aContext->StyleDisplay();
+ NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(),
+ "Framesets should not be positioned and should not float");
+#endif
+
+ return new (aPresShell) nsHTMLFramesetFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame)
+
+/*******************************************************************************
+ * nsHTMLFramesetBorderFrame
+ ******************************************************************************/
+nsHTMLFramesetBorderFrame::nsHTMLFramesetBorderFrame(nsStyleContext* aContext,
+ int32_t aWidth,
+ bool aVertical,
+ bool aVisibility)
+ : nsLeafFrame(aContext), mWidth(aWidth), mVertical(aVertical), mVisibility(aVisibility)
+{
+ mCanResize = true;
+ mColor = NO_COLOR;
+ mPrevNeighbor = 0;
+ mNextNeighbor = 0;
+}
+
+nsHTMLFramesetBorderFrame::~nsHTMLFramesetBorderFrame()
+{
+ //printf("nsHTMLFramesetBorderFrame destructor %p \n", this);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)
+
+nscoord nsHTMLFramesetBorderFrame::GetIntrinsicISize()
+{
+ // No intrinsic width
+ return 0;
+}
+
+nscoord nsHTMLFramesetBorderFrame::GetIntrinsicBSize()
+{
+ // No intrinsic height
+ return 0;
+}
+
+void nsHTMLFramesetBorderFrame::SetVisibility(bool aVisibility)
+{
+ mVisibility = aVisibility;
+}
+
+void nsHTMLFramesetBorderFrame::SetColor(nscolor aColor)
+{
+ mColor = aColor;
+}
+
+
+void
+nsHTMLFramesetBorderFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBorderFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ // Override Reflow(), since we don't want to deal with what our
+ // computed values are.
+ SizeToAvailSize(aReflowInput, aDesiredSize);
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ aStatus = NS_FRAME_COMPLETE;
+}
+
+class nsDisplayFramesetBorder : public nsDisplayItem {
+public:
+ nsDisplayFramesetBorder(nsDisplayListBuilder* aBuilder,
+ nsHTMLFramesetBorderFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame) {
+ MOZ_COUNT_CTOR(nsDisplayFramesetBorder);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayFramesetBorder() {
+ MOZ_COUNT_DTOR(nsDisplayFramesetBorder);
+ }
+#endif
+
+ // REVIEW: see old GetFrameForPoint
+ // Receives events in its bounds
+ virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+ HitTestState* aState,
+ nsTArray<nsIFrame*> *aOutFrames) override {
+ aOutFrames->AppendElement(mFrame);
+ }
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ NS_DISPLAY_DECL_NAME("FramesetBorder", TYPE_FRAMESET_BORDER)
+};
+
+void nsDisplayFramesetBorder::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ static_cast<nsHTMLFramesetBorderFrame*>(mFrame)->
+ PaintBorder(aCtx->GetDrawTarget(), ToReferenceFrame());
+}
+
+void
+nsHTMLFramesetBorderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayFramesetBorder(aBuilder, this));
+}
+
+void nsHTMLFramesetBorderFrame::PaintBorder(DrawTarget* aDrawTarget,
+ nsPoint aPt)
+{
+ nscoord widthInPixels = nsPresContext::AppUnitsToIntCSSPixels(mWidth);
+ nscoord pixelWidth = nsPresContext::CSSPixelsToAppUnits(1);
+
+ if (widthInPixels <= 0)
+ return;
+
+ ColorPattern bgColor(ToDeviceColor(
+ LookAndFeel::GetColor(LookAndFeel::eColorID_WidgetBackground,
+ NS_RGB(200, 200, 200))));
+
+ ColorPattern fgColor(ToDeviceColor(
+ LookAndFeel::GetColor(LookAndFeel::eColorID_WidgetForeground,
+ NS_RGB(0, 0, 0))));
+
+ ColorPattern hltColor(ToDeviceColor(
+ LookAndFeel::GetColor(LookAndFeel::eColorID_Widget3DHighlight,
+ NS_RGB(255, 255, 255))));
+
+ ColorPattern sdwColor(ToDeviceColor(
+ LookAndFeel::GetColor(LookAndFeel::eColorID_Widget3DShadow,
+ NS_RGB(128, 128, 128))));
+
+ ColorPattern color(ToDeviceColor(NS_RGB(255, 255, 255))); // default to white
+ if (mVisibility) {
+ color = (NO_COLOR == mColor) ? bgColor :
+ ColorPattern(ToDeviceColor(mColor));
+ }
+
+ int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
+
+ Point toRefFrame = NSPointToPoint(aPt, appUnitsPerDevPixel);
+
+ AutoRestoreTransform autoRestoreTransform(aDrawTarget);
+ aDrawTarget->SetTransform(
+ aDrawTarget->GetTransform().PreTranslate(toRefFrame));
+
+ nsPoint start(0, 0);
+ nsPoint end = mVertical ? nsPoint(0, mRect.height) : nsPoint(mRect.width, 0);
+
+ // draw grey or white first
+ for (int i = 0; i < widthInPixels; i++) {
+ StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
+ color);
+ if (mVertical) {
+ start.x += pixelWidth;
+ end.x = start.x;
+ } else {
+ start.y += pixelWidth;
+ end.y = start.y;
+ }
+ }
+
+ if (!mVisibility)
+ return;
+
+ if (widthInPixels >= 5) {
+ start.x = (mVertical) ? pixelWidth : 0;
+ start.y = (mVertical) ? 0 : pixelWidth;
+ end.x = (mVertical) ? start.x : mRect.width;
+ end.y = (mVertical) ? mRect.height : start.y;
+ StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
+ hltColor);
+ }
+
+ if (widthInPixels >= 2) {
+ start.x = (mVertical) ? mRect.width - (2 * pixelWidth) : 0;
+ start.y = (mVertical) ? 0 : mRect.height - (2 * pixelWidth);
+ end.x = (mVertical) ? start.x : mRect.width;
+ end.y = (mVertical) ? mRect.height : start.y;
+ StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
+ sdwColor);
+ }
+
+ if (widthInPixels >= 1) {
+ start.x = (mVertical) ? mRect.width - pixelWidth : 0;
+ start.y = (mVertical) ? 0 : mRect.height - pixelWidth;
+ end.x = (mVertical) ? start.x : mRect.width;
+ end.y = (mVertical) ? mRect.height : start.y;
+ StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
+ fgColor);
+ }
+}
+
+
+nsresult
+nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+ *aEventStatus = nsEventStatus_eIgnore;
+
+ //XXX Mouse setting logic removed. The remaining logic should also move.
+ if (!mCanResize) {
+ return NS_OK;
+ }
+
+ if (aEvent->mMessage == eMouseDown &&
+ aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+ nsHTMLFramesetFrame* parentFrame = do_QueryFrame(GetParent());
+ if (parentFrame) {
+ parentFrame->StartMouseDrag(aPresContext, this, aEvent);
+ *aEventStatus = nsEventStatus_eConsumeNoDefault;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsHTMLFramesetBorderFrame::GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor)
+{
+ if (!mCanResize) {
+ aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
+ } else {
+ aCursor.mCursor = (mVertical) ? NS_STYLE_CURSOR_EW_RESIZE : NS_STYLE_CURSOR_NS_RESIZE;
+ }
+ return NS_OK;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult nsHTMLFramesetBorderFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("FramesetBorder"), aResult);
+}
+#endif
+
+/*******************************************************************************
+ * nsHTMLFramesetBlankFrame
+ ******************************************************************************/
+
+NS_QUERYFRAME_HEAD(nsHTMLFramesetBlankFrame)
+ NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)
+
+nsHTMLFramesetBlankFrame::~nsHTMLFramesetBlankFrame()
+{
+ //printf("nsHTMLFramesetBlankFrame destructor %p \n", this);
+}
+
+nscoord nsHTMLFramesetBlankFrame::GetIntrinsicISize()
+{
+ // No intrinsic width
+ return 0;
+}
+
+nscoord nsHTMLFramesetBlankFrame::GetIntrinsicBSize()
+{
+ // No intrinsic height
+ return 0;
+}
+
+void
+nsHTMLFramesetBlankFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBlankFrame");
+
+ // Override Reflow(), since we don't want to deal with what our
+ // computed values are.
+ SizeToAvailSize(aReflowInput, aDesiredSize);
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ aStatus = NS_FRAME_COMPLETE;
+}
+
+class nsDisplayFramesetBlank : public nsDisplayItem {
+public:
+ nsDisplayFramesetBlank(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame) :
+ nsDisplayItem(aBuilder, aFrame) {
+ MOZ_COUNT_CTOR(nsDisplayFramesetBlank);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayFramesetBlank() {
+ MOZ_COUNT_DTOR(nsDisplayFramesetBlank);
+ }
+#endif
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ NS_DISPLAY_DECL_NAME("FramesetBlank", TYPE_FRAMESET_BLANK)
+};
+
+void nsDisplayFramesetBlank::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ DrawTarget* drawTarget = aCtx->GetDrawTarget();
+ int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+ Rect rect =
+ NSRectToSnappedRect(mVisibleRect, appUnitsPerDevPixel, *drawTarget);
+ ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
+ drawTarget->FillRect(rect, white);
+}
+
+void
+nsHTMLFramesetBlankFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayFramesetBlank(aBuilder, this));
+}
diff --git a/layout/generic/nsFrameSetFrame.h b/layout/generic/nsFrameSetFrame.h
new file mode 100644
index 000000000..ac6ab07ce
--- /dev/null
+++ b/layout/generic/nsFrameSetFrame.h
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for HTML <frameset> elements */
+
+#ifndef nsHTMLFrameset_h___
+#define nsHTMLFrameset_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "nsContainerFrame.h"
+#include "nsColor.h"
+
+class nsIContent;
+class nsPresContext;
+struct nsRect;
+struct nsSize;
+class nsIAtom;
+class nsHTMLFramesetBorderFrame;
+class nsHTMLFramesetFrame;
+
+#define NO_COLOR 0xFFFFFFFA
+
+// defined at HTMLFrameSetElement.h
+struct nsFramesetSpec;
+
+struct nsBorderColor
+{
+ nscolor mLeft;
+ nscolor mRight;
+ nscolor mTop;
+ nscolor mBottom;
+
+ nsBorderColor() { Set(NO_COLOR); }
+ ~nsBorderColor() {}
+ void Set(nscolor aColor) { mLeft = mRight = mTop = mBottom = aColor; }
+};
+
+enum nsFrameborder {
+ eFrameborder_Yes = 0,
+ eFrameborder_No,
+ eFrameborder_Notset
+};
+
+struct nsFramesetDrag {
+ nsHTMLFramesetFrame* mSource; // frameset whose border was dragged to cause the resize
+ int32_t mIndex; // index of left col or top row of effected area
+ int32_t mChange; // pos for left to right or top to bottom, neg otherwise
+ bool mVertical; // vertical if true, otherwise horizontal
+
+ nsFramesetDrag();
+ void Reset(bool aVertical,
+ int32_t aIndex,
+ int32_t aChange,
+ nsHTMLFramesetFrame* aSource);
+ void UnSet();
+};
+
+/*******************************************************************************
+ * nsHTMLFramesetFrame
+ ******************************************************************************/
+class nsHTMLFramesetFrame : public nsContainerFrame
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsHTMLFramesetFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsHTMLFramesetFrame(nsStyleContext* aContext);
+
+ virtual ~nsHTMLFramesetFrame();
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+
+ static bool gDragInProgress;
+
+ void GetSizeOfChild(nsIFrame* aChild, mozilla::WritingMode aWM,
+ mozilla::LogicalSize& aSize);
+
+ void GetSizeOfChildAt(int32_t aIndexInParent,
+ mozilla::WritingMode aWM,
+ mozilla::LogicalSize& aSize,
+ nsIntPoint& aCellIndex);
+
+ virtual nsresult HandleEvent(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) override;
+
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nsIAtom* GetType() const override;
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ virtual bool IsLeaf() const override;
+
+ void StartMouseDrag(nsPresContext* aPresContext,
+ nsHTMLFramesetBorderFrame* aBorder,
+ mozilla::WidgetGUIEvent* aEvent);
+
+ void MouseDrag(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent);
+
+ void EndMouseDrag(nsPresContext* aPresContext);
+
+ nsFrameborder GetParentFrameborder() { return mParentFrameborder; }
+
+ void SetParentFrameborder(nsFrameborder aValue) { mParentFrameborder = aValue; }
+
+ nsFramesetDrag& GetDrag() { return mDrag; }
+
+ void RecalculateBorderResize();
+
+protected:
+ void Scale(nscoord aDesired,
+ int32_t aNumIndicies,
+ int32_t* aIndicies,
+ int32_t aNumItems,
+ int32_t* aItems);
+
+ void CalculateRowCol(nsPresContext* aPresContext,
+ nscoord aSize,
+ int32_t aNumSpecs,
+ const nsFramesetSpec* aSpecs,
+ nscoord* aValues);
+
+ void GenerateRowCol(nsPresContext* aPresContext,
+ nscoord aSize,
+ int32_t aNumSpecs,
+ const nsFramesetSpec* aSpecs,
+ nscoord* aValues,
+ nsString& aNewAttr);
+
+ virtual void GetDesiredSize(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ ReflowOutput& aDesiredSize);
+
+ int32_t GetBorderWidth(nsPresContext* aPresContext,
+ bool aTakeForcingIntoAccount);
+
+ int32_t GetParentBorderWidth() { return mParentBorderWidth; }
+
+ void SetParentBorderWidth(int32_t aWidth) { mParentBorderWidth = aWidth; }
+
+ nscolor GetParentBorderColor() { return mParentBorderColor; }
+
+ void SetParentBorderColor(nscolor aColor) { mParentBorderColor = aColor; }
+
+ nsFrameborder GetFrameBorder();
+
+ nsFrameborder GetFrameBorder(nsIContent* aContent);
+
+ nscolor GetBorderColor();
+
+ nscolor GetBorderColor(nsIContent* aFrameContent);
+
+ bool GetNoResize(nsIFrame* aChildFrame);
+
+ void ReflowPlaceChild(nsIFrame* aChild,
+ nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsPoint& aOffset,
+ nsSize& aSize,
+ nsIntPoint* aCellIndex = 0);
+
+ bool CanResize(bool aVertical, bool aLeft);
+
+ bool CanChildResize(bool aVertical, bool aLeft, int32_t aChildX);
+
+ void SetBorderResize(nsHTMLFramesetBorderFrame* aBorderFrame);
+
+ template<typename T, class D = mozilla::DefaultDelete<T>>
+ using UniquePtr = mozilla::UniquePtr<T, D>;
+
+ nsFramesetDrag mDrag;
+ nsBorderColor mEdgeColors;
+ nsHTMLFramesetBorderFrame* mDragger;
+ nsHTMLFramesetFrame* mTopLevelFrameset;
+ UniquePtr<nsHTMLFramesetBorderFrame*[]> mVerBorders; // vertical borders
+ UniquePtr<nsHTMLFramesetBorderFrame*[]> mHorBorders; // horizontal borders
+ UniquePtr<nsFrameborder[]> mChildFrameborder; // the frameborder attr of children
+ UniquePtr<nsBorderColor[]> mChildBorderColors;
+ UniquePtr<nscoord[]> mRowSizes; // currently computed row sizes
+ UniquePtr<nscoord[]> mColSizes; // currently computed col sizes
+ mozilla::LayoutDeviceIntPoint mFirstDragPoint;
+ int32_t mNumRows;
+ int32_t mNumCols;
+ int32_t mNonBorderChildCount;
+ int32_t mNonBlankChildCount;
+ int32_t mEdgeVisibility;
+ nsFrameborder mParentFrameborder;
+ nscolor mParentBorderColor;
+ int32_t mParentBorderWidth;
+ int32_t mPrevNeighborOrigSize; // used during resize
+ int32_t mNextNeighborOrigSize;
+ int32_t mMinDrag;
+ int32_t mChildCount;
+};
+
+#endif
diff --git a/layout/generic/nsFrameState.cpp b/layout/generic/nsFrameState.cpp
new file mode 100644
index 000000000..92b8e8324
--- /dev/null
+++ b/layout/generic/nsFrameState.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* constants for frame state bits and a type to store them in a uint64_t */
+
+#include "nsFrameState.h"
+
+#include "nsBlockFrame.h"
+#include "nsBoxFrame.h"
+#include "nsBulletFrame.h"
+#include "nsFlexContainerFrame.h"
+#include "nsGridContainerFrame.h"
+#include "nsGfxScrollFrame.h"
+#include "nsIFrame.h"
+#include "nsISVGChildFrame.h"
+#include "nsImageFrame.h"
+#include "nsInlineFrame.h"
+#include "nsPlaceholderFrame.h"
+#include "nsRubyTextFrame.h"
+#include "nsRubyTextContainerFrame.h"
+#include "nsSVGContainerFrame.h"
+#include "nsTableCellFrame.h"
+#include "nsTableRowFrame.h"
+#include "nsTableRowGroupFrame.h"
+#include "nsTextFrame.h"
+
+namespace mozilla {
+
+#ifdef DEBUG
+nsCString
+GetFrameState(nsIFrame* aFrame)
+{
+ nsCString result;
+ AutoTArray<const char*,3> groups;
+
+ nsFrameState state = aFrame->GetStateBits();
+
+ if (state == nsFrameState(0)) {
+ result.Assign('0');
+ return result;
+ }
+
+#define FRAME_STATE_GROUP(name_, class_) \
+ { \
+ class_* frame = do_QueryFrame(aFrame); \
+ if (frame && (groups.IsEmpty() || strcmp(groups.LastElement(), #name_))) {\
+ groups.AppendElement(#name_); \
+ } \
+ }
+#define FRAME_STATE_BIT(group_, value_, name_) \
+ if ((state & NS_FRAME_STATE_BIT(value_)) && groups.Contains(#group_)) { \
+ if (!result.IsEmpty()) { \
+ result.Insert(" | ", 0); \
+ } \
+ result.Insert(#name_, 0); \
+ state = state & ~NS_FRAME_STATE_BIT(value_); \
+ }
+#include "nsFrameStateBits.h"
+#undef FRAME_STATE_GROUP
+#undef FRAME_STATE_BIT
+
+ if (state) {
+ result.AppendPrintf(" | 0x%0llx", state);
+ }
+
+ return result;
+}
+
+void
+PrintFrameState(nsIFrame* aFrame)
+{
+ printf("%s\n", GetFrameState(aFrame).get());
+}
+#endif
+
+} // namespace mozilla
diff --git a/layout/generic/nsFrameState.h b/layout/generic/nsFrameState.h
new file mode 100644
index 000000000..144122f36
--- /dev/null
+++ b/layout/generic/nsFrameState.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* constants for frame state bits and a type to store them in a uint64_t */
+
+#ifndef nsFrameState_h_
+#define nsFrameState_h_
+
+#include <stdint.h>
+
+#ifdef DEBUG
+#include "nsString.h"
+
+class nsIFrame;
+#endif
+
+typedef uint64_t nsFrameState_size_t;
+
+#define NS_FRAME_STATE_BIT(n_) (nsFrameState(nsFrameState_size_t(1) << (n_)))
+
+enum nsFrameState : nsFrameState_size_t {
+#define FRAME_STATE_BIT(group_, value_, name_) \
+ name_ = NS_FRAME_STATE_BIT(value_),
+#include "nsFrameStateBits.h"
+#undef FRAME_STATE_BIT
+};
+
+inline nsFrameState operator|(nsFrameState aLeft, nsFrameState aRight)
+{
+ return nsFrameState(nsFrameState_size_t(aLeft) | nsFrameState_size_t(aRight));
+}
+
+inline nsFrameState operator&(nsFrameState aLeft, nsFrameState aRight)
+{
+ return nsFrameState(nsFrameState_size_t(aLeft) & nsFrameState_size_t(aRight));
+}
+
+inline nsFrameState& operator|=(nsFrameState& aLeft, nsFrameState aRight)
+{
+ aLeft = aLeft | aRight;
+ return aLeft;
+}
+
+inline nsFrameState& operator&=(nsFrameState& aLeft, nsFrameState aRight)
+{
+ aLeft = aLeft & aRight;
+ return aLeft;
+}
+
+inline nsFrameState operator~(nsFrameState aRight)
+{
+ return nsFrameState(~nsFrameState_size_t(aRight));
+}
+
+inline nsFrameState operator^(nsFrameState aLeft, nsFrameState aRight)
+{
+ return nsFrameState(nsFrameState_size_t(aLeft) ^ nsFrameState_size_t(aRight));
+}
+
+inline nsFrameState& operator^=(nsFrameState& aLeft, nsFrameState aRight)
+{
+ aLeft = aLeft ^ aRight;
+ return aLeft;
+}
+
+// Bits 20-31 and 60-63 of the frame state are reserved for implementations.
+#define NS_FRAME_IMPL_RESERVED nsFrameState(0xF0000000FFF00000)
+#define NS_FRAME_RESERVED ~NS_FRAME_IMPL_RESERVED
+
+namespace mozilla {
+#ifdef DEBUG
+nsCString GetFrameState(nsIFrame* aFrame);
+void PrintFrameState(nsIFrame* aFrame);
+#endif
+} // namespace mozilla
+
+#endif /* nsFrameState_h_ */
diff --git a/layout/generic/nsFrameStateBits.h b/layout/generic/nsFrameStateBits.h
new file mode 100644
index 000000000..f8b1e541c
--- /dev/null
+++ b/layout/generic/nsFrameStateBits.h
@@ -0,0 +1,675 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* a list of all frame state bits, for preprocessing */
+
+/******
+
+ This file contains definitions for frame state bits -- values used
+ in nsIFrame::mState -- and groups of frame state bits and which
+ classes they apply to.
+
+ There are two macros that can be defined before #including this
+ file:
+
+ FRAME_STATE_GROUP(name_, class_)
+
+ This denotes the existence of a named group of frame state bits.
+ name_ is the name of the group and class_ is the name of a frame
+ class that uses the frame state bits that are a part of the group.
+
+ The main group of frame state bits is named "Generic" and is
+ defined to apply to nsIFrame, i.e. all frames. All of the global
+ frame state bits -- bits 0..19 and 32..59 -- are in this group.
+
+ FRAME_STATE_BIT(group_, value_, name_)
+
+ This denotes the existence of a frame state bit. group_ indicates
+ which group the bit belongs to, value_ is the bit number (0..63),
+ and name_ is the name of the frame state bit constant.
+
+ Note that if you add a new frame state group, you'll need to #include
+ the header for its frame classes in nsFrameState.cpp and, if they don't
+ already, add nsQueryFrame implementations (which can be DEBUG-only) to
+ the frame classes.
+
+ ******/
+
+#ifndef FRAME_STATE_GROUP
+#define FRAME_STATE_GROUP(name_, class_) /* nothing */
+#define DEFINED_FRAME_STATE_GROUP
+#endif
+
+#ifndef FRAME_STATE_BIT
+#define FRAME_STATE_BIT(group_, value_, name_) /* nothing */
+#define DEFINED_FRAME_STATE_BIT
+#endif
+
+// == Frame state bits that apply to all frames ===============================
+
+FRAME_STATE_GROUP(Generic, nsIFrame)
+
+// This bit is set when the frame is actively being reflowed. It is set in many
+// frames' Reflow() by calling MarkInReflow() and unset in DidReflow().
+FRAME_STATE_BIT(Generic, 0, NS_FRAME_IN_REFLOW)
+
+// This bit is set when a frame is created. After it has been reflowed
+// once (during the DidReflow with a finished state) the bit is
+// cleared.
+FRAME_STATE_BIT(Generic, 1, NS_FRAME_FIRST_REFLOW)
+
+// For a continuation frame, if this bit is set, then this a "fluid"
+// continuation, i.e., across a line boundary. Otherwise it's a "hard"
+// continuation, e.g. a bidi continuation.
+FRAME_STATE_BIT(Generic, 2, NS_FRAME_IS_FLUID_CONTINUATION)
+
+// For nsIAnonymousContentCreator content that's created using ContentInfo.
+FRAME_STATE_BIT(Generic, 3, NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT)
+
+// If this bit is set, then a reference to the frame is being held
+// elsewhere. The frame may want to send a notification when it is
+// destroyed to allow these references to be cleared.
+FRAME_STATE_BIT(Generic, 4, NS_FRAME_EXTERNAL_REFERENCE)
+
+// If this bit is set, this frame or one of its descendants has a
+// percentage block-size that depends on an ancestor of this frame.
+// (Or it did at one point in the past, since we don't necessarily clear
+// the bit when it's no longer needed; it's an optimization.)
+FRAME_STATE_BIT(Generic, 5, NS_FRAME_CONTAINS_RELATIVE_BSIZE)
+
+// If this bit is set, then the frame corresponds to generated content
+FRAME_STATE_BIT(Generic, 6, NS_FRAME_GENERATED_CONTENT)
+
+// If this bit is set the frame is a continuation that is holding overflow,
+// i.e. it is a next-in-flow created to hold overflow after the box's
+// height has ended. This means the frame should be a) at the top of the
+// page and b) invisible: no borders, zero height, ignored in margin
+// collapsing, etc. See nsContainerFrame.h
+FRAME_STATE_BIT(Generic, 7, NS_FRAME_IS_OVERFLOW_CONTAINER)
+
+// If this bit is set, then the frame has been moved out of the flow,
+// e.g., it is absolutely positioned or floated
+FRAME_STATE_BIT(Generic, 8, NS_FRAME_OUT_OF_FLOW)
+
+// Frame can be an abs/fixed pos. container, if its style says so.
+// MarkAs[Not]AbsoluteContainingBlock will assert that this bit is set.
+// NS_FRAME_HAS_ABSPOS_CHILDREN must not be set when this bit is unset.
+FRAME_STATE_BIT(Generic, 9, NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)
+
+// If this bit is set, then the frame and _all_ of its descendant frames need
+// to be reflowed.
+// This bit is set when the frame is first created.
+// This bit is cleared by DidReflow after the required call to Reflow has
+// finished.
+// Do not set this bit yourself if you plan to pass the frame to
+// nsIPresShell::FrameNeedsReflow. Pass the right arguments instead.
+FRAME_STATE_BIT(Generic, 10, NS_FRAME_IS_DIRTY)
+
+// If this bit is set then the frame is too deep in the frame tree, and
+// we'll stop updating it and its children, to prevent stack overflow
+// and the like.
+FRAME_STATE_BIT(Generic, 11, NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
+
+// If this bit is set, then either:
+// 1. the frame has at least one child that has the NS_FRAME_IS_DIRTY bit or
+// NS_FRAME_HAS_DIRTY_CHILDREN bit set, or
+// 2. the frame has had at least one child removed since the last reflow, or
+// 3. the frame has had a style change that requires the frame to be reflowed
+// but does not _necessarily_ require its descendants to be reflowed (e.g.,
+// for a 'height', 'width', 'margin', etc. change, it's up to the
+// applicable Reflow methods to decide whether the frame's children
+// _actually_ need to be reflowed).
+// If this bit is set but the NS_FRAME_IS_DIRTY is not set, then Reflow still
+// needs to be called on the frame, but Reflow will likely not do as much work
+// as it would if NS_FRAME_IS_DIRTY were set. See the comment documenting
+// nsFrame::Reflow for more.
+// This bit is cleared by DidReflow after the required call to Reflow has
+// finished.
+// Do not set this bit yourself if you plan to pass the frame to
+// nsIPresShell::FrameNeedsReflow. Pass the right arguments instead.
+FRAME_STATE_BIT(Generic, 12, NS_FRAME_HAS_DIRTY_CHILDREN)
+
+// If this bit is set, the frame has an associated view
+FRAME_STATE_BIT(Generic, 13, NS_FRAME_HAS_VIEW)
+
+// If this bit is set, the frame was created from anonymous content.
+FRAME_STATE_BIT(Generic, 14, NS_FRAME_INDEPENDENT_SELECTION)
+
+// If this bit is set, the frame is part of the mangled frame hierarchy
+// that results when an inline has been split because of a nested block.
+// See the comments in nsCSSFrameConstructor::ConstructInline for
+// more details.
+FRAME_STATE_BIT(Generic, 15, NS_FRAME_PART_OF_IBSPLIT)
+
+// If this bit is set, then transforms (e.g. CSS or SVG transforms) are allowed
+// to affect the frame, and a transform may currently be in affect. If this bit
+// is not set, then any transforms on the frame will be ignored.
+// This is used primarily in GetTransformMatrix to optimize for the
+// common case.
+FRAME_STATE_BIT(Generic, 16, NS_FRAME_MAY_BE_TRANSFORMED)
+
+// If this bit is set, the frame itself is a bidi continuation,
+// or is incomplete (its next sibling is a bidi continuation)
+FRAME_STATE_BIT(Generic, 17, NS_FRAME_IS_BIDI)
+
+// If this bit is set the frame has descendant with a view
+FRAME_STATE_BIT(Generic, 18, NS_FRAME_HAS_CHILD_WITH_VIEW)
+
+// If this bit is set, then reflow may be dispatched from the current
+// frame instead of the root frame.
+FRAME_STATE_BIT(Generic, 19, NS_FRAME_REFLOW_ROOT)
+
+// NOTE: Bits 20-31 and 60-63 of the frame state are reserved for specific
+// frame classes.
+
+// This bit is set on floats whose parent does not contain their
+// placeholder. This can happen for two reasons: (1) the float was
+// split, and this piece is the continuation, or (2) the entire float
+// didn't fit on the page.
+// Note that this bit is also shared by text frames for
+// TEXT_IS_IN_TOKEN_MATHML. That's OK because we only check the
+// NS_FRAME_IS_PUSHED_FLOAT bit on frames which we already know are
+// out-of-flow.
+FRAME_STATE_BIT(Generic, 32, NS_FRAME_IS_PUSHED_FLOAT)
+
+// This bit acts as a loop flag for recursive paint server drawing.
+FRAME_STATE_BIT(Generic, 33, NS_FRAME_DRAWING_AS_PAINTSERVER)
+
+// Intrinsic ISize depending on the frame's BSize is rare but possible.
+// This flag indicates that the frame has (or once had) a descendant in that
+// situation (possibly the frame itself).
+FRAME_STATE_BIT(Generic, 34, NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)
+
+// A flag that tells us we can take the common path with respect to style
+// properties for this frame when building event regions. This flag is cleared
+// when any styles are changed and then we recompute it on the next build
+// of the event regions.
+FRAME_STATE_BIT(Generic, 35, NS_FRAME_SIMPLE_EVENT_REGIONS)
+
+// Frame is a display root and the retained layer tree needs to be updated
+// at the next paint via display list construction.
+// Only meaningful for display roots, so we don't really need a global state
+// bit; we could free up this bit with a little extra complexity.
+FRAME_STATE_BIT(Generic, 36, NS_FRAME_UPDATE_LAYER_TREE)
+
+// Frame can accept absolutely positioned children.
+FRAME_STATE_BIT(Generic, 37, NS_FRAME_HAS_ABSPOS_CHILDREN)
+
+// A display item for this frame has been painted as part of a PaintedLayer.
+FRAME_STATE_BIT(Generic, 38, NS_FRAME_PAINTED_THEBES)
+
+// Frame is or is a descendant of something with a fixed block-size, unless
+// that ancestor is a body or html element, and has no closer ancestor that is
+// overflow:auto or overflow:scroll.
+FRAME_STATE_BIT(Generic, 39, NS_FRAME_IN_CONSTRAINED_BSIZE)
+
+// This is only set during painting
+FRAME_STATE_BIT(Generic, 40, NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)
+
+// Is this frame a container for font size inflation, i.e., is it a
+// frame whose width is used to determine the inflation factor for
+// everything whose nearest ancestor container for this frame?
+FRAME_STATE_BIT(Generic, 41, NS_FRAME_FONT_INFLATION_CONTAINER)
+
+// Does this frame manage a region in which we do font size inflation,
+// i.e., roughly, is it an element establishing a new block formatting
+// context?
+FRAME_STATE_BIT(Generic, 42, NS_FRAME_FONT_INFLATION_FLOW_ROOT)
+
+// This bit is set on SVG frames that are laid out using SVG's coordinate
+// system based layout (as opposed to any of the CSS layout models). Note that
+// this does not include nsSVGOuterSVGFrame since it takes part is CSS layout.
+FRAME_STATE_BIT(Generic, 43, NS_FRAME_SVG_LAYOUT)
+
+// Is this frame allowed to have generated (::before/::after) content?
+FRAME_STATE_BIT(Generic, 44, NS_FRAME_MAY_HAVE_GENERATED_CONTENT)
+
+// This bit is set on frames that create ContainerLayers with component
+// alpha children. With BasicLayers we avoid creating these, so we mark
+// the frames for future reference.
+FRAME_STATE_BIT(Generic, 45, NS_FRAME_NO_COMPONENT_ALPHA)
+
+// This bit indicates that we're tracking visibility for this frame, and that
+// the frame has a VisibilityStateProperty property.
+FRAME_STATE_BIT(Generic, 46, NS_FRAME_VISIBILITY_IS_TRACKED)
+
+// The frame is a descendant of SVGTextFrame and is thus used for SVG
+// text layout.
+FRAME_STATE_BIT(Generic, 47, NS_FRAME_IS_SVG_TEXT)
+
+// Frame is marked as needing painting
+FRAME_STATE_BIT(Generic, 48, NS_FRAME_NEEDS_PAINT)
+
+// Frame has a descendant frame that needs painting - This includes
+// cross-doc children.
+FRAME_STATE_BIT(Generic, 49, NS_FRAME_DESCENDANT_NEEDS_PAINT)
+
+// Frame is a descendant of a popup
+FRAME_STATE_BIT(Generic, 50, NS_FRAME_IN_POPUP)
+
+// Frame has only descendant frames that needs painting - This includes
+// cross-doc children. This guarantees that all descendents have
+// NS_FRAME_NEEDS_PAINT and NS_FRAME_ALL_DESCENDANTS_NEED_PAINT, or they
+// have no display items.
+FRAME_STATE_BIT(Generic, 51, NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)
+
+// Frame is marked as NS_FRAME_NEEDS_PAINT and also has an explicit
+// rect stored to invalidate.
+FRAME_STATE_BIT(Generic, 52, NS_FRAME_HAS_INVALID_RECT)
+
+// Frame is not displayed directly due to it being, or being under, an SVG
+// <defs> element or an SVG resource element (<mask>, <pattern>, etc.)
+FRAME_STATE_BIT(Generic, 53, NS_FRAME_IS_NONDISPLAY)
+
+// Frame has a LayerActivityProperty property
+FRAME_STATE_BIT(Generic, 54, NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)
+
+// Set for all descendants of MathML sub/supscript elements (other than the
+// base frame) to indicate that the SSTY font feature should be used.
+FRAME_STATE_BIT(Generic, 58, NS_FRAME_MATHML_SCRIPT_DESCENDANT)
+
+// This state bit is set on frames within token MathML elements if the
+// token represents an <mi> tag whose inner HTML consists of a single
+// non-whitespace character to allow special rendering behaviour.
+FRAME_STATE_BIT(Generic, 59, NS_FRAME_IS_IN_SINGLE_CHAR_MI)
+
+// NOTE: Bits 20-31 and 60-63 of the frame state are reserved for specific
+// frame classes.
+
+
+// == Frame state bits that apply to box frames ===============================
+
+FRAME_STATE_GROUP(Box, nsBoxFrame)
+
+FRAME_STATE_BIT(Box, 20, NS_STATE_BOX_CHILD_RESERVED)
+FRAME_STATE_BIT(Box, 21, NS_STATE_STACK_NOT_POSITIONED)
+FRAME_STATE_BIT(Box, 22, NS_STATE_IS_HORIZONTAL)
+FRAME_STATE_BIT(Box, 23, NS_STATE_AUTO_STRETCH)
+FRAME_STATE_BIT(Box, 24, NS_STATE_IS_ROOT)
+FRAME_STATE_BIT(Box, 25, NS_STATE_CURRENTLY_IN_DEBUG)
+FRAME_STATE_BIT(Box, 26, NS_STATE_SET_TO_DEBUG)
+FRAME_STATE_BIT(Box, 27, NS_STATE_DEBUG_WAS_SET)
+FRAME_STATE_BIT(Box, 28, NS_STATE_MENU_HAS_POPUP_LIST)
+FRAME_STATE_BIT(Box, 29, NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
+FRAME_STATE_BIT(Box, 30, NS_STATE_EQUAL_SIZE)
+FRAME_STATE_BIT(Box, 31, NS_STATE_IS_DIRECTION_NORMAL)
+FRAME_STATE_BIT(Box, 60, NS_FRAME_MOUSE_THROUGH_ALWAYS)
+FRAME_STATE_BIT(Box, 61, NS_FRAME_MOUSE_THROUGH_NEVER)
+
+
+// == Frame state bits that apply to flex container frames ====================
+
+FRAME_STATE_GROUP(FlexContainer, nsFlexContainerFrame)
+
+// Set for a flex container whose children have been reordered due to 'order'.
+// (Means that we have to be more thorough about checking them for sortedness.)
+FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_CHILDREN_REORDERED)
+
+// Set for a flex container that is emulating a legacy
+// 'display:-webkit-{inline-}box' container.
+FRAME_STATE_BIT(FlexContainer, 21, NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX)
+
+// True if the container has no flex items; may lie if there is a pending reflow
+FRAME_STATE_BIT(FlexContainer, 22, NS_STATE_FLEX_SYNTHESIZE_BASELINE)
+
+// == Frame state bits that apply to grid container frames ====================
+
+FRAME_STATE_GROUP(GridContainer, nsGridContainerFrame)
+
+// True iff the normal flow children are already in CSS 'order' in the
+// order they occur in the child frame list.
+FRAME_STATE_BIT(GridContainer, 20, NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
+
+// True iff some first-in-flow in-flow children were pushed.
+// Note that those child frames may have been removed without this bit
+// being updated for performance reasons, so code shouldn't depend on
+// actually finding any pushed items when this bit is set.
+FRAME_STATE_BIT(GridContainer, 21, NS_STATE_GRID_DID_PUSH_ITEMS)
+
+// True iff computed grid values should be generated on the next reflow
+FRAME_STATE_BIT(GridContainer, 22, NS_STATE_GRID_GENERATE_COMPUTED_VALUES)
+
+// True if the container has no grid items; may lie if there is a pending reflow
+FRAME_STATE_BIT(GridContainer, 23, NS_STATE_GRID_SYNTHESIZE_BASELINE)
+
+// == Frame state bits that apply to SVG frames ===============================
+
+FRAME_STATE_GROUP(SVG, nsISVGChildFrame)
+FRAME_STATE_GROUP(SVG, nsSVGContainerFrame)
+
+FRAME_STATE_BIT(SVG, 20, NS_STATE_IS_OUTER_SVG)
+
+// If this bit is set, we are a <clipPath> element or descendant.
+FRAME_STATE_BIT(SVG, 21, NS_STATE_SVG_CLIPPATH_CHILD)
+
+// For SVG text, the NS_FRAME_IS_DIRTY and NS_FRAME_HAS_DIRTY_CHILDREN bits
+// indicate that our anonymous block child needs to be reflowed, and that
+// mPositions will likely need to be updated as a consequence. These are set,
+// for example, when the font-family changes. Sometimes we only need to
+// update mPositions though. For example if the x/y attributes change.
+// mPositioningDirty is used to indicate this latter "things are dirty" case
+// to allow us to avoid reflowing the anonymous block when it is not
+// necessary.
+FRAME_STATE_BIT(SVG, 22, NS_STATE_SVG_POSITIONING_DIRTY)
+
+// For text, whether the values from x/y/dx/dy attributes have any percentage
+// values that are used in determining the positions of glyphs. The value will
+// be true even if a positioning value is overridden by a descendant element's
+// attribute with a non-percentage length. For example,
+// NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES would be set for:
+//
+// <text x="10%"><tspan x="0">abc</tspan></text>
+//
+// Percentage values beyond the number of addressable characters, however, do
+// not influence NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES. For example,
+// NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES would be false for:
+//
+// <text x="10 20 30 40%">abc</text>
+//
+// NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES is used to determine whether
+// to recompute mPositions when the viewport size changes. So although the
+// first example above shows that NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES
+// can be true even if a viewport size change will not affect mPositions,
+// determining a completley accurate value for
+// NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES would require extra work that is
+// probably not worth it.
+FRAME_STATE_BIT(SVG, 23, NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)
+
+FRAME_STATE_BIT(SVG, 24, NS_STATE_SVG_TEXT_IN_REFLOW)
+
+
+// == Frame state bits that apply to text frames ==============================
+
+FRAME_STATE_GROUP(Text, nsTextFrame)
+
+// -- Flags set during reflow -------------------------------------------------
+
+// nsTextFrame.cpp defines TEXT_REFLOW_FLAGS to be all of these bits.
+
+// This bit is set on the first frame in a continuation indicating
+// that it was chopped short because of :first-letter style.
+FRAME_STATE_BIT(Text, 20, TEXT_FIRST_LETTER)
+
+// This bit is set on frames that are logically adjacent to the start of the
+// line (i.e. no prior frame on line with actual displayed in-flow content).
+FRAME_STATE_BIT(Text, 21, TEXT_START_OF_LINE)
+
+// This bit is set on frames that are logically adjacent to the end of the
+// line (i.e. no following on line with actual displayed in-flow content).
+FRAME_STATE_BIT(Text, 22, TEXT_END_OF_LINE)
+
+// This bit is set on frames that end with a hyphenated break.
+FRAME_STATE_BIT(Text, 23, TEXT_HYPHEN_BREAK)
+
+// This bit is set on frames that trimmed trailing whitespace characters when
+// calculating their width during reflow.
+FRAME_STATE_BIT(Text, 24, TEXT_TRIMMED_TRAILING_WHITESPACE)
+
+// This bit is set on frames that have justification enabled. We record
+// this in a state bit because we don't always have the containing block
+// easily available to check text-align on.
+FRAME_STATE_BIT(Text, 25, TEXT_JUSTIFICATION_ENABLED)
+
+// Set this bit if the textframe has overflow area for IME/spellcheck underline.
+FRAME_STATE_BIT(Text, 26, TEXT_SELECTION_UNDERLINE_OVERFLOWED)
+
+// -- Cache bits for IsEmpty() ------------------------------------------------
+
+// nsTextFrame.cpp defines TEXT_WHITESPACE_FLAGS to be both of these bits.
+
+// Set this bit if the textframe is known to be only collapsible whitespace.
+FRAME_STATE_BIT(Text, 27, TEXT_IS_ONLY_WHITESPACE)
+
+// Set this bit if the textframe is known to be not only collapsible whitespace.
+FRAME_STATE_BIT(Text, 28, TEXT_ISNOT_ONLY_WHITESPACE)
+
+// -- Other state bits --------------------------------------------------------
+
+// Set when this text frame is mentioned in the userdata for mTextRun
+FRAME_STATE_BIT(Text, 29, TEXT_IN_TEXTRUN_USER_DATA)
+
+// This state bit is set on frames whose character data offsets need to be
+// fixed up
+FRAME_STATE_BIT(Text, 30, TEXT_OFFSETS_NEED_FIXING)
+
+// This state bit is set on frames that have some non-collapsed characters after
+// reflow
+FRAME_STATE_BIT(Text, 31, TEXT_HAS_NONCOLLAPSED_CHARACTERS)
+
+// This state bit is set on children of token MathML elements.
+// NOTE: TEXT_IS_IN_TOKEN_MATHML has a global state bit value that is shared
+// with NS_FRAME_IS_PUSHED_FLOAT.
+FRAME_STATE_BIT(Text, 32, TEXT_IS_IN_TOKEN_MATHML)
+
+// Set when this text frame is mentioned in the userdata for the
+// uninflated textrun property
+FRAME_STATE_BIT(Text, 60, TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA)
+
+FRAME_STATE_BIT(Text, 61, TEXT_HAS_FONT_INFLATION)
+
+// Set when this text frame contains nothing that will actually render
+FRAME_STATE_BIT(Text, 62, TEXT_NO_RENDERED_GLYPHS)
+
+// Whether this frame is cached in the Offset Frame Cache
+// (OffsetToFrameProperty)
+FRAME_STATE_BIT(Text, 63, TEXT_IN_OFFSET_CACHE)
+
+
+// == Frame state bits that apply to block frames =============================
+
+FRAME_STATE_GROUP(Block, nsBlockFrame)
+
+// Something in the block has changed that requires Bidi resolution to be
+// performed on the block. This flag must be either set on all blocks in a
+// continuation chain or none of them.
+FRAME_STATE_BIT(Block, 20, NS_BLOCK_NEEDS_BIDI_RESOLUTION)
+
+FRAME_STATE_BIT(Block, 21, NS_BLOCK_HAS_PUSHED_FLOATS)
+
+// This indicates that this is a frame from which child margins can be
+// calculated. The absence of this flag implies that child margin calculations
+// should ignore the frame and look further up the parent chain. Used in
+// nsBlockReflowContext::ComputeCollapsedBStartMargin() via
+// nsBlockFrame::IsMarginRoot().
+//
+// This causes the BlockReflowInput's constructor to set the
+// mIsBStartMarginRoot and mIsBEndMarginRoot flags.
+FRAME_STATE_BIT(Block, 22, NS_BLOCK_MARGIN_ROOT)
+
+// This indicates that a block frame should create its own float manager. This
+// is required by each block frame that can contain floats. The float manager is
+// used to reserve space for the floated frames.
+FRAME_STATE_BIT(Block, 23, NS_BLOCK_FLOAT_MGR)
+
+FRAME_STATE_BIT(Block, 24, NS_BLOCK_HAS_LINE_CURSOR)
+
+FRAME_STATE_BIT(Block, 25, NS_BLOCK_HAS_OVERFLOW_LINES)
+
+FRAME_STATE_BIT(Block, 26, NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)
+
+// Set on any block that has descendant frames in the normal
+// flow with 'clear' set to something other than 'none'
+// (including <BR CLEAR="..."> frames)
+FRAME_STATE_BIT(Block, 27, NS_BLOCK_HAS_CLEAR_CHILDREN)
+
+// NS_BLOCK_CLIP_PAGINATED_OVERFLOW is only set in paginated prescontexts, on
+// blocks which were forced to not have scrollframes but still need to clip
+// the display of their kids.
+FRAME_STATE_BIT(Block, 28, NS_BLOCK_CLIP_PAGINATED_OVERFLOW)
+
+// NS_BLOCK_HAS_FIRST_LETTER_STYLE means that the block has first-letter style,
+// even if it has no actual first-letter frame among its descendants.
+FRAME_STATE_BIT(Block, 29, NS_BLOCK_HAS_FIRST_LETTER_STYLE)
+
+// NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET and NS_BLOCK_FRAME_HAS_INSIDE_BULLET
+// means the block has an associated bullet frame, they are mutually exclusive.
+FRAME_STATE_BIT(Block, 30, NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)
+FRAME_STATE_BIT(Block, 31, NS_BLOCK_FRAME_HAS_INSIDE_BULLET)
+
+// This block has had a child marked dirty, so before we reflow we need
+// to look through the lines to find any such children and mark
+// appropriate lines dirty.
+FRAME_STATE_BIT(Block, 61, NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)
+
+// Are our cached intrinsic widths intrinsic widths for font size
+// inflation? i.e., what was the current state of
+// GetPresContext()->mInflationDisabledForShrinkWrap at the time they
+// were computed?
+// nsBlockFrame is the only thing that caches intrinsic widths that
+// needs to track this because it's the only thing that caches intrinsic
+// widths that lives inside of things (form controls) that do intrinsic
+// sizing with font inflation enabled.
+FRAME_STATE_BIT(Block, 62, NS_BLOCK_FRAME_INTRINSICS_INFLATED)
+
+// NS_BLOCK_HAS_FIRST_LETTER_CHILD means that there is an inflow first-letter
+// frame among the block's descendants. If there is a floating first-letter
+// frame, or the block has first-letter style but has no first letter, this
+// bit is not set. This bit is set on the first continuation only.
+FRAME_STATE_BIT(Block, 63, NS_BLOCK_HAS_FIRST_LETTER_CHILD)
+
+
+// == Frame state bits that apply to bullet frames ============================
+
+FRAME_STATE_GROUP(Bullet, nsBulletFrame)
+
+FRAME_STATE_BIT(Block, 62, BULLET_FRAME_HAS_FONT_INFLATION)
+FRAME_STATE_BIT(Block, 63, BULLET_FRAME_IMAGE_LOADING)
+
+
+// == Frame state bits that apply to scroll frames ============================
+
+FRAME_STATE_GROUP(Scroll, nsIScrollableFrame)
+
+// When set, the next scroll operation on the scrollframe will invalidate its
+// entire contents. Useful for text-overflow.
+// This bit is cleared after each time the scrollframe is scrolled. Whoever
+// needs to set it should set it again on each paint.
+FRAME_STATE_BIT(Scroll, 20, NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL)
+
+
+// == Frame state bits that apply to image frames =============================
+
+FRAME_STATE_GROUP(Image, nsImageFrame)
+
+FRAME_STATE_BIT(Image, 20, IMAGE_SIZECONSTRAINED)
+FRAME_STATE_BIT(Image, 21, IMAGE_GOTINITIALREFLOW)
+
+
+// == Frame state bits that apply to inline frames ============================
+
+FRAME_STATE_GROUP(Inline, nsInlineFrame)
+
+/** In Bidi inline start (or end) margin/padding/border should be applied to
+ * first (or last) frame (or a continuation frame).
+ * This state value shows if this frame is first (or last) continuation
+ * or not.
+ */
+
+FRAME_STATE_BIT(Inline, 21, NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
+FRAME_STATE_BIT(Inline, 22, NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST)
+FRAME_STATE_BIT(Inline, 23, NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST)
+// nsRubyTextFrame inherits from nsInlineFrame
+
+
+// == Frame state bits that apply to ruby text frames =========================
+
+FRAME_STATE_GROUP(RubyText, nsRubyTextFrame)
+
+// inherits from nsInlineFrame
+FRAME_STATE_BIT(RubyText, 24, NS_RUBY_TEXT_FRAME_AUTOHIDE)
+
+
+// == Frame state bits that apply to ruby text container frames ===============
+
+FRAME_STATE_GROUP(RubyTextContainer, nsRubyTextContainerFrame)
+
+FRAME_STATE_BIT(RubyTextContainer, 20, NS_RUBY_TEXT_CONTAINER_IS_SPAN)
+
+
+// == Frame state bits that apply to placeholder frames =======================
+
+FRAME_STATE_GROUP(Placeholder, nsPlaceholderFrame)
+
+// Frame state bits that are used to keep track of what this is a
+// placeholder for.
+
+FRAME_STATE_BIT(Placeholder, 20, PLACEHOLDER_FOR_FLOAT)
+FRAME_STATE_BIT(Placeholder, 21, PLACEHOLDER_FOR_ABSPOS)
+FRAME_STATE_BIT(Placeholder, 22, PLACEHOLDER_FOR_FIXEDPOS)
+FRAME_STATE_BIT(Placeholder, 23, PLACEHOLDER_FOR_POPUP)
+FRAME_STATE_BIT(Placeholder, 24, PLACEHOLDER_FOR_TOPLAYER)
+
+// This bit indicates that the out-of-flow frame's static position needs to be
+// determined using the CSS Box Alignment properties
+// ([align,justify]-[self,content]). When this is set, the placeholder frame's
+// position doesn't represent the static position, as it usually would --
+// rather, it represents the logical start corner of the alignment containing
+// block. Then, after we've determined the out-of-flow frame's size, we can
+// resolve the actual static position using the alignment properties.
+FRAME_STATE_BIT(Placeholder, 25, PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)
+
+
+// == Frame state bits that apply to table cell frames ========================
+
+FRAME_STATE_GROUP(TableCell, nsTableCellFrame)
+
+FRAME_STATE_BIT(TableCell, 28, NS_TABLE_CELL_HAS_PCT_OVER_BSIZE)
+FRAME_STATE_BIT(TableCell, 29, NS_TABLE_CELL_HAD_SPECIAL_REFLOW)
+FRAME_STATE_BIT(TableCell, 31, NS_TABLE_CELL_CONTENT_EMPTY)
+
+
+// == Frame state bits that apply to table column frames ======================
+
+// Bits 28-31 on nsTableColFrames are used to store the column type.
+
+
+// == Frame state bits that apply to table column group frames ================
+
+// Bits 30-31 on nsTableColGroupFrames are used to store the column type.
+
+
+// == Frame state bits that apply to table rows and table row group frames ====
+
+FRAME_STATE_GROUP(TableRowAndRowGroup, nsTableRowFrame)
+FRAME_STATE_GROUP(TableRowAndRowGroup, nsTableRowGroupFrame)
+
+// see nsTableRowGroupFrame::InitRepeatedFrame
+FRAME_STATE_BIT(TableRowAndRowGroup, 28, NS_REPEATED_ROW_OR_ROWGROUP)
+
+
+// == Frame state bits that apply to table row frames =========================
+
+FRAME_STATE_GROUP(TableRow, nsTableRowFrame)
+
+// Indicates whether this row has any cells that have
+// non-auto-bsize and rowspan=1
+FRAME_STATE_BIT(TableRow, 29, NS_ROW_HAS_CELL_WITH_STYLE_BSIZE)
+
+FRAME_STATE_BIT(TableRow, 30, NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE)
+
+
+// == Frame state bits that apply to table row group frames ===================
+
+FRAME_STATE_GROUP(TableRowGroup, nsTableRowGroupFrame)
+
+FRAME_STATE_BIT(TableRowGroup, 27, NS_ROWGROUP_HAS_ROW_CURSOR)
+FRAME_STATE_BIT(TableRowGroup, 30, NS_ROWGROUP_HAS_STYLE_BSIZE)
+
+// thead or tfoot should be repeated on every printed page
+FRAME_STATE_BIT(TableRowGroup, 31, NS_ROWGROUP_REPEATABLE)
+
+FRAME_STATE_GROUP(Table, nsTableFrame)
+
+FRAME_STATE_BIT(Table, 28, NS_TABLE_PART_HAS_FIXED_BACKGROUND)
+
+#ifdef DEFINED_FRAME_STATE_GROUP
+#undef DEFINED_FRAME_STATE_GROUP
+#undef FRAME_STATE_GROUP
+#endif
+
+#ifdef DEFINED_FRAME_STATE_BIT
+#undef DEFINED_FRAME_STATE_BIT
+#undef FRAME_STATE_BIT
+#endif
diff --git a/layout/generic/nsFrameUtil.cpp b/layout/generic/nsFrameUtil.cpp
new file mode 100644
index 000000000..e6977b464
--- /dev/null
+++ b/layout/generic/nsFrameUtil.cpp
@@ -0,0 +1,670 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* utilities for regression tests based on frame tree comparison */
+
+#include "nsIFrameUtil.h"
+#include "nsFrame.h"
+#include "nsString.h"
+#include "nsRect.h"
+#include <stdlib.h>
+#include "plstr.h"
+
+
+#ifdef DEBUG
+class nsFrameUtil : public nsIFrameUtil {
+protected:
+ virtual ~nsFrameUtil();
+
+public:
+ nsFrameUtil();
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput=0) override;
+ NS_IMETHOD DumpRegressionData(FILE* aInputFile, FILE* aOutputFile) override;
+
+ struct Node;
+ struct Tag;
+
+ struct NodeList {
+ NodeList();
+ ~NodeList();
+
+ static void Destroy(NodeList* aLists);
+
+ NodeList* next; // for lists of lists
+ Node* node;
+ char* name;
+ };
+
+ struct Node {
+ Node();
+ ~Node();
+
+ static void Destroy(Node* aNode);
+
+ static Node* Read(FILE* aFile, Tag* aTag);
+
+ static Node* ReadTree(FILE* aFile);
+
+ Node* next;
+ char* type;
+ uint32_t state;
+ nsRect bbox;
+ nsCString styleData;
+ NodeList* lists;
+ };
+
+ struct Tag {
+ Tag();
+ ~Tag();
+
+ static Tag* Parse(FILE* aFile);
+
+ void AddAttr(char* aAttr, char* aValue);
+
+ const char* GetAttr(const char* aAttr);
+
+ void ReadAttrs(FILE* aFile);
+
+ void ToString(nsString& aResult);
+
+ enum Type {
+ open,
+ close,
+ openClose
+ };
+
+ char* name;
+ Type type;
+ char** attributes;
+ int32_t num;
+ int32_t size;
+ char** values;
+ };
+
+ static char* Copy(const char* aString);
+
+ static void DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent);
+ static void DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent);
+ static bool CompareTrees(Node* aNode1, Node* aNode2);
+};
+
+char*
+nsFrameUtil::Copy(const char* aString)
+{
+ if (aString) {
+ int l = ::strlen(aString);
+ char* c = new char[l+1];
+ if (!c)
+ return nullptr;
+ memcpy(c, aString, l+1);
+ return c;
+ }
+ return nullptr;
+}
+
+//----------------------------------------------------------------------
+
+nsFrameUtil::NodeList::NodeList()
+ : next(nullptr), node(nullptr), name(nullptr)
+{
+}
+
+nsFrameUtil::NodeList::~NodeList()
+{
+ if (nullptr != name) {
+ delete name;
+ }
+ if (nullptr != node) {
+ Node::Destroy(node);
+ }
+}
+
+void
+nsFrameUtil::NodeList::Destroy(NodeList* aLists)
+{
+ while (nullptr != aLists) {
+ NodeList* next = aLists->next;
+ delete aLists;
+ aLists = next;
+ }
+}
+
+//----------------------------------------------------------------------
+
+nsFrameUtil::Node::Node()
+ : next(nullptr), type(nullptr), state(0), lists(nullptr)
+{
+}
+
+nsFrameUtil::Node::~Node()
+{
+ if (nullptr != type) {
+ delete type;
+ }
+ if (nullptr != lists) {
+ NodeList::Destroy(lists);
+ }
+}
+
+void
+nsFrameUtil::Node::Destroy(Node* aList)
+{
+ while (nullptr != aList) {
+ Node* next = aList->next;
+ delete aList;
+ aList = next;
+ }
+}
+
+static int32_t GetInt(nsFrameUtil::Tag* aTag, const char* aAttr)
+{
+ const char* value = aTag->GetAttr(aAttr);
+ if (nullptr != value) {
+ return int32_t( atoi(value) );
+ }
+ return 0;
+}
+
+nsFrameUtil::Node*
+nsFrameUtil::Node::ReadTree(FILE* aFile)
+{
+ Tag* tag = Tag::Parse(aFile);
+ if (nullptr == tag) {
+ return nullptr;
+ }
+ if (PL_strcmp(tag->name, "frame") != 0) {
+ delete tag;
+ return nullptr;
+ }
+ Node* result = Read(aFile, tag);
+ fclose(aFile);
+ return result;
+}
+
+nsFrameUtil::Node*
+nsFrameUtil::Node::Read(FILE* aFile, Tag* tag)
+{
+ Node* node = new Node;
+ node->type = Copy(tag->GetAttr("type"));
+ if (!node->type) {
+ /* crash() */
+ }
+ node->state = GetInt(tag, "state");
+ delete tag;
+
+ for (;;) {
+ tag = Tag::Parse(aFile);
+ if (nullptr == tag) break;
+ if (PL_strcmp(tag->name, "frame") == 0) {
+ delete tag;
+ break;
+ }
+ if (PL_strcmp(tag->name, "bbox") == 0) {
+ nscoord x = nscoord( GetInt(tag, "x") );
+ nscoord y = nscoord( GetInt(tag, "y") );
+ nscoord w = nscoord( GetInt(tag, "w") );
+ nscoord h = nscoord( GetInt(tag, "h") );
+ node->bbox.SetRect(x, y, w, h);
+ }
+ else if (PL_strcmp(tag->name, "child-list") == 0) {
+ NodeList* list = new NodeList();
+ list->name = Copy(tag->GetAttr("name"));
+ if (!list->name) {
+ /* crash() */
+ }
+ list->next = node->lists;
+ node->lists = list;
+ delete tag;
+
+ Node** tailp = &list->node;
+ for (;;) {
+ tag = Tag::Parse(aFile);
+ if (nullptr == tag) {
+ break;
+ }
+ if (PL_strcmp(tag->name, "child-list") == 0) {
+ break;
+ }
+ if (PL_strcmp(tag->name, "frame") != 0) {
+ break;
+ }
+ Node* child = Node::Read(aFile, tag);
+ if (nullptr == child) {
+ break;
+ }
+ *tailp = child;
+ tailp = &child->next;
+ }
+ }
+ else if((PL_strcmp(tag->name, "font") == 0) ||
+ (PL_strcmp(tag->name, "color") == 0) ||
+ (PL_strcmp(tag->name, "spacing") == 0) ||
+ (PL_strcmp(tag->name, "list") == 0) ||
+ (PL_strcmp(tag->name, "position") == 0) ||
+ (PL_strcmp(tag->name, "text") == 0) ||
+ (PL_strcmp(tag->name, "display") == 0) ||
+ (PL_strcmp(tag->name, "table") == 0) ||
+ (PL_strcmp(tag->name, "content") == 0) ||
+ (PL_strcmp(tag->name, "UI") == 0) ||
+ (PL_strcmp(tag->name, "print") == 0)) {
+ const char* attr = tag->GetAttr("data");
+ node->styleData.Append('|');
+ node->styleData.Append(attr ? attr : "null attr");
+ }
+
+ delete tag;
+ }
+ return node;
+}
+
+//----------------------------------------------------------------------
+
+nsFrameUtil::Tag::Tag()
+ : name(nullptr), type(open), attributes(nullptr), num(0), size(0),
+ values(nullptr)
+{
+}
+
+nsFrameUtil::Tag::~Tag()
+{
+ int32_t i, n = num;
+ if (0 != n) {
+ for (i = 0; i < n; i++) {
+ delete attributes[i];
+ delete values[i];
+ }
+ delete attributes;
+ delete values;
+ }
+}
+
+void
+nsFrameUtil::Tag::AddAttr(char* aAttr, char* aValue)
+{
+ if (num == size) {
+ int32_t newSize = size * 2 + 4;
+ char** a = new char*[newSize];
+ char** v = new char*[newSize];
+ if (0 != num) {
+ memcpy(a, attributes, num * sizeof(char*));
+ memcpy(v, values, num * sizeof(char*));
+ delete attributes;
+ delete values;
+ }
+ attributes = a;
+ values = v;
+ size = newSize;
+ }
+ attributes[num] = aAttr;
+ values[num] = aValue;
+ num = num + 1;
+}
+
+const char*
+nsFrameUtil::Tag::GetAttr(const char* aAttr)
+{
+ int32_t i, n = num;
+ for (i = 0; i < n; i++) {
+ if (PL_strcmp(attributes[i], aAttr) == 0) {
+ return values[i];
+ }
+ }
+ return nullptr;
+}
+
+static inline int IsWhiteSpace(int c) {
+ return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
+}
+
+static bool EatWS(FILE* aFile)
+{
+ for (;;) {
+ int c = getc(aFile);
+ if (c < 0) {
+ return false;
+ }
+ if (!IsWhiteSpace(c)) {
+ ungetc(c, aFile);
+ break;
+ }
+ }
+ return true;
+}
+
+static bool Expect(FILE* aFile, char aChar)
+{
+ int c = getc(aFile);
+ if (c < 0) return false;
+ if (c != aChar) {
+ ungetc(c, aFile);
+ return false;
+ }
+ return true;
+}
+
+static char* ReadIdent(FILE* aFile)
+{
+ char id[1000];
+ char* ip = id;
+ char* end = ip + sizeof(id) - 1;
+ while (ip < end) {
+ int c = fgetc(aFile);
+ if (c < 0) return nullptr;
+ if ((c == '=') || (c == '>') || (c == '/') || IsWhiteSpace(c)) {
+ ungetc(c, aFile);
+ break;
+ }
+ *ip++ = char(c);
+ }
+ *ip = '\0';
+ return nsFrameUtil::Copy(id);
+ /* may return a null pointer */
+}
+
+static char* ReadString(FILE* aFile)
+{
+ if (!Expect(aFile, '\"')) {
+ return nullptr;
+ }
+ char id[1000];
+ char* ip = id;
+ char* end = ip + sizeof(id) - 1;
+ while (ip < end) {
+ int c = fgetc(aFile);
+ if (c < 0) return nullptr;
+ if (c == '\"') {
+ break;
+ }
+ *ip++ = char(c);
+ }
+ *ip = '\0';
+ return nsFrameUtil::Copy(id);
+ /* may return a null pointer */
+}
+
+void
+nsFrameUtil::Tag::ReadAttrs(FILE* aFile)
+{
+ for (;;) {
+ if (!EatWS(aFile)) {
+ break;
+ }
+ int c = getc(aFile);
+ if (c < 0) break;
+ if (c == '/') {
+ if (!EatWS(aFile)) {
+ return;
+ }
+ if (Expect(aFile, '>')) {
+ type = openClose;
+ break;
+ }
+ }
+ else if (c == '>') {
+ break;
+ }
+ ungetc(c, aFile);
+ char* attr = ReadIdent(aFile);
+ if ((nullptr == attr) || !EatWS(aFile)) {
+ break;
+ }
+ char* value = nullptr;
+ if (Expect(aFile, '=')) {
+ value = ReadString(aFile);
+ if (nullptr == value) {
+ delete [] attr;
+ break;
+ }
+ }
+ AddAttr(attr, value);
+ }
+}
+
+nsFrameUtil::Tag*
+nsFrameUtil::Tag::Parse(FILE* aFile)
+{
+ if (!EatWS(aFile)) {
+ return nullptr;
+ }
+ if (Expect(aFile, '<')) {
+ Tag* tag = new Tag;
+ if (Expect(aFile, '/')) {
+ tag->type = close;
+ }
+ else {
+ tag->type = open;
+ }
+ tag->name = ReadIdent(aFile);
+ tag->ReadAttrs(aFile);
+ return tag;
+ }
+ return nullptr;
+}
+
+void
+nsFrameUtil::Tag::ToString(nsString& aResult)
+{
+ aResult.Truncate();
+ aResult.Append(char16_t('<'));
+ if (type == close) {
+ aResult.Append(char16_t('/'));
+ }
+ aResult.AppendASCII(name);
+ if (0 != num) {
+ int32_t i, n = num;
+ for (i = 0; i < n; i++) {
+ aResult.Append(char16_t(' '));
+ aResult.AppendASCII(attributes[i]);
+ if (values[i]) {
+ aResult.AppendLiteral("=\"");
+ aResult.AppendASCII(values[i]);
+ aResult.Append(char16_t('\"'));
+ }
+ }
+ }
+ if (type == openClose) {
+ aResult.Append(char16_t('/'));
+ }
+ aResult.Append(char16_t('>'));
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+NS_NewFrameUtil(nsIFrameUtil** aResult)
+{
+ NS_PRECONDITION(nullptr != aResult, "null pointer");
+ if (nullptr == aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsFrameUtil* it = new nsFrameUtil();
+
+ NS_ADDREF(*aResult = it);
+ return NS_OK;
+}
+
+nsFrameUtil::nsFrameUtil()
+{
+}
+
+nsFrameUtil::~nsFrameUtil()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsFrameUtil, nsIFrameUtil)
+
+void
+nsFrameUtil::DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent)
+{
+ nsFrame::IndentBy(aOutputFile, aIndent);
+ fprintf(aOutputFile, "%s 0x%x %d,%d,%d,%d, %s\n", aNode->type, aNode->state,
+ aNode->bbox.x, aNode->bbox.y,
+ aNode->bbox.width, aNode->bbox.height,
+ aNode->styleData.get());
+}
+
+void
+nsFrameUtil::DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent)
+{
+ while (nullptr != aNode) {
+ DumpNode(aNode, aOutputFile, aIndent);
+ nsFrameUtil::NodeList* lists = aNode->lists;
+ if (nullptr != lists) {
+ while (nullptr != lists) {
+ nsFrame::IndentBy(aOutputFile, aIndent);
+ fprintf(aOutputFile, " list: %s\n",
+ lists->name ? lists->name : "primary");
+ DumpTree(lists->node, aOutputFile, aIndent + 1);
+ lists = lists->next;
+ }
+ }
+ aNode = aNode->next;
+ }
+}
+
+bool
+nsFrameUtil::CompareTrees(Node* tree1, Node* tree2)
+{
+ bool result = true;
+ for (;; tree1 = tree1->next, tree2 = tree2->next) {
+ // Make sure both nodes are non-null, or at least agree with each other
+ if (nullptr == tree1) {
+ if (nullptr == tree2) {
+ break;
+ }
+ printf("first tree prematurely ends\n");
+ return false;
+ }
+ else if (nullptr == tree2) {
+ printf("second tree prematurely ends\n");
+ return false;
+ }
+
+ // Check the attributes that we care about
+ if (0 != PL_strcmp(tree1->type, tree2->type)) {
+ printf("frame type mismatch: %s vs. %s\n", tree1->type, tree2->type);
+ printf("Node 1:\n");
+ DumpNode(tree1, stdout, 1);
+ printf("Node 2:\n");
+ DumpNode(tree2, stdout, 1);
+ return false;
+ }
+
+ // Ignore the XUL scrollbar frames
+ static const char kScrollbarFrame[] = "ScrollbarFrame";
+ if (0 == PL_strncmp(tree1->type, kScrollbarFrame, sizeof(kScrollbarFrame) - 1))
+ continue;
+
+ if (tree1->state != tree2->state) {
+ printf("frame state mismatch: 0x%x vs. 0x%x\n",
+ tree1->state, tree2->state);
+ printf("Node 1:\n");
+ DumpNode(tree1, stdout, 1);
+ printf("Node 2:\n");
+ DumpNode(tree2, stdout, 1);
+ result = false; // we have a non-critical failure, so remember that but continue
+ }
+ if (tree1->bbox.IsEqualInterior(tree2->bbox)) {
+ printf("frame bbox mismatch: %d,%d,%d,%d vs. %d,%d,%d,%d\n",
+ tree1->bbox.x, tree1->bbox.y,
+ tree1->bbox.width, tree1->bbox.height,
+ tree2->bbox.x, tree2->bbox.y,
+ tree2->bbox.width, tree2->bbox.height);
+ printf("Node 1:\n");
+ DumpNode(tree1, stdout, 1);
+ printf("Node 2:\n");
+ DumpNode(tree2, stdout, 1);
+ result = false; // we have a non-critical failure, so remember that but continue
+ }
+ if (tree1->styleData != tree2->styleData) {
+ printf("frame style data mismatch: %s vs. %s\n",
+ tree1->styleData.get(),
+ tree2->styleData.get());
+ }
+
+ // Check child lists too
+ NodeList* list1 = tree1->lists;
+ NodeList* list2 = tree2->lists;
+ for (;;) {
+ if (nullptr == list1) {
+ if (nullptr != list2) {
+ printf("first tree prematurely ends (no child lists)\n");
+ printf("Node 1:\n");
+ DumpNode(tree1, stdout, 1);
+ printf("Node 2:\n");
+ DumpNode(tree2, stdout, 1);
+ return false;
+ }
+ else {
+ break;
+ }
+ }
+ if (nullptr == list2) {
+ printf("second tree prematurely ends (no child lists)\n");
+ printf("Node 1:\n");
+ DumpNode(tree1, stdout, 1);
+ printf("Node 2:\n");
+ DumpNode(tree2, stdout, 1);
+ return false;
+ }
+ if (0 != PL_strcmp(list1->name, list2->name)) {
+ printf("child-list name mismatch: %s vs. %s\n",
+ list1->name ? list1->name : "(null)",
+ list2->name ? list2->name : "(null)");
+ result = false; // we have a non-critical failure, so remember that but continue
+ }
+ else {
+ bool equiv = CompareTrees(list1->node, list2->node);
+ if (!equiv) {
+ return equiv;
+ }
+ }
+ list1 = list1->next;
+ list2 = list2->next;
+ }
+ }
+ return result;
+}
+
+NS_IMETHODIMP
+nsFrameUtil::CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput)
+{
+ Node* tree1 = Node::ReadTree(aFile1);
+ Node* tree2 = Node::ReadTree(aFile2);
+
+ nsresult rv = NS_OK;
+ if (!CompareTrees(tree1, tree2)) {
+ // only output this if aRegressionOutput is 0
+ if( 0 == aRegressionOutput ){
+ printf("Regression data 1:\n");
+ DumpTree(tree1, stdout, 0);
+ printf("Regression data 2:\n");
+ DumpTree(tree2, stdout, 0);
+ }
+ rv = NS_ERROR_FAILURE;
+ }
+
+ Node::Destroy(tree1);
+ Node::Destroy(tree2);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFrameUtil::DumpRegressionData(FILE* aInputFile, FILE* aOutputFile)
+{
+ Node* tree1 = Node::ReadTree(aInputFile);
+ if (nullptr != tree1) {
+ DumpTree(tree1, aOutputFile, 0);
+ Node::Destroy(tree1);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+#endif
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
new file mode 100644
index 000000000..f664845b6
--- /dev/null
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -0,0 +1,6162 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object to wrap rendering objects that should be scrollable */
+
+#include "nsGfxScrollFrame.h"
+
+#include "ActiveLayerTracker.h"
+#include "base/compiler_specific.h"
+#include "DisplayItemClip.h"
+#include "DisplayItemScrollClip.h"
+#include "nsCOMPtr.h"
+#include "nsIContentViewer.h"
+#include "nsPresContext.h"
+#include "nsView.h"
+#include "nsIScrollable.h"
+#include "nsContainerFrame.h"
+#include "nsGkAtoms.h"
+#include "nsNameSpaceManager.h"
+#include "nsContentList.h"
+#include "nsIDocumentInlines.h"
+#include "nsFontMetrics.h"
+#include "nsBoxLayoutState.h"
+#include "mozilla/dom/NodeInfo.h"
+#include "nsScrollbarFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "nsITextControlFrame.h"
+#include "nsIDOMHTMLTextAreaElement.h"
+#include "nsNodeInfoManager.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsPresState.h"
+#include "nsIHTMLDocument.h"
+#include "nsContentUtils.h"
+#include "nsLayoutUtils.h"
+#include "nsBidiPresUtils.h"
+#include "nsBidiUtils.h"
+#include "mozilla/ContentEvents.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/dom/Element.h"
+#include <stdint.h>
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Telemetry.h"
+#include "FrameLayerBuilder.h"
+#include "nsSMILKeySpline.h"
+#include "nsSubDocumentFrame.h"
+#include "nsSVGOuterSVGFrame.h"
+#include "nsIObjectLoadingContent.h"
+#include "mozilla/Attributes.h"
+#include "ScrollbarActivity.h"
+#include "nsRefreshDriver.h"
+#include "nsThemeConstants.h"
+#include "nsSVGIntegrationUtils.h"
+#include "nsIScrollPositionListener.h"
+#include "StickyScrollContainer.h"
+#include "nsIFrameInlines.h"
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "AsyncScrollBase.h"
+#include "ScrollSnap.h"
+#include "UnitTransforms.h"
+#include "nsPluginFrame.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include <mozilla/layers/AxisPhysicsModel.h>
+#include <mozilla/layers/AxisPhysicsMSDModel.h>
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ScrollLinkedEffectDetector.h"
+#include "mozilla/Unused.h"
+#include "LayersLogging.h" // for Stringify
+#include <algorithm>
+#include <cstdlib> // for std::abs(int/long)
+#include <cmath> // for std::abs(float/double)
+
+#define PAINT_SKIP_LOG(...)
+// #define PAINT_SKIP_LOG(...) printf_stderr("PSKIP: " __VA_ARGS__)
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+
+static uint32_t
+GetOverflowChange(const nsRect& aCurScrolledRect, const nsRect& aPrevScrolledRect)
+{
+ uint32_t result = 0;
+ if (aPrevScrolledRect.x != aCurScrolledRect.x ||
+ aPrevScrolledRect.width != aCurScrolledRect.width) {
+ result |= nsIScrollableFrame::HORIZONTAL;
+ }
+ if (aPrevScrolledRect.y != aCurScrolledRect.y ||
+ aPrevScrolledRect.height != aCurScrolledRect.height) {
+ result |= nsIScrollableFrame::VERTICAL;
+ }
+ return result;
+}
+
+//----------------------------------------------------------------------
+
+//----------nsHTMLScrollFrame-------------------------------------------
+
+nsHTMLScrollFrame*
+NS_NewHTMLScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
+{
+ return new (aPresShell) nsHTMLScrollFrame(aContext, aIsRoot);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsHTMLScrollFrame)
+
+nsHTMLScrollFrame::nsHTMLScrollFrame(nsStyleContext* aContext, bool aIsRoot)
+ : nsContainerFrame(aContext),
+ mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
+{
+}
+
+void
+nsHTMLScrollFrame::ScrollbarActivityStarted() const
+{
+ if (mHelper.mScrollbarActivity) {
+ mHelper.mScrollbarActivity->ActivityStarted();
+ }
+}
+
+void
+nsHTMLScrollFrame::ScrollbarActivityStopped() const
+{
+ if (mHelper.mScrollbarActivity) {
+ mHelper.mScrollbarActivity->ActivityStopped();
+ }
+}
+
+nsresult
+nsHTMLScrollFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+ return mHelper.CreateAnonymousContent(aElements);
+}
+
+void
+nsHTMLScrollFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter)
+{
+ mHelper.AppendAnonymousContentTo(aElements, aFilter);
+}
+
+void
+nsHTMLScrollFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ DestroyAbsoluteFrames(aDestructRoot);
+ mHelper.Destroy();
+ nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+void
+nsHTMLScrollFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ nsContainerFrame::SetInitialChildList(aListID, aChildList);
+ mHelper.ReloadChildFrames();
+}
+
+
+void
+nsHTMLScrollFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
+ mFrames.AppendFrames(nullptr, aFrameList);
+ mHelper.ReloadChildFrames();
+}
+
+void
+nsHTMLScrollFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
+ NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
+ "inserting after sibling frame with different parent");
+ mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
+ mHelper.ReloadChildFrames();
+}
+
+void
+nsHTMLScrollFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
+ mFrames.DestroyFrame(aOldFrame);
+ mHelper.ReloadChildFrames();
+}
+
+nsSplittableType
+nsHTMLScrollFrame::GetSplittableType() const
+{
+ return NS_FRAME_NOT_SPLITTABLE;
+}
+
+nsIAtom*
+nsHTMLScrollFrame::GetType() const
+{
+ return nsGkAtoms::scrollFrame;
+}
+
+/**
+ HTML scrolling implementation
+
+ All other things being equal, we prefer layouts with fewer scrollbars showing.
+*/
+
+namespace mozilla {
+
+struct MOZ_STACK_CLASS ScrollReflowInput {
+ const ReflowInput& mReflowInput;
+ nsBoxLayoutState mBoxState;
+ ScrollbarStyles mStyles;
+ nsMargin mComputedBorder;
+
+ // === Filled in by ReflowScrolledFrame ===
+ nsOverflowAreas mContentsOverflowAreas;
+ MOZ_INIT_OUTSIDE_CTOR
+ bool mReflowedContentsWithHScrollbar;
+ MOZ_INIT_OUTSIDE_CTOR
+ bool mReflowedContentsWithVScrollbar;
+
+ // === Filled in when TryLayout succeeds ===
+ // The size of the inside-border area
+ nsSize mInsideBorderSize;
+ // Whether we decided to show the horizontal scrollbar
+ MOZ_INIT_OUTSIDE_CTOR
+ bool mShowHScrollbar;
+ // Whether we decided to show the vertical scrollbar
+ MOZ_INIT_OUTSIDE_CTOR
+ bool mShowVScrollbar;
+
+ ScrollReflowInput(nsIScrollableFrame* aFrame,
+ const ReflowInput& aState) :
+ mReflowInput(aState),
+ // mBoxState is just used for scrollbars so we don't need to
+ // worry about the reflow depth here
+ mBoxState(aState.mFrame->PresContext(), aState.mRenderingContext, 0),
+ mStyles(aFrame->GetScrollbarStyles()) {
+ }
+};
+
+} // namespace mozilla
+
+// XXXldb Can this go away?
+static nsSize ComputeInsideBorderSize(ScrollReflowInput* aState,
+ const nsSize& aDesiredInsideBorderSize)
+{
+ // aDesiredInsideBorderSize is the frame size; i.e., it includes
+ // borders and padding (but the scrolled child doesn't have
+ // borders). The scrolled child has the same padding as us.
+ nscoord contentWidth = aState->mReflowInput.ComputedWidth();
+ if (contentWidth == NS_UNCONSTRAINEDSIZE) {
+ contentWidth = aDesiredInsideBorderSize.width -
+ aState->mReflowInput.ComputedPhysicalPadding().LeftRight();
+ }
+ nscoord contentHeight = aState->mReflowInput.ComputedHeight();
+ if (contentHeight == NS_UNCONSTRAINEDSIZE) {
+ contentHeight = aDesiredInsideBorderSize.height -
+ aState->mReflowInput.ComputedPhysicalPadding().TopBottom();
+ }
+
+ contentWidth = aState->mReflowInput.ApplyMinMaxWidth(contentWidth);
+ contentHeight = aState->mReflowInput.ApplyMinMaxHeight(contentHeight);
+ return nsSize(contentWidth + aState->mReflowInput.ComputedPhysicalPadding().LeftRight(),
+ contentHeight + aState->mReflowInput.ComputedPhysicalPadding().TopBottom());
+}
+
+static void
+GetScrollbarMetrics(nsBoxLayoutState& aState, nsIFrame* aBox, nsSize* aMin,
+ nsSize* aPref, bool aVertical)
+{
+ NS_ASSERTION(aState.GetRenderingContext(),
+ "Must have rendering context in layout state for size "
+ "computations");
+
+ if (aMin) {
+ *aMin = aBox->GetXULMinSize(aState);
+ nsBox::AddMargin(aBox, *aMin);
+ if (aMin->width < 0) {
+ aMin->width = 0;
+ }
+ if (aMin->height < 0) {
+ aMin->height = 0;
+ }
+ }
+
+ if (aPref) {
+ *aPref = aBox->GetXULPrefSize(aState);
+ nsBox::AddMargin(aBox, *aPref);
+ if (aPref->width < 0) {
+ aPref->width = 0;
+ }
+ if (aPref->height < 0) {
+ aPref->height = 0;
+ }
+ }
+}
+
+/**
+ * Assuming that we know the metrics for our wrapped frame and
+ * whether the horizontal and/or vertical scrollbars are present,
+ * compute the resulting layout and return true if the layout is
+ * consistent. If the layout is consistent then we fill in the
+ * computed fields of the ScrollReflowInput.
+ *
+ * The layout is consistent when both scrollbars are showing if and only
+ * if they should be showing. A horizontal scrollbar should be showing if all
+ * following conditions are met:
+ * 1) the style is not HIDDEN
+ * 2) our inside-border height is at least the scrollbar height (i.e., the
+ * scrollbar fits vertically)
+ * 3) our scrollport width (the inside-border width minus the width allocated for a
+ * vertical scrollbar, if showing) is at least the scrollbar's min-width
+ * (i.e., the scrollbar fits horizontally)
+ * 4) the style is SCROLL, or the kid's overflow-area XMost is
+ * greater than the scrollport width
+ *
+ * @param aForce if true, then we just assume the layout is consistent.
+ */
+bool
+nsHTMLScrollFrame::TryLayout(ScrollReflowInput* aState,
+ ReflowOutput* aKidMetrics,
+ bool aAssumeHScroll, bool aAssumeVScroll,
+ bool aForce)
+{
+ if ((aState->mStyles.mVertical == NS_STYLE_OVERFLOW_HIDDEN && aAssumeVScroll) ||
+ (aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && aAssumeHScroll)) {
+ NS_ASSERTION(!aForce, "Shouldn't be forcing a hidden scrollbar to show!");
+ return false;
+ }
+
+ if (aAssumeVScroll != aState->mReflowedContentsWithVScrollbar ||
+ (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar &&
+ ScrolledContentDependsOnHeight(aState))) {
+ if (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar) {
+ nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
+ mHelper.mScrolledFrame);
+ }
+ ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll, aKidMetrics,
+ false);
+ }
+
+ nsSize vScrollbarMinSize(0, 0);
+ nsSize vScrollbarPrefSize(0, 0);
+ if (mHelper.mVScrollbarBox) {
+ GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
+ &vScrollbarMinSize,
+ aAssumeVScroll ? &vScrollbarPrefSize : nullptr, true);
+ nsScrollbarFrame* scrollbar = do_QueryFrame(mHelper.mVScrollbarBox);
+ scrollbar->SetScrollbarMediatorContent(mContent);
+ }
+ nscoord vScrollbarDesiredWidth = aAssumeVScroll ? vScrollbarPrefSize.width : 0;
+ nscoord vScrollbarMinHeight = aAssumeVScroll ? vScrollbarMinSize.height : 0;
+
+ nsSize hScrollbarMinSize(0, 0);
+ nsSize hScrollbarPrefSize(0, 0);
+ if (mHelper.mHScrollbarBox) {
+ GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
+ &hScrollbarMinSize,
+ aAssumeHScroll ? &hScrollbarPrefSize : nullptr, false);
+ nsScrollbarFrame* scrollbar = do_QueryFrame(mHelper.mHScrollbarBox);
+ scrollbar->SetScrollbarMediatorContent(mContent);
+ }
+ nscoord hScrollbarDesiredHeight = aAssumeHScroll ? hScrollbarPrefSize.height : 0;
+ nscoord hScrollbarMinWidth = aAssumeHScroll ? hScrollbarMinSize.width : 0;
+
+ // First, compute our inside-border size and scrollport size
+ // XXXldb Can we depend more on ComputeSize here?
+ nsSize desiredInsideBorderSize;
+ desiredInsideBorderSize.width = vScrollbarDesiredWidth +
+ std::max(aKidMetrics->Width(), hScrollbarMinWidth);
+ desiredInsideBorderSize.height = hScrollbarDesiredHeight +
+ std::max(aKidMetrics->Height(), vScrollbarMinHeight);
+ aState->mInsideBorderSize =
+ ComputeInsideBorderSize(aState, desiredInsideBorderSize);
+ nsSize scrollPortSize = nsSize(std::max(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
+ std::max(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
+
+ nsSize visualScrollPortSize = scrollPortSize;
+ nsIPresShell* presShell = PresContext()->PresShell();
+ if (mHelper.mIsRoot && presShell->IsScrollPositionClampingScrollPortSizeSet()) {
+ nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(this, false);
+ float resolution = presShell->GetResolution();
+ compositionSize.width /= resolution;
+ compositionSize.height /= resolution;
+ visualScrollPortSize = nsSize(std::max(0, compositionSize.width - vScrollbarDesiredWidth),
+ std::max(0, compositionSize.height - hScrollbarDesiredHeight));
+ }
+
+ if (!aForce) {
+ nsRect scrolledRect =
+ mHelper.GetUnsnappedScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
+ scrollPortSize);
+ nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
+
+ // If the style is HIDDEN then we already know that aAssumeHScroll is false
+ if (aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
+ bool wantHScrollbar =
+ aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
+ scrolledRect.XMost() >= visualScrollPortSize.width + oneDevPixel ||
+ scrolledRect.x <= -oneDevPixel;
+ if (scrollPortSize.width < hScrollbarMinSize.width)
+ wantHScrollbar = false;
+ if (wantHScrollbar != aAssumeHScroll)
+ return false;
+ }
+
+ // If the style is HIDDEN then we already know that aAssumeVScroll is false
+ if (aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
+ bool wantVScrollbar =
+ aState->mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL ||
+ scrolledRect.YMost() >= visualScrollPortSize.height + oneDevPixel ||
+ scrolledRect.y <= -oneDevPixel;
+ if (scrollPortSize.height < vScrollbarMinSize.height)
+ wantVScrollbar = false;
+ if (wantVScrollbar != aAssumeVScroll)
+ return false;
+ }
+ }
+
+ nscoord vScrollbarActualWidth = aState->mInsideBorderSize.width - scrollPortSize.width;
+
+ aState->mShowHScrollbar = aAssumeHScroll;
+ aState->mShowVScrollbar = aAssumeVScroll;
+ nsPoint scrollPortOrigin(aState->mComputedBorder.left,
+ aState->mComputedBorder.top);
+ if (!IsScrollbarOnRight()) {
+ scrollPortOrigin.x += vScrollbarActualWidth;
+ }
+ mHelper.mScrollPort = nsRect(scrollPortOrigin, scrollPortSize);
+ return true;
+}
+
+// XXX Height/BSize mismatch needs to be addressed here; check the caller!
+// Currently this will only behave as expected for horizontal writing modes.
+// (See bug 1175509.)
+bool
+nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowInput* aState)
+{
+ // Return true if ReflowScrolledFrame is going to do something different
+ // based on the presence of a horizontal scrollbar.
+ return mHelper.mScrolledFrame->HasAnyStateBits(
+ NS_FRAME_CONTAINS_RELATIVE_BSIZE | NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE) ||
+ aState->mReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
+ aState->mReflowInput.ComputedMinBSize() > 0 ||
+ aState->mReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE;
+}
+
+void
+nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowInput* aState,
+ bool aAssumeHScroll,
+ bool aAssumeVScroll,
+ ReflowOutput* aMetrics,
+ bool aFirstPass)
+{
+ WritingMode wm = mHelper.mScrolledFrame->GetWritingMode();
+
+ // these could be NS_UNCONSTRAINEDSIZE ... std::min arithmetic should
+ // be OK
+ LogicalMargin padding = aState->mReflowInput.ComputedLogicalPadding();
+ nscoord availISize =
+ aState->mReflowInput.ComputedISize() + padding.IStartEnd(wm);
+
+ nscoord computedBSize = aState->mReflowInput.ComputedBSize();
+ nscoord computedMinBSize = aState->mReflowInput.ComputedMinBSize();
+ nscoord computedMaxBSize = aState->mReflowInput.ComputedMaxBSize();
+ if (!ShouldPropagateComputedBSizeToScrolledContent()) {
+ computedBSize = NS_UNCONSTRAINEDSIZE;
+ computedMinBSize = 0;
+ computedMaxBSize = NS_UNCONSTRAINEDSIZE;
+ }
+
+ if (wm.IsVertical()) {
+ if (aAssumeVScroll) {
+ nsSize vScrollbarPrefSize;
+ GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
+ nullptr, &vScrollbarPrefSize, false);
+ if (computedBSize != NS_UNCONSTRAINEDSIZE) {
+ computedBSize = std::max(0, computedBSize - vScrollbarPrefSize.width);
+ }
+ computedMinBSize = std::max(0, computedMinBSize - vScrollbarPrefSize.width);
+ if (computedMaxBSize != NS_UNCONSTRAINEDSIZE) {
+ computedMaxBSize = std::max(0, computedMaxBSize - vScrollbarPrefSize.width);
+ }
+ }
+
+ if (aAssumeHScroll) {
+ nsSize hScrollbarPrefSize;
+ GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
+ nullptr, &hScrollbarPrefSize, true);
+ availISize = std::max(0, availISize - hScrollbarPrefSize.height);
+ }
+ } else {
+ if (aAssumeHScroll) {
+ nsSize hScrollbarPrefSize;
+ GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
+ nullptr, &hScrollbarPrefSize, false);
+ if (computedBSize != NS_UNCONSTRAINEDSIZE) {
+ computedBSize = std::max(0, computedBSize - hScrollbarPrefSize.height);
+ }
+ computedMinBSize = std::max(0, computedMinBSize - hScrollbarPrefSize.height);
+ if (computedMaxBSize != NS_UNCONSTRAINEDSIZE) {
+ computedMaxBSize = std::max(0, computedMaxBSize - hScrollbarPrefSize.height);
+ }
+ }
+
+ if (aAssumeVScroll) {
+ nsSize vScrollbarPrefSize;
+ GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
+ nullptr, &vScrollbarPrefSize, true);
+ availISize = std::max(0, availISize - vScrollbarPrefSize.width);
+ }
+ }
+
+ nsPresContext* presContext = PresContext();
+
+ // Pass false for aInit so we can pass in the correct padding.
+ ReflowInput
+ kidReflowInput(presContext, aState->mReflowInput,
+ mHelper.mScrolledFrame,
+ LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE),
+ nullptr, ReflowInput::CALLER_WILL_INIT);
+ const nsMargin physicalPadding = padding.GetPhysicalMargin(wm);
+ kidReflowInput.Init(presContext, nullptr, nullptr,
+ &physicalPadding);
+ kidReflowInput.mFlags.mAssumingHScrollbar = aAssumeHScroll;
+ kidReflowInput.mFlags.mAssumingVScrollbar = aAssumeVScroll;
+ kidReflowInput.SetComputedBSize(computedBSize);
+ kidReflowInput.ComputedMinBSize() = computedMinBSize;
+ kidReflowInput.ComputedMaxBSize() = computedMaxBSize;
+ if (aState->mReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode())) {
+ kidReflowInput.SetBResize(true);
+ }
+
+ // Temporarily set mHasHorizontalScrollbar/mHasVerticalScrollbar to
+ // reflect our assumptions while we reflow the child.
+ bool didHaveHorizontalScrollbar = mHelper.mHasHorizontalScrollbar;
+ bool didHaveVerticalScrollbar = mHelper.mHasVerticalScrollbar;
+ mHelper.mHasHorizontalScrollbar = aAssumeHScroll;
+ mHelper.mHasVerticalScrollbar = aAssumeVScroll;
+
+ nsReflowStatus status;
+ // No need to pass a true container-size to ReflowChild or
+ // FinishReflowChild, because it's only used there when positioning
+ // the frame (i.e. if NS_FRAME_NO_MOVE_FRAME isn't set)
+ const nsSize dummyContainerSize;
+ ReflowChild(mHelper.mScrolledFrame, presContext, *aMetrics,
+ kidReflowInput, wm, LogicalPoint(wm), dummyContainerSize,
+ NS_FRAME_NO_MOVE_FRAME, status);
+
+ mHelper.mHasHorizontalScrollbar = didHaveHorizontalScrollbar;
+ mHelper.mHasVerticalScrollbar = didHaveVerticalScrollbar;
+
+ // Don't resize or position the view (if any) because we're going to resize
+ // it to the correct size anyway in PlaceScrollArea. Allowing it to
+ // resize here would size it to the natural height of the frame,
+ // which will usually be different from the scrollport height;
+ // invalidating the difference will cause unnecessary repainting.
+ FinishReflowChild(mHelper.mScrolledFrame, presContext,
+ *aMetrics, &kidReflowInput, wm, LogicalPoint(wm),
+ dummyContainerSize,
+ NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW);
+
+ // XXX Some frames (e.g., nsPluginFrame, nsFrameFrame, nsTextFrame) don't bother
+ // setting their mOverflowArea. This is wrong because every frame should
+ // always set mOverflowArea. In fact nsPluginFrame and nsFrameFrame don't
+ // support the 'outline' property because of this. Rather than fix the world
+ // right now, just fix up the overflow area if necessary. Note that we don't
+ // check HasOverflowRect() because it could be set even though the
+ // overflow area doesn't include the frame bounds.
+ aMetrics->UnionOverflowAreasWithDesiredBounds();
+
+ if (MOZ_UNLIKELY(StyleDisplay()->mOverflowClipBox ==
+ NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
+ nsOverflowAreas childOverflow;
+ nsLayoutUtils::UnionChildOverflow(mHelper.mScrolledFrame, childOverflow);
+ nsRect childScrollableOverflow = childOverflow.ScrollableOverflow();
+ childScrollableOverflow.Inflate(padding.GetPhysicalMargin(wm));
+ nsRect contentArea =
+ wm.IsVertical() ? nsRect(0, 0, computedBSize, availISize)
+ : nsRect(0, 0, availISize, computedBSize);
+ if (!contentArea.Contains(childScrollableOverflow)) {
+ aMetrics->mOverflowAreas.ScrollableOverflow() = childScrollableOverflow;
+ }
+ }
+
+ aState->mContentsOverflowAreas = aMetrics->mOverflowAreas;
+ aState->mReflowedContentsWithHScrollbar = aAssumeHScroll;
+ aState->mReflowedContentsWithVScrollbar = aAssumeVScroll;
+}
+
+bool
+nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowInput& aState)
+{
+ if (aState.mStyles.mHorizontal != NS_STYLE_OVERFLOW_AUTO)
+ // no guessing required
+ return aState.mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL;
+
+ return mHelper.mHasHorizontalScrollbar;
+}
+
+bool
+nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowInput& aState)
+{
+ if (aState.mStyles.mVertical != NS_STYLE_OVERFLOW_AUTO)
+ // no guessing required
+ return aState.mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL;
+
+ // If we've had at least one non-initial reflow, then just assume
+ // the state of the vertical scrollbar will be what we determined
+ // last time.
+ if (mHelper.mHadNonInitialReflow) {
+ return mHelper.mHasVerticalScrollbar;
+ }
+
+ // If this is the initial reflow, guess false because usually
+ // we have very little content by then.
+ if (InInitialReflow())
+ return false;
+
+ if (mHelper.mIsRoot) {
+ nsIFrame *f = mHelper.mScrolledFrame->PrincipalChildList().FirstChild();
+ if (f && f->GetType() == nsGkAtoms::svgOuterSVGFrame &&
+ static_cast<nsSVGOuterSVGFrame*>(f)->VerticalScrollbarNotNeeded()) {
+ // Common SVG case - avoid a bad guess.
+ return false;
+ }
+ // Assume that there will be a scrollbar; it seems to me
+ // that 'most pages' do have a scrollbar, and anyway, it's cheaper
+ // to do an extra reflow for the pages that *don't* need a
+ // scrollbar (because on average they will have less content).
+ return true;
+ }
+
+ // For non-viewports, just guess that we don't need a scrollbar.
+ // XXX I wonder if statistically this is the right idea; I'm
+ // basically guessing that there are a lot of overflow:auto DIVs
+ // that get their intrinsic size and don't overflow
+ return false;
+}
+
+bool
+nsHTMLScrollFrame::InInitialReflow() const
+{
+ // We're in an initial reflow if NS_FRAME_FIRST_REFLOW is set, unless we're a
+ // root scrollframe. In that case we want to skip this clause altogether.
+ // The guess here is that there are lots of overflow:auto divs out there that
+ // end up auto-sizing so they don't overflow, and that the root basically
+ // always needs a scrollbar if it did last time we loaded this page (good
+ // assumption, because our initial reflow is no longer synchronous).
+ return !mHelper.mIsRoot && (GetStateBits() & NS_FRAME_FIRST_REFLOW);
+}
+
+void
+nsHTMLScrollFrame::ReflowContents(ScrollReflowInput* aState,
+ const ReflowOutput& aDesiredSize)
+{
+ ReflowOutput kidDesiredSize(aDesiredSize.GetWritingMode(), aDesiredSize.mFlags);
+ ReflowScrolledFrame(aState, GuessHScrollbarNeeded(*aState),
+ GuessVScrollbarNeeded(*aState), &kidDesiredSize, true);
+
+ // There's an important special case ... if the child appears to fit
+ // in the inside-border rect (but overflows the scrollport), we
+ // should try laying it out without a vertical scrollbar. It will
+ // usually fit because making the available-width wider will not
+ // normally make the child taller. (The only situation I can think
+ // of is when you have a line containing %-width inline replaced
+ // elements whose percentages sum to more than 100%, so increasing
+ // the available width makes the line break where it was fitting
+ // before.) If we don't treat this case specially, then we will
+ // decide that showing scrollbars is OK because the content
+ // overflows when we're showing scrollbars and we won't try to
+ // remove the vertical scrollbar.
+
+ // Detecting when we enter this special case is important for when
+ // people design layouts that exactly fit the container "most of the
+ // time".
+
+ // XXX Is this check really sufficient to catch all the incremental cases
+ // where the ideal case doesn't have a scrollbar?
+ if ((aState->mReflowedContentsWithHScrollbar || aState->mReflowedContentsWithVScrollbar) &&
+ aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
+ aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
+ nsSize insideBorderSize =
+ ComputeInsideBorderSize(aState,
+ nsSize(kidDesiredSize.Width(), kidDesiredSize.Height()));
+ nsRect scrolledRect =
+ mHelper.GetUnsnappedScrolledRectInternal(kidDesiredSize.ScrollableOverflow(),
+ insideBorderSize);
+ if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
+ // Let's pretend we had no scrollbars coming in here
+ ReflowScrolledFrame(aState, false, false, &kidDesiredSize, false);
+ }
+ }
+
+ // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
+ // Do this first because changing the vertical scrollbar setting is expensive,
+ // forcing a reflow always.
+
+ // Try leaving the horizontal scrollbar unchanged first. This will be more
+ // efficient.
+ if (TryLayout(aState, &kidDesiredSize, aState->mReflowedContentsWithHScrollbar,
+ aState->mReflowedContentsWithVScrollbar, false))
+ return;
+ if (TryLayout(aState, &kidDesiredSize, !aState->mReflowedContentsWithHScrollbar,
+ aState->mReflowedContentsWithVScrollbar, false))
+ return;
+
+ // OK, now try toggling the vertical scrollbar. The performance advantage
+ // of trying the status-quo horizontal scrollbar state
+ // does not exist here (we'll have to reflow due to the vertical scrollbar
+ // change), so always try no horizontal scrollbar first.
+ bool newVScrollbarState = !aState->mReflowedContentsWithVScrollbar;
+ if (TryLayout(aState, &kidDesiredSize, false, newVScrollbarState, false))
+ return;
+ if (TryLayout(aState, &kidDesiredSize, true, newVScrollbarState, false))
+ return;
+
+ // OK, we're out of ideas. Try again enabling whatever scrollbars we can
+ // enable and force the layout to stick even if it's inconsistent.
+ // This just happens sometimes.
+ TryLayout(aState, &kidDesiredSize,
+ aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN,
+ aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN,
+ true);
+}
+
+void
+nsHTMLScrollFrame::PlaceScrollArea(ScrollReflowInput& aState,
+ const nsPoint& aScrollPosition)
+{
+ nsIFrame *scrolledFrame = mHelper.mScrolledFrame;
+ // Set the x,y of the scrolled frame to the correct value
+ scrolledFrame->SetPosition(mHelper.mScrollPort.TopLeft() - aScrollPosition);
+
+ // Recompute our scrollable overflow, taking perspective children into
+ // account. Note that this only recomputes the overflow areas stored on the
+ // helper (which are used to compute scrollable length and scrollbar thumb
+ // sizes) but not the overflow areas stored on the frame. This seems to work
+ // for now, but it's possible that we may need to update both in the future.
+ AdjustForPerspective(aState.mContentsOverflowAreas.ScrollableOverflow());
+
+ nsRect scrolledArea;
+ // Preserve the width or height of empty rects
+ nsSize portSize = mHelper.mScrollPort.Size();
+ nsRect scrolledRect =
+ mHelper.GetUnsnappedScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
+ portSize);
+ scrolledArea.UnionRectEdges(scrolledRect,
+ nsRect(nsPoint(0,0), portSize));
+
+ // Store the new overflow area. Note that this changes where an outline
+ // of the scrolled frame would be painted, but scrolled frames can't have
+ // outlines (the outline would go on this scrollframe instead).
+ // Using FinishAndStoreOverflow is needed so the overflow rect
+ // gets set correctly. It also messes with the overflow rect in the
+ // -moz-hidden-unscrollable case, but scrolled frames can't have
+ // 'overflow' either.
+ // This needs to happen before SyncFrameViewAfterReflow so
+ // HasOverflowRect() will return the correct value.
+ nsOverflowAreas overflow(scrolledArea, scrolledArea);
+ scrolledFrame->FinishAndStoreOverflow(overflow,
+ scrolledFrame->GetSize());
+
+ // Note that making the view *exactly* the size of the scrolled area
+ // is critical, since the view scrolling code uses the size of the
+ // scrolled view to clamp scroll requests.
+ // Normally the scrolledFrame won't have a view but in some cases it
+ // might create its own.
+ nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame->PresContext(),
+ scrolledFrame,
+ scrolledFrame->GetView(),
+ scrolledArea,
+ 0);
+}
+
+nscoord
+nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(nsRenderingContext *aRenderingContext)
+{
+ ScrollbarStyles ss = GetScrollbarStyles();
+ if (ss.mVertical != NS_STYLE_OVERFLOW_SCROLL || !mHelper.mVScrollbarBox)
+ return 0;
+
+ // Don't need to worry about reflow depth here since it's
+ // just for scrollbars
+ nsBoxLayoutState bls(PresContext(), aRenderingContext, 0);
+ nsSize vScrollbarPrefSize(0, 0);
+ GetScrollbarMetrics(bls, mHelper.mVScrollbarBox,
+ nullptr, &vScrollbarPrefSize, true);
+ return vScrollbarPrefSize.width;
+}
+
+/* virtual */ nscoord
+nsHTMLScrollFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = mHelper.mScrolledFrame->GetMinISize(aRenderingContext);
+ DISPLAY_MIN_WIDTH(this, result);
+ return result + GetIntrinsicVScrollbarWidth(aRenderingContext);
+}
+
+/* virtual */ nscoord
+nsHTMLScrollFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = mHelper.mScrolledFrame->GetPrefISize(aRenderingContext);
+ DISPLAY_PREF_WIDTH(this, result);
+ return NSCoordSaturatingAdd(result, GetIntrinsicVScrollbarWidth(aRenderingContext));
+}
+
+nsresult
+nsHTMLScrollFrame::GetXULPadding(nsMargin& aMargin)
+{
+ // Our padding hangs out on the inside of the scrollframe, but XUL doesn't
+ // reaize that. If we're stuck inside a XUL box, we need to claim no
+ // padding.
+ // @see also nsXULScrollFrame::GetXULPadding.
+ aMargin.SizeTo(0,0,0,0);
+ return NS_OK;
+}
+
+bool
+nsHTMLScrollFrame::IsXULCollapsed()
+{
+ // We're never collapsed in the box sense.
+ return false;
+}
+
+// Return the <browser> if the scrollframe is for the root frame directly
+// inside a <browser>.
+static nsIContent*
+GetBrowserRoot(nsIContent* aContent)
+{
+ if (aContent) {
+ nsIDocument* doc = aContent->GetUncomposedDoc();
+ if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
+ nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
+ if (frameElement &&
+ frameElement->NodeInfo()->Equals(nsGkAtoms::browser, kNameSpaceID_XUL))
+ return frameElement;
+ }
+ }
+
+ return nullptr;
+}
+
+// When we have perspective set on the outer scroll frame, and transformed
+// children (possibly with preserve-3d) then the effective transform on the
+// child depends on the offset to the scroll frame, which changes as we scroll.
+// This perspective transform can cause the element to move relative to the
+// scrolled inner frame, which would cause the scrollable length changes during
+// scrolling if we didn't account for it. Since we don't want scrollHeight/Width
+// and the size of scrollbar thumbs to change during scrolling, we compute the
+// scrollable overflow by determining the scroll position at which the child
+// becomes completely visible within the scrollport rather than using the union
+// of the overflow areas at their current position.
+void
+GetScrollableOverflowForPerspective(nsIFrame* aScrolledFrame,
+ nsIFrame* aCurrentFrame,
+ const nsRect aScrollPort,
+ nsPoint aOffset,
+ nsRect& aScrolledFrameOverflowArea)
+{
+ // Iterate over all children except pop-ups.
+ FrameChildListIDs skip = nsIFrame::kSelectPopupList | nsIFrame::kPopupList;
+ for (nsIFrame::ChildListIterator childLists(aCurrentFrame);
+ !childLists.IsDone(); childLists.Next()) {
+ if (skip.Contains(childLists.CurrentID())) {
+ continue;
+ }
+
+ for (nsIFrame* child : childLists.CurrentList()) {
+ nsPoint offset = aOffset;
+
+ // When we reach a direct child of the scroll, then we record the offset
+ // to convert from that frame's coordinate into the scroll frame's
+ // coordinates. Preserve-3d descendant frames use the same offset as their
+ // ancestors, since TransformRect already converts us into the coordinate
+ // space of the preserve-3d root.
+ if (aScrolledFrame == aCurrentFrame) {
+ offset = child->GetPosition();
+ }
+
+ if (child->Extend3DContext()) {
+ // If we're a preserve-3d frame, then recurse and include our
+ // descendants since overflow of preserve-3d frames is only included
+ // in the post-transform overflow area of the preserve-3d root frame.
+ GetScrollableOverflowForPerspective(aScrolledFrame, child, aScrollPort,
+ offset, aScrolledFrameOverflowArea);
+ }
+
+ // If we're transformed, then we want to consider the possibility that
+ // this frame might move relative to the scrolled frame when scrolling.
+ // For preserve-3d, leaf frames have correct overflow rects relative to
+ // themselves. preserve-3d 'nodes' (intermediate frames and the root) have
+ // only their untransformed children included in their overflow relative
+ // to self, which is what we want to include here.
+ if (child->IsTransformed()) {
+ // Compute the overflow rect for this leaf transform frame in the
+ // coordinate space of the scrolled frame.
+ nsPoint scrollPos = aScrolledFrame->GetPosition();
+ nsRect preScroll = nsDisplayTransform::TransformRect(
+ child->GetScrollableOverflowRectRelativeToSelf(), child);
+
+ // Temporarily override the scroll position of the scrolled frame by
+ // 10 CSS pixels, and then recompute what the overflow rect would be.
+ // This scroll position may not be valid, but that shouldn't matter
+ // for our calculations.
+ aScrolledFrame->SetPosition(scrollPos + nsPoint(600, 600));
+ nsRect postScroll = nsDisplayTransform::TransformRect(
+ child->GetScrollableOverflowRectRelativeToSelf(), child);
+ aScrolledFrame->SetPosition(scrollPos);
+
+ // Compute how many app units the overflow rects moves by when we adjust
+ // the scroll position by 1 app unit.
+ double rightDelta =
+ (postScroll.XMost() - preScroll.XMost() + 600.0) / 600.0;
+ double bottomDelta =
+ (postScroll.YMost() - preScroll.YMost() + 600.0) / 600.0;
+
+ // We can't ever have negative scrolling.
+ NS_ASSERTION(rightDelta > 0.0f && bottomDelta > 0.0f,
+ "Scrolling can't be reversed!");
+
+ // Move preScroll into the coordinate space of the scrollport.
+ preScroll += offset + scrollPos;
+
+ // For each of the four edges of preScroll, figure out how far they
+ // extend beyond the scrollport. Ignore negative values since that means
+ // that side is already scrolled in to view and we don't need to add
+ // overflow to account for it.
+ nsMargin overhang(std::max(0, aScrollPort.Y() - preScroll.Y()),
+ std::max(0, preScroll.XMost() - aScrollPort.XMost()),
+ std::max(0, preScroll.YMost() - aScrollPort.YMost()),
+ std::max(0, aScrollPort.X() - preScroll.X()));
+
+ // Scale according to rightDelta/bottomDelta to adjust for the different
+ // scroll rates.
+ overhang.top /= bottomDelta;
+ overhang.right /= rightDelta;
+ overhang.bottom /= bottomDelta;
+ overhang.left /= rightDelta;
+
+ // Take the minimum overflow rect that would allow the current scroll
+ // position, using the size of the scroll port and offset by the
+ // inverse of the scroll position.
+ nsRect overflow = aScrollPort - scrollPos;
+
+ // Expand it by our margins to get an overflow rect that would allow all
+ // edges of our transformed content to be scrolled into view.
+ overflow.Inflate(overhang);
+
+ // Merge it with the combined overflow
+ aScrolledFrameOverflowArea.UnionRect(aScrolledFrameOverflowArea,
+ overflow);
+ } else if (aCurrentFrame == aScrolledFrame) {
+ aScrolledFrameOverflowArea.UnionRect(
+ aScrolledFrameOverflowArea,
+ child->GetScrollableOverflowRectRelativeToParent());
+ }
+ }
+ }
+}
+
+void
+nsHTMLScrollFrame::AdjustForPerspective(nsRect& aScrollableOverflow)
+{
+ // If we have perspective that is being applied to our children, then
+ // the effective transform on the child depends on the relative position
+ // of the child to us and changes during scrolling.
+ if (!ChildrenHavePerspective()) {
+ return;
+ }
+ aScrollableOverflow.SetEmpty();
+ GetScrollableOverflowForPerspective(mHelper.mScrolledFrame,
+ mHelper.mScrolledFrame,
+ mHelper.mScrollPort,
+ nsPoint(), aScrollableOverflow);
+}
+
+void
+nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ mHelper.HandleScrollbarStyleSwitching();
+
+ ScrollReflowInput state(this, aReflowInput);
+ // sanity check: ensure that if we have no scrollbar, we treat it
+ // as hidden.
+ if (!mHelper.mVScrollbarBox || mHelper.mNeverHasVerticalScrollbar)
+ state.mStyles.mVertical = NS_STYLE_OVERFLOW_HIDDEN;
+ if (!mHelper.mHScrollbarBox || mHelper.mNeverHasHorizontalScrollbar)
+ state.mStyles.mHorizontal = NS_STYLE_OVERFLOW_HIDDEN;
+
+ //------------ Handle Incremental Reflow -----------------
+ bool reflowHScrollbar = true;
+ bool reflowVScrollbar = true;
+ bool reflowScrollCorner = true;
+ if (!aReflowInput.ShouldReflowAllKids()) {
+ #define NEEDS_REFLOW(frame_) \
+ ((frame_) && NS_SUBTREE_DIRTY(frame_))
+
+ reflowHScrollbar = NEEDS_REFLOW(mHelper.mHScrollbarBox);
+ reflowVScrollbar = NEEDS_REFLOW(mHelper.mVScrollbarBox);
+ reflowScrollCorner = NEEDS_REFLOW(mHelper.mScrollCornerBox) ||
+ NEEDS_REFLOW(mHelper.mResizerBox);
+
+ #undef NEEDS_REFLOW
+ }
+
+ if (mHelper.mIsRoot) {
+ mHelper.mCollapsedResizer = true;
+
+ nsIContent* browserRoot = GetBrowserRoot(mContent);
+ if (browserRoot) {
+ bool showResizer = browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer);
+ reflowScrollCorner = showResizer == mHelper.mCollapsedResizer;
+ mHelper.mCollapsedResizer = !showResizer;
+ }
+ }
+
+ nsRect oldScrollAreaBounds = mHelper.mScrollPort;
+ nsRect oldScrolledAreaBounds =
+ mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
+ nsPoint oldScrollPosition = mHelper.GetScrollPosition();
+
+ state.mComputedBorder = aReflowInput.ComputedPhysicalBorderPadding() -
+ aReflowInput.ComputedPhysicalPadding();
+
+ ReflowContents(&state, aDesiredSize);
+
+ aDesiredSize.Width() = state.mInsideBorderSize.width +
+ state.mComputedBorder.LeftRight();
+ aDesiredSize.Height() = state.mInsideBorderSize.height +
+ state.mComputedBorder.TopBottom();
+
+ // Set the size of the frame now since computing the perspective-correct
+ // overflow (within PlaceScrollArea) can rely on it.
+ SetSize(aDesiredSize.GetWritingMode(),
+ aDesiredSize.Size(aDesiredSize.GetWritingMode()));
+
+ // Restore the old scroll position, for now, even if that's not valid anymore
+ // because we changed size. We'll fix it up in a post-reflow callback, because
+ // our current size may only be temporary (e.g. we're compute XUL desired sizes).
+ PlaceScrollArea(state, oldScrollPosition);
+ if (!mHelper.mPostedReflowCallback) {
+ // Make sure we'll try scrolling to restored position
+ PresContext()->PresShell()->PostReflowCallback(&mHelper);
+ mHelper.mPostedReflowCallback = true;
+ }
+
+ bool didHaveHScrollbar = mHelper.mHasHorizontalScrollbar;
+ bool didHaveVScrollbar = mHelper.mHasVerticalScrollbar;
+ mHelper.mHasHorizontalScrollbar = state.mShowHScrollbar;
+ mHelper.mHasVerticalScrollbar = state.mShowVScrollbar;
+ nsRect newScrollAreaBounds = mHelper.mScrollPort;
+ nsRect newScrolledAreaBounds =
+ mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
+ if (mHelper.mSkippedScrollbarLayout ||
+ reflowHScrollbar || reflowVScrollbar || reflowScrollCorner ||
+ (GetStateBits() & NS_FRAME_IS_DIRTY) ||
+ didHaveHScrollbar != state.mShowHScrollbar ||
+ didHaveVScrollbar != state.mShowVScrollbar ||
+ !oldScrollAreaBounds.IsEqualEdges(newScrollAreaBounds) ||
+ !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
+ if (!mHelper.mSupppressScrollbarUpdate) {
+ mHelper.mSkippedScrollbarLayout = false;
+ mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, state.mShowHScrollbar);
+ mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, state.mShowVScrollbar);
+ // place and reflow scrollbars
+ nsRect insideBorderArea =
+ nsRect(nsPoint(state.mComputedBorder.left, state.mComputedBorder.top),
+ state.mInsideBorderSize);
+ mHelper.LayoutScrollbars(state.mBoxState, insideBorderArea,
+ oldScrollAreaBounds);
+ } else {
+ mHelper.mSkippedScrollbarLayout = true;
+ }
+ }
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ if (mHelper.IsIgnoringViewportClipping()) {
+ aDesiredSize.mOverflowAreas.UnionWith(
+ state.mContentsOverflowAreas + mHelper.mScrolledFrame->GetPosition());
+ }
+
+ mHelper.UpdateSticky();
+ FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
+
+ if (!InInitialReflow() && !mHelper.mHadNonInitialReflow) {
+ mHelper.mHadNonInitialReflow = true;
+ }
+
+ if (mHelper.mIsRoot && !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
+ mHelper.PostScrolledAreaEvent();
+ }
+
+ mHelper.UpdatePrevScrolledRect();
+
+ aStatus = NS_FRAME_COMPLETE;
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+ mHelper.PostOverflowEvent();
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsHTMLScrollFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult);
+}
+#endif
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsHTMLScrollFrame::AccessibleType()
+{
+ if (IsTableCaption()) {
+ return GetRect().IsEmpty() ? a11y::eNoType : a11y::eHTMLCaptionType;
+ }
+
+ // Create an accessible regardless of focusable state because the state can be
+ // changed during frame life cycle without any notifications to accessibility.
+ if (mContent->IsRootOfNativeAnonymousSubtree() ||
+ GetScrollbarStyles().IsHiddenInBothDirections()) {
+ return a11y::eNoType;
+ }
+
+ return a11y::eHyperTextType;
+}
+#endif
+
+NS_QUERYFRAME_HEAD(nsHTMLScrollFrame)
+ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+ NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
+ NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
+ NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+//----------nsXULScrollFrame-------------------------------------------
+
+nsXULScrollFrame*
+NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+ bool aIsRoot, bool aClipAllDescendants)
+{
+ return new (aPresShell) nsXULScrollFrame(aContext, aIsRoot,
+ aClipAllDescendants);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame)
+
+nsXULScrollFrame::nsXULScrollFrame(nsStyleContext* aContext,
+ bool aIsRoot, bool aClipAllDescendants)
+ : nsBoxFrame(aContext, aIsRoot),
+ mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
+{
+ SetXULLayoutManager(nullptr);
+ mHelper.mClipAllDescendants = aClipAllDescendants;
+}
+
+void
+nsXULScrollFrame::ScrollbarActivityStarted() const
+{
+ if (mHelper.mScrollbarActivity) {
+ mHelper.mScrollbarActivity->ActivityStarted();
+ }
+}
+
+void
+nsXULScrollFrame::ScrollbarActivityStopped() const
+{
+ if (mHelper.mScrollbarActivity) {
+ mHelper.mScrollbarActivity->ActivityStopped();
+ }
+}
+
+nsMargin
+ScrollFrameHelper::GetDesiredScrollbarSizes(nsBoxLayoutState* aState)
+{
+ NS_ASSERTION(aState && aState->GetRenderingContext(),
+ "Must have rendering context in layout state for size "
+ "computations");
+
+ nsMargin result(0, 0, 0, 0);
+
+ if (mVScrollbarBox) {
+ nsSize size = mVScrollbarBox->GetXULPrefSize(*aState);
+ nsBox::AddMargin(mVScrollbarBox, size);
+ if (IsScrollbarOnRight())
+ result.left = size.width;
+ else
+ result.right = size.width;
+ }
+
+ if (mHScrollbarBox) {
+ nsSize size = mHScrollbarBox->GetXULPrefSize(*aState);
+ nsBox::AddMargin(mHScrollbarBox, size);
+ // We don't currently support any scripts that would require a scrollbar
+ // at the top. (Are there any?)
+ result.bottom = size.height;
+ }
+
+ return result;
+}
+
+nscoord
+ScrollFrameHelper::GetNondisappearingScrollbarWidth(nsBoxLayoutState* aState,
+ WritingMode aWM)
+{
+ NS_ASSERTION(aState && aState->GetRenderingContext(),
+ "Must have rendering context in layout state for size "
+ "computations");
+
+ bool verticalWM = aWM.IsVertical();
+ if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
+ // We're using overlay scrollbars, so we need to get the width that
+ // non-disappearing scrollbars would have.
+ nsITheme* theme = aState->PresContext()->GetTheme();
+ if (theme &&
+ theme->ThemeSupportsWidget(aState->PresContext(),
+ verticalWM ? mHScrollbarBox
+ : mVScrollbarBox,
+ NS_THEME_SCROLLBAR_NON_DISAPPEARING)) {
+ LayoutDeviceIntSize size;
+ bool canOverride = true;
+ theme->GetMinimumWidgetSize(aState->PresContext(),
+ verticalWM ? mHScrollbarBox
+ : mVScrollbarBox,
+ NS_THEME_SCROLLBAR_NON_DISAPPEARING,
+ &size,
+ &canOverride);
+ return aState->PresContext()->
+ DevPixelsToAppUnits(verticalWM ? size.height : size.width);
+ }
+ }
+
+ nsMargin sizes(GetDesiredScrollbarSizes(aState));
+ return verticalWM ? sizes.TopBottom() : sizes.LeftRight();
+}
+
+void
+ScrollFrameHelper::HandleScrollbarStyleSwitching()
+{
+ // Check if we switched between scrollbar styles.
+ if (mScrollbarActivity &&
+ LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) == 0) {
+ mScrollbarActivity->Destroy();
+ mScrollbarActivity = nullptr;
+ mOuter->PresContext()->ThemeChanged();
+ }
+ else if (!mScrollbarActivity &&
+ LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
+ mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(mOuter));
+ mOuter->PresContext()->ThemeChanged();
+ }
+}
+
+#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
+static bool IsFocused(nsIContent* aContent)
+{
+ // Some content elements, like the GetContent() of a scroll frame
+ // for a text input field, are inside anonymous subtrees, but the focus
+ // manager always reports a non-anonymous element as the focused one, so
+ // walk up the tree until we reach a non-anonymous element.
+ while (aContent && aContent->IsInAnonymousSubtree()) {
+ aContent = aContent->GetParent();
+ }
+
+ return aContent ? nsContentUtils::IsFocusedContent(aContent) : false;
+}
+#endif
+
+void
+ScrollFrameHelper::SetScrollableByAPZ(bool aScrollable)
+{
+ mScrollableByAPZ = aScrollable;
+}
+
+void
+ScrollFrameHelper::SetZoomableByAPZ(bool aZoomable)
+{
+ if (mZoomableByAPZ != aZoomable) {
+ // We might be changing the result of WantAsyncScroll() so schedule a
+ // paint to make sure we pick up the result of that change.
+ mZoomableByAPZ = aZoomable;
+ mOuter->SchedulePaint();
+ }
+}
+
+bool
+ScrollFrameHelper::WantAsyncScroll() const
+{
+ // If zooming is allowed, and this is a frame that's allowed to zoom, then
+ // we want it to be async-scrollable or zooming will not be permitted.
+ if (mZoomableByAPZ) {
+ return true;
+ }
+
+ ScrollbarStyles styles = GetScrollbarStylesFromFrame();
+ nscoord oneDevPixel = GetScrolledFrame()->PresContext()->AppUnitsPerDevPixel();
+ nsRect scrollRange = GetScrollRange();
+ bool isVScrollable = (scrollRange.height >= oneDevPixel) &&
+ (styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
+ bool isHScrollable = (scrollRange.width >= oneDevPixel) &&
+ (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN);
+
+#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
+ // Mobile platforms need focus to scroll.
+ bool canScrollWithoutScrollbars = IsFocused(mOuter->GetContent());
+#else
+ bool canScrollWithoutScrollbars = true;
+#endif
+
+ // The check for scroll bars was added in bug 825692 to prevent layerization
+ // of text inputs for performance reasons.
+ bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || canScrollWithoutScrollbars);
+ bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || canScrollWithoutScrollbars);
+ return isVAsyncScrollable || isHAsyncScrollable;
+}
+
+static nsRect
+GetOnePixelRangeAroundPoint(nsPoint aPoint, bool aIsHorizontal)
+{
+ nsRect allowedRange(aPoint, nsSize());
+ nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
+ if (aIsHorizontal) {
+ allowedRange.x = aPoint.x - halfPixel;
+ allowedRange.width = halfPixel*2 - 1;
+ } else {
+ allowedRange.y = aPoint.y - halfPixel;
+ allowedRange.height = halfPixel*2 - 1;
+ }
+ return allowedRange;
+}
+
+void
+ScrollFrameHelper::ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap)
+{
+ ScrollByUnit(aScrollbar, nsIScrollableFrame::SMOOTH, aDirection,
+ nsIScrollableFrame::PAGES, aSnap);
+}
+
+void
+ScrollFrameHelper::ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap)
+{
+ ScrollByUnit(aScrollbar, nsIScrollableFrame::INSTANT, aDirection,
+ nsIScrollableFrame::WHOLE, aSnap);
+}
+
+void
+ScrollFrameHelper::ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap)
+{
+ bool isHorizontal = aScrollbar->IsXULHorizontal();
+ nsIntPoint delta;
+ if (isHorizontal) {
+ const double kScrollMultiplier =
+ Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
+ NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
+ delta.x = aDirection * kScrollMultiplier;
+ if (GetLineScrollAmount().width * delta.x > GetPageScrollAmount().width) {
+ // The scroll frame is so small that the delta would be more
+ // than an entire page. Scroll by one page instead to maintain
+ // context.
+ ScrollByPage(aScrollbar, aDirection);
+ return;
+ }
+ } else {
+ const double kScrollMultiplier =
+ Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
+ NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
+ delta.y = aDirection * kScrollMultiplier;
+ if (GetLineScrollAmount().height * delta.y > GetPageScrollAmount().height) {
+ // The scroll frame is so small that the delta would be more
+ // than an entire page. Scroll by one page instead to maintain
+ // context.
+ ScrollByPage(aScrollbar, aDirection);
+ return;
+ }
+ }
+
+ nsIntPoint overflow;
+ ScrollBy(delta, nsIScrollableFrame::LINES, nsIScrollableFrame::SMOOTH,
+ &overflow, nsGkAtoms::other, nsIScrollableFrame::NOT_MOMENTUM,
+ aSnap);
+}
+
+void
+ScrollFrameHelper::RepeatButtonScroll(nsScrollbarFrame* aScrollbar)
+{
+ aScrollbar->MoveToNewPosition();
+}
+
+void
+ScrollFrameHelper::ThumbMoved(nsScrollbarFrame* aScrollbar,
+ nscoord aOldPos,
+ nscoord aNewPos)
+{
+ MOZ_ASSERT(aScrollbar != nullptr);
+ bool isHorizontal = aScrollbar->IsXULHorizontal();
+ nsPoint current = GetScrollPosition();
+ nsPoint dest = current;
+ if (isHorizontal) {
+ dest.x = IsPhysicalLTR() ? aNewPos : aNewPos - GetScrollRange().width;
+ } else {
+ dest.y = aNewPos;
+ }
+ nsRect allowedRange = GetOnePixelRangeAroundPoint(dest, isHorizontal);
+
+ // Don't try to scroll if we're already at an acceptable place.
+ // Don't call Contains here since Contains returns false when the point is
+ // on the bottom or right edge of the rectangle.
+ if (allowedRange.ClampPoint(current) == current) {
+ return;
+ }
+
+ ScrollTo(dest, nsIScrollableFrame::INSTANT, &allowedRange);
+}
+
+void
+ScrollFrameHelper::ScrollbarReleased(nsScrollbarFrame* aScrollbar)
+{
+ // Scrollbar scrolling does not result in fling gestures, clear any
+ // accumulated velocity
+ mVelocityQueue.Reset();
+
+ // Perform scroll snapping, if needed. Scrollbar movement uses the same
+ // smooth scrolling animation as keyboard scrolling.
+ ScrollSnap(mDestination, nsIScrollableFrame::SMOOTH);
+}
+
+void
+ScrollFrameHelper::ScrollByUnit(nsScrollbarFrame* aScrollbar,
+ nsIScrollableFrame::ScrollMode aMode,
+ int32_t aDirection,
+ nsIScrollableFrame::ScrollUnit aUnit,
+ nsIScrollbarMediator::ScrollSnapMode aSnap)
+{
+ MOZ_ASSERT(aScrollbar != nullptr);
+ bool isHorizontal = aScrollbar->IsXULHorizontal();
+ nsIntPoint delta;
+ if (isHorizontal) {
+ delta.x = aDirection;
+ } else {
+ delta.y = aDirection;
+ }
+ nsIntPoint overflow;
+ ScrollBy(delta, aUnit, aMode, &overflow, nsGkAtoms::other,
+ nsIScrollableFrame::NOT_MOMENTUM, aSnap);
+}
+
+nsresult
+nsXULScrollFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+ return mHelper.CreateAnonymousContent(aElements);
+}
+
+void
+nsXULScrollFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter)
+{
+ mHelper.AppendAnonymousContentTo(aElements, aFilter);
+}
+
+void
+nsXULScrollFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ mHelper.Destroy();
+ nsBoxFrame::DestroyFrom(aDestructRoot);
+}
+
+void
+nsXULScrollFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ nsBoxFrame::SetInitialChildList(aListID, aChildList);
+ if (aListID == kPrincipalList) {
+ mHelper.ReloadChildFrames();
+ }
+}
+
+
+void
+nsXULScrollFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ nsBoxFrame::AppendFrames(aListID, aFrameList);
+ mHelper.ReloadChildFrames();
+}
+
+void
+nsXULScrollFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
+ mHelper.ReloadChildFrames();
+}
+
+void
+nsXULScrollFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ nsBoxFrame::RemoveFrame(aListID, aOldFrame);
+ mHelper.ReloadChildFrames();
+}
+
+nsSplittableType
+nsXULScrollFrame::GetSplittableType() const
+{
+ return NS_FRAME_NOT_SPLITTABLE;
+}
+
+nsresult
+nsXULScrollFrame::GetXULPadding(nsMargin& aMargin)
+{
+ aMargin.SizeTo(0,0,0,0);
+ return NS_OK;
+}
+
+nsIAtom*
+nsXULScrollFrame::GetType() const
+{
+ return nsGkAtoms::scrollFrame;
+}
+
+nscoord
+nsXULScrollFrame::GetXULBoxAscent(nsBoxLayoutState& aState)
+{
+ if (!mHelper.mScrolledFrame)
+ return 0;
+
+ nscoord ascent = mHelper.mScrolledFrame->GetXULBoxAscent(aState);
+ nsMargin m(0,0,0,0);
+ GetXULBorderAndPadding(m);
+ ascent += m.top;
+ GetXULMargin(m);
+ ascent += m.top;
+
+ return ascent;
+}
+
+nsSize
+nsXULScrollFrame::GetXULPrefSize(nsBoxLayoutState& aState)
+{
+#ifdef DEBUG_LAYOUT
+ PropagateDebug(aState);
+#endif
+
+ nsSize pref = mHelper.mScrolledFrame->GetXULPrefSize(aState);
+
+ ScrollbarStyles styles = GetScrollbarStyles();
+
+ // scrolled frames don't have their own margins
+
+ if (mHelper.mVScrollbarBox &&
+ styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
+ nsSize vSize = mHelper.mVScrollbarBox->GetXULPrefSize(aState);
+ nsBox::AddMargin(mHelper.mVScrollbarBox, vSize);
+ pref.width += vSize.width;
+ }
+
+ if (mHelper.mHScrollbarBox &&
+ styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
+ nsSize hSize = mHelper.mHScrollbarBox->GetXULPrefSize(aState);
+ nsBox::AddMargin(mHelper.mHScrollbarBox, hSize);
+ pref.height += hSize.height;
+ }
+
+ AddBorderAndPadding(pref);
+ bool widthSet, heightSet;
+ nsIFrame::AddXULPrefSize(this, pref, widthSet, heightSet);
+ return pref;
+}
+
+nsSize
+nsXULScrollFrame::GetXULMinSize(nsBoxLayoutState& aState)
+{
+#ifdef DEBUG_LAYOUT
+ PropagateDebug(aState);
+#endif
+
+ nsSize min = mHelper.mScrolledFrame->GetXULMinSizeForScrollArea(aState);
+
+ ScrollbarStyles styles = GetScrollbarStyles();
+
+ if (mHelper.mVScrollbarBox &&
+ styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
+ nsSize vSize = mHelper.mVScrollbarBox->GetXULMinSize(aState);
+ AddMargin(mHelper.mVScrollbarBox, vSize);
+ min.width += vSize.width;
+ if (min.height < vSize.height)
+ min.height = vSize.height;
+ }
+
+ if (mHelper.mHScrollbarBox &&
+ styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
+ nsSize hSize = mHelper.mHScrollbarBox->GetXULMinSize(aState);
+ AddMargin(mHelper.mHScrollbarBox, hSize);
+ min.height += hSize.height;
+ if (min.width < hSize.width)
+ min.width = hSize.width;
+ }
+
+ AddBorderAndPadding(min);
+ bool widthSet, heightSet;
+ nsIFrame::AddXULMinSize(aState, this, min, widthSet, heightSet);
+ return min;
+}
+
+nsSize
+nsXULScrollFrame::GetXULMaxSize(nsBoxLayoutState& aState)
+{
+#ifdef DEBUG_LAYOUT
+ PropagateDebug(aState);
+#endif
+
+ nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
+
+ AddBorderAndPadding(maxSize);
+ bool widthSet, heightSet;
+ nsIFrame::AddXULMaxSize(this, maxSize, widthSet, heightSet);
+ return maxSize;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsXULScrollFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult);
+}
+#endif
+
+NS_IMETHODIMP
+nsXULScrollFrame::DoXULLayout(nsBoxLayoutState& aState)
+{
+ uint32_t flags = aState.LayoutFlags();
+ nsresult rv = XULLayout(aState);
+ aState.SetLayoutFlags(flags);
+
+ nsBox::DoXULLayout(aState);
+ return rv;
+}
+
+NS_QUERYFRAME_HEAD(nsXULScrollFrame)
+ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+ NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
+ NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
+ NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
+NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
+
+//-------------------- Helper ----------------------
+
+#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
+
+// AsyncSmoothMSDScroll has ref counting.
+class ScrollFrameHelper::AsyncSmoothMSDScroll final : public nsARefreshObserver {
+public:
+ AsyncSmoothMSDScroll(const nsPoint &aInitialPosition,
+ const nsPoint &aInitialDestination,
+ const nsSize &aInitialVelocity,
+ const nsRect &aRange,
+ const mozilla::TimeStamp &aStartTime,
+ nsPresContext* aPresContext)
+ : mXAxisModel(aInitialPosition.x, aInitialDestination.x,
+ aInitialVelocity.width,
+ gfxPrefs::ScrollBehaviorSpringConstant(),
+ gfxPrefs::ScrollBehaviorDampingRatio())
+ , mYAxisModel(aInitialPosition.y, aInitialDestination.y,
+ aInitialVelocity.height,
+ gfxPrefs::ScrollBehaviorSpringConstant(),
+ gfxPrefs::ScrollBehaviorDampingRatio())
+ , mRange(aRange)
+ , mLastRefreshTime(aStartTime)
+ , mCallee(nullptr)
+ , mOneDevicePixelInAppUnits(aPresContext->DevPixelsToAppUnits(1))
+ {
+ Telemetry::SetHistogramRecordingEnabled(
+ Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, true);
+ }
+
+ NS_INLINE_DECL_REFCOUNTING(AsyncSmoothMSDScroll, override)
+
+ nsSize GetVelocity() {
+ // In nscoords per second
+ return nsSize(mXAxisModel.GetVelocity(), mYAxisModel.GetVelocity());
+ }
+
+ nsPoint GetPosition() {
+ // In nscoords
+ return nsPoint(NSToCoordRound(mXAxisModel.GetPosition()), NSToCoordRound(mYAxisModel.GetPosition()));
+ }
+
+ void SetDestination(const nsPoint &aDestination) {
+ mXAxisModel.SetDestination(static_cast<int32_t>(aDestination.x));
+ mYAxisModel.SetDestination(static_cast<int32_t>(aDestination.y));
+ }
+
+ void SetRange(const nsRect &aRange)
+ {
+ mRange = aRange;
+ }
+
+ nsRect GetRange()
+ {
+ return mRange;
+ }
+
+ void Simulate(const TimeDuration& aDeltaTime)
+ {
+ mXAxisModel.Simulate(aDeltaTime);
+ mYAxisModel.Simulate(aDeltaTime);
+
+ nsPoint desired = GetPosition();
+ nsPoint clamped = mRange.ClampPoint(desired);
+ if(desired.x != clamped.x) {
+ // The scroll has hit the "wall" at the left or right edge of the allowed
+ // scroll range.
+ // Absorb the impact to avoid bounceback effect.
+ mXAxisModel.SetVelocity(0.0);
+ mXAxisModel.SetPosition(clamped.x);
+ }
+
+ if(desired.y != clamped.y) {
+ // The scroll has hit the "wall" at the left or right edge of the allowed
+ // scroll range.
+ // Absorb the impact to avoid bounceback effect.
+ mYAxisModel.SetVelocity(0.0);
+ mYAxisModel.SetPosition(clamped.y);
+ }
+ }
+
+ bool IsFinished()
+ {
+ return mXAxisModel.IsFinished(mOneDevicePixelInAppUnits) &&
+ mYAxisModel.IsFinished(mOneDevicePixelInAppUnits);
+ }
+
+ virtual void WillRefresh(mozilla::TimeStamp aTime) override {
+ mozilla::TimeDuration deltaTime = aTime - mLastRefreshTime;
+ mLastRefreshTime = aTime;
+
+ // The callback may release "this".
+ // We don't access members after returning, so no need for KungFuDeathGrip.
+ ScrollFrameHelper::AsyncSmoothMSDScrollCallback(mCallee, deltaTime);
+ }
+
+ /*
+ * Set a refresh observer for smooth scroll iterations (and start observing).
+ * Should be used at most once during the lifetime of this object.
+ * Return value: true on success, false otherwise.
+ */
+ bool SetRefreshObserver(ScrollFrameHelper *aCallee) {
+ NS_ASSERTION(aCallee && !mCallee, "AsyncSmoothMSDScroll::SetRefreshObserver - Invalid usage.");
+
+ if (!RefreshDriver(aCallee)->AddRefreshObserver(this, Flush_Style)) {
+ return false;
+ }
+
+ mCallee = aCallee;
+ return true;
+ }
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~AsyncSmoothMSDScroll() {
+ RemoveObserver();
+ Telemetry::SetHistogramRecordingEnabled(
+ Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, false);
+ }
+
+ nsRefreshDriver* RefreshDriver(ScrollFrameHelper* aCallee) {
+ return aCallee->mOuter->PresContext()->RefreshDriver();
+ }
+
+ /*
+ * The refresh driver doesn't hold a reference to its observers,
+ * so releasing this object can (and is) used to remove the observer on DTOR.
+ * Currently, this object is released once the scrolling ends.
+ */
+ void RemoveObserver() {
+ if (mCallee) {
+ RefreshDriver(mCallee)->RemoveRefreshObserver(this, Flush_Style);
+ }
+ }
+
+ mozilla::layers::AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
+ nsRect mRange;
+ mozilla::TimeStamp mLastRefreshTime;
+ ScrollFrameHelper *mCallee;
+ nscoord mOneDevicePixelInAppUnits;
+};
+
+// AsyncScroll has ref counting.
+class ScrollFrameHelper::AsyncScroll final
+ : public nsARefreshObserver,
+ public AsyncScrollBase
+{
+public:
+ typedef mozilla::TimeStamp TimeStamp;
+ typedef mozilla::TimeDuration TimeDuration;
+
+ explicit AsyncScroll(nsPoint aStartPos)
+ : AsyncScrollBase(aStartPos)
+ , mCallee(nullptr)
+ {
+ Telemetry::SetHistogramRecordingEnabled(
+ Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, true);
+ }
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~AsyncScroll() {
+ RemoveObserver();
+ Telemetry::SetHistogramRecordingEnabled(
+ Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, false);
+ }
+
+public:
+ void InitSmoothScroll(TimeStamp aTime, nsPoint aDestination,
+ nsIAtom *aOrigin, const nsRect& aRange,
+ const nsSize& aCurrentVelocity);
+ void Init(const nsRect& aRange) {
+ mRange = aRange;
+ }
+
+ // Most recent scroll origin.
+ nsCOMPtr<nsIAtom> mOrigin;
+
+ // Allowed destination positions around mDestination
+ nsRect mRange;
+ bool mIsSmoothScroll;
+
+private:
+ void InitPreferences(TimeStamp aTime, nsIAtom *aOrigin);
+
+// The next section is observer/callback management
+// Bodies of WillRefresh and RefreshDriver contain ScrollFrameHelper specific code.
+public:
+ NS_INLINE_DECL_REFCOUNTING(AsyncScroll, override)
+
+ /*
+ * Set a refresh observer for smooth scroll iterations (and start observing).
+ * Should be used at most once during the lifetime of this object.
+ * Return value: true on success, false otherwise.
+ */
+ bool SetRefreshObserver(ScrollFrameHelper *aCallee) {
+ NS_ASSERTION(aCallee && !mCallee, "AsyncScroll::SetRefreshObserver - Invalid usage.");
+
+ if (!RefreshDriver(aCallee)->AddRefreshObserver(this, Flush_Style)) {
+ return false;
+ }
+
+ mCallee = aCallee;
+ APZCCallbackHelper::SuppressDisplayport(true, mCallee->mOuter->PresContext()->PresShell());
+ return true;
+ }
+
+ virtual void WillRefresh(mozilla::TimeStamp aTime) override {
+ // The callback may release "this".
+ // We don't access members after returning, so no need for KungFuDeathGrip.
+ ScrollFrameHelper::AsyncScrollCallback(mCallee, aTime);
+ }
+
+private:
+ ScrollFrameHelper *mCallee;
+
+ nsRefreshDriver* RefreshDriver(ScrollFrameHelper* aCallee) {
+ return aCallee->mOuter->PresContext()->RefreshDriver();
+ }
+
+ /*
+ * The refresh driver doesn't hold a reference to its observers,
+ * so releasing this object can (and is) used to remove the observer on DTOR.
+ * Currently, this object is released once the scrolling ends.
+ */
+ void RemoveObserver() {
+ if (mCallee) {
+ RefreshDriver(mCallee)->RemoveRefreshObserver(this, Flush_Style);
+ APZCCallbackHelper::SuppressDisplayport(false, mCallee->mOuter->PresContext()->PresShell());
+ }
+ }
+};
+
+/*
+ * Calculate duration, possibly dynamically according to events rate and event origin.
+ * (also maintain previous timestamps - which are only used here).
+ */
+void
+ScrollFrameHelper::AsyncScroll::InitPreferences(TimeStamp aTime, nsIAtom *aOrigin)
+{
+ if (!aOrigin || aOrigin == nsGkAtoms::restore) {
+ // We don't have special prefs for "restore", just treat it as "other".
+ // "restore" scrolls are (for now) always instant anyway so unless something
+ // changes we should never have aOrigin == nsGkAtoms::restore here.
+ aOrigin = nsGkAtoms::other;
+ }
+ // Likewise we should never get APZ-triggered scrolls here, and if that changes
+ // something is likely broken somewhere.
+ MOZ_ASSERT(aOrigin != nsGkAtoms::apz);
+
+ // Read preferences only on first iteration or for a different event origin.
+ if (!mIsFirstIteration && aOrigin == mOrigin) {
+ return;
+ }
+
+ mOrigin = aOrigin;
+ mOriginMinMS = mOriginMaxMS = 0;
+ bool isOriginSmoothnessEnabled = false;
+ mIntervalRatio = 1;
+
+ // Default values for all preferences are defined in all.js
+ static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
+ static const bool kDefaultIsSmoothEnabled = true;
+
+ nsAutoCString originName;
+ aOrigin->ToUTF8String(originName);
+ nsAutoCString prefBase = NS_LITERAL_CSTRING("general.smoothScroll.") + originName;
+
+ isOriginSmoothnessEnabled = Preferences::GetBool(prefBase.get(), kDefaultIsSmoothEnabled);
+ if (isOriginSmoothnessEnabled) {
+ nsAutoCString prefMin = prefBase + NS_LITERAL_CSTRING(".durationMinMS");
+ nsAutoCString prefMax = prefBase + NS_LITERAL_CSTRING(".durationMaxMS");
+ mOriginMinMS = Preferences::GetInt(prefMin.get(), kDefaultMinMS);
+ mOriginMaxMS = Preferences::GetInt(prefMax.get(), kDefaultMaxMS);
+
+ static const int32_t kSmoothScrollMaxAllowedAnimationDurationMS = 10000;
+ mOriginMaxMS = clamped(mOriginMaxMS, 0, kSmoothScrollMaxAllowedAnimationDurationMS);
+ mOriginMinMS = clamped(mOriginMinMS, 0, mOriginMaxMS);
+ }
+
+ // Keep the animation duration longer than the average event intervals
+ // (to "connect" consecutive scroll animations before the scroll comes to a stop).
+ static const double kDefaultDurationToIntervalRatio = 2; // Duplicated at all.js
+ mIntervalRatio = Preferences::GetInt("general.smoothScroll.durationToIntervalRatio",
+ kDefaultDurationToIntervalRatio * 100) / 100.0;
+
+ // Duration should be at least as long as the intervals -> ratio is at least 1
+ mIntervalRatio = std::max(1.0, mIntervalRatio);
+
+ if (mIsFirstIteration) {
+ InitializeHistory(aTime);
+ }
+}
+
+void
+ScrollFrameHelper::AsyncScroll::InitSmoothScroll(TimeStamp aTime,
+ nsPoint aDestination,
+ nsIAtom *aOrigin,
+ const nsRect& aRange,
+ const nsSize& aCurrentVelocity)
+{
+ InitPreferences(aTime, aOrigin);
+ mRange = aRange;
+
+ Update(aTime, aDestination, aCurrentVelocity);
+}
+
+bool
+ScrollFrameHelper::IsSmoothScrollingEnabled()
+{
+ return Preferences::GetBool(SMOOTH_SCROLL_PREF_NAME, false);
+}
+
+class ScrollFrameActivityTracker final : public nsExpirationTracker<ScrollFrameHelper,4> {
+public:
+ // Wait for 3-4s between scrolls before we remove our layers.
+ // That's 4 generations of 1s each.
+ enum { TIMEOUT_MS = 1000 };
+ ScrollFrameActivityTracker()
+ : nsExpirationTracker<ScrollFrameHelper,4>(TIMEOUT_MS,
+ "ScrollFrameActivityTracker")
+ {}
+ ~ScrollFrameActivityTracker() {
+ AgeAllGenerations();
+ }
+
+ virtual void NotifyExpired(ScrollFrameHelper *aObject) {
+ RemoveObject(aObject);
+ aObject->MarkNotRecentlyScrolled();
+ }
+};
+
+static ScrollFrameActivityTracker *gScrollFrameActivityTracker = nullptr;
+
+// There are situations when a scroll frame is destroyed and then re-created
+// for the same content element. In this case we want to increment the scroll
+// generation between the old and new scrollframes. If the new one knew about
+// the old one then it could steal the old generation counter and increment it
+// but it doesn't have that reference so instead we use a static global to
+// ensure the new one gets a fresh value.
+static uint32_t sScrollGenerationCounter = 0;
+
+ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
+ bool aIsRoot)
+ : mHScrollbarBox(nullptr)
+ , mVScrollbarBox(nullptr)
+ , mScrolledFrame(nullptr)
+ , mScrollCornerBox(nullptr)
+ , mResizerBox(nullptr)
+ , mOuter(aOuter)
+ , mAsyncScroll(nullptr)
+ , mAsyncSmoothMSDScroll(nullptr)
+ , mLastScrollOrigin(nsGkAtoms::other)
+ , mAllowScrollOriginDowngrade(false)
+ , mLastSmoothScrollOrigin(nullptr)
+ , mScrollGeneration(++sScrollGenerationCounter)
+ , mDestination(0, 0)
+ , mScrollPosAtLastPaint(0, 0)
+ , mRestorePos(-1, -1)
+ , mLastPos(-1, -1)
+ , mScrollPosForLayerPixelAlignment(-1, -1)
+ , mLastUpdateFramesPos(-1, -1)
+ , mHadDisplayPortAtLastFrameUpdate(false)
+ , mDisplayPortAtLastFrameUpdate()
+ , mNeverHasVerticalScrollbar(false)
+ , mNeverHasHorizontalScrollbar(false)
+ , mHasVerticalScrollbar(false)
+ , mHasHorizontalScrollbar(false)
+ , mFrameIsUpdatingScrollbar(false)
+ , mDidHistoryRestore(false)
+ , mIsRoot(aIsRoot)
+ , mClipAllDescendants(aIsRoot)
+ , mSupppressScrollbarUpdate(false)
+ , mSkippedScrollbarLayout(false)
+ , mHadNonInitialReflow(false)
+ , mHorizontalOverflow(false)
+ , mVerticalOverflow(false)
+ , mPostedReflowCallback(false)
+ , mMayHaveDirtyFixedChildren(false)
+ , mUpdateScrollbarAttributes(false)
+ , mHasBeenScrolledRecently(false)
+ , mCollapsedResizer(false)
+ , mWillBuildScrollableLayer(false)
+ , mIsScrollParent(false)
+ , mIsScrollableLayerInRootContainer(false)
+ , mHasBeenScrolled(false)
+ , mIgnoreMomentumScroll(false)
+ , mTransformingByAPZ(false)
+ , mScrollableByAPZ(false)
+ , mZoomableByAPZ(false)
+ , mScrollsClipOnUnscrolledOutOfFlow(false)
+ , mVelocityQueue(aOuter->PresContext())
+{
+ if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
+ mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
+ }
+
+ EnsureFrameVisPrefsCached();
+
+ if (IsAlwaysActive() &&
+ gfxPrefs::LayersTilesEnabled() &&
+ !nsLayoutUtils::UsesAsyncScrolling(mOuter) &&
+ mOuter->GetContent()) {
+ // If we have tiling but no APZ, then set a 0-margin display port on
+ // active scroll containers so that we paint by whole tile increments
+ // when scrolling.
+ nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
+ mOuter->PresContext()->PresShell(),
+ ScreenMargin(),
+ 0,
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ mOuter, nsLayoutUtils::RepaintMode::DoNotRepaint);
+ }
+
+}
+
+ScrollFrameHelper::~ScrollFrameHelper()
+{
+}
+
+/*
+ * Callback function from AsyncSmoothMSDScroll, used in ScrollFrameHelper::ScrollTo
+ */
+void
+ScrollFrameHelper::AsyncSmoothMSDScrollCallback(ScrollFrameHelper* aInstance,
+ mozilla::TimeDuration aDeltaTime)
+{
+ NS_ASSERTION(aInstance != nullptr, "aInstance must not be null");
+ NS_ASSERTION(aInstance->mAsyncSmoothMSDScroll,
+ "Did not expect AsyncSmoothMSDScrollCallback without an active MSD scroll.");
+
+ nsRect range = aInstance->mAsyncSmoothMSDScroll->GetRange();
+ aInstance->mAsyncSmoothMSDScroll->Simulate(aDeltaTime);
+
+ if (!aInstance->mAsyncSmoothMSDScroll->IsFinished()) {
+ nsPoint destination = aInstance->mAsyncSmoothMSDScroll->GetPosition();
+ // Allow this scroll operation to land on any pixel boundary within the
+ // allowed scroll range for this frame.
+ // If the MSD is under-dampened or the destination is changed rapidly,
+ // it is expected (and desired) that the scrolling may overshoot.
+ nsRect intermediateRange =
+ nsRect(destination, nsSize()).UnionEdges(range);
+ aInstance->ScrollToImpl(destination, intermediateRange);
+ // 'aInstance' might be destroyed here
+ return;
+ }
+
+ aInstance->CompleteAsyncScroll(range);
+}
+
+/*
+ * Callback function from AsyncScroll, used in ScrollFrameHelper::ScrollTo
+ */
+void
+ScrollFrameHelper::AsyncScrollCallback(ScrollFrameHelper* aInstance,
+ mozilla::TimeStamp aTime)
+{
+ MOZ_ASSERT(aInstance != nullptr, "aInstance must not be null");
+ MOZ_ASSERT(aInstance->mAsyncScroll,
+ "Did not expect AsyncScrollCallback without an active async scroll.");
+
+ if (!aInstance || !aInstance->mAsyncScroll) {
+ return; // XXX wallpaper bug 1107353 for now.
+ }
+
+ nsRect range = aInstance->mAsyncScroll->mRange;
+ if (aInstance->mAsyncScroll->mIsSmoothScroll) {
+ if (!aInstance->mAsyncScroll->IsFinished(aTime)) {
+ nsPoint destination = aInstance->mAsyncScroll->PositionAt(aTime);
+ // Allow this scroll operation to land on any pixel boundary between the
+ // current position and the final allowed range. (We don't want
+ // intermediate steps to be more constrained than the final step!)
+ nsRect intermediateRange =
+ nsRect(aInstance->GetScrollPosition(), nsSize()).UnionEdges(range);
+ aInstance->ScrollToImpl(destination, intermediateRange);
+ // 'aInstance' might be destroyed here
+ return;
+ }
+ }
+
+ aInstance->CompleteAsyncScroll(range);
+}
+
+void
+ScrollFrameHelper::CompleteAsyncScroll(const nsRect &aRange, nsIAtom* aOrigin)
+{
+ // Apply desired destination range since this is the last step of scrolling.
+ mAsyncSmoothMSDScroll = nullptr;
+ mAsyncScroll = nullptr;
+ nsWeakFrame weakFrame(mOuter);
+ ScrollToImpl(mDestination, aRange, aOrigin);
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ // We are done scrolling, set our destination to wherever we actually ended
+ // up scrolling to.
+ mDestination = GetScrollPosition();
+}
+
+bool
+ScrollFrameHelper::HasPluginFrames()
+{
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (XRE_IsContentProcess()) {
+ nsPresContext* presContext = mOuter->PresContext();
+ nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
+ if (!rootPresContext || rootPresContext->NeedToComputePluginGeometryUpdates()) {
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+bool
+ScrollFrameHelper::HasPerspective() const
+{
+ const nsStyleDisplay* disp = mOuter->StyleDisplay();
+ return disp->mChildPerspective.GetUnit() != eStyleUnit_None;
+}
+
+bool
+ScrollFrameHelper::HasBgAttachmentLocal() const
+{
+ const nsStyleBackground* bg = mOuter->StyleBackground();
+ return bg->HasLocalBackground();
+}
+
+void
+ScrollFrameHelper::SetScrollsClipOnUnscrolledOutOfFlow()
+{
+ mScrollsClipOnUnscrolledOutOfFlow = true;
+}
+
+void
+ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode)
+{
+ nsPoint current = GetScrollPosition();
+ CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
+ nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
+ nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
+ nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2*halfPixel - 1, 2*halfPixel - 1);
+ // XXX I don't think the following blocks are needed anymore, now that
+ // ScrollToImpl simply tries to scroll an integer number of layer
+ // pixels from the current position
+ if (currentCSSPixels.x == aScrollPosition.x) {
+ pt.x = current.x;
+ range.x = pt.x;
+ range.width = 0;
+ }
+ if (currentCSSPixels.y == aScrollPosition.y) {
+ pt.y = current.y;
+ range.y = pt.y;
+ range.height = 0;
+ }
+ ScrollTo(pt, aMode, &range);
+ // 'this' might be destroyed here
+}
+
+void
+ScrollFrameHelper::ScrollToCSSPixelsApproximate(const CSSPoint& aScrollPosition,
+ nsIAtom *aOrigin)
+{
+ nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
+ nscoord halfRange = nsPresContext::CSSPixelsToAppUnits(1000);
+ nsRect range(pt.x - halfRange, pt.y - halfRange, 2*halfRange - 1, 2*halfRange - 1);
+ ScrollToWithOrigin(pt, nsIScrollableFrame::INSTANT, aOrigin, &range);
+ // 'this' might be destroyed here
+}
+
+CSSIntPoint
+ScrollFrameHelper::GetScrollPositionCSSPixels()
+{
+ return CSSIntPoint::FromAppUnitsRounded(GetScrollPosition());
+}
+
+/*
+ * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
+ * based on the setting of the smoothness scroll pref
+ */
+void
+ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode,
+ nsIAtom *aOrigin,
+ const nsRect* aRange,
+ nsIScrollbarMediator::ScrollSnapMode aSnap)
+{
+
+ if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
+ GetSnapPointForDestination(nsIScrollableFrame::DEVICE_PIXELS,
+ mDestination,
+ aScrollPosition);
+ }
+
+ nsRect scrollRange = GetScrollRangeForClamping();
+ mDestination = scrollRange.ClampPoint(aScrollPosition);
+ if (mDestination != aScrollPosition && aOrigin == nsGkAtoms::restore && !PageIsStillLoading()) {
+ // If we're doing a restore but the scroll position is clamped, promote
+ // the origin from one that APZ can clobber to one that it can't clobber.
+ aOrigin = nsGkAtoms::other;
+ }
+
+ nsRect range = aRange ? *aRange : nsRect(aScrollPosition, nsSize(0, 0));
+
+ if (aMode != nsIScrollableFrame::SMOOTH_MSD) {
+ // If we get a non-smooth-scroll, reset the cached APZ scroll destination,
+ // so that we know to process the next smooth-scroll destined for APZ.
+ mApzSmoothScrollDestination = Nothing();
+ }
+
+ if (aMode == nsIScrollableFrame::INSTANT) {
+ // Asynchronous scrolling is not allowed, so we'll kill any existing
+ // async-scrolling process and do an instant scroll.
+ CompleteAsyncScroll(range, aOrigin);
+ return;
+ }
+
+ nsPresContext* presContext = mOuter->PresContext();
+ TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
+ bool isSmoothScroll = (aMode == nsIScrollableFrame::SMOOTH) &&
+ IsSmoothScrollingEnabled();
+
+ nsSize currentVelocity(0, 0);
+
+ if (gfxPrefs::ScrollBehaviorEnabled()) {
+ if (aMode == nsIScrollableFrame::SMOOTH_MSD) {
+ mIgnoreMomentumScroll = true;
+ if (!mAsyncSmoothMSDScroll) {
+ nsPoint sv = mVelocityQueue.GetVelocity();
+ currentVelocity.width = sv.x;
+ currentVelocity.height = sv.y;
+ if (mAsyncScroll) {
+ if (mAsyncScroll->mIsSmoothScroll) {
+ currentVelocity = mAsyncScroll->VelocityAt(now);
+ }
+ mAsyncScroll = nullptr;
+ }
+
+ if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && WantAsyncScroll()) {
+ if (mApzSmoothScrollDestination == Some(mDestination) &&
+ mScrollGeneration == sScrollGenerationCounter) {
+ // If we already sent APZ a smooth-scroll request to this
+ // destination with this generation (i.e. it was the last request
+ // we sent), then don't send another one because it is redundant.
+ // This is to avoid a scenario where pages do repeated scrollBy
+ // calls, incrementing the generation counter, and blocking APZ from
+ // syncing the scroll offset back to the main thread.
+ // Note that if we get two smooth-scroll requests to the same
+ // destination with some other scroll in between,
+ // mApzSmoothScrollDestination will get reset to Nothing() and so
+ // we shouldn't have the problem where this check discards a
+ // legitimate smooth-scroll.
+ // Note: if there are two separate scrollframes both getting smooth
+ // scrolled at the same time, sScrollGenerationCounter can get
+ // incremented and this early-exit won't get taken. Bug 1231177 is
+ // on file for this.
+ return;
+ }
+
+ // The animation will be handled in the compositor, pass the
+ // information needed to start the animation and skip the main-thread
+ // animation for this scroll.
+ mLastSmoothScrollOrigin = aOrigin;
+ mApzSmoothScrollDestination = Some(mDestination);
+ mScrollGeneration = ++sScrollGenerationCounter;
+
+ if (!nsLayoutUtils::HasDisplayPort(mOuter->GetContent())) {
+ // If this frame doesn't have a displayport then there won't be an
+ // APZC instance for it and so there won't be anything to process
+ // this smooth scroll request. We should set a displayport on this
+ // frame to force an APZC which can handle the request.
+ nsLayoutUtils::CalculateAndSetDisplayPortMargins(
+ mOuter->GetScrollTargetFrame(),
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ nsIFrame* frame = do_QueryFrame(mOuter->GetScrollTargetFrame());
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ frame,
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ }
+
+ // Schedule a paint to ensure that the frame metrics get updated on
+ // the compositor thread.
+ mOuter->SchedulePaint();
+ return;
+ }
+
+ mAsyncSmoothMSDScroll =
+ new AsyncSmoothMSDScroll(GetScrollPosition(), mDestination,
+ currentVelocity, GetScrollRangeForClamping(),
+ now, presContext);
+
+ if (!mAsyncSmoothMSDScroll->SetRefreshObserver(this)) {
+ // Observer setup failed. Scroll the normal way.
+ CompleteAsyncScroll(range, aOrigin);
+ return;
+ }
+ } else {
+ // A previous smooth MSD scroll is still in progress, so we just need to
+ // update its destination.
+ mAsyncSmoothMSDScroll->SetDestination(mDestination);
+ }
+
+ return;
+ } else {
+ if (mAsyncSmoothMSDScroll) {
+ currentVelocity = mAsyncSmoothMSDScroll->GetVelocity();
+ mAsyncSmoothMSDScroll = nullptr;
+ }
+ }
+ }
+
+ if (!mAsyncScroll) {
+ mAsyncScroll = new AsyncScroll(GetScrollPosition());
+ if (!mAsyncScroll->SetRefreshObserver(this)) {
+ // Observer setup failed. Scroll the normal way.
+ CompleteAsyncScroll(range, aOrigin);
+ return;
+ }
+ }
+
+ mAsyncScroll->mIsSmoothScroll = isSmoothScroll;
+
+ if (isSmoothScroll) {
+ mAsyncScroll->InitSmoothScroll(now, mDestination, aOrigin, range, currentVelocity);
+ } else {
+ mAsyncScroll->Init(range);
+ }
+}
+
+// We can't use nsContainerFrame::PositionChildViews here because
+// we don't want to invalidate views that have moved.
+static void AdjustViews(nsIFrame* aFrame)
+{
+ nsView* view = aFrame->GetView();
+ if (view) {
+ nsPoint pt;
+ aFrame->GetParent()->GetClosestView(&pt);
+ pt += aFrame->GetPosition();
+ view->SetPosition(pt.x, pt.y);
+
+ return;
+ }
+
+ if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
+ return;
+ }
+
+ // Call AdjustViews recursively for all child frames except the popup list as
+ // the views for popups are not scrolled.
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ if (lists.CurrentID() == nsIFrame::kPopupList) {
+ continue;
+ }
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ AdjustViews(childFrames.get());
+ }
+ }
+}
+
+static bool
+NeedToInvalidateOnScroll(nsIFrame* aFrame)
+{
+ return (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL) != 0;
+}
+
+bool ScrollFrameHelper::IsIgnoringViewportClipping() const
+{
+ if (!mIsRoot)
+ return false;
+ nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
+ (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
+ return subdocFrame && !subdocFrame->ShouldClipSubdocument();
+}
+
+void ScrollFrameHelper::MarkScrollbarsDirtyForReflow() const
+{
+ nsIPresShell* presShell = mOuter->PresContext()->PresShell();
+ if (mVScrollbarBox) {
+ presShell->FrameNeedsReflow(mVScrollbarBox, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
+ if (mHScrollbarBox) {
+ presShell->FrameNeedsReflow(mHScrollbarBox, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
+}
+
+bool ScrollFrameHelper::ShouldClampScrollPosition() const
+{
+ if (!mIsRoot)
+ return true;
+ nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
+ (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
+ return !subdocFrame || subdocFrame->ShouldClampScrollPosition();
+}
+
+bool ScrollFrameHelper::IsAlwaysActive() const
+{
+ if (nsDisplayItem::ForceActiveLayers()) {
+ return true;
+ }
+
+ // Unless this is the root scrollframe for a non-chrome document
+ // which is the direct child of a chrome document, we default to not
+ // being "active".
+ if (!(mIsRoot && mOuter->PresContext()->IsRootContentDocument())) {
+ return false;
+ }
+
+ // If we have scrolled before, then we should stay active.
+ if (mHasBeenScrolled) {
+ return true;
+ }
+
+ // If we're overflow:hidden, then start as inactive until
+ // we get scrolled manually.
+ ScrollbarStyles styles = GetScrollbarStylesFromFrame();
+ return (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
+ styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
+}
+
+/*static*/ void
+RemoveDisplayPortCallback(nsITimer* aTimer, void* aClosure)
+{
+ ScrollFrameHelper* helper = static_cast<ScrollFrameHelper*>(aClosure);
+
+ // This function only ever gets called from the expiry timer, so it must
+ // be non-null here. Set it to null here so that we don't keep resetting
+ // it unnecessarily in MarkRecentlyScrolled().
+ MOZ_ASSERT(helper->mDisplayPortExpiryTimer);
+ helper->mDisplayPortExpiryTimer = nullptr;
+
+ if (!helper->AllowDisplayPortExpiration() || helper->mIsScrollParent) {
+ // If this is a scroll parent for some other scrollable frame, don't
+ // expire the displayport because it would break scroll handoff. Once the
+ // descendant scrollframes have their displayports expired, they will
+ // trigger the displayport expiration on this scrollframe as well, and
+ // mIsScrollParent will presumably be false when that kicks in.
+ return;
+ }
+
+ // Remove the displayport from this scrollframe if it's been a while
+ // since it's scrolled, except if it needs to be always active. Note that
+ // there is one scrollframe that doesn't fall under this general rule, and
+ // that is the one that nsLayoutUtils::MaybeCreateDisplayPort decides to put
+ // a displayport on (i.e. the first scrollframe that WantAsyncScroll()s).
+ // If that scrollframe is this one, we remove the displayport anyway, and
+ // as part of the next paint MaybeCreateDisplayPort will put another
+ // displayport back on it. Although the displayport will "flicker" off and
+ // back on, the layer itself should never disappear, because this all
+ // happens between actual painting. If the displayport is reset to a
+ // different position that's ok; this scrollframe hasn't been scrolled
+ // recently and so the reset should be correct.
+ nsLayoutUtils::RemoveDisplayPort(helper->mOuter->GetContent());
+ nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(helper->mOuter);
+ helper->mOuter->SchedulePaint();
+ // Be conservative and unflag this this scrollframe as being scrollable by
+ // APZ. If it is still scrollable this will get flipped back soon enough.
+ helper->mScrollableByAPZ = false;
+}
+
+void ScrollFrameHelper::MarkNotRecentlyScrolled()
+{
+ if (!mHasBeenScrolledRecently)
+ return;
+
+ mHasBeenScrolledRecently = false;
+ mOuter->SchedulePaint();
+}
+
+void ScrollFrameHelper::MarkRecentlyScrolled()
+{
+ mHasBeenScrolledRecently = true;
+ if (IsAlwaysActive())
+ return;
+
+ if (mActivityExpirationState.IsTracked()) {
+ gScrollFrameActivityTracker->MarkUsed(this);
+ } else {
+ if (!gScrollFrameActivityTracker) {
+ gScrollFrameActivityTracker = new ScrollFrameActivityTracker();
+ }
+ gScrollFrameActivityTracker->AddObject(this);
+ }
+
+ // If we just scrolled and there's a displayport expiry timer in place,
+ // reset the timer.
+ ResetDisplayPortExpiryTimer();
+}
+
+void ScrollFrameHelper::ResetDisplayPortExpiryTimer()
+{
+ if (mDisplayPortExpiryTimer) {
+ mDisplayPortExpiryTimer->InitWithFuncCallback(
+ RemoveDisplayPortCallback, this,
+ gfxPrefs::APZDisplayPortExpiryTime(), nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+bool ScrollFrameHelper::AllowDisplayPortExpiration()
+{
+ if (IsAlwaysActive()) {
+ return false;
+ }
+ if (mIsRoot && mOuter->PresContext()->IsRoot()) {
+ return false;
+ }
+ return true;
+}
+
+void ScrollFrameHelper::TriggerDisplayPortExpiration()
+{
+ if (!AllowDisplayPortExpiration()) {
+ return;
+ }
+
+ if (!gfxPrefs::APZDisplayPortExpiryTime()) {
+ // a zero time disables the expiry
+ return;
+ }
+
+ if (!mDisplayPortExpiryTimer) {
+ mDisplayPortExpiryTimer = do_CreateInstance("@mozilla.org/timer;1");
+ }
+ ResetDisplayPortExpiryTimer();
+}
+
+void ScrollFrameHelper::ScrollVisual()
+{
+ // Mark this frame as having been scrolled. If this is the root
+ // scroll frame of a content document, then IsAlwaysActive()
+ // will return true from now on and MarkNotRecentlyScrolled() won't
+ // have any effect.
+ mHasBeenScrolled = true;
+
+ AdjustViews(mScrolledFrame);
+ // We need to call this after fixing up the view positions
+ // to be consistent with the frame hierarchy.
+ bool needToInvalidateOnScroll = NeedToInvalidateOnScroll(mOuter);
+ mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
+ if (needToInvalidateOnScroll) {
+ MarkNotRecentlyScrolled();
+ } else {
+ MarkRecentlyScrolled();
+ }
+
+}
+
+/**
+ * Clamp desired scroll position aDesired and range [aDestLower, aDestUpper]
+ * to [aBoundLower, aBoundUpper] and then select the appunit value from among
+ * aBoundLower, aBoundUpper and those such that (aDesired - aCurrent) *
+ * aRes/aAppUnitsPerPixel is an integer (or as close as we can get
+ * modulo rounding to appunits) that is in [aDestLower, aDestUpper] and
+ * closest to aDesired. If no such value exists, return the nearest in
+ * [aDestLower, aDestUpper].
+ */
+static nscoord
+ClampAndAlignWithPixels(nscoord aDesired,
+ nscoord aBoundLower, nscoord aBoundUpper,
+ nscoord aDestLower, nscoord aDestUpper,
+ nscoord aAppUnitsPerPixel, double aRes,
+ nscoord aCurrent)
+{
+ // Intersect scroll range with allowed range, by clamping the ends
+ // of aRange to be within bounds
+ nscoord destLower = clamped(aDestLower, aBoundLower, aBoundUpper);
+ nscoord destUpper = clamped(aDestUpper, aBoundLower, aBoundUpper);
+
+ nscoord desired = clamped(aDesired, destLower, destUpper);
+
+ double currentLayerVal = (aRes*aCurrent)/aAppUnitsPerPixel;
+ double desiredLayerVal = (aRes*desired)/aAppUnitsPerPixel;
+ double delta = desiredLayerVal - currentLayerVal;
+ double nearestLayerVal = NS_round(delta) + currentLayerVal;
+
+ // Convert back from PaintedLayer space to appunits relative to the top-left
+ // of the scrolled frame.
+ nscoord aligned =
+ NSToCoordRoundWithClamp(nearestLayerVal*aAppUnitsPerPixel/aRes);
+
+ // Use a bound if it is within the allowed range and closer to desired than
+ // the nearest pixel-aligned value.
+ if (aBoundUpper == destUpper &&
+ static_cast<decltype(Abs(desired))>(aBoundUpper - desired) <
+ Abs(desired - aligned))
+ return aBoundUpper;
+
+ if (aBoundLower == destLower &&
+ static_cast<decltype(Abs(desired))>(desired - aBoundLower) <
+ Abs(aligned - desired))
+ return aBoundLower;
+
+ // Accept the nearest pixel-aligned value if it is within the allowed range.
+ if (aligned >= destLower && aligned <= destUpper)
+ return aligned;
+
+ // Check if opposite pixel boundary fits into allowed range.
+ double oppositeLayerVal =
+ nearestLayerVal + ((nearestLayerVal < desiredLayerVal) ? 1.0 : -1.0);
+ nscoord opposite =
+ NSToCoordRoundWithClamp(oppositeLayerVal*aAppUnitsPerPixel/aRes);
+ if (opposite >= destLower && opposite <= destUpper) {
+ return opposite;
+ }
+
+ // No alignment available.
+ return desired;
+}
+
+/**
+ * Clamp desired scroll position aPt to aBounds and then snap
+ * it to the same layer pixel edges as aCurrent, keeping it within aRange
+ * during snapping. aCurrent is the current scroll position.
+ */
+static nsPoint
+ClampAndAlignWithLayerPixels(const nsPoint& aPt,
+ const nsRect& aBounds,
+ const nsRect& aRange,
+ const nsPoint& aCurrent,
+ nscoord aAppUnitsPerPixel,
+ const gfxSize& aScale)
+{
+ return nsPoint(ClampAndAlignWithPixels(aPt.x, aBounds.x, aBounds.XMost(),
+ aRange.x, aRange.XMost(),
+ aAppUnitsPerPixel, aScale.width,
+ aCurrent.x),
+ ClampAndAlignWithPixels(aPt.y, aBounds.y, aBounds.YMost(),
+ aRange.y, aRange.YMost(),
+ aAppUnitsPerPixel, aScale.height,
+ aCurrent.y));
+}
+
+/* static */ void
+ScrollFrameHelper::ScrollActivityCallback(nsITimer *aTimer, void* anInstance)
+{
+ ScrollFrameHelper* self = static_cast<ScrollFrameHelper*>(anInstance);
+
+ // Fire the synth mouse move.
+ self->mScrollActivityTimer->Cancel();
+ self->mScrollActivityTimer = nullptr;
+ self->mOuter->PresContext()->PresShell()->SynthesizeMouseMove(true);
+}
+
+
+void
+ScrollFrameHelper::ScheduleSyntheticMouseMove()
+{
+ if (!mScrollActivityTimer) {
+ mScrollActivityTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mScrollActivityTimer)
+ return;
+ }
+
+ mScrollActivityTimer->InitWithFuncCallback(
+ ScrollActivityCallback, this, 100, nsITimer::TYPE_ONE_SHOT);
+}
+
+void
+ScrollFrameHelper::NotifyApproximateFrameVisibilityUpdate()
+{
+ mLastUpdateFramesPos = GetScrollPosition();
+ mHadDisplayPortAtLastFrameUpdate =
+ nsLayoutUtils::GetDisplayPort(mOuter->GetContent(),
+ &mDisplayPortAtLastFrameUpdate);
+}
+
+bool
+ScrollFrameHelper::GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort)
+{
+ if (mHadDisplayPortAtLastFrameUpdate) {
+ *aDisplayPort = mDisplayPortAtLastFrameUpdate;
+ }
+ return mHadDisplayPortAtLastFrameUpdate;
+}
+
+void
+ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOrigin)
+{
+ if (aOrigin == nullptr) {
+ // If no origin was specified, we still want to set it to something that's
+ // non-null, so that we can use nullness to distinguish if the frame was scrolled
+ // at all. Default it to some generic placeholder.
+ aOrigin = nsGkAtoms::other;
+ }
+
+ nsPresContext* presContext = mOuter->PresContext();
+ nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+ // 'scale' is our estimate of the scale factor that will be applied
+ // when rendering the scrolled content to its own PaintedLayer.
+ gfxSize scale = FrameLayerBuilder::GetPaintedLayerScaleForFrame(mScrolledFrame);
+ nsPoint curPos = GetScrollPosition();
+ nsPoint alignWithPos = mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)
+ ? curPos : mScrollPosForLayerPixelAlignment;
+ // Try to align aPt with curPos so they have an integer number of layer
+ // pixels between them. This gives us the best chance of scrolling without
+ // having to invalidate due to changes in subpixel rendering.
+ // Note that when we actually draw into a PaintedLayer, the coordinates
+ // that get mapped onto the layer buffer pixels are from the display list,
+ // which are relative to the display root frame's top-left increasing down,
+ // whereas here our coordinates are scroll positions which increase upward
+ // and are relative to the scrollport top-left. This difference doesn't actually
+ // matter since all we are about is that there be an integer number of
+ // layer pixels between pt and curPos.
+ nsPoint pt =
+ ClampAndAlignWithLayerPixels(aPt,
+ GetScrollRangeForClamping(),
+ aRange,
+ alignWithPos,
+ appUnitsPerDevPixel,
+ scale);
+ if (pt == curPos) {
+ return;
+ }
+
+ bool needFrameVisibilityUpdate = mLastUpdateFramesPos == nsPoint(-1,-1);
+
+ nsPoint dist(std::abs(pt.x - mLastUpdateFramesPos.x),
+ std::abs(pt.y - mLastUpdateFramesPos.y));
+ nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
+ nscoord horzAllowance = std::max(scrollPortSize.width / std::max(sHorzScrollFraction, 1),
+ nsPresContext::AppUnitsPerCSSPixel());
+ nscoord vertAllowance = std::max(scrollPortSize.height / std::max(sVertScrollFraction, 1),
+ nsPresContext::AppUnitsPerCSSPixel());
+ if (dist.x >= horzAllowance || dist.y >= vertAllowance) {
+ needFrameVisibilityUpdate = true;
+ }
+
+ // notify the listeners.
+ for (uint32_t i = 0; i < mListeners.Length(); i++) {
+ mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
+ }
+
+ nsRect oldDisplayPort;
+ nsIContent* content = mOuter->GetContent();
+ nsLayoutUtils::GetHighResolutionDisplayPort(content, &oldDisplayPort);
+ oldDisplayPort.MoveBy(-mScrolledFrame->GetPosition());
+
+ // Update frame position for scrolling
+ mScrolledFrame->SetPosition(mScrollPort.TopLeft() - pt);
+
+ // If |mLastScrollOrigin| is already set to something that can clobber APZ's
+ // scroll offset, then we don't want to change it to something that can't.
+ // If we allowed this, then we could end up in a state where APZ ignores
+ // legitimate scroll offset updates because the origin has been masked by
+ // a later change within the same refresh driver tick.
+ bool isScrollOriginDowngrade =
+ nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin) &&
+ !nsLayoutUtils::CanScrollOriginClobberApz(aOrigin);
+ bool allowScrollOriginChange = mAllowScrollOriginDowngrade ||
+ !isScrollOriginDowngrade;
+ if (allowScrollOriginChange) {
+ mLastScrollOrigin = aOrigin;
+ mAllowScrollOriginDowngrade = false;
+ }
+ mLastSmoothScrollOrigin = nullptr;
+ mScrollGeneration = ++sScrollGenerationCounter;
+
+ ScrollVisual();
+
+ bool schedulePaint = true;
+ if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && gfxPrefs::APZPaintSkipping()) {
+ // If APZ is enabled with paint-skipping, there are certain conditions in
+ // which we can skip paints:
+ // 1) If APZ triggered this scroll, and the tile-aligned displayport is
+ // unchanged.
+ // 2) If non-APZ triggered this scroll, but we can handle it by just asking
+ // APZ to update the scroll position. Again we make this conditional on
+ // the tile-aligned displayport being unchanged.
+ // We do the displayport check first since it's common to all scenarios,
+ // and then if the displayport is unchanged, we check if APZ triggered,
+ // or can handle, this scroll. If so, we set schedulePaint to false and
+ // skip the paint.
+ // Because of bug 1264297, we also don't do paint-skipping for elements with
+ // perspective, because the displayport may not have captured everything
+ // that needs to be painted. So even if the final tile-aligned displayport
+ // is the same, we force a repaint for these elements. Bug 1254260 tracks
+ // fixing this properly.
+ nsRect displayPort;
+ bool usingDisplayPort =
+ nsLayoutUtils::GetHighResolutionDisplayPort(content, &displayPort);
+ displayPort.MoveBy(-mScrolledFrame->GetPosition());
+
+ PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d clip %d bglocal %d\n",
+ Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
+ usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
+ mScrollableByAPZ, HasPluginFrames(), HasPerspective(),
+ mScrollsClipOnUnscrolledOutOfFlow, HasBgAttachmentLocal());
+ if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) &&
+ !HasPerspective() && !mScrollsClipOnUnscrolledOutOfFlow &&
+ !HasBgAttachmentLocal()) {
+ bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
+ bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
+ if (!apzDisabled && !HasPluginFrames()) {
+ if (LastScrollOrigin() == nsGkAtoms::apz) {
+ schedulePaint = false;
+ PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
+ } else if (mScrollableByAPZ) {
+ nsIWidget* widget = presContext->GetNearestWidget();
+ LayerManager* manager = widget ? widget->GetLayerManager() : nullptr;
+ if (manager) {
+ mozilla::layers::FrameMetrics::ViewID id;
+ DebugOnly<bool> success = nsLayoutUtils::FindIDFor(content, &id);
+ MOZ_ASSERT(success); // we have a displayport, we better have an ID
+
+ // Schedule an empty transaction to carry over the scroll offset update,
+ // instead of a full transaction. This empty transaction might still get
+ // squashed into a full transaction if something happens to trigger one.
+ schedulePaint = false;
+ manager->SetPendingScrollUpdateForNextTransaction(id,
+ { mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()) });
+ mOuter->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
+ PAINT_SKIP_LOG("Skipping due to APZ-forwarded main-thread scroll\n");
+ }
+ }
+ }
+ }
+ }
+
+ if (schedulePaint) {
+ mOuter->SchedulePaint();
+
+ if (needFrameVisibilityUpdate) {
+ presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
+ }
+ }
+
+ if (mOuter->ChildrenHavePerspective()) {
+ // The overflow areas of descendants may depend on the scroll position,
+ // so ensure they get updated.
+
+ // First we recompute the overflow areas of the transformed children
+ // that use the perspective. FinishAndStoreOverflow only calls this
+ // if the size changes, so we need to do it manually.
+ mOuter->RecomputePerspectiveChildrenOverflow(mOuter);
+
+ // Update the overflow for the scrolled frame to take any changes from the
+ // children into account.
+ mScrolledFrame->UpdateOverflow();
+
+ // Update the overflow for the outer so that we recompute scrollbars.
+ mOuter->UpdateOverflow();
+ }
+
+ ScheduleSyntheticMouseMove();
+
+ { // scope the AutoScrollbarRepaintSuppression
+ AutoScrollbarRepaintSuppression repaintSuppression(this, !schedulePaint);
+ nsWeakFrame weakFrame(mOuter);
+ UpdateScrollbarPosition();
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ }
+
+ PostScrollEvent();
+
+ // notify the listeners.
+ for (uint32_t i = 0; i < mListeners.Length(); i++) {
+ mListeners[i]->ScrollPositionDidChange(pt.x, pt.y);
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
+ if (docShell) {
+ docShell->NotifyScrollObservers();
+ }
+}
+
+static int32_t
+MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder)
+{
+ int32_t maxZIndex = -1;
+ for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
+ maxZIndex = std::max(maxZIndex, item->ZIndex());
+ }
+ return maxZIndex;
+}
+
+// Finds the max z-index of the items in aList that meet the following conditions
+// 1) have z-index auto or z-index >= 0.
+// 2) aFrame is a proper ancestor of the item's frame.
+// Returns -1 if there is no such item.
+static int32_t
+MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList, nsIFrame* aFrame)
+{
+ int32_t maxZIndex = -1;
+ for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
+ nsIFrame* itemFrame = item->Frame();
+ // Perspective items return the scroll frame as their Frame(), so consider
+ // their TransformFrame() instead.
+ if (item->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
+ itemFrame = static_cast<nsDisplayPerspective*>(item)->TransformFrame();
+ }
+ if (nsLayoutUtils::IsProperAncestorFrame(aFrame, itemFrame)) {
+ maxZIndex = std::max(maxZIndex, item->ZIndex());
+ }
+ }
+ return maxZIndex;
+}
+
+template<class T>
+static void
+AppendInternalItemToTop(const nsDisplayListSet& aLists,
+ T* aItem,
+ int32_t aZIndex)
+{
+ if (aZIndex >= 0) {
+ aItem->SetOverrideZIndex(aZIndex);
+ aLists.PositionedDescendants()->AppendNewToTop(aItem);
+ } else {
+ aLists.Content()->AppendNewToTop(aItem);
+ }
+}
+
+static const uint32_t APPEND_OWN_LAYER = 0x1;
+static const uint32_t APPEND_POSITIONED = 0x2;
+static const uint32_t APPEND_SCROLLBAR_CONTAINER = 0x4;
+
+static void
+AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
+ nsDisplayList* aSource, nsIFrame* aSourceFrame, uint32_t aFlags)
+{
+ if (aSource->IsEmpty())
+ return;
+
+ nsDisplayWrapList* newItem;
+ if (aFlags & APPEND_OWN_LAYER) {
+ uint32_t flags = (aFlags & APPEND_SCROLLBAR_CONTAINER)
+ ? nsDisplayOwnLayer::SCROLLBAR_CONTAINER
+ : 0;
+ newItem = new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource, flags);
+ } else {
+ newItem = new (aBuilder) nsDisplayWrapList(aBuilder, aSourceFrame, aSource);
+ }
+
+ if (aFlags & APPEND_POSITIONED) {
+ // We want overlay scrollbars to always be on top of the scrolled content,
+ // but we don't want them to unnecessarily cover overlapping elements from
+ // outside our scroll frame.
+ int32_t zIndex = MaxZIndexInList(aLists.PositionedDescendants(), aBuilder);
+ AppendInternalItemToTop(aLists, newItem, zIndex);
+ } else {
+ aLists.BorderBackground()->AppendNewToTop(newItem);
+ }
+}
+
+struct HoveredStateComparator
+{
+ bool Equals(nsIFrame* A, nsIFrame* B) const {
+ bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::hover);
+ bool bHovered = B->GetContent()->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::hover);
+ return aHovered == bHovered;
+ }
+ bool LessThan(nsIFrame* A, nsIFrame* B) const {
+ bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::hover);
+ bool bHovered = B->GetContent()->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::hover);
+ return !aHovered && bHovered;
+ }
+};
+
+void
+ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists,
+ bool aCreateLayer,
+ bool aPositioned)
+{
+ nsITheme* theme = mOuter->PresContext()->GetTheme();
+ if (theme &&
+ theme->ShouldHideScrollbars()) {
+ return;
+ }
+
+ bool overlayScrollbars =
+ LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0;
+
+ AutoTArray<nsIFrame*, 3> scrollParts;
+ for (nsIFrame* kid : mOuter->PrincipalChildList()) {
+ if (kid == mScrolledFrame ||
+ (kid->IsAbsPosContainingBlock() || overlayScrollbars) != aPositioned)
+ continue;
+
+ scrollParts.AppendElement(kid);
+ }
+ if (scrollParts.IsEmpty()) {
+ return;
+ }
+
+ // We can't check will-change budget during display list building phase.
+ // This means that we will build scroll bar layers for out of budget
+ // will-change: scroll position.
+ mozilla::layers::FrameMetrics::ViewID scrollTargetId = IsMaybeScrollingActive()
+ ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
+ : mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
+
+ scrollParts.Sort(HoveredStateComparator());
+
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+ // Don't let scrollparts extent outside our frame's border-box, if these are
+ // viewport scrollbars. They would create layerization problems. This wouldn't
+ // normally be an issue but themes can add overflow areas to scrollbar parts.
+ if (mIsRoot) {
+ clipState.ClipContentDescendants(
+ mOuter->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mOuter));
+ }
+
+ for (uint32_t i = 0; i < scrollParts.Length(); ++i) {
+ uint32_t flags = 0;
+ uint32_t appendToTopFlags = 0;
+ if (scrollParts[i] == mVScrollbarBox) {
+ flags |= nsDisplayOwnLayer::VERTICAL_SCROLLBAR;
+ appendToTopFlags |= APPEND_SCROLLBAR_CONTAINER;
+ }
+ if (scrollParts[i] == mHScrollbarBox) {
+ flags |= nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR;
+ appendToTopFlags |= APPEND_SCROLLBAR_CONTAINER;
+ }
+
+ // The display port doesn't necessarily include the scrollbars, so just
+ // include all of the scrollbars if we are in a RCD-RSF. We only do
+ // this for the root scrollframe of the root content document, which is
+ // zoomable, and where the scrollbar sizes are bounded by the widget.
+ nsRect dirty = mIsRoot && mOuter->PresContext()->IsRootContentDocument()
+ ? scrollParts[i]->GetVisualOverflowRectRelativeToParent()
+ : aDirtyRect;
+ nsDisplayListBuilder::AutoBuildingDisplayList
+ buildingForChild(aBuilder, scrollParts[i],
+ dirty + mOuter->GetOffsetTo(scrollParts[i]), true);
+
+ // Always create layers for overlay scrollbars so that we don't create a
+ // giant layer covering the whole scrollport if both scrollbars are visible.
+ bool isOverlayScrollbar = (flags != 0) && overlayScrollbars;
+ bool createLayer = aCreateLayer || isOverlayScrollbar;
+
+ nsDisplayListBuilder::AutoCurrentScrollbarInfoSetter
+ infoSetter(aBuilder, scrollTargetId, flags, createLayer);
+ nsDisplayListCollection partList;
+ mOuter->BuildDisplayListForChild(
+ aBuilder, scrollParts[i], dirty, partList,
+ nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
+
+ if (createLayer) {
+ appendToTopFlags |= APPEND_OWN_LAYER;
+ }
+ if (aPositioned) {
+ appendToTopFlags |= APPEND_POSITIONED;
+ }
+
+ // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into
+ // partList.PositionedDescendants().
+ ::AppendToTop(aBuilder, aLists,
+ partList.PositionedDescendants(), scrollParts[i],
+ appendToTopFlags);
+ }
+}
+
+/* static */ bool ScrollFrameHelper::sFrameVisPrefsCached = false;
+/* static */ uint32_t ScrollFrameHelper::sHorzExpandScrollPort = 0;
+/* static */ uint32_t ScrollFrameHelper::sVertExpandScrollPort = 1;
+/* static */ int32_t ScrollFrameHelper::sHorzScrollFraction = 2;
+/* static */ int32_t ScrollFrameHelper::sVertScrollFraction = 2;
+
+/* static */ void
+ScrollFrameHelper::EnsureFrameVisPrefsCached()
+{
+ if (!sFrameVisPrefsCached) {
+ Preferences::AddUintVarCache(&sHorzExpandScrollPort,
+ "layout.framevisibility.numscrollportwidths", (uint32_t)0);
+ Preferences::AddUintVarCache(&sVertExpandScrollPort,
+ "layout.framevisibility.numscrollportheights", 1);
+
+ Preferences::AddIntVarCache(&sHorzScrollFraction,
+ "layout.framevisibility.amountscrollbeforeupdatehorizontal", 2);
+ Preferences::AddIntVarCache(&sVertScrollFraction,
+ "layout.framevisibility.amountscrollbeforeupdatevertical", 2);
+
+ sFrameVisPrefsCached = true;
+ }
+}
+
+nsRect
+ScrollFrameHelper::ExpandRectToNearlyVisible(const nsRect& aRect) const
+{
+ // We don't want to expand a rect in a direction that we can't scroll, so we
+ // check the scroll range.
+ nsRect scrollRange = GetScrollRangeForClamping();
+ nsPoint scrollPos = GetScrollPosition();
+ nsMargin expand(0, 0, 0, 0);
+
+ nscoord vertShift = sVertExpandScrollPort * aRect.height;
+ if (scrollRange.y < scrollPos.y) {
+ expand.top = vertShift;
+ }
+ if (scrollPos.y < scrollRange.YMost()) {
+ expand.bottom = vertShift;
+ }
+
+ nscoord horzShift = sHorzExpandScrollPort * aRect.width;
+ if (scrollRange.x < scrollPos.x) {
+ expand.left = horzShift;
+ }
+ if (scrollPos.x < scrollRange.XMost()) {
+ expand.right = horzShift;
+ }
+
+ nsRect rect = aRect;
+ rect.Inflate(expand);
+ return rect;
+}
+
+static bool
+ShouldBeClippedByFrame(nsIFrame* aClipFrame, nsIFrame* aClippedFrame)
+{
+ return nsLayoutUtils::IsProperAncestorFrame(aClipFrame, aClippedFrame);
+}
+
+static void
+ClipItemsExceptCaret(nsDisplayList* aList,
+ nsDisplayListBuilder* aBuilder,
+ nsIFrame* aClipFrame,
+ const DisplayItemClip* aNonCaretClip,
+ const DisplayItemScrollClip* aNonCaretScrollClip)
+{
+ for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
+ if (!ShouldBeClippedByFrame(aClipFrame, i->Frame())) {
+ continue;
+ }
+
+ if (i->GetType() != nsDisplayItem::TYPE_CARET) {
+ bool unused;
+ nsRect bounds = i->GetBounds(aBuilder, &unused);
+ if (aNonCaretClip && aNonCaretClip->IsRectAffectedByClip(bounds)) {
+ DisplayItemClip newClip;
+ newClip.IntersectWith(i->GetClip());
+ newClip.IntersectWith(*aNonCaretClip);
+ i->SetClip(aBuilder, newClip);
+ }
+
+ if (aNonCaretScrollClip) {
+ const DisplayItemScrollClip* currentScrollClip = i->ScrollClip();
+ MOZ_ASSERT(DisplayItemScrollClip::IsAncestor(aNonCaretScrollClip->mParent,
+ currentScrollClip));
+
+ // Overwrite the existing scroll clip with aNonCaretScrollClip, unless
+ // the current scroll clip is deeper than aNonCaretScrollClip (which
+ // means that the display item is nested inside another scroll frame).
+ if (!currentScrollClip ||
+ currentScrollClip->mParent == aNonCaretScrollClip->mParent) {
+ i->SetScrollClip(aNonCaretScrollClip);
+ }
+ }
+ }
+ nsDisplayList* children = i->GetSameCoordinateSystemChildren();
+ if (children) {
+ ClipItemsExceptCaret(children, aBuilder, aClipFrame, aNonCaretClip,
+ aNonCaretScrollClip);
+ }
+ }
+}
+
+static void
+ClipListsExceptCaret(nsDisplayListCollection* aLists,
+ nsDisplayListBuilder* aBuilder,
+ nsIFrame* aClipFrame,
+ const DisplayItemClip* aNonCaretClip,
+ const DisplayItemScrollClip* aNonCaretScrollClip)
+{
+ ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+ ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+ ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+ ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+ ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+ ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, aNonCaretClip, aNonCaretScrollClip);
+}
+
+void
+ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (aBuilder->IsForFrameVisibility()) {
+ NotifyApproximateFrameVisibilityUpdate();
+ }
+
+ mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ if (aBuilder->IsPaintingToWindow()) {
+ mScrollPosAtLastPaint = GetScrollPosition();
+ if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
+ MarkNotRecentlyScrolled();
+ }
+ if (IsMaybeScrollingActive()) {
+ if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
+ mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
+ }
+ } else {
+ mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
+ }
+ }
+
+ // It's safe to get this value before the DecideScrollableLayer call below
+ // because that call cannot create a displayport for root scroll frames,
+ // and hence it cannot create an ignore scroll frame.
+ bool ignoringThisScrollFrame =
+ aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping();
+
+ // Overflow clipping can never clip frames outside our subtree, so there
+ // is no need to worry about whether we are a moving frame that might clip
+ // non-moving frames.
+ // Not all our descendants will be clipped by overflow clipping, but all
+ // the ones that aren't clipped will be out of flow frames that have already
+ // had dirty rects saved for them by their parent frames calling
+ // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
+ // dirty rect here.
+ nsRect dirtyRect = aDirtyRect;
+ if (!ignoringThisScrollFrame) {
+ dirtyRect = dirtyRect.Intersect(mScrollPort);
+ }
+
+ Unused << DecideScrollableLayer(aBuilder, &dirtyRect,
+ /* aAllowCreateDisplayPort = */ !mIsRoot);
+
+ bool usingDisplayPort = aBuilder->IsPaintingToWindow() &&
+ nsLayoutUtils::HasDisplayPort(mOuter->GetContent());
+
+ if (aBuilder->IsForFrameVisibility()) {
+ // We expand the dirty rect to catch frames just outside of the scroll port.
+ // We use the dirty rect instead of the whole scroll port to prevent
+ // too much expansion in the presence of very large (bigger than the
+ // viewport) scroll ports.
+ dirtyRect = ExpandRectToNearlyVisible(dirtyRect);
+ }
+
+ // We put non-overlay scrollbars in their own layers when this is the root
+ // scroll frame and we are a toplevel content document. In this situation,
+ // the scrollbar(s) would normally be assigned their own layer anyway, since
+ // they're not scrolled with the rest of the document. But when both
+ // scrollbars are visible, the layer's visible rectangle would be the size
+ // of the viewport, so most layer implementations would create a layer buffer
+ // that's much larger than necessary. Creating independent layers for each
+ // scrollbar works around the problem.
+ bool createLayersForScrollbars = mIsRoot &&
+ mOuter->PresContext()->IsRootContentDocument();
+
+ if (ignoringThisScrollFrame) {
+ // Root scrollframes have FrameMetrics and clipping on their container
+ // layers, so don't apply clipping again.
+ mAddClipRectToLayer = false;
+
+ // If we are a root scroll frame that has a display port we want to add
+ // scrollbars, they will be children of the scrollable layer, but they get
+ // adjusted by the APZC automatically.
+ bool addScrollBars = mIsRoot && usingDisplayPort && !aBuilder->IsForEventDelivery();
+
+ if (addScrollBars) {
+ // Add classic scrollbars.
+ AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
+ createLayersForScrollbars, false);
+ }
+
+ // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
+ // The scrolled frame shouldn't have its own background/border, so we
+ // can just pass aLists directly.
+ mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame,
+ dirtyRect, aLists);
+
+ if (addScrollBars) {
+ // Add overlay scrollbars.
+ AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
+ createLayersForScrollbars, true);
+ }
+
+ return;
+ }
+
+ // Root scrollframes have FrameMetrics and clipping on their container
+ // layers, so don't apply clipping again.
+ mAddClipRectToLayer =
+ !(mIsRoot && mOuter->PresContext()->PresShell()->GetIsViewportOverridden());
+
+ // Whether we might want to build a scrollable layer for this scroll frame
+ // at some point in the future. This controls whether we add the information
+ // to the layer tree (a scroll info layer if necessary, and add the right
+ // area to the dispatch to content layer event regions) necessary to activate
+ // a scroll frame so it creates a scrollable layer.
+ bool couldBuildLayer = false;
+ if (mWillBuildScrollableLayer) {
+ couldBuildLayer = true;
+ } else {
+ couldBuildLayer =
+ nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
+ WantAsyncScroll() &&
+ // If we are using containers for root frames, and we are the root
+ // scroll frame for the display root, then we don't need a scroll
+ // info layer. nsDisplayList::PaintForFrame already calls
+ // ComputeFrameMetrics for us.
+ (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) ||
+ (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()));
+ }
+
+ // Now display the scrollbars and scrollcorner. These parts are drawn
+ // in the border-background layer, on top of our own background and
+ // borders and underneath borders and backgrounds of later elements
+ // in the tree.
+ // Note that this does not apply for overlay scrollbars; those are drawn
+ // in the positioned-elements layer on top of everything else by the call
+ // to AppendScrollPartsTo(..., true) further down.
+ AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
+ createLayersForScrollbars, false);
+
+ const nsStyleDisplay* disp = mOuter->StyleDisplay();
+ if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
+ aBuilder->AddToWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize());
+ }
+
+ mScrollParentID = aBuilder->GetCurrentScrollParentId();
+
+ Maybe<nsRect> contentBoxClipForCaret;
+ Maybe<nsRect> contentBoxClipForNonCaretContent;
+ if (MOZ_UNLIKELY(mOuter->StyleDisplay()->mOverflowClipBox ==
+ NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
+ // We only clip if there is *scrollable* overflow, to avoid clipping
+ // *visual* overflow unnecessarily.
+ nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
+ nsRect so = mScrolledFrame->GetScrollableOverflowRect();
+ if (clipRect.width != so.width || clipRect.height != so.height ||
+ so.x < 0 || so.y < 0) {
+ clipRect.Deflate(mOuter->GetUsedPadding());
+ contentBoxClipForNonCaretContent = Some(clipRect);
+ if (nsIFrame* caretFrame = aBuilder->GetCaretFrame()) {
+ // Avoid clipping it in a zero-height line box (heuristic only).
+ if (caretFrame->GetRect().height != 0) {
+ nsRect caretRect = aBuilder->GetCaretRect();
+ // Allow the caret to stick out of the content box clip by half the
+ // caret height on the top, and its full width on the right.
+ clipRect.Inflate(nsMargin(caretRect.height / 2, caretRect.width, 0, 0));
+ contentBoxClipForCaret = Some(clipRect);
+ }
+ }
+ }
+ }
+
+ nsIScrollableFrame* sf = do_QueryFrame(mOuter);
+ MOZ_ASSERT(sf);
+
+ nsDisplayListCollection scrolledContent;
+ {
+ // Note that setting the current scroll parent id here means that positioned children
+ // of this scroll info layer will pick up the scroll info layer as their scroll handoff
+ // parent. This is intentional because that is what happens for positioned children
+ // of scroll layers, and we want to maintain consistent behaviour between scroll layers
+ // and scroll info layers.
+ nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
+ aBuilder,
+ couldBuildLayer && mScrolledFrame->GetContent()
+ ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
+ : aBuilder->GetCurrentScrollParentId());
+
+ nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
+ // Our override of GetBorderRadii ensures we never have a radius at
+ // the corners where we have a scrollbar.
+ nscoord radii[8];
+ bool haveRadii = mOuter->GetPaddingBoxBorderRadii(radii);
+ if (mIsRoot) {
+ clipRect.SizeTo(nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
+ if (mOuter->PresContext()->IsRootContentDocument()) {
+ double res = mOuter->PresContext()->PresShell()->GetResolution();
+ clipRect.width = NSToCoordRound(clipRect.width / res);
+ clipRect.height = NSToCoordRound(clipRect.height / res);
+ }
+ }
+
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+ if (mClipAllDescendants) {
+ clipState.ClipContentDescendants(clipRect, haveRadii ? radii : nullptr);
+ } else {
+ clipState.ClipContainingBlockDescendants(clipRect, haveRadii ? radii : nullptr);
+ }
+
+ DisplayItemScrollClip* inactiveScrollClip = nullptr;
+
+ {
+ DisplayListClipState::AutoSaveRestore contentBoxClipState(aBuilder);
+ if (contentBoxClipForCaret) {
+ if (mClipAllDescendants) {
+ contentBoxClipState.ClipContentDescendants(*contentBoxClipForCaret);
+ } else {
+ contentBoxClipState.ClipContainingBlockDescendants(*contentBoxClipForCaret);
+ }
+ }
+
+ DisplayListClipState::AutoSaveRestore clipStateForScrollClip(aBuilder);
+ if (mWillBuildScrollableLayer) {
+ if (mClipAllDescendants) {
+ clipStateForScrollClip.TurnClipIntoScrollClipForContentDescendants(aBuilder, sf);
+ } else {
+ clipStateForScrollClip.TurnClipIntoScrollClipForContainingBlockDescendants(aBuilder, sf);
+ }
+ } else {
+ // Create a scroll clip anyway because we might need to activate for scroll handoff.
+ if (mClipAllDescendants) {
+ inactiveScrollClip = clipStateForScrollClip.InsertInactiveScrollClipForContentDescendants(aBuilder, sf);
+ } else {
+ inactiveScrollClip = clipStateForScrollClip.InsertInactiveScrollClipForContainingBlockDescendants(aBuilder, sf);
+ }
+ MOZ_ASSERT(!inactiveScrollClip->mIsAsyncScrollable);
+ }
+
+ // Clip our contents to the unsnapped scrolled rect. This makes sure that
+ // we don't have display items over the subpixel seam at the edge of the
+ // scrolled area.
+ DisplayListClipState::AutoSaveRestore scrolledRectClipState(aBuilder);
+ nsRect scrolledRectClip =
+ GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
+ mScrollPort.Size()) + mScrolledFrame->GetPosition();
+ if (usingDisplayPort) {
+ // Clip the contents to the display port.
+ // The dirty rect already acts kind of like a clip, in that
+ // FrameLayerBuilder intersects item bounds and opaque regions with
+ // it, but it doesn't have the consistent snapping behavior of a
+ // true clip.
+ // For a case where this makes a difference, imagine the following
+ // scenario: The display port has an edge that falls on a fractional
+ // layer pixel, and there's an opaque display item that covers the
+ // whole display port up until that fractional edge, and there is a
+ // transparent display item that overlaps the edge. We want to prevent
+ // this transparent item from enlarging the scrolled layer's visible
+ // region beyond its opaque region. The dirty rect doesn't do that -
+ // it gets rounded out, whereas a true clip gets rounded to nearest
+ // pixels.
+ // If there is no display port, we don't need this because the clip
+ // from the scroll port is still applied.
+ scrolledRectClip = scrolledRectClip.Intersect(dirtyRect);
+ }
+ scrolledRectClipState.ClipContainingBlockDescendants(
+ scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
+
+ mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, scrolledContent);
+ }
+
+ if (contentBoxClipForNonCaretContent) {
+ DisplayListClipState::AutoSaveRestore contentBoxClipState(aBuilder);
+ if (mClipAllDescendants) {
+ contentBoxClipState.ClipContentDescendants(*contentBoxClipForNonCaretContent);
+ } else {
+ contentBoxClipState.ClipContainingBlockDescendants(*contentBoxClipForNonCaretContent);
+ }
+
+ DisplayListClipState::AutoSaveRestore clipStateForScrollClip(aBuilder);
+ if (mWillBuildScrollableLayer) {
+ if (mClipAllDescendants) {
+ clipStateForScrollClip.TurnClipIntoScrollClipForContentDescendants(aBuilder, sf);
+ } else {
+ clipStateForScrollClip.TurnClipIntoScrollClipForContainingBlockDescendants(aBuilder, sf);
+ }
+ }
+ const DisplayItemClip* clipNonCaret = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
+ const DisplayItemScrollClip* scrollClipNonCaret = aBuilder->ClipState().GetCurrentInnermostScrollClip();
+ ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame,
+ clipNonCaret, scrollClipNonCaret);
+ }
+
+ if (aBuilder->IsPaintingToWindow()) {
+ mIsScrollParent = idSetter.ShouldForceLayerForScrollParent();
+ }
+ if (idSetter.ShouldForceLayerForScrollParent() &&
+ !gfxPrefs::LayoutUseContainersForRootFrames())
+ {
+ // Note that forcing layerization of scroll parents follows the scroll
+ // handoff chain which is subject to the out-of-flow-frames caveat noted
+ // above (where the idSetter variable is created).
+ //
+ // This is not compatible when using containes for root scrollframes.
+ MOZ_ASSERT(couldBuildLayer && mScrolledFrame->GetContent());
+ if (inactiveScrollClip) {
+ inactiveScrollClip->mIsAsyncScrollable = true;
+ }
+ if (!mWillBuildScrollableLayer) {
+ // Set a displayport so next paint we don't have to force layerization
+ // after the fact.
+ nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
+ mOuter->PresContext()->PresShell(),
+ ScreenMargin(),
+ 0,
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ // Call DecideScrollableLayer to recompute mWillBuildScrollableLayer and
+ // recompute the current animated geometry root if needed.
+ // It's too late to change the dirty rect so pass a copy.
+ nsRect copyOfDirtyRect = dirtyRect;
+ Unused << DecideScrollableLayer(aBuilder, &copyOfDirtyRect,
+ /* aAllowCreateDisplayPort = */ false);
+ }
+ }
+ }
+
+ if (mWillBuildScrollableLayer) {
+ aBuilder->ForceLayerForScrollParent();
+ }
+
+ if (couldBuildLayer) {
+ // Make sure that APZ will dispatch events back to content so we can create
+ // a displayport for this frame. We'll add the item later on.
+ nsDisplayLayerEventRegions* inactiveRegionItem = nullptr;
+ if (aBuilder->IsPaintingToWindow() &&
+ !mWillBuildScrollableLayer &&
+ aBuilder->IsBuildingLayerEventRegions())
+ {
+ inactiveRegionItem = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, mScrolledFrame);
+ inactiveRegionItem->AddInactiveScrollPort(mScrollPort + aBuilder->ToReferenceFrame(mOuter));
+ }
+
+ if (inactiveRegionItem) {
+ int32_t zIndex =
+ MaxZIndexInListOfItemsContainedInFrame(scrolledContent.PositionedDescendants(), mOuter);
+ AppendInternalItemToTop(scrolledContent, inactiveRegionItem, zIndex);
+ }
+
+ if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) {
+ aBuilder->AppendNewScrollInfoItemForHoisting(
+ new (aBuilder) nsDisplayScrollInfoLayer(aBuilder, mScrolledFrame,
+ mOuter));
+ }
+ }
+ // Now display overlay scrollbars and the resizer, if we have one.
+ AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent,
+ createLayersForScrollbars, true);
+ scrolledContent.MoveTo(aLists);
+}
+
+bool
+ScrollFrameHelper::DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+ nsRect* aDirtyRect,
+ bool aAllowCreateDisplayPort)
+{
+ // Save and check if this changes so we can recompute the current agr.
+ bool oldWillBuildScrollableLayer = mWillBuildScrollableLayer;
+
+ bool wasUsingDisplayPort = false;
+ bool usingDisplayPort = false;
+ nsIContent* content = mOuter->GetContent();
+ if (aBuilder->IsPaintingToWindow()) {
+ wasUsingDisplayPort = nsLayoutUtils::HasDisplayPort(content);
+
+ if (aAllowCreateDisplayPort) {
+ nsLayoutUtils::MaybeCreateDisplayPort(*aBuilder, mOuter);
+
+ nsRect displayportBase = *aDirtyRect;
+ nsPresContext* pc = mOuter->PresContext();
+ if (mIsRoot && (pc->IsRootContentDocument() || !pc->GetParentPresContext())) {
+ displayportBase =
+ nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
+ } else {
+ // Make the displayport base equal to the dirty rect restricted to
+ // the scrollport and the root composition bounds, relative to the
+ // scrollport.
+ displayportBase = aDirtyRect->Intersect(mScrollPort);
+
+ // Only restrict to the root composition bounds if necessary,
+ // as the required coordinate transformation is expensive.
+ if (wasUsingDisplayPort) {
+ const nsPresContext* rootPresContext =
+ pc->GetToplevelContentDocumentPresContext();
+ if (!rootPresContext) {
+ rootPresContext = pc->GetRootPresContext();
+ }
+ if (rootPresContext) {
+ const nsIPresShell* const rootPresShell = rootPresContext->PresShell();
+ nsIFrame* rootFrame = rootPresShell->GetRootScrollFrame();
+ if (!rootFrame) {
+ rootFrame = rootPresShell->GetRootFrame();
+ }
+ if (rootFrame) {
+ nsRect rootCompBounds =
+ nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootFrame));
+
+ // If rootFrame is the RCD-RSF then CalculateCompositionSizeForFrame
+ // did not take the document's resolution into account, so we must.
+ if (rootPresContext->IsRootContentDocument() &&
+ rootFrame == rootPresShell->GetRootScrollFrame()) {
+ rootCompBounds = rootCompBounds.RemoveResolution(rootPresShell->GetResolution());
+ }
+
+ // We want to convert the root composition bounds from the coordinate
+ // space of |rootFrame| to the coordinate space of |mOuter|. We do
+ // that with the TransformRect call below. However, since we care
+ // about the root composition bounds relative to what the user is
+ // actually seeing, we also need to incorporate the APZ callback
+ // transforms into this. Most of the time those transforms are
+ // negligible, but in some cases (e.g. when a zoom is applied on
+ // an overflow:hidden document) it is not (see bug 1280013).
+ // XXX: Eventually we may want to create a modified version of
+ // TransformRect that includes the APZ callback transforms
+ // directly.
+ nsLayoutUtils::TransformRect(rootFrame, mOuter, rootCompBounds);
+ rootCompBounds += CSSPoint::ToAppUnits(
+ nsLayoutUtils::GetCumulativeApzCallbackTransform(mOuter));
+
+ displayportBase = displayportBase.Intersect(rootCompBounds);
+ }
+ }
+ }
+
+ displayportBase -= mScrollPort.TopLeft();
+ }
+
+ nsLayoutUtils::SetDisplayPortBase(mOuter->GetContent(), displayportBase);
+ }
+
+ // If we don't have aAllowCreateDisplayPort == true then should have already
+ // been called with aAllowCreateDisplayPort == true which should have set a
+ // displayport base.
+ MOZ_ASSERT(content->GetProperty(nsGkAtoms::DisplayPortBase));
+ nsRect displayPort;
+ usingDisplayPort =
+ nsLayoutUtils::GetDisplayPort(content, &displayPort, RelativeTo::ScrollFrame);
+
+ if (usingDisplayPort) {
+ // Override the dirty rectangle if the displayport has been set.
+ *aDirtyRect = displayPort;
+ } else if (mIsRoot) {
+ // The displayPort getter takes care of adjusting for resolution. So if
+ // we have resolution but no displayPort then we need to adjust for
+ // resolution here.
+ nsIPresShell* presShell = mOuter->PresContext()->PresShell();
+ *aDirtyRect = aDirtyRect->RemoveResolution(
+ presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f);
+ }
+ }
+
+ // Since making new layers is expensive, only create a scrollable layer
+ // for some scroll frames.
+ // When a displayport is being used, force building of a layer so that
+ // the compositor can find the scrollable layer for async scrolling.
+ // If the element is marked 'scrollgrab', also force building of a layer
+ // so that APZ can implement scroll grabbing.
+ mWillBuildScrollableLayer = usingDisplayPort || nsContentUtils::HasScrollgrab(content);
+
+ // The cached animated geometry root for the display builder is out of
+ // date if we just introduced a new animated geometry root.
+ if ((oldWillBuildScrollableLayer != mWillBuildScrollableLayer) || (wasUsingDisplayPort != usingDisplayPort)) {
+ aBuilder->RecomputeCurrentAnimatedGeometryRoot();
+ }
+
+ if (gfxPrefs::LayoutUseContainersForRootFrames() && mWillBuildScrollableLayer && mIsRoot) {
+ mIsScrollableLayerInRootContainer = true;
+ }
+
+ return mWillBuildScrollableLayer;
+}
+
+
+Maybe<ScrollMetadata>
+ScrollFrameHelper::ComputeScrollMetadata(Layer* aLayer,
+ nsIFrame* aContainerReferenceFrame,
+ const ContainerLayerParameters& aParameters,
+ const DisplayItemClip* aClip) const
+{
+ if (!mWillBuildScrollableLayer || mIsScrollableLayerInRootContainer) {
+ return Nothing();
+ }
+
+ nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame);
+
+ Maybe<nsRect> parentLayerClip;
+ // For containerful frames, the clip is on the container layer.
+ if (aClip &&
+ (!gfxPrefs::LayoutUseContainersForRootFrames() || mAddClipRectToLayer)) {
+ parentLayerClip = Some(aClip->GetClipRect());
+ }
+
+ bool isRootContent = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
+ bool thisScrollFrameUsesAsyncScrolling = nsLayoutUtils::UsesAsyncScrolling(mOuter);
+ if (!thisScrollFrameUsesAsyncScrolling) {
+ if (parentLayerClip) {
+ // If APZ is not enabled, we still need the displayport to be clipped
+ // in the compositor.
+ ParentLayerIntRect displayportClip =
+ ViewAs<ParentLayerPixel>(
+ parentLayerClip->ScaleToNearestPixels(
+ aParameters.mXScale,
+ aParameters.mYScale,
+ mScrolledFrame->PresContext()->AppUnitsPerDevPixel()));
+
+ ParentLayerIntRect layerClip;
+ if (const ParentLayerIntRect* origClip = aLayer->GetClipRect().ptrOr(nullptr)) {
+ layerClip = displayportClip.Intersect(*origClip);
+ } else {
+ layerClip = displayportClip;
+ }
+ aLayer->SetClipRect(Some(layerClip));
+ }
+
+ // Return early, since if we don't use APZ we don't need FrameMetrics.
+ return Nothing();
+ }
+
+ MOZ_ASSERT(mScrolledFrame->GetContent());
+
+ nsRect scrollport = mScrollPort + toReferenceFrame;
+
+ return Some(nsLayoutUtils::ComputeScrollMetadata(
+ mScrolledFrame, mOuter, mOuter->GetContent(),
+ aContainerReferenceFrame, aLayer, mScrollParentID,
+ scrollport, parentLayerClip, isRootContent, aParameters));
+}
+
+bool
+ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const
+{
+ // Use the right rect depending on if a display port is set.
+ nsRect displayPort;
+ bool usingDisplayport =
+ nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort, RelativeTo::ScrollFrame);
+ return aRect.Intersects(ExpandRectToNearlyVisible(usingDisplayport ? displayPort : mScrollPort));
+}
+
+static void HandleScrollPref(nsIScrollable *aScrollable, int32_t aOrientation,
+ uint8_t& aValue)
+{
+ int32_t pref;
+ aScrollable->GetDefaultScrollbarPreferences(aOrientation, &pref);
+ switch (pref) {
+ case nsIScrollable::Scrollbar_Auto:
+ // leave |aValue| untouched
+ break;
+ case nsIScrollable::Scrollbar_Never:
+ aValue = NS_STYLE_OVERFLOW_HIDDEN;
+ break;
+ case nsIScrollable::Scrollbar_Always:
+ aValue = NS_STYLE_OVERFLOW_SCROLL;
+ break;
+ }
+}
+
+ScrollbarStyles
+ScrollFrameHelper::GetScrollbarStylesFromFrame() const
+{
+ nsPresContext* presContext = mOuter->PresContext();
+ if (!presContext->IsDynamic() &&
+ !(mIsRoot && presContext->HasPaginatedScrolling())) {
+ return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
+ }
+
+ if (!mIsRoot) {
+ const nsStyleDisplay* disp = mOuter->StyleDisplay();
+ return ScrollbarStyles(disp);
+ }
+
+ ScrollbarStyles result = presContext->GetViewportScrollbarStylesOverride();
+ nsCOMPtr<nsISupports> container = presContext->GetContainerWeak();
+ nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(container);
+ if (scrollable) {
+ HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_X,
+ result.mHorizontal);
+ HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_Y,
+ result.mVertical);
+ }
+ return result;
+}
+
+nsRect
+ScrollFrameHelper::GetScrollRange() const
+{
+ return GetScrollRange(mScrollPort.width, mScrollPort.height);
+}
+
+nsRect
+ScrollFrameHelper::GetScrollRange(nscoord aWidth, nscoord aHeight) const
+{
+ nsRect range = GetScrolledRect();
+ range.width = std::max(range.width - aWidth, 0);
+ range.height = std::max(range.height - aHeight, 0);
+ return range;
+}
+
+nsRect
+ScrollFrameHelper::GetScrollRangeForClamping() const
+{
+ if (!ShouldClampScrollPosition()) {
+ return nsRect(nscoord_MIN/2, nscoord_MIN/2,
+ nscoord_MAX - nscoord_MIN/2, nscoord_MAX - nscoord_MIN/2);
+ }
+ nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
+ return GetScrollRange(scrollPortSize.width, scrollPortSize.height);
+}
+
+nsSize
+ScrollFrameHelper::GetScrollPositionClampingScrollPortSize() const
+{
+ nsIPresShell* presShell = mOuter->PresContext()->PresShell();
+ if (mIsRoot && presShell->IsScrollPositionClampingScrollPortSizeSet()) {
+ return presShell->GetScrollPositionClampingScrollPortSize();
+ }
+ return mScrollPort.Size();
+}
+
+static void
+AdjustForWholeDelta(int32_t aDelta, nscoord* aCoord)
+{
+ if (aDelta < 0) {
+ *aCoord = nscoord_MIN;
+ } else if (aDelta > 0) {
+ *aCoord = nscoord_MAX;
+ }
+}
+
+/**
+ * Calculate lower/upper scrollBy range in given direction.
+ * @param aDelta specifies scrollBy direction, if 0 then range will be 0 size
+ * @param aPos desired destination in AppUnits
+ * @param aNeg/PosTolerance defines relative range distance
+ * below and above of aPos point
+ * @param aMultiplier used for conversion of tolerance into appUnis
+ */
+static void
+CalcRangeForScrollBy(int32_t aDelta, nscoord aPos,
+ float aNegTolerance,
+ float aPosTolerance,
+ nscoord aMultiplier,
+ nscoord* aLower, nscoord* aUpper)
+{
+ if (!aDelta) {
+ *aLower = *aUpper = aPos;
+ return;
+ }
+ *aLower = aPos - NSToCoordRound(aMultiplier * (aDelta > 0 ? aNegTolerance : aPosTolerance));
+ *aUpper = aPos + NSToCoordRound(aMultiplier * (aDelta > 0 ? aPosTolerance : aNegTolerance));
+}
+
+void
+ScrollFrameHelper::ScrollBy(nsIntPoint aDelta,
+ nsIScrollableFrame::ScrollUnit aUnit,
+ nsIScrollableFrame::ScrollMode aMode,
+ nsIntPoint* aOverflow,
+ nsIAtom *aOrigin,
+ nsIScrollableFrame::ScrollMomentum aMomentum,
+ nsIScrollbarMediator::ScrollSnapMode aSnap)
+{
+ // When a smooth scroll is being processed on a frame, mouse wheel and trackpad
+ // momentum scroll event updates must notcancel the SMOOTH or SMOOTH_MSD
+ // scroll animations, enabling Javascript that depends on them to be responsive
+ // without forcing the user to wait for the fling animations to completely stop.
+ switch (aMomentum) {
+ case nsIScrollableFrame::NOT_MOMENTUM:
+ mIgnoreMomentumScroll = false;
+ break;
+ case nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT:
+ if (mIgnoreMomentumScroll) {
+ return;
+ }
+ break;
+ }
+
+ if (mAsyncSmoothMSDScroll != nullptr) {
+ // When CSSOM-View scroll-behavior smooth scrolling is interrupted,
+ // the scroll is not completed to avoid non-smooth snapping to the
+ // prior smooth scroll's destination.
+ mDestination = GetScrollPosition();
+ }
+
+ nsSize deltaMultiplier;
+ float negativeTolerance;
+ float positiveTolerance;
+ if (!aOrigin){
+ aOrigin = nsGkAtoms::other;
+ }
+ bool isGenericOrigin = (aOrigin == nsGkAtoms::other);
+ switch (aUnit) {
+ case nsIScrollableFrame::DEVICE_PIXELS: {
+ nscoord appUnitsPerDevPixel =
+ mOuter->PresContext()->AppUnitsPerDevPixel();
+ deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
+ if (isGenericOrigin){
+ aOrigin = nsGkAtoms::pixels;
+ }
+ negativeTolerance = positiveTolerance = 0.5f;
+ break;
+ }
+ case nsIScrollableFrame::LINES: {
+ deltaMultiplier = GetLineScrollAmount();
+ if (isGenericOrigin){
+ aOrigin = nsGkAtoms::lines;
+ }
+ negativeTolerance = positiveTolerance = 0.1f;
+ break;
+ }
+ case nsIScrollableFrame::PAGES: {
+ deltaMultiplier = GetPageScrollAmount();
+ if (isGenericOrigin){
+ aOrigin = nsGkAtoms::pages;
+ }
+ negativeTolerance = 0.05f;
+ positiveTolerance = 0;
+ break;
+ }
+ case nsIScrollableFrame::WHOLE: {
+ nsPoint pos = GetScrollPosition();
+ AdjustForWholeDelta(aDelta.x, &pos.x);
+ AdjustForWholeDelta(aDelta.y, &pos.y);
+ if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
+ GetSnapPointForDestination(aUnit, mDestination, pos);
+ }
+ ScrollTo(pos, aMode);
+ // 'this' might be destroyed here
+ if (aOverflow) {
+ *aOverflow = nsIntPoint(0, 0);
+ }
+ return;
+ }
+ default:
+ NS_ERROR("Invalid scroll mode");
+ return;
+ }
+
+ nsPoint newPos = mDestination + nsPoint(aDelta.x*deltaMultiplier.width, aDelta.y*deltaMultiplier.height);
+
+ if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
+ ScrollbarStyles styles = GetScrollbarStylesFromFrame();
+ if (styles.mScrollSnapTypeY != NS_STYLE_SCROLL_SNAP_TYPE_NONE ||
+ styles.mScrollSnapTypeX != NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
+ nscoord appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
+ deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
+ negativeTolerance = 0.1f;
+ positiveTolerance = 0;
+ nsIScrollableFrame::ScrollUnit snapUnit = aUnit;
+ if (aOrigin == nsGkAtoms::mouseWheel) {
+ // When using a clicky scroll wheel, snap point selection works the same
+ // as keyboard up/down/left/right navigation, but with varying amounts
+ // of scroll delta.
+ snapUnit = nsIScrollableFrame::LINES;
+ }
+ GetSnapPointForDestination(snapUnit, mDestination, newPos);
+ }
+ }
+
+ // Calculate desired range values.
+ nscoord rangeLowerX, rangeUpperX, rangeLowerY, rangeUpperY;
+ CalcRangeForScrollBy(aDelta.x, newPos.x, negativeTolerance, positiveTolerance,
+ deltaMultiplier.width, &rangeLowerX, &rangeUpperX);
+ CalcRangeForScrollBy(aDelta.y, newPos.y, negativeTolerance, positiveTolerance,
+ deltaMultiplier.height, &rangeLowerY, &rangeUpperY);
+ nsRect range(rangeLowerX,
+ rangeLowerY,
+ rangeUpperX - rangeLowerX,
+ rangeUpperY - rangeLowerY);
+ nsWeakFrame weakFrame(mOuter);
+ ScrollToWithOrigin(newPos, aMode, aOrigin, &range);
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+
+ if (aOverflow) {
+ nsPoint clampAmount = newPos - mDestination;
+ float appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
+ *aOverflow = nsIntPoint(
+ NSAppUnitsToIntPixels(clampAmount.x, appUnitsPerDevPixel),
+ NSAppUnitsToIntPixels(clampAmount.y, appUnitsPerDevPixel));
+ }
+
+ if (aUnit == nsIScrollableFrame::DEVICE_PIXELS &&
+ !nsLayoutUtils::AsyncPanZoomEnabled(mOuter))
+ {
+ // When APZ is disabled, we must track the velocity
+ // on the main thread; otherwise, the APZC will manage this.
+ mVelocityQueue.Sample(GetScrollPosition());
+ }
+}
+
+void
+ScrollFrameHelper::ScrollSnap(nsIScrollableFrame::ScrollMode aMode)
+{
+ float flingSensitivity = gfxPrefs::ScrollSnapPredictionSensitivity();
+ int maxVelocity = gfxPrefs::ScrollSnapPredictionMaxVelocity();
+ maxVelocity = nsPresContext::CSSPixelsToAppUnits(maxVelocity);
+ int maxOffset = maxVelocity * flingSensitivity;
+ nsPoint velocity = mVelocityQueue.GetVelocity();
+ // Multiply each component individually to avoid integer multiply
+ nsPoint predictedOffset = nsPoint(velocity.x * flingSensitivity,
+ velocity.y * flingSensitivity);
+ predictedOffset.Clamp(maxOffset);
+ nsPoint pos = GetScrollPosition();
+ nsPoint destinationPos = pos + predictedOffset;
+ ScrollSnap(destinationPos, aMode);
+}
+
+void
+ScrollFrameHelper::ScrollSnap(const nsPoint &aDestination,
+ nsIScrollableFrame::ScrollMode aMode)
+{
+ nsRect scrollRange = GetScrollRangeForClamping();
+ nsPoint pos = GetScrollPosition();
+ nsPoint snapDestination = scrollRange.ClampPoint(aDestination);
+ if (GetSnapPointForDestination(nsIScrollableFrame::DEVICE_PIXELS,
+ pos,
+ snapDestination)) {
+ ScrollTo(snapDestination, aMode);
+ }
+}
+
+nsSize
+ScrollFrameHelper::GetLineScrollAmount() const
+{
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(mOuter);
+ NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit");
+ static nscoord sMinLineScrollAmountInPixels = -1;
+ if (sMinLineScrollAmountInPixels < 0) {
+ Preferences::AddIntVarCache(&sMinLineScrollAmountInPixels,
+ "mousewheel.min_line_scroll_amount", 1);
+ }
+ int32_t appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
+ nscoord minScrollAmountInAppUnits =
+ std::max(1, sMinLineScrollAmountInPixels) * appUnitsPerDevPixel;
+ nscoord horizontalAmount = fm ? fm->AveCharWidth() : 0;
+ nscoord verticalAmount = fm ? fm->MaxHeight() : 0;
+ return nsSize(std::max(horizontalAmount, minScrollAmountInAppUnits),
+ std::max(verticalAmount, minScrollAmountInAppUnits));
+}
+
+/**
+ * Compute the scrollport size excluding any fixed-pos headers and
+ * footers. A header or footer is an box that spans that entire width
+ * of the viewport and touches the top (or bottom, respectively) of the
+ * viewport. We also want to consider fixed elements that stack or overlap
+ * to effectively create a larger header or footer. Headers and footers that
+ * cover more than a third of the the viewport are ignored since they
+ * probably aren't true headers and footers and we don't want to restrict
+ * scrolling too much in such cases. This is a bit conservative --- some
+ * pages use elements as headers or footers that don't span the entire width
+ * of the viewport --- but it should be a good start.
+ */
+struct TopAndBottom
+{
+ TopAndBottom(nscoord aTop, nscoord aBottom) : top(aTop), bottom(aBottom) {}
+
+ nscoord top, bottom;
+};
+struct TopComparator
+{
+ bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
+ return A.top == B.top;
+ }
+ bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
+ return A.top < B.top;
+ }
+};
+struct ReverseBottomComparator
+{
+ bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
+ return A.bottom == B.bottom;
+ }
+ bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
+ return A.bottom > B.bottom;
+ }
+};
+static nsSize
+GetScrollPortSizeExcludingHeadersAndFooters(nsIFrame* aViewportFrame,
+ const nsRect& aScrollPort)
+{
+ nsTArray<TopAndBottom> list;
+ nsFrameList fixedFrames = aViewportFrame->GetChildList(nsIFrame::kFixedList);
+ for (nsFrameList::Enumerator iterator(fixedFrames); !iterator.AtEnd();
+ iterator.Next()) {
+ nsIFrame* f = iterator.get();
+ nsRect r = f->GetRectRelativeToSelf();
+ r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, aViewportFrame);
+ r = r.Intersect(aScrollPort);
+ if ((r.width >= aScrollPort.width / 2 ||
+ r.width >= NSIntPixelsToAppUnits(800, AppUnitsPerCSSPixel())) &&
+ r.height <= aScrollPort.height/3) {
+ list.AppendElement(TopAndBottom(r.y, r.YMost()));
+ }
+ }
+
+ list.Sort(TopComparator());
+ nscoord headerBottom = 0;
+ for (uint32_t i = 0; i < list.Length(); ++i) {
+ if (list[i].top <= headerBottom) {
+ headerBottom = std::max(headerBottom, list[i].bottom);
+ }
+ }
+
+ list.Sort(ReverseBottomComparator());
+ nscoord footerTop = aScrollPort.height;
+ for (uint32_t i = 0; i < list.Length(); ++i) {
+ if (list[i].bottom >= footerTop) {
+ footerTop = std::min(footerTop, list[i].top);
+ }
+ }
+
+ headerBottom = std::min(aScrollPort.height/3, headerBottom);
+ footerTop = std::max(aScrollPort.height - aScrollPort.height/3, footerTop);
+
+ return nsSize(aScrollPort.width, footerTop - headerBottom);
+}
+
+nsSize
+ScrollFrameHelper::GetPageScrollAmount() const
+{
+ nsSize lineScrollAmount = GetLineScrollAmount();
+ nsSize effectiveScrollPortSize;
+ if (mIsRoot) {
+ // Reduce effective scrollport height by the height of any fixed-pos
+ // headers or footers
+ nsIFrame* root = mOuter->PresContext()->PresShell()->GetRootFrame();
+ effectiveScrollPortSize =
+ GetScrollPortSizeExcludingHeadersAndFooters(root, mScrollPort);
+ } else {
+ effectiveScrollPortSize = mScrollPort.Size();
+ }
+ // The page increment is the size of the page, minus the smaller of
+ // 10% of the size or 2 lines.
+ return nsSize(
+ effectiveScrollPortSize.width -
+ std::min(effectiveScrollPortSize.width/10, 2*lineScrollAmount.width),
+ effectiveScrollPortSize.height -
+ std::min(effectiveScrollPortSize.height/10, 2*lineScrollAmount.height));
+}
+
+ /**
+ * this code is resposible for restoring the scroll position back to some
+ * saved position. if the user has not moved the scroll position manually
+ * we keep scrolling down until we get to our original position. keep in
+ * mind that content could incrementally be coming in. we only want to stop
+ * when we reach our new position.
+ */
+void
+ScrollFrameHelper::ScrollToRestoredPosition()
+{
+ if (mRestorePos.y == -1 || mLastPos.x == -1 || mLastPos.y == -1) {
+ return;
+ }
+ // make sure our scroll position did not change for where we last put
+ // it. if it does then the user must have moved it, and we no longer
+ // need to restore.
+ //
+ // In the RTL case, we check whether the scroll position changed using the
+ // logical scroll position, but we scroll to the physical scroll position in
+ // all cases
+
+ // if we didn't move, we still need to restore
+ if (GetLogicalScrollPosition() == mLastPos) {
+ // if our desired position is different to the scroll position, scroll.
+ // remember that we could be incrementally loading so we may enter
+ // and scroll many times.
+ if (mRestorePos != mLastPos /* GetLogicalScrollPosition() */) {
+ nsPoint scrollToPos = mRestorePos;
+ if (!IsPhysicalLTR()) {
+ // convert from logical to physical scroll position
+ scrollToPos.x = mScrollPort.x -
+ (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
+ }
+ nsWeakFrame weakFrame(mOuter);
+ ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
+ nsGkAtoms::restore, nullptr);
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ if (PageIsStillLoading() || NS_SUBTREE_DIRTY(mOuter)) {
+ // If we're trying to do a history scroll restore, then we want to
+ // keep trying this until we succeed, because the page can be loading
+ // incrementally. So re-get the scroll position for the next iteration,
+ // it might not be exactly equal to mRestorePos due to rounding and
+ // clamping.
+ mLastPos = GetLogicalScrollPosition();
+ return;
+ }
+ }
+ // If we get here, either we reached the desired position (mLastPos ==
+ // mRestorePos) or we're not trying to do a history scroll restore, so
+ // we can stop after the scroll attempt above.
+ mRestorePos.y = -1;
+ mLastPos.x = -1;
+ mLastPos.y = -1;
+ } else {
+ // user moved the position, so we won't need to restore
+ mLastPos.x = -1;
+ mLastPos.y = -1;
+ }
+}
+
+bool
+ScrollFrameHelper::PageIsStillLoading()
+{
+ bool loadCompleted = false;
+ nsCOMPtr<nsIDocShell> ds = mOuter->GetContent()->GetComposedDoc()->GetDocShell();
+ if (ds) {
+ nsCOMPtr<nsIContentViewer> cv;
+ ds->GetContentViewer(getter_AddRefs(cv));
+ cv->GetLoadCompleted(&loadCompleted);
+ }
+ return !loadCompleted;
+}
+
+nsresult
+ScrollFrameHelper::FireScrollPortEvent()
+{
+ mAsyncScrollPortEvent.Forget();
+
+ // Keep this in sync with PostOverflowEvent().
+ nsSize scrollportSize = mScrollPort.Size();
+ nsSize childSize = GetScrolledRect().Size();
+
+ bool newVerticalOverflow = childSize.height > scrollportSize.height;
+ bool vertChanged = mVerticalOverflow != newVerticalOverflow;
+
+ bool newHorizontalOverflow = childSize.width > scrollportSize.width;
+ bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
+
+ if (!vertChanged && !horizChanged) {
+ return NS_OK;
+ }
+
+ // If both either overflowed or underflowed then we dispatch only one
+ // DOM event.
+ bool both = vertChanged && horizChanged &&
+ newVerticalOverflow == newHorizontalOverflow;
+ InternalScrollPortEvent::OrientType orient;
+ if (both) {
+ orient = InternalScrollPortEvent::eBoth;
+ mHorizontalOverflow = newHorizontalOverflow;
+ mVerticalOverflow = newVerticalOverflow;
+ }
+ else if (vertChanged) {
+ orient = InternalScrollPortEvent::eVertical;
+ mVerticalOverflow = newVerticalOverflow;
+ if (horizChanged) {
+ // We need to dispatch a separate horizontal DOM event. Do that the next
+ // time around since dispatching the vertical DOM event might destroy
+ // the frame.
+ PostOverflowEvent();
+ }
+ }
+ else {
+ orient = InternalScrollPortEvent::eHorizontal;
+ mHorizontalOverflow = newHorizontalOverflow;
+ }
+
+ InternalScrollPortEvent event(true,
+ (orient == InternalScrollPortEvent::eHorizontal ? mHorizontalOverflow :
+ mVerticalOverflow) ?
+ eScrollPortOverflow : eScrollPortUnderflow, nullptr);
+ event.mOrient = orient;
+ return EventDispatcher::Dispatch(mOuter->GetContent(),
+ mOuter->PresContext(), &event);
+}
+
+void
+ScrollFrameHelper::ReloadChildFrames()
+{
+ mScrolledFrame = nullptr;
+ mHScrollbarBox = nullptr;
+ mVScrollbarBox = nullptr;
+ mScrollCornerBox = nullptr;
+ mResizerBox = nullptr;
+
+ for (nsIFrame* frame : mOuter->PrincipalChildList()) {
+ nsIContent* content = frame->GetContent();
+ if (content == mOuter->GetContent()) {
+ NS_ASSERTION(!mScrolledFrame, "Already found the scrolled frame");
+ mScrolledFrame = frame;
+ } else {
+ nsAutoString value;
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::orient, value);
+ if (!value.IsEmpty()) {
+ // probably a scrollbar then
+ if (value.LowerCaseEqualsLiteral("horizontal")) {
+ NS_ASSERTION(!mHScrollbarBox, "Found multiple horizontal scrollbars?");
+ mHScrollbarBox = frame;
+ } else {
+ NS_ASSERTION(!mVScrollbarBox, "Found multiple vertical scrollbars?");
+ mVScrollbarBox = frame;
+ }
+ } else if (content->IsXULElement(nsGkAtoms::resizer)) {
+ NS_ASSERTION(!mResizerBox, "Found multiple resizers");
+ mResizerBox = frame;
+ } else if (content->IsXULElement(nsGkAtoms::scrollcorner)) {
+ // probably a scrollcorner
+ NS_ASSERTION(!mScrollCornerBox, "Found multiple scrollcorners");
+ mScrollCornerBox = frame;
+ }
+ }
+ }
+}
+
+nsresult
+ScrollFrameHelper::CreateAnonymousContent(
+ nsTArray<nsIAnonymousContentCreator::ContentInfo>& aElements)
+{
+ nsPresContext* presContext = mOuter->PresContext();
+ nsIFrame* parent = mOuter->GetParent();
+
+ // Don't create scrollbars if we're an SVG document being used as an image,
+ // or if we're printing/print previewing.
+ // (In the printing case, we allow scrollbars if this is the child of the
+ // viewport & paginated scrolling is enabled, because then we must be the
+ // scroll frame for the print preview window, & that does need scrollbars.)
+ if (presContext->Document()->IsBeingUsedAsImage() ||
+ (!presContext->IsDynamic() &&
+ !(mIsRoot && presContext->HasPaginatedScrolling()))) {
+ mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
+ return NS_OK;
+ }
+
+ // Check if the frame is resizable. Note:
+ // "The effect of the resize property on generated content is undefined.
+ // Implementations should not apply the resize property to generated
+ // content." [1]
+ // For info on what is generated content, see [2].
+ // [1]: https://drafts.csswg.org/css-ui/#resize
+ // [2]: https://www.w3.org/TR/CSS2/generate.html#content
+ int8_t resizeStyle = mOuter->StyleDisplay()->mResize;
+ bool isResizable = resizeStyle != NS_STYLE_RESIZE_NONE &&
+ !mOuter->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT);
+
+ nsIScrollableFrame *scrollable = do_QueryFrame(mOuter);
+
+ // If we're the scrollframe for the root, then we want to construct
+ // our scrollbar frames no matter what. That way later dynamic
+ // changes to propagated overflow styles will show or hide
+ // scrollbars on the viewport without requiring frame reconstruction
+ // of the viewport (good!).
+ bool canHaveHorizontal;
+ bool canHaveVertical;
+ if (!mIsRoot) {
+ ScrollbarStyles styles = scrollable->GetScrollbarStyles();
+ canHaveHorizontal = styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
+ canHaveVertical = styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
+ if (!canHaveHorizontal && !canHaveVertical && !isResizable) {
+ // Nothing to do.
+ return NS_OK;
+ }
+ } else {
+ canHaveHorizontal = true;
+ canHaveVertical = true;
+ }
+
+ // The anonymous <div> used by <inputs> never gets scrollbars.
+ nsITextControlFrame* textFrame = do_QueryFrame(parent);
+ if (textFrame) {
+ // Make sure we are not a text area.
+ nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(parent->GetContent()));
+ if (!textAreaElement) {
+ mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
+ return NS_OK;
+ }
+ }
+
+ nsNodeInfoManager *nodeInfoManager =
+ presContext->Document()->NodeInfoManager();
+ RefPtr<NodeInfo> nodeInfo;
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbar, nullptr,
+ kNameSpaceID_XUL,
+ nsIDOMNode::ELEMENT_NODE);
+ NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+ if (canHaveHorizontal) {
+ RefPtr<NodeInfo> ni = nodeInfo;
+ NS_TrustedNewXULElement(getter_AddRefs(mHScrollbarContent), ni.forget());
+#ifdef DEBUG
+ // Scrollbars can get restyled by theme changes. Whether such a restyle
+ // will actually reconstruct them correctly if it involves a frame
+ // reconstruct... I don't know. :(
+ mHScrollbarContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
+ reinterpret_cast<void*>(true));
+#endif // DEBUG
+
+ mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
+ NS_LITERAL_STRING("horizontal"), false);
+ mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+ NS_LITERAL_STRING("always"), false);
+ if (mIsRoot) {
+ mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
+ NS_LITERAL_STRING("true"), false);
+ }
+ if (!aElements.AppendElement(mHScrollbarContent))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (canHaveVertical) {
+ RefPtr<NodeInfo> ni = nodeInfo;
+ NS_TrustedNewXULElement(getter_AddRefs(mVScrollbarContent), ni.forget());
+#ifdef DEBUG
+ // Scrollbars can get restyled by theme changes. Whether such a restyle
+ // will actually reconstruct them correctly if it involves a frame
+ // reconstruct... I don't know. :(
+ mVScrollbarContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
+ reinterpret_cast<void*>(true));
+#endif // DEBUG
+
+ mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
+ NS_LITERAL_STRING("vertical"), false);
+ mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+ NS_LITERAL_STRING("always"), false);
+ if (mIsRoot) {
+ mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
+ NS_LITERAL_STRING("true"), false);
+ }
+ if (!aElements.AppendElement(mVScrollbarContent))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (isResizable) {
+ RefPtr<NodeInfo> nodeInfo;
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nullptr,
+ kNameSpaceID_XUL,
+ nsIDOMNode::ELEMENT_NODE);
+ NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+ NS_TrustedNewXULElement(getter_AddRefs(mResizerContent), nodeInfo.forget());
+
+ nsAutoString dir;
+ switch (resizeStyle) {
+ case NS_STYLE_RESIZE_HORIZONTAL:
+ if (IsScrollbarOnRight()) {
+ dir.AssignLiteral("right");
+ }
+ else {
+ dir.AssignLiteral("left");
+ }
+ break;
+ case NS_STYLE_RESIZE_VERTICAL:
+ dir.AssignLiteral("bottom");
+ break;
+ case NS_STYLE_RESIZE_BOTH:
+ dir.AssignLiteral("bottomend");
+ break;
+ default:
+ NS_WARNING("only resizable types should have resizers");
+ }
+ mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, false);
+
+ if (mIsRoot) {
+ nsIContent* browserRoot = GetBrowserRoot(mOuter->GetContent());
+ mCollapsedResizer = !(browserRoot &&
+ browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer));
+ }
+ else {
+ mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
+ NS_LITERAL_STRING("_parent"), false);
+ }
+
+ mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+ NS_LITERAL_STRING("always"), false);
+
+ if (!aElements.AppendElement(mResizerContent))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (canHaveHorizontal && canHaveVertical) {
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nullptr,
+ kNameSpaceID_XUL,
+ nsIDOMNode::ELEMENT_NODE);
+ NS_TrustedNewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo.forget());
+ if (!aElements.AppendElement(mScrollCornerContent))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+void
+ScrollFrameHelper::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter)
+{
+ if (mHScrollbarContent) {
+ aElements.AppendElement(mHScrollbarContent);
+ }
+
+ if (mVScrollbarContent) {
+ aElements.AppendElement(mVScrollbarContent);
+ }
+
+ if (mScrollCornerContent) {
+ aElements.AppendElement(mScrollCornerContent);
+ }
+
+ if (mResizerContent) {
+ aElements.AppendElement(mResizerContent);
+ }
+}
+
+void
+ScrollFrameHelper::Destroy()
+{
+ if (mScrollbarActivity) {
+ mScrollbarActivity->Destroy();
+ mScrollbarActivity = nullptr;
+ }
+
+ // Unbind any content created in CreateAnonymousContent from the tree
+ nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent);
+ nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent);
+ nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent);
+ nsContentUtils::DestroyAnonymousContent(&mResizerContent);
+
+ if (mPostedReflowCallback) {
+ mOuter->PresContext()->PresShell()->CancelReflowCallback(this);
+ mPostedReflowCallback = false;
+ }
+
+ if (mDisplayPortExpiryTimer) {
+ mDisplayPortExpiryTimer->Cancel();
+ mDisplayPortExpiryTimer = nullptr;
+ }
+ if (mActivityExpirationState.IsTracked()) {
+ gScrollFrameActivityTracker->RemoveObject(this);
+ }
+ if (gScrollFrameActivityTracker &&
+ gScrollFrameActivityTracker->IsEmpty()) {
+ delete gScrollFrameActivityTracker;
+ gScrollFrameActivityTracker = nullptr;
+ }
+
+ if (mScrollActivityTimer) {
+ mScrollActivityTimer->Cancel();
+ mScrollActivityTimer = nullptr;
+ }
+}
+
+/**
+ * Called when we want to update the scrollbar position, either because scrolling happened
+ * or the user moved the scrollbar position and we need to undo that (e.g., when the user
+ * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
+ * to its initial position for the start of the smooth sequence).
+ */
+void
+ScrollFrameHelper::UpdateScrollbarPosition()
+{
+ nsWeakFrame weakFrame(mOuter);
+ mFrameIsUpdatingScrollbar = true;
+
+ nsPoint pt = GetScrollPosition();
+ if (mVScrollbarBox) {
+ SetCoordAttribute(mVScrollbarBox->GetContent(), nsGkAtoms::curpos,
+ pt.y - GetScrolledRect().y);
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ }
+ if (mHScrollbarBox) {
+ SetCoordAttribute(mHScrollbarBox->GetContent(), nsGkAtoms::curpos,
+ pt.x - GetScrolledRect().x);
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ }
+
+ mFrameIsUpdatingScrollbar = false;
+}
+
+void ScrollFrameHelper::CurPosAttributeChanged(nsIContent* aContent)
+{
+ NS_ASSERTION(aContent, "aContent must not be null");
+ NS_ASSERTION((mHScrollbarBox && mHScrollbarBox->GetContent() == aContent) ||
+ (mVScrollbarBox && mVScrollbarBox->GetContent() == aContent),
+ "unexpected child");
+
+ // Attribute changes on the scrollbars happen in one of three ways:
+ // 1) The scrollbar changed the attribute in response to some user event
+ // 2) We changed the attribute in response to a ScrollPositionDidChange
+ // callback from the scrolling view
+ // 3) We changed the attribute to adjust the scrollbars for the start
+ // of a smooth scroll operation
+ //
+ // In cases 2 and 3 we do not need to scroll because we're just
+ // updating our scrollbar.
+ if (mFrameIsUpdatingScrollbar)
+ return;
+
+ nsRect scrolledRect = GetScrolledRect();
+
+ nsPoint current = GetScrollPosition() - scrolledRect.TopLeft();
+ nsPoint dest;
+ nsRect allowedRange;
+ dest.x = GetCoordAttribute(mHScrollbarBox, nsGkAtoms::curpos, current.x,
+ &allowedRange.x, &allowedRange.width);
+ dest.y = GetCoordAttribute(mVScrollbarBox, nsGkAtoms::curpos, current.y,
+ &allowedRange.y, &allowedRange.height);
+ current += scrolledRect.TopLeft();
+ dest += scrolledRect.TopLeft();
+ allowedRange += scrolledRect.TopLeft();
+
+ // Don't try to scroll if we're already at an acceptable place.
+ // Don't call Contains here since Contains returns false when the point is
+ // on the bottom or right edge of the rectangle.
+ if (allowedRange.ClampPoint(current) == current) {
+ return;
+ }
+
+ if (mScrollbarActivity) {
+ RefPtr<ScrollbarActivity> scrollbarActivity(mScrollbarActivity);
+ scrollbarActivity->ActivityOccurred();
+ }
+
+ bool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::smooth);
+ if (isSmooth) {
+ // Make sure an attribute-setting callback occurs even if the view
+ // didn't actually move yet. We need to make sure other listeners
+ // see that the scroll position is not (yet) what they thought it
+ // was.
+ nsWeakFrame weakFrame(mOuter);
+ UpdateScrollbarPosition();
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ }
+ ScrollToWithOrigin(dest,
+ isSmooth ? nsIScrollableFrame::SMOOTH : nsIScrollableFrame::INSTANT,
+ nsGkAtoms::scrollbars, &allowedRange);
+ // 'this' might be destroyed here
+}
+
+/* ============= Scroll events ========== */
+
+ScrollFrameHelper::ScrollEvent::ScrollEvent(ScrollFrameHelper* aHelper)
+ : mHelper(aHelper)
+{
+ mDriver = mHelper->mOuter->PresContext()->RefreshDriver();
+ mDriver->AddRefreshObserver(this, Flush_Layout);
+}
+
+ScrollFrameHelper::ScrollEvent::~ScrollEvent()
+{
+ if (mDriver) {
+ mDriver->RemoveRefreshObserver(this, Flush_Layout);
+ mDriver = nullptr;
+ }
+}
+
+void
+ScrollFrameHelper::ScrollEvent::WillRefresh(mozilla::TimeStamp aTime)
+{
+ mDriver->RemoveRefreshObserver(this, Flush_Layout);
+ mDriver = nullptr;
+ mHelper->FireScrollEvent();
+}
+
+void
+ScrollFrameHelper::FireScrollEvent()
+{
+ MOZ_ASSERT(mScrollEvent);
+ mScrollEvent = nullptr;
+
+ ActiveLayerTracker::SetCurrentScrollHandlerFrame(mOuter);
+ WidgetGUIEvent event(true, eScroll, nullptr);
+ nsEventStatus status = nsEventStatus_eIgnore;
+ nsIContent* content = mOuter->GetContent();
+ nsPresContext* prescontext = mOuter->PresContext();
+ // Fire viewport scroll events at the document (where they
+ // will bubble to the window)
+ mozilla::layers::ScrollLinkedEffectDetector detector(content->GetComposedDoc());
+ if (mIsRoot) {
+ nsIDocument* doc = content->GetUncomposedDoc();
+ if (doc) {
+ EventDispatcher::Dispatch(doc, prescontext, &event, nullptr, &status);
+ }
+ } else {
+ // scroll events fired at elements don't bubble (although scroll events
+ // fired at documents do, to the window)
+ event.mFlags.mBubbles = false;
+ EventDispatcher::Dispatch(content, prescontext, &event, nullptr, &status);
+ }
+ ActiveLayerTracker::SetCurrentScrollHandlerFrame(nullptr);
+}
+
+void
+ScrollFrameHelper::PostScrollEvent()
+{
+ if (mScrollEvent)
+ return;
+
+ // The ScrollEvent constructor registers itself with the refresh driver.
+ mScrollEvent = new ScrollEvent(this);
+}
+
+NS_IMETHODIMP
+ScrollFrameHelper::AsyncScrollPortEvent::Run()
+{
+ if (mHelper) {
+ mHelper->mOuter->PresContext()->GetPresShell()->
+ FlushPendingNotifications(Flush_InterruptibleLayout);
+ }
+ return mHelper ? mHelper->FireScrollPortEvent() : NS_OK;
+}
+
+bool
+nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom)
+{
+ if (!mHelper.mHScrollbarBox)
+ return true;
+
+ return AddRemoveScrollbar(aState, aOnBottom, true, true);
+}
+
+bool
+nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState& aState, bool aOnRight)
+{
+ if (!mHelper.mVScrollbarBox)
+ return true;
+
+ return AddRemoveScrollbar(aState, aOnRight, false, true);
+}
+
+void
+nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom)
+{
+ // removing a scrollbar should always fit
+ DebugOnly<bool> result = AddRemoveScrollbar(aState, aOnBottom, true, false);
+ NS_ASSERTION(result, "Removing horizontal scrollbar failed to fit??");
+}
+
+void
+nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState& aState, bool aOnRight)
+{
+ // removing a scrollbar should always fit
+ DebugOnly<bool> result = AddRemoveScrollbar(aState, aOnRight, false, false);
+ NS_ASSERTION(result, "Removing vertical scrollbar failed to fit??");
+}
+
+bool
+nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState& aState,
+ bool aOnRightOrBottom, bool aHorizontal, bool aAdd)
+{
+ if (aHorizontal) {
+ if (mHelper.mNeverHasHorizontalScrollbar || !mHelper.mHScrollbarBox)
+ return false;
+
+ nsSize hSize = mHelper.mHScrollbarBox->GetXULPrefSize(aState);
+ nsBox::AddMargin(mHelper.mHScrollbarBox, hSize);
+
+ mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, aAdd);
+
+ bool hasHorizontalScrollbar;
+ bool fit = AddRemoveScrollbar(hasHorizontalScrollbar,
+ mHelper.mScrollPort.y,
+ mHelper.mScrollPort.height,
+ hSize.height, aOnRightOrBottom, aAdd);
+ mHelper.mHasHorizontalScrollbar = hasHorizontalScrollbar; // because mHasHorizontalScrollbar is a bool
+ if (!fit)
+ mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, !aAdd);
+
+ return fit;
+ } else {
+ if (mHelper.mNeverHasVerticalScrollbar || !mHelper.mVScrollbarBox)
+ return false;
+
+ nsSize vSize = mHelper.mVScrollbarBox->GetXULPrefSize(aState);
+ nsBox::AddMargin(mHelper.mVScrollbarBox, vSize);
+
+ mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, aAdd);
+
+ bool hasVerticalScrollbar;
+ bool fit = AddRemoveScrollbar(hasVerticalScrollbar,
+ mHelper.mScrollPort.x,
+ mHelper.mScrollPort.width,
+ vSize.width, aOnRightOrBottom, aAdd);
+ mHelper.mHasVerticalScrollbar = hasVerticalScrollbar; // because mHasVerticalScrollbar is a bool
+ if (!fit)
+ mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, !aAdd);
+
+ return fit;
+ }
+}
+
+bool
+nsXULScrollFrame::AddRemoveScrollbar(bool& aHasScrollbar, nscoord& aXY,
+ nscoord& aSize, nscoord aSbSize,
+ bool aOnRightOrBottom, bool aAdd)
+{
+ nscoord size = aSize;
+ nscoord xy = aXY;
+
+ if (size != NS_INTRINSICSIZE) {
+ if (aAdd) {
+ size -= aSbSize;
+ if (!aOnRightOrBottom && size >= 0)
+ xy += aSbSize;
+ } else {
+ size += aSbSize;
+ if (!aOnRightOrBottom)
+ xy -= aSbSize;
+ }
+ }
+
+ // not enough room? Yes? Return true.
+ if (size >= 0) {
+ aHasScrollbar = aAdd;
+ aSize = size;
+ aXY = xy;
+ return true;
+ }
+
+ aHasScrollbar = false;
+ return false;
+}
+
+void
+nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState,
+ const nsPoint& aScrollPosition)
+{
+ uint32_t oldflags = aState.LayoutFlags();
+ nsRect childRect = nsRect(mHelper.mScrollPort.TopLeft() - aScrollPosition,
+ mHelper.mScrollPort.Size());
+ int32_t flags = NS_FRAME_NO_MOVE_VIEW;
+
+ nsSize minSize = mHelper.mScrolledFrame->GetXULMinSize(aState);
+
+ if (minSize.height > childRect.height)
+ childRect.height = minSize.height;
+
+ if (minSize.width > childRect.width)
+ childRect.width = minSize.width;
+
+ // TODO: Handle transformed children that inherit perspective
+ // from this frame. See AdjustForPerspective for how we handle
+ // this for HTML scroll frames.
+
+ aState.SetLayoutFlags(flags);
+ ClampAndSetBounds(aState, childRect, aScrollPosition);
+ mHelper.mScrolledFrame->XULLayout(aState);
+
+ childRect = mHelper.mScrolledFrame->GetRect();
+
+ if (childRect.width < mHelper.mScrollPort.width ||
+ childRect.height < mHelper.mScrollPort.height)
+ {
+ childRect.width = std::max(childRect.width, mHelper.mScrollPort.width);
+ childRect.height = std::max(childRect.height, mHelper.mScrollPort.height);
+
+ // remove overflow areas when we update the bounds,
+ // because we've already accounted for it
+ // REVIEW: Have we accounted for both?
+ ClampAndSetBounds(aState, childRect, aScrollPosition, true);
+ }
+
+ aState.SetLayoutFlags(oldflags);
+
+}
+
+void ScrollFrameHelper::PostOverflowEvent()
+{
+ if (mAsyncScrollPortEvent.IsPending())
+ return;
+
+ // Keep this in sync with FireScrollPortEvent().
+ nsSize scrollportSize = mScrollPort.Size();
+ nsSize childSize = GetScrolledRect().Size();
+
+ bool newVerticalOverflow = childSize.height > scrollportSize.height;
+ bool vertChanged = mVerticalOverflow != newVerticalOverflow;
+
+ bool newHorizontalOverflow = childSize.width > scrollportSize.width;
+ bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
+
+ if (!vertChanged && !horizChanged) {
+ return;
+ }
+
+ nsRootPresContext* rpc = mOuter->PresContext()->GetRootPresContext();
+ if (!rpc)
+ return;
+
+ mAsyncScrollPortEvent = new AsyncScrollPortEvent(this);
+ rpc->AddWillPaintObserver(mAsyncScrollPortEvent.get());
+}
+
+nsIFrame*
+ScrollFrameHelper::GetFrameForDir() const
+{
+ nsIFrame *frame = mOuter;
+ // XXX This is a bit on the slow side.
+ if (mIsRoot) {
+ // If we're the root scrollframe, we need the root element's style data.
+ nsPresContext *presContext = mOuter->PresContext();
+ nsIDocument *document = presContext->Document();
+ Element *root = document->GetRootElement();
+
+ // But for HTML and XHTML we want the body element.
+ nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
+ if (htmlDoc) {
+ Element *bodyElement = document->GetBodyElement();
+ if (bodyElement)
+ root = bodyElement; // we can trust the document to hold on to it
+ }
+
+ if (root) {
+ nsIFrame *rootsFrame = root->GetPrimaryFrame();
+ if (rootsFrame)
+ frame = rootsFrame;
+ }
+ }
+
+ return frame;
+}
+
+bool
+ScrollFrameHelper::IsScrollbarOnRight() const
+{
+ nsPresContext *presContext = mOuter->PresContext();
+
+ // The position of the scrollbar in top-level windows depends on the pref
+ // layout.scrollbar.side. For non-top-level elements, it depends only on the
+ // directionaliy of the element (equivalent to a value of "1" for the pref).
+ if (!mIsRoot) {
+ return IsPhysicalLTR();
+ }
+ switch (presContext->GetCachedIntPref(kPresContext_ScrollbarSide)) {
+ default:
+ case 0: // UI directionality
+ return presContext->GetCachedIntPref(kPresContext_BidiDirection)
+ == IBMBIDI_TEXTDIRECTION_LTR;
+ case 1: // Document / content directionality
+ return IsPhysicalLTR();
+ case 2: // Always right
+ return true;
+ case 3: // Always left
+ return false;
+ }
+}
+
+bool
+ScrollFrameHelper::IsMaybeScrollingActive() const
+{
+ const nsStyleDisplay* disp = mOuter->StyleDisplay();
+ if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
+ return true;
+ }
+
+ return mHasBeenScrolledRecently ||
+ IsAlwaysActive() ||
+ mWillBuildScrollableLayer;
+}
+
+bool
+ScrollFrameHelper::IsScrollingActive(nsDisplayListBuilder* aBuilder) const
+{
+ const nsStyleDisplay* disp = mOuter->StyleDisplay();
+ if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL) &&
+ aBuilder->IsInWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize())) {
+ return true;
+ }
+
+ return mHasBeenScrolledRecently ||
+ IsAlwaysActive() ||
+ mWillBuildScrollableLayer;
+}
+
+/**
+ * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
+ * cause any of the scrollbars to need to be reflowed.
+ */
+nsresult
+nsXULScrollFrame::XULLayout(nsBoxLayoutState& aState)
+{
+ bool scrollbarRight = IsScrollbarOnRight();
+ bool scrollbarBottom = true;
+
+ // get the content rect
+ nsRect clientRect(0,0,0,0);
+ GetXULClientRect(clientRect);
+
+ nsRect oldScrollAreaBounds = mHelper.mScrollPort;
+ nsPoint oldScrollPosition = mHelper.GetLogicalScrollPosition();
+
+ // the scroll area size starts off as big as our content area
+ mHelper.mScrollPort = clientRect;
+
+ /**************
+ Our basic strategy here is to first try laying out the content with
+ the scrollbars in their current state. We're hoping that that will
+ just "work"; the content will overflow wherever there's a scrollbar
+ already visible. If that does work, then there's no need to lay out
+ the scrollarea. Otherwise we fix up the scrollbars; first we add a
+ vertical one to scroll the content if necessary, or remove it if
+ it's not needed. Then we reflow the content if the scrollbar
+ changed. Then we add a horizontal scrollbar if necessary (or
+ remove if not needed), and if that changed, we reflow the content
+ again. At this point, any scrollbars that are needed to scroll the
+ content have been added.
+
+ In the second phase we check to see if any scrollbars are too small
+ to display, and if so, we remove them. We check the horizontal
+ scrollbar first; removing it might make room for the vertical
+ scrollbar, and if we have room for just one scrollbar we'll save
+ the vertical one.
+
+ Finally we position and size the scrollbars and scrollcorner (the
+ square that is needed in the corner of the window when two
+ scrollbars are visible), and reflow any fixed position views
+ (if we're the viewport and we added or removed a scrollbar).
+ **************/
+
+ ScrollbarStyles styles = GetScrollbarStyles();
+
+ // Look at our style do we always have vertical or horizontal scrollbars?
+ if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL)
+ mHelper.mHasHorizontalScrollbar = true;
+ if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL)
+ mHelper.mHasVerticalScrollbar = true;
+
+ if (mHelper.mHasHorizontalScrollbar)
+ AddHorizontalScrollbar(aState, scrollbarBottom);
+
+ if (mHelper.mHasVerticalScrollbar)
+ AddVerticalScrollbar(aState, scrollbarRight);
+
+ // layout our the scroll area
+ LayoutScrollArea(aState, oldScrollPosition);
+
+ // now look at the content area and see if we need scrollbars or not
+ bool needsLayout = false;
+
+ // if we have 'auto' scrollbars look at the vertical case
+ if (styles.mVertical != NS_STYLE_OVERFLOW_SCROLL) {
+ // These are only good until the call to LayoutScrollArea.
+ nsRect scrolledRect = mHelper.GetScrolledRect();
+
+ // There are two cases to consider
+ if (scrolledRect.height <= mHelper.mScrollPort.height
+ || styles.mVertical != NS_STYLE_OVERFLOW_AUTO) {
+ if (mHelper.mHasVerticalScrollbar) {
+ // We left room for the vertical scrollbar, but it's not needed;
+ // remove it.
+ RemoveVerticalScrollbar(aState, scrollbarRight);
+ needsLayout = true;
+ }
+ } else {
+ if (!mHelper.mHasVerticalScrollbar) {
+ // We didn't leave room for the vertical scrollbar, but it turns
+ // out we needed it
+ if (AddVerticalScrollbar(aState, scrollbarRight))
+ needsLayout = true;
+ }
+ }
+
+ // ok layout at the right size
+ if (needsLayout) {
+ nsBoxLayoutState resizeState(aState);
+ LayoutScrollArea(resizeState, oldScrollPosition);
+ needsLayout = false;
+ }
+ }
+
+
+ // if scrollbars are auto look at the horizontal case
+ if (styles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL)
+ {
+ // These are only good until the call to LayoutScrollArea.
+ nsRect scrolledRect = mHelper.GetScrolledRect();
+
+ // if the child is wider that the scroll area
+ // and we don't have a scrollbar add one.
+ if ((scrolledRect.width > mHelper.mScrollPort.width)
+ && styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
+
+ if (!mHelper.mHasHorizontalScrollbar) {
+ // no scrollbar?
+ if (AddHorizontalScrollbar(aState, scrollbarBottom))
+ needsLayout = true;
+
+ // if we added a horizontal scrollbar and we did not have a vertical
+ // there is a chance that by adding the horizontal scrollbar we will
+ // suddenly need a vertical scrollbar. Is a special case but its
+ // important.
+ //if (!mHasVerticalScrollbar && scrolledRect.height > scrollAreaRect.height - sbSize.height)
+ // printf("****Gfx Scrollbar Special case hit!!*****\n");
+
+ }
+ } else {
+ // if the area is smaller or equal to and we have a scrollbar then
+ // remove it.
+ if (mHelper.mHasHorizontalScrollbar) {
+ RemoveHorizontalScrollbar(aState, scrollbarBottom);
+ needsLayout = true;
+ }
+ }
+ }
+
+ // we only need to set the rect. The inner child stays the same size.
+ if (needsLayout) {
+ nsBoxLayoutState resizeState(aState);
+ LayoutScrollArea(resizeState, oldScrollPosition);
+ needsLayout = false;
+ }
+
+ // get the preferred size of the scrollbars
+ nsSize hMinSize(0, 0);
+ if (mHelper.mHScrollbarBox && mHelper.mHasHorizontalScrollbar) {
+ GetScrollbarMetrics(aState, mHelper.mHScrollbarBox, &hMinSize, nullptr, false);
+ }
+ nsSize vMinSize(0, 0);
+ if (mHelper.mVScrollbarBox && mHelper.mHasVerticalScrollbar) {
+ GetScrollbarMetrics(aState, mHelper.mVScrollbarBox, &vMinSize, nullptr, true);
+ }
+
+ // Disable scrollbars that are too small
+ // Disable horizontal scrollbar first. If we have to disable only one
+ // scrollbar, we'd rather keep the vertical scrollbar.
+ // Note that we always give horizontal scrollbars their preferred height,
+ // never their min-height. So check that there's room for the preferred height.
+ if (mHelper.mHasHorizontalScrollbar &&
+ (hMinSize.width > clientRect.width - vMinSize.width
+ || hMinSize.height > clientRect.height)) {
+ RemoveHorizontalScrollbar(aState, scrollbarBottom);
+ needsLayout = true;
+ }
+ // Now disable vertical scrollbar if necessary
+ if (mHelper.mHasVerticalScrollbar &&
+ (vMinSize.height > clientRect.height - hMinSize.height
+ || vMinSize.width > clientRect.width)) {
+ RemoveVerticalScrollbar(aState, scrollbarRight);
+ needsLayout = true;
+ }
+
+ // we only need to set the rect. The inner child stays the same size.
+ if (needsLayout) {
+ nsBoxLayoutState resizeState(aState);
+ LayoutScrollArea(resizeState, oldScrollPosition);
+ }
+
+ if (!mHelper.mSupppressScrollbarUpdate) {
+ mHelper.LayoutScrollbars(aState, clientRect, oldScrollAreaBounds);
+ }
+ if (!mHelper.mPostedReflowCallback) {
+ // Make sure we'll try scrolling to restored position
+ PresContext()->PresShell()->PostReflowCallback(&mHelper);
+ mHelper.mPostedReflowCallback = true;
+ }
+ if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+ mHelper.mHadNonInitialReflow = true;
+ }
+
+ mHelper.UpdateSticky();
+
+ // Set up overflow areas for block frames for the benefit of
+ // text-overflow.
+ nsIFrame* f = mHelper.mScrolledFrame->GetContentInsertionFrame();
+ if (nsLayoutUtils::GetAsBlock(f)) {
+ nsRect origRect = f->GetRect();
+ nsRect clippedRect = origRect;
+ clippedRect.MoveBy(mHelper.mScrollPort.TopLeft());
+ clippedRect.IntersectRect(clippedRect, mHelper.mScrollPort);
+ nsOverflowAreas overflow = f->GetOverflowAreas();
+ f->FinishAndStoreOverflow(overflow, clippedRect.Size());
+ clippedRect.MoveTo(origRect.TopLeft());
+ f->SetRect(clippedRect);
+ }
+
+ mHelper.UpdatePrevScrolledRect();
+
+ mHelper.PostOverflowEvent();
+ return NS_OK;
+}
+
+void
+ScrollFrameHelper::FinishReflowForScrollbar(nsIContent* aContent,
+ nscoord aMinXY, nscoord aMaxXY,
+ nscoord aCurPosXY,
+ nscoord aPageIncrement,
+ nscoord aIncrement)
+{
+ // Scrollbars assume zero is the minimum position, so translate for them.
+ SetCoordAttribute(aContent, nsGkAtoms::curpos, aCurPosXY - aMinXY);
+ SetScrollbarEnabled(aContent, aMaxXY - aMinXY);
+ SetCoordAttribute(aContent, nsGkAtoms::maxpos, aMaxXY - aMinXY);
+ SetCoordAttribute(aContent, nsGkAtoms::pageincrement, aPageIncrement);
+ SetCoordAttribute(aContent, nsGkAtoms::increment, aIncrement);
+}
+
+bool
+ScrollFrameHelper::ReflowFinished()
+{
+ mPostedReflowCallback = false;
+
+ if (NS_SUBTREE_DIRTY(mOuter)) {
+ // We will get another call after the next reflow and scrolling
+ // later is less janky.
+ return false;
+ }
+
+ nsAutoScriptBlocker scriptBlocker;
+ ScrollToRestoredPosition();
+
+ // Clamp current scroll position to new bounds. Normally this won't
+ // do anything.
+ nsPoint currentScrollPos = GetScrollPosition();
+ ScrollToImpl(currentScrollPos, nsRect(currentScrollPos, nsSize(0, 0)));
+ if (!mAsyncScroll && !mAsyncSmoothMSDScroll && !mApzSmoothScrollDestination) {
+ // We need to have mDestination track the current scroll position,
+ // in case it falls outside the new reflow area. mDestination is used
+ // by ScrollBy as its starting position.
+ mDestination = GetScrollPosition();
+ }
+
+ if (!mUpdateScrollbarAttributes) {
+ return false;
+ }
+ mUpdateScrollbarAttributes = false;
+
+ // Update scrollbar attributes.
+ nsPresContext* presContext = mOuter->PresContext();
+
+ if (mMayHaveDirtyFixedChildren) {
+ mMayHaveDirtyFixedChildren = false;
+ nsIFrame* parentFrame = mOuter->GetParent();
+ for (nsIFrame* fixedChild =
+ parentFrame->GetChildList(nsIFrame::kFixedList).FirstChild();
+ fixedChild; fixedChild = fixedChild->GetNextSibling()) {
+ // force a reflow of the fixed child
+ presContext->PresShell()->
+ FrameNeedsReflow(fixedChild, nsIPresShell::eResize,
+ NS_FRAME_HAS_DIRTY_CHILDREN);
+ }
+ }
+
+ nsRect scrolledContentRect = GetScrolledRect();
+ nsSize scrollClampingScrollPort = GetScrollPositionClampingScrollPortSize();
+ nscoord minX = scrolledContentRect.x;
+ nscoord maxX = scrolledContentRect.XMost() - scrollClampingScrollPort.width;
+ nscoord minY = scrolledContentRect.y;
+ nscoord maxY = scrolledContentRect.YMost() - scrollClampingScrollPort.height;
+
+ // Suppress handling of the curpos attribute changes we make here.
+ NS_ASSERTION(!mFrameIsUpdatingScrollbar, "We shouldn't be reentering here");
+ mFrameIsUpdatingScrollbar = true;
+
+ nsCOMPtr<nsIContent> vScroll =
+ mVScrollbarBox ? mVScrollbarBox->GetContent() : nullptr;
+ nsCOMPtr<nsIContent> hScroll =
+ mHScrollbarBox ? mHScrollbarBox->GetContent() : nullptr;
+
+ // Note, in some cases mOuter may get deleted while finishing reflow
+ // for scrollbars. XXXmats is this still true now that we have a script
+ // blocker in this scope? (if not, remove the weak frame checks below).
+ if (vScroll || hScroll) {
+ nsWeakFrame weakFrame(mOuter);
+ nsPoint scrollPos = GetScrollPosition();
+ nsSize lineScrollAmount = GetLineScrollAmount();
+ if (vScroll) {
+ const double kScrollMultiplier =
+ Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
+ NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
+ nscoord increment = lineScrollAmount.height * kScrollMultiplier;
+ // We normally use (scrollArea.height - increment) for height
+ // of page scrolling. However, it is too small when
+ // increment is very large. (If increment is larger than
+ // scrollArea.height, direction of scrolling will be opposite).
+ // To avoid it, we use (float(scrollArea.height) * 0.8) as
+ // lower bound value of height of page scrolling. (bug 383267)
+ // XXX shouldn't we use GetPageScrollAmount here?
+ nscoord pageincrement = nscoord(scrollClampingScrollPort.height - increment);
+ nscoord pageincrementMin = nscoord(float(scrollClampingScrollPort.height) * 0.8);
+ FinishReflowForScrollbar(vScroll, minY, maxY, scrollPos.y,
+ std::max(pageincrement, pageincrementMin),
+ increment);
+ }
+ if (hScroll) {
+ const double kScrollMultiplier =
+ Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
+ NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
+ nscoord increment = lineScrollAmount.width * kScrollMultiplier;
+ FinishReflowForScrollbar(hScroll, minX, maxX, scrollPos.x,
+ nscoord(float(scrollClampingScrollPort.width) * 0.8),
+ increment);
+ }
+ NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
+ }
+
+ mFrameIsUpdatingScrollbar = false;
+ // We used to rely on the curpos attribute changes above to scroll the
+ // view. However, for scrolling to the left of the viewport, we
+ // rescale the curpos attribute, which means that operations like
+ // resizing the window while it is scrolled all the way to the left
+ // hold the curpos attribute constant at 0 while still requiring
+ // scrolling. So we suppress the effect of the changes above with
+ // mFrameIsUpdatingScrollbar and call CurPosAttributeChanged here.
+ // (It actually even works some of the time without this, thanks to
+ // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
+ // we hide the scrollbar on a large size change, such as
+ // maximization.)
+ if (!mHScrollbarBox && !mVScrollbarBox)
+ return false;
+ CurPosAttributeChanged(mVScrollbarBox ? mVScrollbarBox->GetContent()
+ : mHScrollbarBox->GetContent());
+ return true;
+}
+
+void
+ScrollFrameHelper::ReflowCallbackCanceled()
+{
+ mPostedReflowCallback = false;
+}
+
+bool
+ScrollFrameHelper::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ nsIScrollableFrame* sf = do_QueryFrame(mOuter);
+ ScrollbarStyles ss = sf->GetScrollbarStyles();
+
+ // Reflow when the change in overflow leads to one of our scrollbars
+ // changing or might require repositioning the scrolled content due to
+ // reduced extents.
+ nsRect scrolledRect = GetScrolledRect();
+ uint32_t overflowChange = GetOverflowChange(scrolledRect, mPrevScrolledRect);
+ mPrevScrolledRect = scrolledRect;
+
+ bool needReflow = false;
+ nsPoint scrollPosition = GetScrollPosition();
+ if (overflowChange & nsIScrollableFrame::HORIZONTAL) {
+ if (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN || scrollPosition.x) {
+ needReflow = true;
+ }
+ }
+ if (overflowChange & nsIScrollableFrame::VERTICAL) {
+ if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN || scrollPosition.y) {
+ needReflow = true;
+ }
+ }
+
+ if (needReflow) {
+ // If there are scrollbars, or we're not at the beginning of the pane,
+ // the scroll position may change. In this case, mark the frame as
+ // needing reflow. Don't use NS_FRAME_IS_DIRTY as dirty as that means
+ // we have to reflow the frame and all its descendants, and we don't
+ // have to do that here. Only this frame needs to be reflowed.
+ mOuter->PresContext()->PresShell()->FrameNeedsReflow(
+ mOuter, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
+ // Ensure that next time nsHTMLScrollFrame::Reflow runs, we don't skip
+ // updating the scrollbars. (Because the overflow area of the scrolled
+ // frame has probably just been updated, Reflow won't see it change.)
+ mSkippedScrollbarLayout = true;
+ return false; // reflowing will update overflow
+ }
+ PostOverflowEvent();
+ return mOuter->nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
+}
+
+void
+ScrollFrameHelper::UpdateSticky()
+{
+ StickyScrollContainer* ssc = StickyScrollContainer::
+ GetStickyScrollContainerForScrollFrame(mOuter);
+ if (ssc) {
+ nsIScrollableFrame* scrollFrame = do_QueryFrame(mOuter);
+ ssc->UpdatePositions(scrollFrame->GetScrollPosition(), mOuter);
+ }
+}
+
+void
+ScrollFrameHelper::UpdatePrevScrolledRect()
+{
+ mPrevScrolledRect = GetScrolledRect();
+}
+
+void
+ScrollFrameHelper::AdjustScrollbarRectForResizer(
+ nsIFrame* aFrame, nsPresContext* aPresContext,
+ nsRect& aRect, bool aHasResizer, bool aVertical)
+{
+ if ((aVertical ? aRect.width : aRect.height) == 0)
+ return;
+
+ // if a content resizer is present, use its size. Otherwise, check if the
+ // widget has a resizer.
+ nsRect resizerRect;
+ if (aHasResizer) {
+ resizerRect = mResizerBox->GetRect();
+ }
+ else {
+ nsPoint offset;
+ nsIWidget* widget = aFrame->GetNearestWidget(offset);
+ LayoutDeviceIntRect widgetRect;
+ if (!widget || !widget->ShowsResizeIndicator(&widgetRect))
+ return;
+
+ resizerRect = nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x,
+ aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y,
+ aPresContext->DevPixelsToAppUnits(widgetRect.width),
+ aPresContext->DevPixelsToAppUnits(widgetRect.height));
+ }
+
+ if (resizerRect.Contains(aRect.BottomRight() - nsPoint(1, 1))) {
+ if (aVertical) {
+ aRect.height = std::max(0, resizerRect.y - aRect.y);
+ } else {
+ aRect.width = std::max(0, resizerRect.x - aRect.x);
+ }
+ } else if (resizerRect.Contains(aRect.BottomLeft() + nsPoint(1, -1))) {
+ if (aVertical) {
+ aRect.height = std::max(0, resizerRect.y - aRect.y);
+ } else {
+ nscoord xmost = aRect.XMost();
+ aRect.x = std::max(aRect.x, resizerRect.XMost());
+ aRect.width = xmost - aRect.x;
+ }
+ }
+}
+
+static void
+AdjustOverlappingScrollbars(nsRect& aVRect, nsRect& aHRect)
+{
+ if (aVRect.IsEmpty() || aHRect.IsEmpty())
+ return;
+
+ const nsRect oldVRect = aVRect;
+ const nsRect oldHRect = aHRect;
+ if (oldVRect.Contains(oldHRect.BottomRight() - nsPoint(1, 1))) {
+ aHRect.width = std::max(0, oldVRect.x - oldHRect.x);
+ } else if (oldVRect.Contains(oldHRect.BottomLeft() - nsPoint(0, 1))) {
+ nscoord overlap = std::min(oldHRect.width, oldVRect.XMost() - oldHRect.x);
+ aHRect.x += overlap;
+ aHRect.width -= overlap;
+ }
+ if (oldHRect.Contains(oldVRect.BottomRight() - nsPoint(1, 1))) {
+ aVRect.height = std::max(0, oldHRect.y - oldVRect.y);
+ }
+}
+
+void
+ScrollFrameHelper::LayoutScrollbars(nsBoxLayoutState& aState,
+ const nsRect& aContentArea,
+ const nsRect& aOldScrollArea)
+{
+ NS_ASSERTION(!mSupppressScrollbarUpdate,
+ "This should have been suppressed");
+
+ nsIPresShell* presShell = mOuter->PresContext()->PresShell();
+
+ bool hasResizer = HasResizer();
+ bool scrollbarOnLeft = !IsScrollbarOnRight();
+ bool overlayScrollBarsWithZoom =
+ mIsRoot && LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) &&
+ presShell->IsScrollPositionClampingScrollPortSizeSet();
+
+ nsSize scrollPortClampingSize = mScrollPort.Size();
+ double res = 1.0;
+ if (overlayScrollBarsWithZoom) {
+ scrollPortClampingSize = presShell->GetScrollPositionClampingScrollPortSize();
+ res = presShell->GetCumulativeResolution();
+ }
+
+ // place the scrollcorner
+ if (mScrollCornerBox || mResizerBox) {
+ NS_PRECONDITION(!mScrollCornerBox || mScrollCornerBox->IsXULBoxFrame(), "Must be a box frame!");
+
+ nsRect r(0, 0, 0, 0);
+ if (aContentArea.x != mScrollPort.x || scrollbarOnLeft) {
+ // scrollbar (if any) on left
+ r.x = aContentArea.x;
+ r.width = mScrollPort.x - aContentArea.x;
+ NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
+ } else {
+ // scrollbar (if any) on right
+ r.width = aContentArea.XMost() - mScrollPort.XMost();
+ r.x = aContentArea.XMost() - r.width;
+ NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
+ }
+ if (aContentArea.y != mScrollPort.y) {
+ NS_ERROR("top scrollbars not supported");
+ } else {
+ // scrollbar (if any) on bottom
+ r.height = aContentArea.YMost() - mScrollPort.YMost();
+ r.y = aContentArea.YMost() - r.height;
+ NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
+ }
+
+ if (mScrollCornerBox) {
+ nsBoxFrame::LayoutChildAt(aState, mScrollCornerBox, r);
+ }
+
+ if (hasResizer) {
+ // if a resizer is present, get its size. Assume a default size of 15 pixels.
+ nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15);
+ nsSize resizerMinSize = mResizerBox->GetXULMinSize(aState);
+
+ nscoord vScrollbarWidth = mVScrollbarBox ?
+ mVScrollbarBox->GetXULPrefSize(aState).width : defaultSize;
+ r.width = std::max(std::max(r.width, vScrollbarWidth), resizerMinSize.width);
+ if (aContentArea.x == mScrollPort.x && !scrollbarOnLeft) {
+ r.x = aContentArea.XMost() - r.width;
+ }
+
+ nscoord hScrollbarHeight = mHScrollbarBox ?
+ mHScrollbarBox->GetXULPrefSize(aState).height : defaultSize;
+ r.height = std::max(std::max(r.height, hScrollbarHeight), resizerMinSize.height);
+ if (aContentArea.y == mScrollPort.y) {
+ r.y = aContentArea.YMost() - r.height;
+ }
+
+ nsBoxFrame::LayoutChildAt(aState, mResizerBox, r);
+ }
+ else if (mResizerBox) {
+ // otherwise lay out the resizer with an empty rectangle
+ nsBoxFrame::LayoutChildAt(aState, mResizerBox, nsRect());
+ }
+ }
+
+ nsPresContext* presContext = mScrolledFrame->PresContext();
+ nsRect vRect;
+ if (mVScrollbarBox) {
+ NS_PRECONDITION(mVScrollbarBox->IsXULBoxFrame(), "Must be a box frame!");
+ vRect = mScrollPort;
+ if (overlayScrollBarsWithZoom) {
+ vRect.height = NSToCoordRound(res * scrollPortClampingSize.height);
+ }
+ vRect.width = aContentArea.width - mScrollPort.width;
+ vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.x + NSToCoordRound(res * scrollPortClampingSize.width);
+ if (mHasVerticalScrollbar) {
+ nsMargin margin;
+ mVScrollbarBox->GetXULMargin(margin);
+ vRect.Deflate(margin);
+ }
+ AdjustScrollbarRectForResizer(mOuter, presContext, vRect, hasResizer, true);
+ }
+
+ nsRect hRect;
+ if (mHScrollbarBox) {
+ NS_PRECONDITION(mHScrollbarBox->IsXULBoxFrame(), "Must be a box frame!");
+ hRect = mScrollPort;
+ if (overlayScrollBarsWithZoom) {
+ hRect.width = NSToCoordRound(res * scrollPortClampingSize.width);
+ }
+ hRect.height = aContentArea.height - mScrollPort.height;
+ hRect.y = mScrollPort.y + NSToCoordRound(res * scrollPortClampingSize.height);
+ if (mHasHorizontalScrollbar) {
+ nsMargin margin;
+ mHScrollbarBox->GetXULMargin(margin);
+ hRect.Deflate(margin);
+ }
+ AdjustScrollbarRectForResizer(mOuter, presContext, hRect, hasResizer, false);
+ }
+
+ if (!LookAndFeel::GetInt(LookAndFeel::eIntID_AllowOverlayScrollbarsOverlap)) {
+ AdjustOverlappingScrollbars(vRect, hRect);
+ }
+ if (mVScrollbarBox) {
+ nsBoxFrame::LayoutChildAt(aState, mVScrollbarBox, vRect);
+ }
+ if (mHScrollbarBox) {
+ nsBoxFrame::LayoutChildAt(aState, mHScrollbarBox, hRect);
+ }
+
+ // may need to update fixed position children of the viewport,
+ // if the client area changed size because of an incremental
+ // reflow of a descendant. (If the outer frame is dirty, the fixed
+ // children will be re-laid out anyway)
+ if (aOldScrollArea.Size() != mScrollPort.Size() &&
+ !(mOuter->GetStateBits() & NS_FRAME_IS_DIRTY) &&
+ mIsRoot) {
+ mMayHaveDirtyFixedChildren = true;
+ }
+
+ // post reflow callback to modify scrollbar attributes
+ mUpdateScrollbarAttributes = true;
+ if (!mPostedReflowCallback) {
+ aState.PresContext()->PresShell()->PostReflowCallback(this);
+ mPostedReflowCallback = true;
+ }
+}
+
+#if DEBUG
+static bool ShellIsAlive(nsWeakPtr& aWeakPtr)
+{
+ nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aWeakPtr));
+ return !!shell;
+}
+#endif
+
+void
+ScrollFrameHelper::SetScrollbarEnabled(nsIContent* aContent, nscoord aMaxPos)
+{
+ DebugOnly<nsWeakPtr> weakShell(
+ do_GetWeakReference(mOuter->PresContext()->PresShell()));
+ if (aMaxPos) {
+ aContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
+ } else {
+ aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
+ NS_LITERAL_STRING("true"), true);
+ }
+ MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
+}
+
+void
+ScrollFrameHelper::SetCoordAttribute(nsIContent* aContent, nsIAtom* aAtom,
+ nscoord aSize)
+{
+ DebugOnly<nsWeakPtr> weakShell(
+ do_GetWeakReference(mOuter->PresContext()->PresShell()));
+ // convert to pixels
+ int32_t pixelSize = nsPresContext::AppUnitsToIntCSSPixels(aSize);
+
+ // only set the attribute if it changed.
+
+ nsAutoString newValue;
+ newValue.AppendInt(pixelSize);
+
+ if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters))
+ return;
+
+ nsWeakFrame weakFrame(mOuter);
+ nsCOMPtr<nsIContent> kungFuDeathGrip = aContent;
+ aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, true);
+ MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+
+ if (mScrollbarActivity) {
+ RefPtr<ScrollbarActivity> scrollbarActivity(mScrollbarActivity);
+ scrollbarActivity->ActivityOccurred();
+ }
+}
+
+static void
+ReduceRadii(nscoord aXBorder, nscoord aYBorder,
+ nscoord& aXRadius, nscoord& aYRadius)
+{
+ // In order to ensure that the inside edge of the border has no
+ // curvature, we need at least one of its radii to be zero.
+ if (aXRadius <= aXBorder || aYRadius <= aYBorder)
+ return;
+
+ // For any corner where we reduce the radii, preserve the corner's shape.
+ double ratio = std::max(double(aXBorder) / aXRadius,
+ double(aYBorder) / aYRadius);
+ aXRadius *= ratio;
+ aYRadius *= ratio;
+}
+
+/**
+ * Implement an override for nsIFrame::GetBorderRadii to ensure that
+ * the clipping region for the border radius does not clip the scrollbars.
+ *
+ * In other words, we require that the border radius be reduced until the
+ * inner border radius at the inner edge of the border is 0 wherever we
+ * have scrollbars.
+ */
+bool
+ScrollFrameHelper::GetBorderRadii(const nsSize& aFrameSize,
+ const nsSize& aBorderArea,
+ Sides aSkipSides,
+ nscoord aRadii[8]) const
+{
+ if (!mOuter->nsContainerFrame::GetBorderRadii(aFrameSize, aBorderArea,
+ aSkipSides, aRadii)) {
+ return false;
+ }
+
+ // Since we can use GetActualScrollbarSizes (rather than
+ // GetDesiredScrollbarSizes) since this doesn't affect reflow, we
+ // probably should.
+ nsMargin sb = GetActualScrollbarSizes();
+ nsMargin border = mOuter->GetUsedBorder();
+
+ if (sb.left > 0 || sb.top > 0) {
+ ReduceRadii(border.left, border.top,
+ aRadii[NS_CORNER_TOP_LEFT_X],
+ aRadii[NS_CORNER_TOP_LEFT_Y]);
+ }
+
+ if (sb.top > 0 || sb.right > 0) {
+ ReduceRadii(border.right, border.top,
+ aRadii[NS_CORNER_TOP_RIGHT_X],
+ aRadii[NS_CORNER_TOP_RIGHT_Y]);
+ }
+
+ if (sb.right > 0 || sb.bottom > 0) {
+ ReduceRadii(border.right, border.bottom,
+ aRadii[NS_CORNER_BOTTOM_RIGHT_X],
+ aRadii[NS_CORNER_BOTTOM_RIGHT_Y]);
+ }
+
+ if (sb.bottom > 0 || sb.left > 0) {
+ ReduceRadii(border.left, border.bottom,
+ aRadii[NS_CORNER_BOTTOM_LEFT_X],
+ aRadii[NS_CORNER_BOTTOM_LEFT_Y]);
+ }
+
+ return true;
+}
+
+static nscoord
+SnapCoord(nscoord aCoord, double aRes, nscoord aAppUnitsPerPixel)
+{
+ double snappedToLayerPixels = NS_round((aRes*aCoord)/aAppUnitsPerPixel);
+ return NSToCoordRoundWithClamp(snappedToLayerPixels*aAppUnitsPerPixel/aRes);
+}
+
+nsRect
+ScrollFrameHelper::GetScrolledRect() const
+{
+ nsRect result =
+ GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
+ mScrollPort.Size());
+
+ if (result.width < mScrollPort.width) {
+ NS_WARNING("Scrolled rect smaller than scrollport?");
+ }
+ if (result.height < mScrollPort.height) {
+ NS_WARNING("Scrolled rect smaller than scrollport?");
+ }
+
+ // Expand / contract the result by up to half a layer pixel so that scrolling
+ // to the right / bottom edge does not change the layer pixel alignment of
+ // the scrolled contents.
+
+ if (result.x == 0 && result.y == 0 &&
+ result.width == mScrollPort.width &&
+ result.height == mScrollPort.height) {
+ // The edges that we would snap are already aligned with the scroll port,
+ // so we can skip all the work below.
+ return result;
+ }
+
+ // For that, we first convert the scroll port and the scrolled rect to rects
+ // relative to the reference frame, since that's the space where painting does
+ // snapping.
+ nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
+ nsIFrame* referenceFrame = nsLayoutUtils::GetReferenceFrame(mOuter);
+ nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(referenceFrame);
+ nsRect scrollPort(mScrollPort.TopLeft() + toReferenceFrame, scrollPortSize);
+ nsRect scrolledRect = result + scrollPort.TopLeft();
+
+ if (scrollPort.Overflows() || scrolledRect.Overflows()) {
+ return result;
+ }
+
+ // Now, snap the bottom right corner of both of these rects.
+ // We snap to layer pixels, so we need to respect the layer's scale.
+ nscoord appUnitsPerDevPixel = mScrolledFrame->PresContext()->AppUnitsPerDevPixel();
+ gfxSize scale = FrameLayerBuilder::GetPaintedLayerScaleForFrame(mScrolledFrame);
+ if (scale.IsEmpty()) {
+ scale = gfxSize(1.0f, 1.0f);
+ }
+
+ // Compute bounds for the scroll position, and computed the snapped scrolled
+ // rect from the scroll position bounds.
+ nscoord snappedScrolledAreaBottom = SnapCoord(scrolledRect.YMost(), scale.height, appUnitsPerDevPixel);
+ nscoord snappedScrollPortBottom = SnapCoord(scrollPort.YMost(), scale.height, appUnitsPerDevPixel);
+ nscoord maximumScrollOffsetY = snappedScrolledAreaBottom - snappedScrollPortBottom;
+ result.SetBottomEdge(scrollPort.height + maximumScrollOffsetY);
+
+ if (GetScrolledFrameDir() == NS_STYLE_DIRECTION_LTR) {
+ nscoord snappedScrolledAreaRight = SnapCoord(scrolledRect.XMost(), scale.width, appUnitsPerDevPixel);
+ nscoord snappedScrollPortRight = SnapCoord(scrollPort.XMost(), scale.width, appUnitsPerDevPixel);
+ nscoord maximumScrollOffsetX = snappedScrolledAreaRight - snappedScrollPortRight;
+ result.SetRightEdge(scrollPort.width + maximumScrollOffsetX);
+ } else {
+ // In RTL, the scrolled area's right edge is at scrollPort.XMost(),
+ // and the scrolled area's x position is zero or negative. We want
+ // the right edge to stay flush with the scroll port, so we snap the
+ // left edge.
+ nscoord snappedScrolledAreaLeft = SnapCoord(scrolledRect.x, scale.width, appUnitsPerDevPixel);
+ nscoord snappedScrollPortLeft = SnapCoord(scrollPort.x, scale.width, appUnitsPerDevPixel);
+ nscoord minimumScrollOffsetX = snappedScrolledAreaLeft - snappedScrollPortLeft;
+ result.SetLeftEdge(minimumScrollOffsetX);
+ }
+
+ return result;
+}
+
+
+uint8_t
+ScrollFrameHelper::GetScrolledFrameDir() const
+{
+ // If the scrolled frame has unicode-bidi: plaintext, the paragraph
+ // direction set by the text content overrides the direction of the frame
+ if (mScrolledFrame->StyleTextReset()->mUnicodeBidi &
+ NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
+ nsIFrame* childFrame = mScrolledFrame->PrincipalChildList().FirstChild();
+ if (childFrame) {
+ return (nsBidiPresUtils::ParagraphDirection(childFrame) == NSBIDI_LTR)
+ ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
+ }
+ }
+
+ return IsBidiLTR() ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
+}
+
+nsRect
+ScrollFrameHelper::GetUnsnappedScrolledRectInternal(const nsRect& aScrolledFrameOverflowArea,
+ const nsSize& aScrollPortSize) const
+{
+ return nsLayoutUtils::GetScrolledRect(mScrolledFrame,
+ aScrolledFrameOverflowArea,
+ aScrollPortSize, GetScrolledFrameDir());
+}
+
+nsMargin
+ScrollFrameHelper::GetActualScrollbarSizes() const
+{
+ nsRect r = mOuter->GetPaddingRect() - mOuter->GetPosition();
+
+ return nsMargin(mScrollPort.y - r.y,
+ r.XMost() - mScrollPort.XMost(),
+ r.YMost() - mScrollPort.YMost(),
+ mScrollPort.x - r.x);
+}
+
+void
+ScrollFrameHelper::SetScrollbarVisibility(nsIFrame* aScrollbar, bool aVisible)
+{
+ nsScrollbarFrame* scrollbar = do_QueryFrame(aScrollbar);
+ if (scrollbar) {
+ // See if we have a mediator.
+ nsIScrollbarMediator* mediator = scrollbar->GetScrollbarMediator();
+ if (mediator) {
+ // Inform the mediator of the visibility change.
+ mediator->VisibilityChanged(aVisible);
+ }
+ }
+}
+
+nscoord
+ScrollFrameHelper::GetCoordAttribute(nsIFrame* aBox, nsIAtom* aAtom,
+ nscoord aDefaultValue,
+ nscoord* aRangeStart,
+ nscoord* aRangeLength)
+{
+ if (aBox) {
+ nsIContent* content = aBox->GetContent();
+
+ nsAutoString value;
+ content->GetAttr(kNameSpaceID_None, aAtom, value);
+ if (!value.IsEmpty())
+ {
+ nsresult error;
+ // convert it to appunits
+ nscoord result = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
+ // Any nscoord value that would round to the attribute value when converted
+ // to CSS pixels is allowed.
+ *aRangeStart = result - halfPixel;
+ *aRangeLength = halfPixel*2 - 1;
+ return result;
+ }
+ }
+
+ // Only this exact default value is allowed.
+ *aRangeStart = aDefaultValue;
+ *aRangeLength = 0;
+ return aDefaultValue;
+}
+
+nsPresState*
+ScrollFrameHelper::SaveState() const
+{
+ nsIScrollbarMediator* mediator = do_QueryFrame(GetScrolledFrame());
+ if (mediator) {
+ // child handles its own scroll state, so don't bother saving state here
+ return nullptr;
+ }
+
+ // Don't store a scroll state if we never have been scrolled or restored
+ // a previous scroll state, and we're not in the middle of a smooth scroll.
+ bool isInSmoothScroll = IsProcessingAsyncScroll() || mLastSmoothScrollOrigin;
+ if (!mHasBeenScrolled && !mDidHistoryRestore && !isInSmoothScroll) {
+ return nullptr;
+ }
+
+ nsPresState* state = new nsPresState();
+ bool allowScrollOriginDowngrade =
+ !nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin) ||
+ mAllowScrollOriginDowngrade;
+ // Save mRestorePos instead of our actual current scroll position, if it's
+ // valid and we haven't moved since the last update of mLastPos (same check
+ // that ScrollToRestoredPosition uses). This ensures if a reframe occurs
+ // while we're in the process of loading content to scroll to a restored
+ // position, we'll keep trying after the reframe. Similarly, if we're in the
+ // middle of a smooth scroll, store the destination so that when we restore
+ // we'll jump straight to the end of the scroll animation, rather than
+ // effectively dropping it. Note that the mRestorePos will override the
+ // smooth scroll destination if both are present.
+ nsPoint pt = GetLogicalScrollPosition();
+ if (isInSmoothScroll) {
+ pt = mDestination;
+ allowScrollOriginDowngrade = false;
+ }
+ if (mRestorePos.y != -1 && pt == mLastPos) {
+ pt = mRestorePos;
+ }
+ state->SetScrollState(pt);
+ state->SetAllowScrollOriginDowngrade(allowScrollOriginDowngrade);
+ if (mIsRoot) {
+ // Only save resolution properties for root scroll frames
+ nsIPresShell* shell = mOuter->PresContext()->PresShell();
+ state->SetResolution(shell->GetResolution());
+ state->SetScaleToResolution(shell->ScaleToResolution());
+ }
+ return state;
+}
+
+void
+ScrollFrameHelper::RestoreState(nsPresState* aState)
+{
+ mRestorePos = aState->GetScrollPosition();
+ MOZ_ASSERT(mLastScrollOrigin == nsGkAtoms::other);
+ mAllowScrollOriginDowngrade = aState->GetAllowScrollOriginDowngrade();
+ mDidHistoryRestore = true;
+ mLastPos = mScrolledFrame ? GetLogicalScrollPosition() : nsPoint(0,0);
+
+ // Resolution properties should only exist on root scroll frames.
+ MOZ_ASSERT(mIsRoot || (!aState->GetScaleToResolution() &&
+ aState->GetResolution() == 1.0));
+
+ if (mIsRoot) {
+ nsIPresShell* presShell = mOuter->PresContext()->PresShell();
+ if (aState->GetScaleToResolution()) {
+ presShell->SetResolutionAndScaleTo(aState->GetResolution());
+ } else {
+ presShell->SetResolution(aState->GetResolution());
+ }
+ }
+}
+
+void
+ScrollFrameHelper::PostScrolledAreaEvent()
+{
+ if (mScrolledAreaEvent.IsPending()) {
+ return;
+ }
+ mScrolledAreaEvent = new ScrolledAreaEvent(this);
+ nsContentUtils::AddScriptRunner(mScrolledAreaEvent.get());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ScrolledArea change event dispatch
+
+NS_IMETHODIMP
+ScrollFrameHelper::ScrolledAreaEvent::Run()
+{
+ if (mHelper) {
+ mHelper->FireScrolledAreaEvent();
+ }
+ return NS_OK;
+}
+
+void
+ScrollFrameHelper::FireScrolledAreaEvent()
+{
+ mScrolledAreaEvent.Forget();
+
+ InternalScrollAreaEvent event(true, eScrolledAreaChanged, nullptr);
+ nsPresContext *prescontext = mOuter->PresContext();
+ nsIContent* content = mOuter->GetContent();
+
+ event.mArea = mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
+
+ nsIDocument *doc = content->GetUncomposedDoc();
+ if (doc) {
+ EventDispatcher::Dispatch(doc, prescontext, &event, nullptr);
+ }
+}
+
+uint32_t
+nsIScrollableFrame::GetPerceivedScrollingDirections() const
+{
+ nscoord oneDevPixel = GetScrolledFrame()->PresContext()->AppUnitsPerDevPixel();
+ uint32_t directions = GetScrollbarVisibility();
+ nsRect scrollRange = GetScrollRange();
+ if (scrollRange.width >= oneDevPixel) {
+ directions |= HORIZONTAL;
+ }
+ if (scrollRange.height >= oneDevPixel) {
+ directions |= VERTICAL;
+ }
+ return directions;
+}
+
+/**
+ * Collect the scroll-snap-coordinates of frames in the subtree rooted at
+ * |aFrame|, relative to |aScrolledFrame|, into |aOutCoords|.
+ */
+void
+CollectScrollSnapCoordinates(nsIFrame* aFrame, nsIFrame* aScrolledFrame,
+ nsTArray<nsPoint>& aOutCoords)
+{
+ nsIFrame::ChildListIterator childLists(aFrame);
+ for (; !childLists.IsDone(); childLists.Next()) {
+ nsFrameList::Enumerator childFrames(childLists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ nsIFrame* f = childFrames.get();
+
+ const nsStyleDisplay* styleDisplay = f->StyleDisplay();
+ size_t coordCount = styleDisplay->mScrollSnapCoordinate.Length();
+
+ if (coordCount) {
+ nsRect frameRect = f->GetRect();
+ nsPoint offset = f->GetOffsetTo(aScrolledFrame);
+ nsRect edgesRect = nsRect(offset, frameRect.Size());
+ for (size_t coordNum = 0; coordNum < coordCount; coordNum++) {
+ const Position& coordPosition =
+ f->StyleDisplay()->mScrollSnapCoordinate[coordNum];
+ nsPoint coordPoint = edgesRect.TopLeft();
+ coordPoint += nsPoint(coordPosition.mXPosition.mLength,
+ coordPosition.mYPosition.mLength);
+ if (coordPosition.mXPosition.mHasPercent) {
+ coordPoint.x += NSToCoordRound(coordPosition.mXPosition.mPercent *
+ frameRect.width);
+ }
+ if (coordPosition.mYPosition.mHasPercent) {
+ coordPoint.y += NSToCoordRound(coordPosition.mYPosition.mPercent *
+ frameRect.height);
+ }
+
+ aOutCoords.AppendElement(coordPoint);
+ }
+ }
+
+ CollectScrollSnapCoordinates(f, aScrolledFrame, aOutCoords);
+ }
+ }
+}
+
+layers::ScrollSnapInfo
+ComputeScrollSnapInfo(const ScrollFrameHelper& aScrollFrame)
+{
+ ScrollSnapInfo result;
+
+ ScrollbarStyles styles = aScrollFrame.GetScrollbarStylesFromFrame();
+
+ if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
+ styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
+ // We won't be snapping, short-circuit the computation.
+ return result;
+ }
+
+ result.mScrollSnapTypeX = styles.mScrollSnapTypeX;
+ result.mScrollSnapTypeY = styles.mScrollSnapTypeY;
+
+ nsSize scrollPortSize = aScrollFrame.GetScrollPortRect().Size();
+
+ result.mScrollSnapDestination = nsPoint(styles.mScrollSnapDestinationX.mLength,
+ styles.mScrollSnapDestinationY.mLength);
+ if (styles.mScrollSnapDestinationX.mHasPercent) {
+ result.mScrollSnapDestination.x +=
+ NSToCoordFloorClamped(styles.mScrollSnapDestinationX.mPercent *
+ scrollPortSize.width);
+ }
+ if (styles.mScrollSnapDestinationY.mHasPercent) {
+ result.mScrollSnapDestination.y +=
+ NSToCoordFloorClamped(styles.mScrollSnapDestinationY.mPercent *
+ scrollPortSize.height);
+ }
+
+ if (styles.mScrollSnapPointsX.GetUnit() != eStyleUnit_None) {
+ result.mScrollSnapIntervalX = Some(nsRuleNode::ComputeCoordPercentCalc(
+ styles.mScrollSnapPointsX, scrollPortSize.width));
+ }
+ if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) {
+ result.mScrollSnapIntervalY = Some(nsRuleNode::ComputeCoordPercentCalc(
+ styles.mScrollSnapPointsY, scrollPortSize.height));
+ }
+
+ CollectScrollSnapCoordinates(aScrollFrame.GetScrolledFrame(),
+ aScrollFrame.GetScrolledFrame(),
+ result.mScrollSnapCoordinates);
+
+ return result;
+}
+
+layers::ScrollSnapInfo
+ScrollFrameHelper::GetScrollSnapInfo() const
+{
+ // TODO(botond): Should we cache it?
+ return ComputeScrollSnapInfo(*this);
+}
+
+bool
+ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUnit,
+ nsPoint aStartPos,
+ nsPoint &aDestination)
+{
+ Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
+ GetScrollSnapInfo(), aUnit, mScrollPort.Size(),
+ GetScrollRangeForClamping(), aStartPos, aDestination);
+ if (snapPoint) {
+ aDestination = snapPoint.ref();
+ return true;
+ }
+ return false;
+}
+
+bool
+ScrollFrameHelper::UsesContainerScrolling() const
+{
+ if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+ return mIsRoot;
+ }
+ return false;
+}
diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h
new file mode 100644
index 000000000..f1ef44ae8
--- /dev/null
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -0,0 +1,1498 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object to wrap rendering objects that should be scrollable */
+
+#ifndef nsGfxScrollFrame_h___
+#define nsGfxScrollFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+#include "nsIAnonymousContentCreator.h"
+#include "nsBoxFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "nsIStatefulFrame.h"
+#include "nsThreadUtils.h"
+#include "nsIReflowCallback.h"
+#include "nsBoxLayoutState.h"
+#include "nsQueryFrame.h"
+#include "nsRefreshDriver.h"
+#include "nsExpirationTracker.h"
+#include "TextOverflow.h"
+#include "ScrollVelocityQueue.h"
+
+class nsPresContext;
+class nsIPresShell;
+class nsIContent;
+class nsIAtom;
+class nsPresState;
+class nsIScrollPositionListener;
+
+namespace mozilla {
+struct ScrollReflowInput;
+namespace layers {
+class Layer;
+} // namespace layers
+namespace layout {
+class ScrollbarActivity;
+} // namespace layout
+
+class ScrollFrameHelper : public nsIReflowCallback {
+public:
+ typedef nsIFrame::Sides Sides;
+ typedef mozilla::CSSIntPoint CSSIntPoint;
+ typedef mozilla::layout::ScrollbarActivity ScrollbarActivity;
+ typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
+ typedef mozilla::layers::Layer Layer;
+
+ class AsyncScroll;
+ class AsyncSmoothMSDScroll;
+
+ ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot);
+ ~ScrollFrameHelper();
+
+ mozilla::ScrollbarStyles GetScrollbarStylesFromFrame() const;
+
+ // If a child frame was added or removed on the scrollframe,
+ // reload our child frame list.
+ // We need this if a scrollbar frame is recreated.
+ void ReloadChildFrames();
+
+ nsresult CreateAnonymousContent(
+ nsTArray<nsIAnonymousContentCreator::ContentInfo>& aElements);
+ void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter);
+ nsresult FireScrollPortEvent();
+ void PostOverflowEvent();
+ void Destroy();
+
+ void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists);
+
+ void AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists,
+ bool aCreateLayer,
+ bool aPositioned);
+
+ bool GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
+ Sides aSkipSides, nscoord aRadii[8]) const;
+
+ // nsIReflowCallback
+ virtual bool ReflowFinished() override;
+ virtual void ReflowCallbackCanceled() override;
+
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * Called when the 'curpos' attribute on one of the scrollbars changes.
+ */
+ void CurPosAttributeChanged(nsIContent* aChild);
+
+ void PostScrollEvent();
+ void FireScrollEvent();
+ void PostScrolledAreaEvent();
+ void FireScrolledAreaEvent();
+
+ bool IsSmoothScrollingEnabled();
+
+ /**
+ * This class handles the dispatching of scroll events to content.
+ *
+ * nsRefreshDriver maintains three lists of refresh observers, one for each
+ * flush type: Flush_Style, Flush_Layout, and Flush_Display.
+ *
+ * During a tick, it runs through each list of observers, in order, and runs
+ * them. To iterate over each list, it uses an EndLimitedIterator, which is
+ * designed to iterate only over elements present when the iterator was
+ * created, not elements added afterwards. This means that, for a given flush
+ * type, a refresh observer added during the execution of another refresh
+ * observer of that flush type, will not run until the next tick.
+ *
+ * During main-thread animation-driven scrolling, ScrollEvents are *posted*
+ * by AsyncScroll::WillRefresh(). AsyncScroll registers itself as a Flush_Style
+ * refresh observer.
+ *
+ * Posting a scroll event, as of bug 1250550, registers a Flush_Layout
+ * refresh observer, which *fires* the event when run. This allows the event
+ * to be fired to content in the same refresh driver tick as it is posted.
+ * This is an important invariant to maintain to reduce scroll event latency
+ * for main-thread scrolling.
+ */
+ class ScrollEvent : public nsARefreshObserver {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(ScrollEvent, override)
+ explicit ScrollEvent(ScrollFrameHelper *helper);
+ void WillRefresh(mozilla::TimeStamp aTime) override;
+ protected:
+ virtual ~ScrollEvent();
+ private:
+ ScrollFrameHelper *mHelper;
+ RefPtr<nsRefreshDriver> mDriver;
+ };
+
+ class AsyncScrollPortEvent : public Runnable {
+ public:
+ NS_DECL_NSIRUNNABLE
+ explicit AsyncScrollPortEvent(ScrollFrameHelper *helper) : mHelper(helper) {}
+ void Revoke() { mHelper = nullptr; }
+ private:
+ ScrollFrameHelper *mHelper;
+ };
+
+ class ScrolledAreaEvent : public Runnable {
+ public:
+ NS_DECL_NSIRUNNABLE
+ explicit ScrolledAreaEvent(ScrollFrameHelper *helper) : mHelper(helper) {}
+ void Revoke() { mHelper = nullptr; }
+ private:
+ ScrollFrameHelper *mHelper;
+ };
+
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void FinishReflowForScrollbar(nsIContent* aContent, nscoord aMinXY,
+ nscoord aMaxXY, nscoord aCurPosXY,
+ nscoord aPageIncrement,
+ nscoord aIncrement);
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void SetScrollbarEnabled(nsIContent* aContent, nscoord aMaxPos);
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void SetCoordAttribute(nsIContent* aContent, nsIAtom* aAtom, nscoord aSize);
+
+ nscoord GetCoordAttribute(nsIFrame* aFrame, nsIAtom* aAtom, nscoord aDefaultValue,
+ nscoord* aRangeStart, nscoord* aRangeLength);
+
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * Update scrollbar curpos attributes to reflect current scroll position
+ */
+ void UpdateScrollbarPosition();
+
+ nsRect GetScrollPortRect() const { return mScrollPort; }
+ nsPoint GetScrollPosition() const {
+ return mScrollPort.TopLeft() - mScrolledFrame->GetPosition();
+ }
+ /**
+ * For LTR frames, the logical scroll position is the offset of the top left
+ * corner of the frame from the top left corner of the scroll port (same as
+ * GetScrollPosition).
+ * For RTL frames, it is the offset of the top right corner of the frame from
+ * the top right corner of the scroll port
+ */
+ nsPoint GetLogicalScrollPosition() const {
+ nsPoint pt;
+ pt.x = IsPhysicalLTR() ?
+ mScrollPort.x - mScrolledFrame->GetPosition().x :
+ mScrollPort.XMost() - mScrolledFrame->GetRect().XMost();
+ pt.y = mScrollPort.y - mScrolledFrame->GetPosition().y;
+ return pt;
+ }
+ nsRect GetScrollRange() const;
+ // Get the scroll range assuming the scrollport has size (aWidth, aHeight).
+ nsRect GetScrollRange(nscoord aWidth, nscoord aHeight) const;
+ nsSize GetScrollPositionClampingScrollPortSize() const;
+ void ScrollSnap(nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD);
+ void ScrollSnap(const nsPoint &aDestination,
+ nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD);
+
+protected:
+ nsRect GetScrollRangeForClamping() const;
+
+public:
+ static void AsyncScrollCallback(ScrollFrameHelper* aInstance,
+ mozilla::TimeStamp aTime);
+ static void AsyncSmoothMSDScrollCallback(ScrollFrameHelper* aInstance,
+ mozilla::TimeDuration aDeltaTime);
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * aRange is the range of allowable scroll positions around the desired
+ * aScrollPosition. Null means only aScrollPosition is allowed.
+ * This is a closed-ended range --- aRange.XMost()/aRange.YMost() are allowed.
+ */
+ void ScrollTo(nsPoint aScrollPosition, nsIScrollableFrame::ScrollMode aMode,
+ const nsRect* aRange = nullptr,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) {
+ ScrollToWithOrigin(aScrollPosition, aMode, nsGkAtoms::other, aRange,
+ aSnap);
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode
+ = nsIScrollableFrame::INSTANT);
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
+ nsIAtom* aOrigin = nullptr);
+
+ CSSIntPoint GetScrollPositionCSSPixels();
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void ScrollToImpl(nsPoint aScrollPosition, const nsRect& aRange, nsIAtom* aOrigin = nullptr);
+ void ScrollVisual();
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void ScrollBy(nsIntPoint aDelta, nsIScrollableFrame::ScrollUnit aUnit,
+ nsIScrollableFrame::ScrollMode aMode, nsIntPoint* aOverflow,
+ nsIAtom* aOrigin = nullptr,
+ nsIScrollableFrame::ScrollMomentum aMomentum = nsIScrollableFrame::NOT_MOMENTUM,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP);
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void ScrollToRestoredPosition();
+
+ bool PageIsStillLoading();
+
+ /**
+ * GetSnapPointForDestination determines which point to snap to after
+ * scrolling. aStartPos gives the position before scrolling and aDestination
+ * gives the position after scrolling, with no snapping. Behaviour is
+ * dependent on the value of aUnit.
+ * Returns true if a suitable snap point could be found and aDestination has
+ * been updated to a valid snapping position.
+ */
+ bool GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUnit,
+ nsPoint aStartPos,
+ nsPoint &aDestination);
+
+ nsSize GetLineScrollAmount() const;
+ nsSize GetPageScrollAmount() const;
+
+ nsPresState* SaveState() const;
+ void RestoreState(nsPresState* aState);
+
+ nsIFrame* GetScrolledFrame() const { return mScrolledFrame; }
+ nsIFrame* GetScrollbarBox(bool aVertical) const {
+ return aVertical ? mVScrollbarBox : mHScrollbarBox;
+ }
+
+ void AddScrollPositionListener(nsIScrollPositionListener* aListener) {
+ mListeners.AppendElement(aListener);
+ }
+ void RemoveScrollPositionListener(nsIScrollPositionListener* aListener) {
+ mListeners.RemoveElement(aListener);
+ }
+
+ static void SetScrollbarVisibility(nsIFrame* aScrollbar, bool aVisible);
+
+ /**
+ * GetScrolledRect is designed to encapsulate deciding which
+ * directions of overflow should be reachable by scrolling and which
+ * should not. Callers should NOT depend on it having any particular
+ * behavior (although nsXULScrollFrame currently does).
+ *
+ * This should only be called when the scrolled frame has been
+ * reflowed with the scroll port size given in mScrollPort.
+ *
+ * Currently it allows scrolling down and to the right for
+ * nsHTMLScrollFrames with LTR directionality and for all
+ * nsXULScrollFrames, and allows scrolling down and to the left for
+ * nsHTMLScrollFrames with RTL directionality.
+ */
+ nsRect GetScrolledRect() const;
+
+ /**
+ * GetUnsnappedScrolledRectInternal is designed to encapsulate deciding which
+ * directions of overflow should be reachable by scrolling and which
+ * should not. Callers should NOT depend on it having any particular
+ * behavior (although nsXULScrollFrame currently does).
+ *
+ * Currently it allows scrolling down and to the right for
+ * nsHTMLScrollFrames with LTR directionality and for all
+ * nsXULScrollFrames, and allows scrolling down and to the left for
+ * nsHTMLScrollFrames with RTL directionality.
+ */
+ nsRect GetUnsnappedScrolledRectInternal(const nsRect& aScrolledOverflowArea,
+ const nsSize& aScrollPortSize) const;
+
+ uint32_t GetScrollbarVisibility() const {
+ return (mHasVerticalScrollbar ? nsIScrollableFrame::VERTICAL : 0) |
+ (mHasHorizontalScrollbar ? nsIScrollableFrame::HORIZONTAL : 0);
+ }
+ nsMargin GetActualScrollbarSizes() const;
+ nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState);
+ nscoord GetNondisappearingScrollbarWidth(nsBoxLayoutState* aState,
+ mozilla::WritingMode aVerticalWM);
+ bool IsPhysicalLTR() const {
+ WritingMode wm = GetFrameForDir()->GetWritingMode();
+ return wm.IsVertical() ? wm.IsVerticalLR() : wm.IsBidiLTR();
+ }
+ bool IsBidiLTR() const {
+ nsIFrame* frame = GetFrameForDir();
+ return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
+ }
+private:
+ nsIFrame* GetFrameForDir() const; // helper for Is{Physical,Bidi}LTR to find
+ // the frame whose directionality we use
+
+public:
+ bool IsScrollbarOnRight() const;
+ bool IsScrollingActive(nsDisplayListBuilder* aBuilder) const;
+ bool IsMaybeScrollingActive() const;
+ bool IsProcessingAsyncScroll() const {
+ return mAsyncScroll != nullptr || mAsyncSmoothMSDScroll != nullptr;
+ }
+ void ResetScrollPositionForLayerPixelAlignment()
+ {
+ mScrollPosForLayerPixelAlignment = GetScrollPosition();
+ }
+
+ bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas);
+
+ void UpdateSticky();
+
+ void UpdatePrevScrolledRect();
+
+ bool IsRectNearlyVisible(const nsRect& aRect) const;
+ nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const;
+
+ // adjust the scrollbar rectangle aRect to account for any visible resizer.
+ // aHasResizer specifies if there is a content resizer, however this method
+ // will also check if a widget resizer is present as well.
+ void AdjustScrollbarRectForResizer(nsIFrame* aFrame, nsPresContext* aPresContext,
+ nsRect& aRect, bool aHasResizer, bool aVertical);
+ // returns true if a resizer should be visible
+ bool HasResizer() { return mResizerBox && !mCollapsedResizer; }
+ void LayoutScrollbars(nsBoxLayoutState& aState,
+ const nsRect& aContentArea,
+ const nsRect& aOldScrollArea);
+
+ bool IsIgnoringViewportClipping() const;
+
+ void MarkScrollbarsDirtyForReflow() const;
+
+ bool ShouldClampScrollPosition() const;
+
+ bool IsAlwaysActive() const;
+ void MarkRecentlyScrolled();
+ void MarkNotRecentlyScrolled();
+ nsExpirationState* GetExpirationState() { return &mActivityExpirationState; }
+
+ void SetTransformingByAPZ(bool aTransforming) {
+ mTransformingByAPZ = aTransforming;
+ if (!mozilla::css::TextOverflow::HasClippedOverflow(mOuter)) {
+ // If the block has some text-overflow stuff we should kick off a paint
+ // because we have special behaviour for it when APZ scrolling is active.
+ mOuter->SchedulePaint();
+ }
+ }
+ bool IsTransformingByAPZ() const {
+ return mTransformingByAPZ;
+ }
+ void SetScrollableByAPZ(bool aScrollable);
+ void SetZoomableByAPZ(bool aZoomable);
+ void SetScrollsClipOnUnscrolledOutOfFlow();
+
+ bool UsesContainerScrolling() const;
+
+ ScrollSnapInfo GetScrollSnapInfo() const;
+
+ bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+ nsRect* aDirtyRect,
+ bool aAllowCreateDisplayPort);
+ void NotifyApproximateFrameVisibilityUpdate();
+ bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort);
+
+ bool AllowDisplayPortExpiration();
+ void TriggerDisplayPortExpiration();
+ void ResetDisplayPortExpiryTimer();
+
+ void ScheduleSyntheticMouseMove();
+ static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
+
+ void HandleScrollbarStyleSwitching();
+
+ nsIAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
+ void AllowScrollOriginDowngrade() { mAllowScrollOriginDowngrade = true; }
+ nsIAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
+ uint32_t CurrentScrollGeneration() const { return mScrollGeneration; }
+ nsPoint LastScrollDestination() const { return mDestination; }
+ void ResetScrollInfoIfGeneration(uint32_t aGeneration) {
+ if (aGeneration == mScrollGeneration) {
+ mLastScrollOrigin = nullptr;
+ mLastSmoothScrollOrigin = nullptr;
+ }
+ }
+ bool WantAsyncScroll() const;
+ Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
+ Layer* aLayer, nsIFrame* aContainerReferenceFrame,
+ const ContainerLayerParameters& aParameters,
+ const mozilla::DisplayItemClip* aClip) const;
+
+ // nsIScrollbarMediator
+ void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP);
+ void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP);
+ void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP);
+ void RepeatButtonScroll(nsScrollbarFrame* aScrollbar);
+ void ThumbMoved(nsScrollbarFrame* aScrollbar,
+ nscoord aOldPos,
+ nscoord aNewPos);
+ void ScrollbarReleased(nsScrollbarFrame* aScrollbar);
+ void ScrollByUnit(nsScrollbarFrame* aScrollbar,
+ nsIScrollableFrame::ScrollMode aMode,
+ int32_t aDirection,
+ nsIScrollableFrame::ScrollUnit aUnit,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP);
+ bool ShouldSuppressScrollbarRepaints() const {
+ return mSuppressScrollbarRepaints;
+ }
+
+ // owning references to the nsIAnonymousContentCreator-built content
+ nsCOMPtr<nsIContent> mHScrollbarContent;
+ nsCOMPtr<nsIContent> mVScrollbarContent;
+ nsCOMPtr<nsIContent> mScrollCornerContent;
+ nsCOMPtr<nsIContent> mResizerContent;
+
+ RefPtr<ScrollEvent> mScrollEvent;
+ nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent;
+ nsRevocableEventPtr<ScrolledAreaEvent> mScrolledAreaEvent;
+ nsIFrame* mHScrollbarBox;
+ nsIFrame* mVScrollbarBox;
+ nsIFrame* mScrolledFrame;
+ nsIFrame* mScrollCornerBox;
+ nsIFrame* mResizerBox;
+ nsContainerFrame* mOuter;
+ RefPtr<AsyncScroll> mAsyncScroll;
+ RefPtr<AsyncSmoothMSDScroll> mAsyncSmoothMSDScroll;
+ RefPtr<ScrollbarActivity> mScrollbarActivity;
+ nsTArray<nsIScrollPositionListener*> mListeners;
+ nsIAtom* mLastScrollOrigin;
+ bool mAllowScrollOriginDowngrade;
+ nsIAtom* mLastSmoothScrollOrigin;
+ Maybe<nsPoint> mApzSmoothScrollDestination;
+ uint32_t mScrollGeneration;
+ nsRect mScrollPort;
+ // Where we're currently scrolling to, if we're scrolling asynchronously.
+ // If we're not in the middle of an asynchronous scroll then this is
+ // just the current scroll position. ScrollBy will choose its
+ // destination based on this value.
+ nsPoint mDestination;
+ nsPoint mScrollPosAtLastPaint;
+
+ // A goal position to try to scroll to as content loads. As long as mLastPos
+ // matches the current logical scroll position, we try to scroll to mRestorePos
+ // after every reflow --- because after each time content is loaded/added to the
+ // scrollable element, there will be a reflow.
+ nsPoint mRestorePos;
+ // The last logical position we scrolled to while trying to restore mRestorePos, or
+ // 0,0 when this is a new frame. Set to -1,-1 once we've scrolled for any reason
+ // other than trying to restore mRestorePos.
+ nsPoint mLastPos;
+
+ nsExpirationState mActivityExpirationState;
+
+ nsCOMPtr<nsITimer> mScrollActivityTimer;
+ nsPoint mScrollPosForLayerPixelAlignment;
+
+ // The scroll position where we last updated frame visibility.
+ nsPoint mLastUpdateFramesPos;
+ bool mHadDisplayPortAtLastFrameUpdate;
+ nsRect mDisplayPortAtLastFrameUpdate;
+
+ nsRect mPrevScrolledRect;
+
+ FrameMetrics::ViewID mScrollParentID;
+
+ // Timer to remove the displayport some time after scrolling has stopped
+ nsCOMPtr<nsITimer> mDisplayPortExpiryTimer;
+
+ bool mNeverHasVerticalScrollbar:1;
+ bool mNeverHasHorizontalScrollbar:1;
+ bool mHasVerticalScrollbar:1;
+ bool mHasHorizontalScrollbar:1;
+ bool mFrameIsUpdatingScrollbar:1;
+ bool mDidHistoryRestore:1;
+ // Is this the scrollframe for the document's viewport?
+ bool mIsRoot:1;
+ // True if we should clip all descendants, false if we should only clip
+ // descendants for which we are the containing block.
+ bool mClipAllDescendants:1;
+ // If true, don't try to layout the scrollbars in Reflow(). This can be
+ // useful if multiple passes are involved, because we don't want to place the
+ // scrollbars at the wrong size.
+ bool mSupppressScrollbarUpdate:1;
+ // If true, we skipped a scrollbar layout due to mSupppressScrollbarUpdate
+ // being set at some point. That means we should lay out scrollbars even if
+ // it might not strictly be needed next time mSupppressScrollbarUpdate is
+ // false.
+ bool mSkippedScrollbarLayout:1;
+
+ bool mHadNonInitialReflow:1;
+ // State used only by PostScrollEvents so we know
+ // which overflow states have changed.
+ bool mHorizontalOverflow:1;
+ bool mVerticalOverflow:1;
+ bool mPostedReflowCallback:1;
+ bool mMayHaveDirtyFixedChildren:1;
+ // If true, need to actually update our scrollbar attributes in the
+ // reflow callback.
+ bool mUpdateScrollbarAttributes:1;
+ // If true, we should be prepared to scroll using this scrollframe
+ // by placing descendant content into its own layer(s)
+ bool mHasBeenScrolledRecently:1;
+ // If true, the resizer is collapsed and not displayed
+ bool mCollapsedResizer:1;
+
+ // If true, the scroll frame should always be active because we always build
+ // a scrollable layer. Used for asynchronous scrolling.
+ bool mWillBuildScrollableLayer:1;
+
+ // If true, the scroll frame is an ancestor of other scrolling frames, so
+ // we shouldn't expire the displayport on this scrollframe unless those
+ // descendant scrollframes also have their displayports removed.
+ bool mIsScrollParent:1;
+
+ // Whether we are the root scroll frame that is used for containerful
+ // scrolling with a display port. If true, the scrollable frame
+ // shouldn't attach frame metrics to its layers because the container
+ // will already have the necessary frame metrics.
+ bool mIsScrollableLayerInRootContainer:1;
+
+ // If true, add clipping in ScrollFrameHelper::ComputeFrameMetrics.
+ bool mAddClipRectToLayer:1;
+
+ // True if this frame has been scrolled at least once
+ bool mHasBeenScrolled:1;
+
+ // True if the events synthesized by OSX to produce momentum scrolling should
+ // be ignored. Reset when the next real, non-synthesized scroll event occurs.
+ bool mIgnoreMomentumScroll:1;
+
+ // True if the APZ is in the process of async-transforming this scrollframe,
+ // (as best as we can tell on the main thread, anyway).
+ bool mTransformingByAPZ:1;
+
+ // True if APZ can scroll this frame asynchronously (i.e. it has an APZC
+ // set up for this frame and it's not a scrollinfo layer).
+ bool mScrollableByAPZ:1;
+
+ // True if the APZ is allowed to zoom this scrollframe.
+ bool mZoomableByAPZ:1;
+
+ // True if we don't want the scrollbar to repaint itself right now.
+ bool mSuppressScrollbarRepaints:1;
+
+ bool mScrollsClipOnUnscrolledOutOfFlow:1;
+
+ mozilla::layout::ScrollVelocityQueue mVelocityQueue;
+
+protected:
+ class AutoScrollbarRepaintSuppression;
+ friend class AutoScrollbarRepaintSuppression;
+ class AutoScrollbarRepaintSuppression {
+ public:
+ AutoScrollbarRepaintSuppression(ScrollFrameHelper* aHelper, bool aSuppress)
+ : mHelper(aHelper)
+ , mOldSuppressValue(aHelper->mSuppressScrollbarRepaints)
+ {
+ mHelper->mSuppressScrollbarRepaints = aSuppress;
+ }
+
+ ~AutoScrollbarRepaintSuppression()
+ {
+ mHelper->mSuppressScrollbarRepaints = mOldSuppressValue;
+ }
+
+ private:
+ ScrollFrameHelper* mHelper;
+ bool mOldSuppressValue;
+ };
+
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ void ScrollToWithOrigin(nsPoint aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode,
+ nsIAtom *aOrigin, // nullptr indicates "other" origin
+ const nsRect* aRange,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP);
+
+ void CompleteAsyncScroll(const nsRect &aRange, nsIAtom* aOrigin = nullptr);
+
+ bool HasPluginFrames();
+ bool HasPerspective() const;
+ bool HasBgAttachmentLocal() const;
+ uint8_t GetScrolledFrameDir() const;
+
+ static void EnsureFrameVisPrefsCached();
+ static bool sFrameVisPrefsCached;
+ // The number of scrollports wide/high to expand when tracking frame visibility.
+ static uint32_t sHorzExpandScrollPort;
+ static uint32_t sVertExpandScrollPort;
+ // The fraction of the scrollport we allow to scroll by before we schedule
+ // an update of frame visibility.
+ static int32_t sHorzScrollFraction;
+ static int32_t sVertScrollFraction;
+};
+
+} // namespace mozilla
+
+/**
+ * The scroll frame creates and manages the scrolling view
+ *
+ * It only supports having a single child frame that typically is an area
+ * frame, but doesn't have to be. The child frame must have a view, though
+ *
+ * Scroll frames don't support incremental changes, i.e. you can't replace
+ * or remove the scrolled frame
+ */
+class nsHTMLScrollFrame : public nsContainerFrame,
+ public nsIScrollableFrame,
+ public nsIAnonymousContentCreator,
+ public nsIStatefulFrame {
+public:
+ typedef mozilla::ScrollFrameHelper ScrollFrameHelper;
+ typedef mozilla::CSSIntPoint CSSIntPoint;
+ typedef mozilla::ScrollReflowInput ScrollReflowInput;
+ friend nsHTMLScrollFrame* NS_NewHTMLScrollFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext,
+ bool aIsRoot);
+
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ virtual mozilla::WritingMode GetWritingMode() const override
+ {
+ if (mHelper.mScrolledFrame) {
+ return mHelper.mScrolledFrame->GetWritingMode();
+ }
+ return nsIFrame::GetWritingMode();
+ }
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override {
+ mHelper.BuildDisplayList(aBuilder, aDirtyRect, aLists);
+ }
+
+ bool TryLayout(ScrollReflowInput* aState,
+ ReflowOutput* aKidMetrics,
+ bool aAssumeVScroll, bool aAssumeHScroll,
+ bool aForce);
+ bool ScrolledContentDependsOnHeight(ScrollReflowInput* aState);
+ void ReflowScrolledFrame(ScrollReflowInput* aState,
+ bool aAssumeHScroll,
+ bool aAssumeVScroll,
+ ReflowOutput* aMetrics,
+ bool aFirstPass);
+ void ReflowContents(ScrollReflowInput* aState,
+ const ReflowOutput& aDesiredSize);
+ void PlaceScrollArea(ScrollReflowInput& aState,
+ const nsPoint& aScrollPosition);
+ nscoord GetIntrinsicVScrollbarWidth(nsRenderingContext *aRenderingContext);
+
+ virtual bool GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
+ Sides aSkipSides, nscoord aRadii[8]) const override {
+ return mHelper.GetBorderRadii(aFrameSize, aBorderArea, aSkipSides, aRadii);
+ }
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual nsresult GetXULPadding(nsMargin& aPadding) override;
+ virtual bool IsXULCollapsed() override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
+ return mHelper.ComputeCustomOverflow(aOverflowAreas);
+ }
+
+ bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+ nscoord* aBaseline) const override {
+ *aBaseline = GetLogicalBaseline(aWM);
+ return true;
+ }
+
+ // Recomputes the scrollable overflow area we store in the helper to take children
+ // that are affected by perpsective set on the outer frame and scroll at different
+ // rates.
+ void AdjustForPerspective(nsRect& aScrollableOverflow);
+
+ // Called to set the child frames. We typically have three: the scroll area,
+ // the vertical scrollbar, and the horizontal scrollbar.
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ virtual nsIScrollableFrame* GetScrollTargetFrame() override {
+ return this;
+ }
+
+ virtual nsContainerFrame* GetContentInsertionFrame() override {
+ return mHelper.GetScrolledFrame()->GetContentInsertionFrame();
+ }
+
+ virtual bool DoesClipChildren() override { return true; }
+ virtual nsSplittableType GetSplittableType() const override;
+
+ virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild) override
+ { nsPoint pt = aChild->GetPosition();
+ if (aChild == mHelper.GetScrolledFrame()) pt += GetScrollPosition();
+ return pt;
+ }
+
+ // nsIAnonymousContentCreator
+ virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
+ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter) override;
+
+ // nsIScrollableFrame
+ virtual nsIFrame* GetScrolledFrame() const override {
+ return mHelper.GetScrolledFrame();
+ }
+ virtual mozilla::ScrollbarStyles GetScrollbarStyles() const override {
+ return mHelper.GetScrollbarStylesFromFrame();
+ }
+ virtual uint32_t GetScrollbarVisibility() const override {
+ return mHelper.GetScrollbarVisibility();
+ }
+ virtual nsMargin GetActualScrollbarSizes() const override {
+ return mHelper.GetActualScrollbarSizes();
+ }
+ virtual nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState) override {
+ return mHelper.GetDesiredScrollbarSizes(aState);
+ }
+ virtual nsMargin GetDesiredScrollbarSizes(nsPresContext* aPresContext,
+ nsRenderingContext* aRC) override {
+ nsBoxLayoutState bls(aPresContext, aRC, 0);
+ return GetDesiredScrollbarSizes(&bls);
+ }
+ virtual nscoord GetNondisappearingScrollbarWidth(nsPresContext* aPresContext,
+ nsRenderingContext* aRC, mozilla::WritingMode aWM) override {
+ nsBoxLayoutState bls(aPresContext, aRC, 0);
+ return mHelper.GetNondisappearingScrollbarWidth(&bls, aWM);
+ }
+ virtual nsRect GetScrolledRect() const override {
+ return mHelper.GetScrolledRect();
+ }
+ virtual nsRect GetScrollPortRect() const override {
+ return mHelper.GetScrollPortRect();
+ }
+ virtual nsPoint GetScrollPosition() const override {
+ return mHelper.GetScrollPosition();
+ }
+ virtual nsPoint GetLogicalScrollPosition() const override {
+ return mHelper.GetLogicalScrollPosition();
+ }
+ virtual nsRect GetScrollRange() const override {
+ return mHelper.GetScrollRange();
+ }
+ virtual nsSize GetScrollPositionClampingScrollPortSize() const override {
+ return mHelper.GetScrollPositionClampingScrollPortSize();
+ }
+ virtual nsSize GetLineScrollAmount() const override {
+ return mHelper.GetLineScrollAmount();
+ }
+ virtual nsSize GetPageScrollAmount() const override {
+ return mHelper.GetPageScrollAmount();
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
+ const nsRect* aRange = nullptr,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP)
+ override {
+ mHelper.ScrollTo(aScrollPosition, aMode, aRange, aSnap);
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode
+ = nsIScrollableFrame::INSTANT) override {
+ mHelper.ScrollToCSSPixels(aScrollPosition, aMode);
+ }
+ virtual void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
+ nsIAtom* aOrigin = nullptr) override {
+ mHelper.ScrollToCSSPixelsApproximate(aScrollPosition, aOrigin);
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual CSSIntPoint GetScrollPositionCSSPixels() override {
+ return mHelper.GetScrollPositionCSSPixels();
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit, ScrollMode aMode,
+ nsIntPoint* aOverflow, nsIAtom* aOrigin = nullptr,
+ nsIScrollableFrame::ScrollMomentum aMomentum = nsIScrollableFrame::NOT_MOMENTUM,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP)
+ override {
+ mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum, aSnap);
+ }
+ virtual void ScrollSnap() override {
+ mHelper.ScrollSnap();
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollToRestoredPosition() override {
+ mHelper.ScrollToRestoredPosition();
+ }
+ virtual void AddScrollPositionListener(nsIScrollPositionListener* aListener) override {
+ mHelper.AddScrollPositionListener(aListener);
+ }
+ virtual void RemoveScrollPositionListener(nsIScrollPositionListener* aListener) override {
+ mHelper.RemoveScrollPositionListener(aListener);
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void CurPosAttributeChanged(nsIContent* aChild) override {
+ mHelper.CurPosAttributeChanged(aChild);
+ }
+ NS_IMETHOD PostScrolledAreaEventForCurrentArea() override {
+ mHelper.PostScrolledAreaEvent();
+ return NS_OK;
+ }
+ virtual bool IsScrollingActive(nsDisplayListBuilder* aBuilder) override {
+ return mHelper.IsScrollingActive(aBuilder);
+ }
+ virtual bool IsProcessingAsyncScroll() override {
+ return mHelper.IsProcessingAsyncScroll();
+ }
+ virtual void ResetScrollPositionForLayerPixelAlignment() override {
+ mHelper.ResetScrollPositionForLayerPixelAlignment();
+ }
+ virtual bool DidHistoryRestore() const override {
+ return mHelper.mDidHistoryRestore;
+ }
+ virtual void ClearDidHistoryRestore() override {
+ mHelper.mDidHistoryRestore = false;
+ }
+ virtual bool IsRectNearlyVisible(const nsRect& aRect) override {
+ return mHelper.IsRectNearlyVisible(aRect);
+ }
+ virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const override {
+ return mHelper.ExpandRectToNearlyVisible(aRect);
+ }
+ virtual nsIAtom* LastScrollOrigin() override {
+ return mHelper.LastScrollOrigin();
+ }
+ virtual void AllowScrollOriginDowngrade() override {
+ mHelper.AllowScrollOriginDowngrade();
+ }
+ virtual nsIAtom* LastSmoothScrollOrigin() override {
+ return mHelper.LastSmoothScrollOrigin();
+ }
+ virtual uint32_t CurrentScrollGeneration() override {
+ return mHelper.CurrentScrollGeneration();
+ }
+ virtual nsPoint LastScrollDestination() override {
+ return mHelper.LastScrollDestination();
+ }
+ virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) override {
+ mHelper.ResetScrollInfoIfGeneration(aGeneration);
+ }
+ virtual bool WantAsyncScroll() const override {
+ return mHelper.WantAsyncScroll();
+ }
+ virtual mozilla::Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
+ Layer* aLayer, nsIFrame* aContainerReferenceFrame,
+ const ContainerLayerParameters& aParameters,
+ const mozilla::DisplayItemClip* aClip) const override
+ {
+ return mHelper.ComputeScrollMetadata(aLayer, aContainerReferenceFrame, aParameters, aClip);
+ }
+ virtual bool IsIgnoringViewportClipping() const override {
+ return mHelper.IsIgnoringViewportClipping();
+ }
+ virtual void MarkScrollbarsDirtyForReflow() const override {
+ mHelper.MarkScrollbarsDirtyForReflow();
+ }
+ virtual bool UsesContainerScrolling() const override {
+ return mHelper.UsesContainerScrolling();
+ }
+ virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+ nsRect* aDirtyRect,
+ bool aAllowCreateDisplayPort) override {
+ return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
+ }
+ virtual void NotifyApproximateFrameVisibilityUpdate() override {
+ mHelper.NotifyApproximateFrameVisibilityUpdate();
+ }
+ virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) override {
+ return mHelper.GetDisplayPortAtLastApproximateFrameVisibilityUpdate(aDisplayPort);
+ }
+ void TriggerDisplayPortExpiration() override {
+ mHelper.TriggerDisplayPortExpiration();
+ }
+
+ // nsIStatefulFrame
+ NS_IMETHOD SaveState(nsPresState** aState) override {
+ NS_ENSURE_ARG_POINTER(aState);
+ *aState = mHelper.SaveState();
+ return NS_OK;
+ }
+ NS_IMETHOD RestoreState(nsPresState* aState) override {
+ NS_ENSURE_ARG_POINTER(aState);
+ mHelper.RestoreState(aState);
+ return NS_OK;
+ }
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::scrollFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+ // nsIScrollbarMediator
+ virtual void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) override {
+ mHelper.ScrollByPage(aScrollbar, aDirection, aSnap);
+ }
+ virtual void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) override {
+ mHelper.ScrollByWhole(aScrollbar, aDirection, aSnap);
+ }
+ virtual void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) override {
+ mHelper.ScrollByLine(aScrollbar, aDirection, aSnap);
+ }
+ virtual void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) override {
+ mHelper.RepeatButtonScroll(aScrollbar);
+ }
+ virtual void ThumbMoved(nsScrollbarFrame* aScrollbar,
+ nscoord aOldPos,
+ nscoord aNewPos) override {
+ mHelper.ThumbMoved(aScrollbar, aOldPos, aNewPos);
+ }
+ virtual void ScrollbarReleased(nsScrollbarFrame* aScrollbar) override {
+ mHelper.ScrollbarReleased(aScrollbar);
+ }
+ virtual void VisibilityChanged(bool aVisible) override {}
+ virtual nsIFrame* GetScrollbarBox(bool aVertical) override {
+ return mHelper.GetScrollbarBox(aVertical);
+ }
+ virtual void ScrollbarActivityStarted() const override;
+ virtual void ScrollbarActivityStopped() const override;
+
+ virtual bool IsScrollbarOnRight() const override {
+ return mHelper.IsScrollbarOnRight();
+ }
+
+ virtual bool ShouldSuppressScrollbarRepaints() const override {
+ return mHelper.ShouldSuppressScrollbarRepaints();
+ }
+
+ virtual void SetTransformingByAPZ(bool aTransforming) override {
+ mHelper.SetTransformingByAPZ(aTransforming);
+ }
+ bool IsTransformingByAPZ() const override {
+ return mHelper.IsTransformingByAPZ();
+ }
+ void SetScrollableByAPZ(bool aScrollable) override {
+ mHelper.SetScrollableByAPZ(aScrollable);
+ }
+ void SetZoomableByAPZ(bool aZoomable) override {
+ mHelper.SetZoomableByAPZ(aZoomable);
+ }
+ void SetScrollsClipOnUnscrolledOutOfFlow() override {
+ mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
+ }
+
+ ScrollSnapInfo GetScrollSnapInfo() const override {
+ return mHelper.GetScrollSnapInfo();
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+protected:
+ nsHTMLScrollFrame(nsStyleContext* aContext, bool aIsRoot);
+ void SetSuppressScrollbarUpdate(bool aSuppress) {
+ mHelper.mSupppressScrollbarUpdate = aSuppress;
+ }
+ bool GuessHScrollbarNeeded(const ScrollReflowInput& aState);
+ bool GuessVScrollbarNeeded(const ScrollReflowInput& aState);
+
+ bool IsScrollbarUpdateSuppressed() const {
+ return mHelper.mSupppressScrollbarUpdate;
+ }
+
+ // Return whether we're in an "initial" reflow. Some reflows with
+ // NS_FRAME_FIRST_REFLOW set are NOT "initial" as far as we're concerned.
+ bool InInitialReflow() const;
+
+ /**
+ * Override this to return false if computed bsize/min-bsize/max-bsize
+ * should NOT be propagated to child content.
+ * nsListControlFrame uses this.
+ */
+ virtual bool ShouldPropagateComputedBSizeToScrolledContent() const { return true; }
+
+private:
+ friend class mozilla::ScrollFrameHelper;
+ ScrollFrameHelper mHelper;
+};
+
+/**
+ * The scroll frame creates and manages the scrolling view
+ *
+ * It only supports having a single child frame that typically is an area
+ * frame, but doesn't have to be. The child frame must have a view, though
+ *
+ * Scroll frames don't support incremental changes, i.e. you can't replace
+ * or remove the scrolled frame
+ */
+class nsXULScrollFrame final : public nsBoxFrame,
+ public nsIScrollableFrame,
+ public nsIAnonymousContentCreator,
+ public nsIStatefulFrame
+{
+public:
+ typedef mozilla::ScrollFrameHelper ScrollFrameHelper;
+ typedef mozilla::CSSIntPoint CSSIntPoint;
+
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsXULScrollFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext,
+ bool aIsRoot,
+ bool aClipAllDescendants);
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override {
+ mHelper.BuildDisplayList(aBuilder, aDirtyRect, aLists);
+ }
+
+ // XXXldb Is this actually used?
+#if 0
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+#endif
+
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
+ return mHelper.ComputeCustomOverflow(aOverflowAreas);
+ }
+
+ bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+ nscoord* aBaseline) const override {
+ *aBaseline = GetLogicalBaseline(aWM);
+ return true;
+ }
+
+ // Called to set the child frames. We typically have three: the scroll area,
+ // the vertical scrollbar, and the horizontal scrollbar.
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+
+ virtual nsIScrollableFrame* GetScrollTargetFrame() override {
+ return this;
+ }
+
+ virtual nsContainerFrame* GetContentInsertionFrame() override {
+ return mHelper.GetScrolledFrame()->GetContentInsertionFrame();
+ }
+
+ virtual bool DoesClipChildren() override { return true; }
+ virtual nsSplittableType GetSplittableType() const override;
+
+ virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild) override
+ { nsPoint pt = aChild->GetPosition();
+ if (aChild == mHelper.GetScrolledFrame())
+ pt += mHelper.GetLogicalScrollPosition();
+ return pt;
+ }
+
+ // nsIAnonymousContentCreator
+ virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
+ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter) override;
+
+ virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsSize GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nscoord GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState) override;
+
+ NS_IMETHOD DoXULLayout(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsresult GetXULPadding(nsMargin& aPadding) override;
+
+ virtual bool GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
+ Sides aSkipSides, nscoord aRadii[8]) const override {
+ return mHelper.GetBorderRadii(aFrameSize, aBorderArea, aSkipSides, aRadii);
+ }
+
+ nsresult XULLayout(nsBoxLayoutState& aState);
+ void LayoutScrollArea(nsBoxLayoutState& aState, const nsPoint& aScrollPosition);
+
+ static bool AddRemoveScrollbar(bool& aHasScrollbar,
+ nscoord& aXY,
+ nscoord& aSize,
+ nscoord aSbSize,
+ bool aOnRightOrBottom,
+ bool aAdd);
+
+ bool AddRemoveScrollbar(nsBoxLayoutState& aState,
+ bool aOnRightOrBottom,
+ bool aHorizontal,
+ bool aAdd);
+
+ bool AddHorizontalScrollbar (nsBoxLayoutState& aState, bool aOnBottom);
+ bool AddVerticalScrollbar (nsBoxLayoutState& aState, bool aOnRight);
+ void RemoveHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom);
+ void RemoveVerticalScrollbar (nsBoxLayoutState& aState, bool aOnRight);
+
+ static void AdjustReflowInputForPrintPreview(nsBoxLayoutState& aState, bool& aSetBack);
+ static void AdjustReflowInputBack(nsBoxLayoutState& aState, bool aSetBack);
+
+ // nsIScrollableFrame
+ virtual nsIFrame* GetScrolledFrame() const override {
+ return mHelper.GetScrolledFrame();
+ }
+ virtual mozilla::ScrollbarStyles GetScrollbarStyles() const override {
+ return mHelper.GetScrollbarStylesFromFrame();
+ }
+ virtual uint32_t GetScrollbarVisibility() const override {
+ return mHelper.GetScrollbarVisibility();
+ }
+ virtual nsMargin GetActualScrollbarSizes() const override {
+ return mHelper.GetActualScrollbarSizes();
+ }
+ virtual nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState) override {
+ return mHelper.GetDesiredScrollbarSizes(aState);
+ }
+ virtual nsMargin GetDesiredScrollbarSizes(nsPresContext* aPresContext,
+ nsRenderingContext* aRC) override {
+ nsBoxLayoutState bls(aPresContext, aRC, 0);
+ return GetDesiredScrollbarSizes(&bls);
+ }
+ virtual nscoord GetNondisappearingScrollbarWidth(nsPresContext* aPresContext,
+ nsRenderingContext* aRC, mozilla::WritingMode aWM) override {
+ nsBoxLayoutState bls(aPresContext, aRC, 0);
+ return mHelper.GetNondisappearingScrollbarWidth(&bls, aWM);
+ }
+ virtual nsRect GetScrolledRect() const override {
+ return mHelper.GetScrolledRect();
+ }
+ virtual nsRect GetScrollPortRect() const override {
+ return mHelper.GetScrollPortRect();
+ }
+ virtual nsPoint GetScrollPosition() const override {
+ return mHelper.GetScrollPosition();
+ }
+ virtual nsPoint GetLogicalScrollPosition() const override {
+ return mHelper.GetLogicalScrollPosition();
+ }
+ virtual nsRect GetScrollRange() const override {
+ return mHelper.GetScrollRange();
+ }
+ virtual nsSize GetScrollPositionClampingScrollPortSize() const override {
+ return mHelper.GetScrollPositionClampingScrollPortSize();
+ }
+ virtual nsSize GetLineScrollAmount() const override {
+ return mHelper.GetLineScrollAmount();
+ }
+ virtual nsSize GetPageScrollAmount() const override {
+ return mHelper.GetPageScrollAmount();
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
+ const nsRect* aRange = nullptr,
+ ScrollSnapMode aSnap = nsIScrollbarMediator::DISABLE_SNAP)
+ override {
+ mHelper.ScrollTo(aScrollPosition, aMode, aRange, aSnap);
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode
+ = nsIScrollableFrame::INSTANT) override {
+ mHelper.ScrollToCSSPixels(aScrollPosition, aMode);
+ }
+ virtual void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
+ nsIAtom* aOrigin = nullptr) override {
+ mHelper.ScrollToCSSPixelsApproximate(aScrollPosition, aOrigin);
+ }
+ virtual CSSIntPoint GetScrollPositionCSSPixels() override {
+ return mHelper.GetScrollPositionCSSPixels();
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit, ScrollMode aMode,
+ nsIntPoint* aOverflow, nsIAtom* aOrigin = nullptr,
+ nsIScrollableFrame::ScrollMomentum aMomentum = nsIScrollableFrame::NOT_MOMENTUM,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP)
+ override {
+ mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum, aSnap);
+ }
+ virtual void ScrollSnap() override {
+ mHelper.ScrollSnap();
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void ScrollToRestoredPosition() override {
+ mHelper.ScrollToRestoredPosition();
+ }
+ virtual void AddScrollPositionListener(nsIScrollPositionListener* aListener) override {
+ mHelper.AddScrollPositionListener(aListener);
+ }
+ virtual void RemoveScrollPositionListener(nsIScrollPositionListener* aListener) override {
+ mHelper.RemoveScrollPositionListener(aListener);
+ }
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ */
+ virtual void CurPosAttributeChanged(nsIContent* aChild) override {
+ mHelper.CurPosAttributeChanged(aChild);
+ }
+ NS_IMETHOD PostScrolledAreaEventForCurrentArea() override {
+ mHelper.PostScrolledAreaEvent();
+ return NS_OK;
+ }
+ virtual bool IsScrollingActive(nsDisplayListBuilder* aBuilder) override {
+ return mHelper.IsScrollingActive(aBuilder);
+ }
+ virtual bool IsProcessingAsyncScroll() override {
+ return mHelper.IsProcessingAsyncScroll();
+ }
+ virtual void ResetScrollPositionForLayerPixelAlignment() override {
+ mHelper.ResetScrollPositionForLayerPixelAlignment();
+ }
+ virtual bool DidHistoryRestore() const override {
+ return mHelper.mDidHistoryRestore;
+ }
+ virtual void ClearDidHistoryRestore() override {
+ mHelper.mDidHistoryRestore = false;
+ }
+ virtual bool IsRectNearlyVisible(const nsRect& aRect) override {
+ return mHelper.IsRectNearlyVisible(aRect);
+ }
+ virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const override {
+ return mHelper.ExpandRectToNearlyVisible(aRect);
+ }
+ virtual nsIAtom* LastScrollOrigin() override {
+ return mHelper.LastScrollOrigin();
+ }
+ virtual void AllowScrollOriginDowngrade() override {
+ mHelper.AllowScrollOriginDowngrade();
+ }
+ virtual nsIAtom* LastSmoothScrollOrigin() override {
+ return mHelper.LastSmoothScrollOrigin();
+ }
+ virtual uint32_t CurrentScrollGeneration() override {
+ return mHelper.CurrentScrollGeneration();
+ }
+ virtual nsPoint LastScrollDestination() override {
+ return mHelper.LastScrollDestination();
+ }
+ virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) override {
+ mHelper.ResetScrollInfoIfGeneration(aGeneration);
+ }
+ virtual bool WantAsyncScroll() const override {
+ return mHelper.WantAsyncScroll();
+ }
+ virtual mozilla::Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
+ Layer* aLayer, nsIFrame* aContainerReferenceFrame,
+ const ContainerLayerParameters& aParameters,
+ const mozilla::DisplayItemClip* aClip) const override
+ {
+ return mHelper.ComputeScrollMetadata(aLayer, aContainerReferenceFrame, aParameters, aClip);
+ }
+ virtual bool IsIgnoringViewportClipping() const override {
+ return mHelper.IsIgnoringViewportClipping();
+ }
+ virtual void MarkScrollbarsDirtyForReflow() const override {
+ mHelper.MarkScrollbarsDirtyForReflow();
+ }
+
+ // nsIStatefulFrame
+ NS_IMETHOD SaveState(nsPresState** aState) override {
+ NS_ENSURE_ARG_POINTER(aState);
+ *aState = mHelper.SaveState();
+ return NS_OK;
+ }
+ NS_IMETHOD RestoreState(nsPresState* aState) override {
+ NS_ENSURE_ARG_POINTER(aState);
+ mHelper.RestoreState(aState);
+ return NS_OK;
+ }
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::scrollFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ // Override bogus IsFrameOfType in nsBoxFrame.
+ if (aFlags & (nsIFrame::eReplacedContainsBlock | nsIFrame::eReplaced))
+ return false;
+ return nsBoxFrame::IsFrameOfType(aFlags);
+ }
+
+ virtual void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) override {
+ mHelper.ScrollByPage(aScrollbar, aDirection, aSnap);
+ }
+ virtual void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) override {
+ mHelper.ScrollByWhole(aScrollbar, aDirection, aSnap);
+ }
+ virtual void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) override {
+ mHelper.ScrollByLine(aScrollbar, aDirection, aSnap);
+ }
+ virtual void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) override {
+ mHelper.RepeatButtonScroll(aScrollbar);
+ }
+ virtual void ThumbMoved(nsScrollbarFrame* aScrollbar,
+ nscoord aOldPos,
+ nscoord aNewPos) override {
+ mHelper.ThumbMoved(aScrollbar, aOldPos, aNewPos);
+ }
+ virtual void ScrollbarReleased(nsScrollbarFrame* aScrollbar) override {
+ mHelper.ScrollbarReleased(aScrollbar);
+ }
+ virtual void VisibilityChanged(bool aVisible) override {}
+ virtual nsIFrame* GetScrollbarBox(bool aVertical) override {
+ return mHelper.GetScrollbarBox(aVertical);
+ }
+
+ virtual void ScrollbarActivityStarted() const override;
+ virtual void ScrollbarActivityStopped() const override;
+
+ virtual bool IsScrollbarOnRight() const override {
+ return mHelper.IsScrollbarOnRight();
+ }
+
+ virtual bool ShouldSuppressScrollbarRepaints() const override {
+ return mHelper.ShouldSuppressScrollbarRepaints();
+ }
+
+ virtual void SetTransformingByAPZ(bool aTransforming) override {
+ mHelper.SetTransformingByAPZ(aTransforming);
+ }
+ virtual bool UsesContainerScrolling() const override {
+ return mHelper.UsesContainerScrolling();
+ }
+ bool IsTransformingByAPZ() const override {
+ return mHelper.IsTransformingByAPZ();
+ }
+ void SetScrollableByAPZ(bool aScrollable) override {
+ mHelper.SetScrollableByAPZ(aScrollable);
+ }
+ void SetZoomableByAPZ(bool aZoomable) override {
+ mHelper.SetZoomableByAPZ(aZoomable);
+ }
+ void SetScrollsClipOnUnscrolledOutOfFlow() override {
+ mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
+ }
+ virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+ nsRect* aDirtyRect,
+ bool aAllowCreateDisplayPort) override {
+ return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
+ }
+ virtual void NotifyApproximateFrameVisibilityUpdate() override {
+ mHelper.NotifyApproximateFrameVisibilityUpdate();
+ }
+ virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) override {
+ return mHelper.GetDisplayPortAtLastApproximateFrameVisibilityUpdate(aDisplayPort);
+ }
+ void TriggerDisplayPortExpiration() override {
+ mHelper.TriggerDisplayPortExpiration();
+ }
+
+ ScrollSnapInfo GetScrollSnapInfo() const override {
+ return mHelper.GetScrollSnapInfo();
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+protected:
+ nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
+ bool aClipAllDescendants);
+
+ void ClampAndSetBounds(nsBoxLayoutState& aState,
+ nsRect& aRect,
+ nsPoint aScrollPosition,
+ bool aRemoveOverflowAreas = false) {
+ /*
+ * For RTL frames, restore the original scrolled position of the right
+ * edge, then subtract the current width to find the physical position.
+ */
+ if (!mHelper.IsPhysicalLTR()) {
+ aRect.x = mHelper.mScrollPort.XMost() - aScrollPosition.x - aRect.width;
+ }
+ mHelper.mScrolledFrame->SetXULBounds(aState, aRect, aRemoveOverflowAreas);
+ }
+
+private:
+ friend class mozilla::ScrollFrameHelper;
+ ScrollFrameHelper mHelper;
+};
+
+#endif /* nsGfxScrollFrame_h___ */
diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp
new file mode 100644
index 000000000..71d5bba21
--- /dev/null
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -0,0 +1,7106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: grid | inline-grid" */
+
+#include "nsGridContainerFrame.h"
+
+#include <algorithm> // for std::stable_sort
+#include <limits>
+#include "mozilla/CSSAlignUtils.h"
+#include "mozilla/dom/GridBinding.h"
+#include "mozilla/Function.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/PodOperations.h" // for PodZero
+#include "mozilla/Poison.h"
+#include "nsAbsoluteContainingBlock.h"
+#include "nsAlgorithm.h" // for clamped()
+#include "nsCSSAnonBoxes.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsDataHashtable.h"
+#include "nsDisplayList.h"
+#include "nsHashKeys.h"
+#include "nsIFrameInlines.h"
+#include "nsPresContext.h"
+#include "nsReadableUtils.h"
+#include "nsRenderingContext.h"
+#include "nsRuleNode.h"
+#include "nsStyleContext.h"
+#include "nsTableWrapperFrame.h"
+
+#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 8
+#define CLANG_CRASH_BUG 1
+#endif
+
+using namespace mozilla;
+
+typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
+typedef nsGridContainerFrame::TrackSize TrackSize;
+const uint32_t nsGridContainerFrame::kTranslatedMaxLine =
+ uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine);
+const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U;
+typedef nsTHashtable< nsPtrHashKey<nsIFrame> > FrameHashtable;
+typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
+typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
+
+// https://drafts.csswg.org/css-sizing/#constraints
+enum class SizingConstraint
+{
+ eMinContent, // sizing under min-content constraint
+ eMaxContent, // sizing under max-content constraint
+ eNoConstraint // no constraint, used during Reflow
+};
+
+static void
+ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
+ nsContainerFrame* aNewParent)
+{
+ NS_ASSERTION(aOldParent == aFrame->GetParent(),
+ "Parent not consistent with expectations");
+
+ aFrame->SetParent(aNewParent);
+
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented
+ nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent);
+}
+
+static void
+ReparentFrames(nsFrameList& aFrameList, nsContainerFrame* aOldParent,
+ nsContainerFrame* aNewParent)
+{
+ for (auto f : aFrameList) {
+ ReparentFrame(f, aOldParent, aNewParent);
+ }
+}
+
+static nscoord
+ClampToCSSMaxBSize(nscoord aSize, const ReflowInput* aReflowInput)
+{
+ auto maxSize = aReflowInput->ComputedMaxBSize();
+ if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
+ MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
+ aSize = std::min(aSize, maxSize);
+ }
+ return aSize;
+}
+
+// Same as above and set aStatus INCOMPLETE if aSize wasn't clamped.
+// (If we clamp aSize it means our size is less than the break point,
+// i.e. we're effectively breaking in our overflow, so we should leave
+// aStatus as is (it will likely be set to OVERFLOW_INCOMPLETE later)).
+static nscoord
+ClampToCSSMaxBSize(nscoord aSize, const ReflowInput* aReflowInput,
+ nsReflowStatus* aStatus)
+{
+ auto maxSize = aReflowInput->ComputedMaxBSize();
+ if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
+ MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
+ if (aSize < maxSize) {
+ NS_FRAME_SET_INCOMPLETE(*aStatus);
+ } else {
+ aSize = maxSize;
+ }
+ } else {
+ NS_FRAME_SET_INCOMPLETE(*aStatus);
+ }
+ return aSize;
+}
+
+static bool
+IsPercentOfIndefiniteSize(const nsStyleCoord& aCoord, nscoord aPercentBasis)
+{
+ return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
+}
+
+static nscoord
+ResolveToDefiniteSize(const nsStyleCoord& aCoord, nscoord aPercentBasis)
+{
+ MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
+ if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
+ return nscoord(0);
+ }
+ return std::max(nscoord(0),
+ nsRuleNode::ComputeCoordPercentCalc(aCoord, aPercentBasis));
+}
+
+static bool
+GetPercentSizeParts(const nsStyleCoord& aCoord, nscoord* aLength, float* aPercent)
+{
+ switch (aCoord.GetUnit()) {
+ case eStyleUnit_Percent:
+ *aLength = 0;
+ *aPercent = aCoord.GetPercentValue();
+ return true;
+ case eStyleUnit_Calc: {
+ nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
+ *aLength = calc->mLength;
+ *aPercent = calc->mPercent;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+static void
+ResolvePercentSizeParts(const nsStyleCoord& aCoord, nscoord aPercentBasis,
+ nscoord* aLength, float* aPercent)
+{
+ MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
+ if (aPercentBasis != NS_UNCONSTRAINEDSIZE) {
+ *aLength = std::max(nscoord(0),
+ nsRuleNode::ComputeCoordPercentCalc(aCoord,
+ aPercentBasis));
+ *aPercent = 0.0f;
+ return;
+ }
+ if (!GetPercentSizeParts(aCoord, aLength, aPercent)) {
+ *aLength = aCoord.ToLength();
+ *aPercent = 0.0f;
+ }
+}
+
+// Synthesize a baseline from a border box. For an alphabetical baseline
+// this is the end edge of the border box. For a central baseline it's
+// the center of the border box.
+// https://drafts.csswg.org/css-align-3/#synthesize-baselines
+// For a 'first baseline' the measure is from the border-box start edge and
+// for a 'last baseline' the measure is from the border-box end edge.
+static nscoord
+SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
+ WritingMode aWM,
+ nscoord aBorderBoxSize)
+{
+ if (aGroup == BaselineSharingGroup::eFirst) {
+ return aWM.IsAlphabeticalBaseline() ? aBorderBoxSize : aBorderBoxSize / 2;
+ }
+ MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+ // Round up for central baseline offset, to be consistent with eFirst.
+ return aWM.IsAlphabeticalBaseline() ? 0 :
+ (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
+}
+
+enum class GridLineSide
+{
+ eBeforeGridGap,
+ eAfterGridGap,
+};
+
+struct nsGridContainerFrame::TrackSize
+{
+ enum StateBits : uint16_t {
+ eAutoMinSizing = 0x1,
+ eMinContentMinSizing = 0x2,
+ eMaxContentMinSizing = 0x4,
+ eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
+ eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
+ // 0x8 is unused, feel free to take it!
+ eAutoMaxSizing = 0x10,
+ eMinContentMaxSizing = 0x20,
+ eMaxContentMaxSizing = 0x40,
+ eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
+ eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
+ eFlexMaxSizing = 0x80,
+ eFrozen = 0x100,
+ eSkipGrowUnlimited1 = 0x200,
+ eSkipGrowUnlimited2 = 0x400,
+ eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
+ eBreakBefore = 0x800,
+ eFitContent = 0x1000,
+ };
+
+ StateBits Initialize(nscoord aPercentageBasis,
+ const nsStyleCoord& aMinCoord,
+ const nsStyleCoord& aMaxCoord);
+ bool IsFrozen() const { return mState & eFrozen; }
+#ifdef DEBUG
+ void Dump() const;
+#endif
+
+ static bool IsMinContent(const nsStyleCoord& aCoord)
+ {
+ return aCoord.GetUnit() == eStyleUnit_Enumerated &&
+ aCoord.GetIntValue() == NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT;
+ }
+ static bool IsDefiniteMaxSizing(StateBits aStateBits)
+ {
+ return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
+ }
+
+ nscoord mBase;
+ nscoord mLimit;
+ nscoord mPosition; // zero until we apply 'align/justify-content'
+ // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
+ // this track. One subtree per baseline-sharing group (per track).
+ nscoord mBaselineSubtreeSize[2];
+ StateBits mState;
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)
+
+namespace mozilla {
+template <>
+struct IsPod<nsGridContainerFrame::TrackSize> : TrueType {};
+}
+
+TrackSize::StateBits
+nsGridContainerFrame::TrackSize::Initialize(nscoord aPercentageBasis,
+ const nsStyleCoord& aMinCoord,
+ const nsStyleCoord& aMaxCoord)
+{
+ MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
+ "track size data is expected to be initialized to zero");
+ auto minSizeUnit = aMinCoord.GetUnit();
+ auto maxSizeUnit = aMaxCoord.GetUnit();
+ if (minSizeUnit == eStyleUnit_None) {
+ // This track is sized using fit-content(size) (represented in style system
+ // with minCoord=None,maxCoord=size). In layout, fit-content(size) behaves
+ // as minmax(auto, max-content), with 'size' as an additional upper-bound.
+ mState = eFitContent;
+ minSizeUnit = eStyleUnit_Auto;
+ maxSizeUnit = eStyleUnit_Enumerated; // triggers max-content sizing below
+ }
+ if (::IsPercentOfIndefiniteSize(aMinCoord, aPercentageBasis)) {
+ // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
+ // "If the inline or block size of the grid container is indefinite,
+ // <percentage> values relative to that size are treated as 'auto'."
+ minSizeUnit = eStyleUnit_Auto;
+ }
+ if (::IsPercentOfIndefiniteSize(aMaxCoord, aPercentageBasis)) {
+ maxSizeUnit = eStyleUnit_Auto;
+ }
+ // http://dev.w3.org/csswg/css-grid/#algo-init
+ switch (minSizeUnit) {
+ case eStyleUnit_Auto:
+ mState |= eAutoMinSizing;
+ break;
+ case eStyleUnit_Enumerated:
+ mState |= IsMinContent(aMinCoord) ? eMinContentMinSizing
+ : eMaxContentMinSizing;
+ break;
+ default:
+ MOZ_ASSERT(minSizeUnit != eStyleUnit_FlexFraction,
+ "<flex> min-sizing is invalid as a track size");
+ mBase = ::ResolveToDefiniteSize(aMinCoord, aPercentageBasis);
+ }
+ switch (maxSizeUnit) {
+ case eStyleUnit_Auto:
+ mState |= eAutoMaxSizing;
+ mLimit = NS_UNCONSTRAINEDSIZE;
+ break;
+ case eStyleUnit_Enumerated:
+ mState |= IsMinContent(aMaxCoord) ? eMinContentMaxSizing
+ : eMaxContentMaxSizing;
+ mLimit = NS_UNCONSTRAINEDSIZE;
+ break;
+ case eStyleUnit_FlexFraction:
+ mState |= eFlexMaxSizing;
+ mLimit = mBase;
+ break;
+ default:
+ mLimit = ::ResolveToDefiniteSize(aMaxCoord, aPercentageBasis);
+ if (mLimit < mBase) {
+ mLimit = mBase;
+ }
+ }
+
+ mBaselineSubtreeSize[BaselineSharingGroup::eFirst] = nscoord(0);
+ mBaselineSubtreeSize[BaselineSharingGroup::eLast] = nscoord(0);
+ return mState;
+}
+
+/**
+ * Is aFrame1 a prev-continuation of aFrame2?
+ */
+static bool
+IsPrevContinuationOf(nsIFrame* aFrame1, nsIFrame* aFrame2)
+{
+ nsIFrame* prev = aFrame2;
+ while ((prev = prev->GetPrevContinuation())) {
+ if (prev == aFrame1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Moves all frames from aSrc into aDest such that the resulting aDest
+ * is still sorted in document content order and continuation order.
+ * Precondition: both |aSrc| and |aDest| must be sorted to begin with.
+ * @param aCommonAncestor a hint for nsLayoutUtils::CompareTreePosition
+ */
+static void
+MergeSortedFrameLists(nsFrameList& aDest, nsFrameList& aSrc,
+ nsIContent* aCommonAncestor)
+{
+ nsIFrame* dest = aDest.FirstChild();
+ for (nsIFrame* src = aSrc.FirstChild(); src; ) {
+ if (!dest) {
+ aDest.AppendFrames(nullptr, aSrc);
+ break;
+ }
+ nsIContent* srcContent = src->GetContent();
+ nsIContent* destContent = dest->GetContent();
+ int32_t result = nsLayoutUtils::CompareTreePosition(srcContent,
+ destContent,
+ aCommonAncestor);
+ if (MOZ_UNLIKELY(result == 0)) {
+ // NOTE: we get here when comparing ::before/::after for the same element.
+ if (MOZ_UNLIKELY(srcContent->IsGeneratedContentContainerForBefore())) {
+ if (MOZ_LIKELY(!destContent->IsGeneratedContentContainerForBefore()) ||
+ ::IsPrevContinuationOf(src, dest)) {
+ result = -1;
+ }
+ } else if (MOZ_UNLIKELY(srcContent->IsGeneratedContentContainerForAfter())) {
+ if (MOZ_UNLIKELY(destContent->IsGeneratedContentContainerForAfter()) &&
+ ::IsPrevContinuationOf(src, dest)) {
+ result = -1;
+ }
+ } else if (::IsPrevContinuationOf(src, dest)) {
+ result = -1;
+ }
+ }
+ if (result < 0) {
+ // src should come before dest
+ nsIFrame* next = src->GetNextSibling();
+ aSrc.RemoveFrame(src);
+ aDest.InsertFrame(nullptr, dest->GetPrevSibling(), src);
+ src = next;
+ } else {
+ dest = dest->GetNextSibling();
+ }
+ }
+ MOZ_ASSERT(aSrc.IsEmpty());
+}
+
+static void
+MergeSortedFrameListsFor(nsFrameList& aDest, nsFrameList& aSrc,
+ nsContainerFrame* aParent)
+{
+ MergeSortedFrameLists(aDest, aSrc, aParent->GetContent());
+}
+
+template<typename Iterator>
+class nsGridContainerFrame::GridItemCSSOrderIteratorT
+{
+public:
+ enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered };
+ enum ChildFilter { eSkipPlaceholders, eIncludeAll };
+ GridItemCSSOrderIteratorT(nsIFrame* aGridContainer,
+ nsIFrame::ChildListID aListID,
+ ChildFilter aFilter = eSkipPlaceholders,
+ OrderState aState = eUnknownOrder)
+ : mChildren(aGridContainer->GetChildList(aListID))
+ , mArrayIndex(0)
+ , mGridItemIndex(0)
+ , mSkipPlaceholders(aFilter == eSkipPlaceholders)
+#ifdef DEBUG
+ , mGridContainer(aGridContainer)
+ , mListID(aListID)
+#endif
+ {
+ size_t count = 0;
+ bool isOrdered = aState != eKnownUnordered;
+ if (aState == eUnknownOrder) {
+ auto maxOrder = std::numeric_limits<int32_t>::min();
+ for (auto child : mChildren) {
+ ++count;
+ int32_t order = child->StylePosition()->mOrder;
+ if (order < maxOrder) {
+ isOrdered = false;
+ break;
+ }
+ maxOrder = order;
+ }
+ }
+ if (isOrdered) {
+ mIter.emplace(begin(mChildren));
+ mIterEnd.emplace(end(mChildren));
+ } else {
+ count *= 2; // XXX somewhat arbitrary estimate for now...
+ mArray.emplace(count);
+ for (Iterator i(begin(mChildren)), iEnd(end(mChildren)); i != iEnd; ++i) {
+ mArray->AppendElement(*i);
+ }
+ // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed.
+ std::stable_sort(mArray->begin(), mArray->end(), CSSOrderComparator);
+ }
+
+ if (mSkipPlaceholders) {
+ SkipPlaceholders();
+ }
+ }
+ ~GridItemCSSOrderIteratorT()
+ {
+ MOZ_ASSERT(IsForward() == mGridItemCount.isNothing());
+ }
+
+ bool IsForward() const;
+ Iterator begin(const nsFrameList& aList);
+ Iterator end(const nsFrameList& aList);
+
+ nsIFrame* operator*() const
+ {
+ MOZ_ASSERT(!AtEnd());
+ if (mIter.isSome()) {
+ return **mIter;
+ }
+ return (*mArray)[mArrayIndex];
+ }
+
+ /**
+ * Return the child index of the current item, placeholders not counted.
+ * It's forbidden to call this method when the current frame is placeholder.
+ */
+ size_t GridItemIndex() const
+ {
+ MOZ_ASSERT(!AtEnd());
+ MOZ_ASSERT((**this)->GetType() != nsGkAtoms::placeholderFrame,
+ "MUST not call this when at a placeholder");
+ MOZ_ASSERT(IsForward() || mGridItemIndex < *mGridItemCount,
+ "Returning an out-of-range mGridItemIndex...");
+ return mGridItemIndex;
+ }
+
+ void SetGridItemCount(size_t aGridItemCount)
+ {
+#ifndef CLANG_CRASH_BUG
+ MOZ_ASSERT(mIter.isSome() || mArray->Length() == aGridItemCount,
+ "grid item count mismatch");
+#endif
+ mGridItemCount.emplace(aGridItemCount);
+ // Note: it's OK if mGridItemIndex underflows -- GridItemIndex()
+ // will not be called unless there is at least one item.
+ mGridItemIndex = IsForward() ? 0 : *mGridItemCount - 1;
+ }
+
+ /**
+ * Skip over placeholder children.
+ */
+ void SkipPlaceholders()
+ {
+ if (mIter.isSome()) {
+ for (; *mIter != *mIterEnd; ++*mIter) {
+ nsIFrame* child = **mIter;
+ if (child->GetType() != nsGkAtoms::placeholderFrame) {
+ return;
+ }
+ }
+ } else {
+ for (; mArrayIndex < mArray->Length(); ++mArrayIndex) {
+ nsIFrame* child = (*mArray)[mArrayIndex];
+ if (child->GetType() != nsGkAtoms::placeholderFrame) {
+ return;
+ }
+ }
+ }
+ }
+
+ bool AtEnd() const
+ {
+#ifndef CLANG_CRASH_BUG
+ // Clang 3.6.2 crashes when compiling this assertion:
+ MOZ_ASSERT(mIter.isSome() || mArrayIndex <= mArray->Length());
+#endif
+ return mIter ? (*mIter == *mIterEnd) : mArrayIndex >= mArray->Length();
+ }
+
+ void Next()
+ {
+#ifdef DEBUG
+ MOZ_ASSERT(!AtEnd());
+ nsFrameList list = mGridContainer->GetChildList(mListID);
+ MOZ_ASSERT(list.FirstChild() == mChildren.FirstChild() &&
+ list.LastChild() == mChildren.LastChild(),
+ "the list of child frames must not change while iterating!");
+#endif
+ if (mSkipPlaceholders ||
+ (**this)->GetType() != nsGkAtoms::placeholderFrame) {
+ IsForward() ? ++mGridItemIndex : --mGridItemIndex;
+ }
+ if (mIter.isSome()) {
+ ++*mIter;
+ } else {
+ ++mArrayIndex;
+ }
+ if (mSkipPlaceholders) {
+ SkipPlaceholders();
+ }
+ }
+
+ void Reset(ChildFilter aFilter = eSkipPlaceholders)
+ {
+ if (mIter.isSome()) {
+ mIter.reset();
+ mIter.emplace(begin(mChildren));
+ mIterEnd.reset();
+ mIterEnd.emplace(end(mChildren));
+ } else {
+ mArrayIndex = 0;
+ }
+ mGridItemIndex = IsForward() ? 0 : *mGridItemCount - 1;
+ mSkipPlaceholders = aFilter == eSkipPlaceholders;
+ if (mSkipPlaceholders) {
+ SkipPlaceholders();
+ }
+ }
+
+ bool IsValid() const { return mIter.isSome() || mArray.isSome(); }
+
+ void Invalidate()
+ {
+ mIter.reset();
+ mArray.reset();
+ mozWritePoison(&mChildren, sizeof(mChildren));
+ }
+
+ bool ItemsAreAlreadyInOrder() const { return mIter.isSome(); }
+
+ static bool CSSOrderComparator(nsIFrame* const& a, nsIFrame* const& b);
+private:
+ nsFrameList mChildren;
+ // Used if child list is already in ascending 'order'.
+ Maybe<Iterator> mIter;
+ Maybe<Iterator> mIterEnd;
+ // Used if child list is *not* in ascending 'order'.
+ // This array is pre-sorted in reverse order for a reverse iterator.
+ Maybe<nsTArray<nsIFrame*>> mArray;
+ size_t mArrayIndex;
+ // The index of the current grid item (placeholders excluded).
+ size_t mGridItemIndex;
+ // The number of grid items (placeholders excluded).
+ // It's only initialized and used in a reverse iterator.
+ Maybe<size_t> mGridItemCount;
+ // Skip placeholder children in the iteration?
+ bool mSkipPlaceholders;
+#ifdef DEBUG
+ nsIFrame* mGridContainer;
+ nsIFrame::ChildListID mListID;
+#endif
+};
+
+using GridItemCSSOrderIterator = nsGridContainerFrame::GridItemCSSOrderIterator;
+using ReverseGridItemCSSOrderIterator = nsGridContainerFrame::ReverseGridItemCSSOrderIterator;
+
+template<>
+bool
+GridItemCSSOrderIterator::CSSOrderComparator(nsIFrame* const& a,
+ nsIFrame* const& b)
+{ return a->StylePosition()->mOrder < b->StylePosition()->mOrder; }
+
+template<>
+bool
+GridItemCSSOrderIterator::IsForward() const { return true; }
+
+template<>
+nsFrameList::iterator
+GridItemCSSOrderIterator::begin(const nsFrameList& aList)
+{ return aList.begin(); }
+
+template<>
+nsFrameList::iterator GridItemCSSOrderIterator::end(const nsFrameList& aList)
+{ return aList.end(); }
+
+template<>
+bool
+ReverseGridItemCSSOrderIterator::CSSOrderComparator(nsIFrame* const& a,
+ nsIFrame* const& b)
+{ return a->StylePosition()->mOrder > b->StylePosition()->mOrder; }
+
+template<>
+bool
+ReverseGridItemCSSOrderIterator::IsForward() const
+{ return false; }
+
+template<>
+nsFrameList::reverse_iterator
+ReverseGridItemCSSOrderIterator::begin(const nsFrameList& aList)
+{ return aList.rbegin(); }
+
+template<>
+nsFrameList::reverse_iterator
+ReverseGridItemCSSOrderIterator::end(const nsFrameList& aList)
+{ return aList.rend(); }
+
+/**
+ * A LineRange can be definite or auto - when it's definite it represents
+ * a consecutive set of tracks between a starting line and an ending line.
+ * Before it's definite it can also represent an auto position with a span,
+ * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
+ * For normal-flow items, the invariant mStart < mEnd holds when both
+ * lines are definite.
+ *
+ * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
+ * "attach this side to the grid container containing block edge".
+ * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
+ * i.e. the invariant is slightly relaxed compared to normal flow items.
+ */
+struct nsGridContainerFrame::LineRange
+{
+ LineRange(int32_t aStart, int32_t aEnd)
+ : mUntranslatedStart(aStart), mUntranslatedEnd(aEnd)
+ {
+#ifdef DEBUG
+ if (!IsAutoAuto()) {
+ if (IsAuto()) {
+ MOZ_ASSERT(aEnd >= nsStyleGridLine::kMinLine &&
+ aEnd <= nsStyleGridLine::kMaxLine, "invalid span");
+ } else {
+ MOZ_ASSERT(aStart >= nsStyleGridLine::kMinLine &&
+ aStart <= nsStyleGridLine::kMaxLine, "invalid start line");
+ MOZ_ASSERT(aEnd == int32_t(kAutoLine) ||
+ (aEnd >= nsStyleGridLine::kMinLine &&
+ aEnd <= nsStyleGridLine::kMaxLine), "invalid end line");
+ }
+ }
+#endif
+ }
+ bool IsAutoAuto() const { return mStart == kAutoLine && mEnd == kAutoLine; }
+ bool IsAuto() const { return mStart == kAutoLine; }
+ bool IsDefinite() const { return mStart != kAutoLine; }
+ uint32_t Extent() const
+ {
+ MOZ_ASSERT(mEnd != kAutoLine, "Extent is undefined for abs.pos. 'auto'");
+ if (IsAuto()) {
+ MOZ_ASSERT(mEnd >= 1 && mEnd < uint32_t(nsStyleGridLine::kMaxLine),
+ "invalid span");
+ return mEnd;
+ }
+ return mEnd - mStart;
+ }
+ /**
+ * Resolve this auto range to start at aStart, making it definite.
+ * Precondition: this range IsAuto()
+ */
+ void ResolveAutoPosition(uint32_t aStart, uint32_t aExplicitGridOffset)
+ {
+ MOZ_ASSERT(IsAuto(), "Why call me?");
+ mStart = aStart;
+ mEnd += aStart;
+ // Clamping to where kMaxLine is in the explicit grid, per
+ // http://dev.w3.org/csswg/css-grid/#overlarge-grids :
+ uint32_t translatedMax = aExplicitGridOffset + nsStyleGridLine::kMaxLine;
+ if (MOZ_UNLIKELY(mStart >= translatedMax)) {
+ mEnd = translatedMax;
+ mStart = mEnd - 1;
+ } else if (MOZ_UNLIKELY(mEnd > translatedMax)) {
+ mEnd = translatedMax;
+ }
+ }
+ /**
+ * Translate the lines to account for (empty) removed tracks. This method
+ * is only for grid items and should only be called after placement.
+ * aNumRemovedTracks contains a count for each line in the grid how many
+ * tracks were removed between the start of the grid and that line.
+ */
+ void AdjustForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks)
+ {
+ MOZ_ASSERT(mStart != kAutoLine, "invalid resolved line for a grid item");
+ MOZ_ASSERT(mEnd != kAutoLine, "invalid resolved line for a grid item");
+ uint32_t numRemovedTracks = aNumRemovedTracks[mStart];
+ MOZ_ASSERT(numRemovedTracks == aNumRemovedTracks[mEnd],
+ "tracks that a grid item spans can't be removed");
+ mStart -= numRemovedTracks;
+ mEnd -= numRemovedTracks;
+ }
+ /**
+ * Translate the lines to account for (empty) removed tracks. This method
+ * is only for abs.pos. children and should only be called after placement.
+ * Same as for in-flow items, but we don't touch 'auto' lines here and we
+ * also need to adjust areas that span into the removed tracks.
+ */
+ void AdjustAbsPosForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks)
+ {
+ if (mStart != nsGridContainerFrame::kAutoLine) {
+ mStart -= aNumRemovedTracks[mStart];
+ }
+ if (mEnd != nsGridContainerFrame::kAutoLine) {
+ MOZ_ASSERT(mStart == nsGridContainerFrame::kAutoLine ||
+ mEnd > mStart, "invalid line range");
+ mEnd -= aNumRemovedTracks[mEnd];
+ }
+ if (mStart == mEnd) {
+ mEnd = nsGridContainerFrame::kAutoLine;
+ }
+ }
+ /**
+ * Return the contribution of this line range for step 2 in
+ * http://dev.w3.org/csswg/css-grid/#auto-placement-algo
+ */
+ uint32_t HypotheticalEnd() const { return mEnd; }
+ /**
+ * Given an array of track sizes, return the starting position and length
+ * of the tracks in this line range.
+ */
+ void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
+ nscoord* aPos, nscoord* aLength) const;
+ /**
+ * Given an array of track sizes, return the length of the tracks in this
+ * line range.
+ */
+ nscoord ToLength(const nsTArray<TrackSize>& aTrackSizes) const;
+ /**
+ * Given an array of track sizes and a grid origin coordinate, adjust the
+ * abs.pos. containing block along an axis given by aPos and aLength.
+ * aPos and aLength should already be initialized to the grid container
+ * containing block for this axis before calling this method.
+ */
+ void ToPositionAndLengthForAbsPos(const Tracks& aTracks,
+ nscoord aGridOrigin,
+ nscoord* aPos, nscoord* aLength) const;
+
+ /**
+ * @note We'll use the signed member while resolving definite positions
+ * to line numbers (1-based), which may become negative for implicit lines
+ * to the top/left of the explicit grid. PlaceGridItems() then translates
+ * the whole grid to a 0,0 origin and we'll use the unsigned member from
+ * there on.
+ */
+ union {
+ uint32_t mStart;
+ int32_t mUntranslatedStart;
+ };
+ union {
+ uint32_t mEnd;
+ int32_t mUntranslatedEnd;
+ };
+protected:
+ LineRange() {}
+};
+
+/**
+ * Helper class to construct a LineRange from translated lines.
+ * The ctor only accepts translated definite line numbers.
+ */
+struct nsGridContainerFrame::TranslatedLineRange : public LineRange
+{
+ TranslatedLineRange(uint32_t aStart, uint32_t aEnd)
+ {
+ MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
+ mStart = aStart;
+ mEnd = aEnd;
+ }
+};
+
+/**
+ * A GridArea is the area in the grid for a grid item.
+ * The area is represented by two LineRanges, both of which can be auto
+ * (@see LineRange) in intermediate steps while the item is being placed.
+ * @see PlaceGridItems
+ */
+struct nsGridContainerFrame::GridArea
+{
+ GridArea(const LineRange& aCols, const LineRange& aRows)
+ : mCols(aCols), mRows(aRows) {}
+ bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
+ LineRange mCols;
+ LineRange mRows;
+};
+
+struct nsGridContainerFrame::GridItemInfo
+{
+ /**
+ * Item state per axis.
+ */
+ enum StateBits : uint8_t {
+ eIsFlexing = 0x1, // does the item span a flex track?
+ eFirstBaseline = 0x2, // participate in 'first baseline' alignment?
+ // ditto 'last baseline', mutually exclusive w. eFirstBaseline
+ eLastBaseline = 0x4,
+ eIsBaselineAligned = eFirstBaseline | eLastBaseline,
+ // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
+ eSelfBaseline = 0x8, // is it *-self:[last ]baseline alignment?
+ // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
+ eContentBaseline = 0x10,
+ eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline,
+ // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto
+ eClampMarginBoxMinSize = 0x20,
+ };
+
+ explicit GridItemInfo(nsIFrame* aFrame,
+ const GridArea& aArea)
+ : mFrame(aFrame)
+ , mArea(aArea)
+ {
+ mState[eLogicalAxisBlock] = StateBits(0);
+ mState[eLogicalAxisInline] = StateBits(0);
+ mBaselineOffset[eLogicalAxisBlock] = nscoord(0);
+ mBaselineOffset[eLogicalAxisInline] = nscoord(0);
+ }
+
+ /**
+ * If the item is [align|justify]-self:[last ]baseline aligned in the given
+ * axis then set aBaselineOffset to the baseline offset and return aAlign.
+ * Otherwise, return a fallback alignment.
+ */
+ uint8_t GetSelfBaseline(uint8_t aAlign, LogicalAxis aAxis,
+ nscoord* aBaselineOffset) const
+ {
+ MOZ_ASSERT(aAlign == NS_STYLE_ALIGN_BASELINE ||
+ aAlign == NS_STYLE_ALIGN_LAST_BASELINE);
+ if (!(mState[aAxis] & eSelfBaseline)) {
+ return aAlign == NS_STYLE_ALIGN_BASELINE ? NS_STYLE_ALIGN_SELF_START
+ : NS_STYLE_ALIGN_SELF_END;
+ }
+ *aBaselineOffset = mBaselineOffset[aAxis];
+ return aAlign;
+ }
+
+ // Return true if we should we clamp this item's Automatic Minimum Size.
+ // https://drafts.csswg.org/css-grid/#min-size-auto
+ bool ShouldClampMinSize(WritingMode aContainerWM,
+ LogicalAxis aContainerAxis,
+ nscoord aPercentageBasis) const
+ {
+ const auto pos = mFrame->StylePosition();
+ const auto& size = aContainerAxis == eLogicalAxisInline ?
+ pos->ISize(aContainerWM) : pos->BSize(aContainerWM);
+ // NOTE: if we have a definite or 'max-content' size then our automatic
+ // minimum size can't affect our size. Excluding these simplifies applying
+ // the clamping in the right cases later.
+ if (size.GetUnit() == eStyleUnit_Auto ||
+ ::IsPercentOfIndefiniteSize(size, aPercentageBasis) || // same as 'auto'
+ (size.GetUnit() == eStyleUnit_Enumerated &&
+ size.GetIntValue() != NS_STYLE_WIDTH_MAX_CONTENT)) {
+ const auto& minSize = aContainerAxis == eLogicalAxisInline ?
+ pos->MinISize(aContainerWM) : pos->MinBSize(aContainerWM);
+ return minSize.GetUnit() == eStyleUnit_Auto &&
+ mFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE;
+ }
+ return false;
+ }
+
+#ifdef DEBUG
+ void Dump() const;
+#endif
+
+ static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b)
+ {
+ return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
+ }
+
+ nsIFrame* const mFrame;
+ GridArea mArea;
+ // Offset from the margin edge to the baseline (LogicalAxis index). It's from
+ // the start edge when eFirstBaseline is set, end edge otherwise. It's mutable
+ // since we update the value fairly late (just before reflowing the item).
+ mutable nscoord mBaselineOffset[2];
+ mutable StateBits mState[2]; // state bits per axis (LogicalAxis index)
+ static_assert(mozilla::eLogicalAxisBlock == 0, "unexpected index value");
+ static_assert(mozilla::eLogicalAxisInline == 1, "unexpected index value");
+};
+
+using GridItemInfo = nsGridContainerFrame::GridItemInfo;
+using ItemState = GridItemInfo::StateBits;
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)
+
+#ifdef DEBUG
+void
+nsGridContainerFrame::GridItemInfo::Dump() const
+{
+ auto Dump1 = [this] (const char* aMsg, LogicalAxis aAxis) {
+ auto state = mState[aAxis];
+ if (!state) {
+ return;
+ }
+ printf("%s", aMsg);
+ if (state & ItemState::eIsFlexing) {
+ printf("flexing ");
+ }
+ if (state & ItemState::eFirstBaseline) {
+ printf("first baseline %s-alignment ",
+ (state & ItemState::eSelfBaseline) ? "self" : "content");
+ }
+ if (state & ItemState::eLastBaseline) {
+ printf("last baseline %s-alignment ",
+ (state & ItemState::eSelfBaseline) ? "self" : "content");
+ }
+ if (state & ItemState::eIsBaselineAligned) {
+ printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset[aAxis],
+ AppUnitsPerCSSPixel()));
+ }
+ printf("\n");
+ };
+ printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd);
+ Dump1(" grid block-axis: ", eLogicalAxisBlock);
+ printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd);
+ Dump1(" grid inline-axis: ", eLogicalAxisInline);
+}
+#endif
+
+/**
+ * Utility class to find line names. It provides an interface to lookup line
+ * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
+ * account.
+ */
+class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap
+{
+public:
+ /**
+ * Create a LineNameMap.
+ * @param aGridTemplate is the grid-template-rows/columns data for this axis
+ * @param aNumRepeatTracks the number of actual tracks associated with
+ * a repeat(auto-fill/fit) track (zero or more), or zero if there is no
+ * specified repeat(auto-fill/fit) track
+ */
+ LineNameMap(const nsStyleGridTemplate& aGridTemplate,
+ uint32_t aNumRepeatTracks)
+ : mLineNameLists(aGridTemplate.mLineNameLists)
+ , mRepeatAutoLineNameListBefore(aGridTemplate.mRepeatAutoLineNameListBefore)
+ , mRepeatAutoLineNameListAfter(aGridTemplate.mRepeatAutoLineNameListAfter)
+ , mRepeatAutoStart(aGridTemplate.HasRepeatAuto() ?
+ aGridTemplate.mRepeatAutoIndex : 0)
+ , mRepeatAutoEnd(mRepeatAutoStart + aNumRepeatTracks)
+ , mRepeatEndDelta(aGridTemplate.HasRepeatAuto() ?
+ int32_t(aNumRepeatTracks) - 1 :
+ 0)
+ , mTemplateLinesEnd(mLineNameLists.Length() + mRepeatEndDelta)
+ , mHasRepeatAuto(aGridTemplate.HasRepeatAuto())
+ {
+ MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
+ MOZ_ASSERT(mRepeatAutoStart <= mLineNameLists.Length());
+ MOZ_ASSERT(!mHasRepeatAuto || mLineNameLists.Length() >= 2);
+ }
+
+ /**
+ * Find the aNth occurrence of aName, searching forward if aNth is positive,
+ * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
+ * aFromIndex (not inclusive), and return a 1-based line number.
+ * Also take into account there is an unconditional match at aImplicitLine
+ * unless it's zero.
+ * Return zero if aNth occurrences can't be found. In that case, aNth has
+ * been decremented with the number of occurrences that were found (if any).
+ *
+ * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
+ * aNth is 2 and aFromIndex is zero. To search for "A -2", aNth is -2 and
+ * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
+ * line when we're searching in reverse). For "span A 2", aNth is 2 when
+ * used on a grid-[row|column]-end property and -2 for a *-start property,
+ * and aFromIndex is the line (which we should skip) on the opposite property.
+ */
+ uint32_t FindNamedLine(const nsString& aName, int32_t* aNth,
+ uint32_t aFromIndex, uint32_t aImplicitLine) const
+ {
+ MOZ_ASSERT(aNth && *aNth != 0);
+ if (*aNth > 0) {
+ return FindLine(aName, aNth, aFromIndex, aImplicitLine);
+ }
+ int32_t nth = -*aNth;
+ int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLine);
+ *aNth = -nth;
+ return line;
+ }
+
+private:
+ /**
+ * @see FindNamedLine, this function searches forward.
+ */
+ uint32_t FindLine(const nsString& aName, int32_t* aNth,
+ uint32_t aFromIndex, uint32_t aImplicitLine) const
+ {
+ MOZ_ASSERT(aNth && *aNth > 0);
+ int32_t nth = *aNth;
+ const uint32_t end = mTemplateLinesEnd;
+ uint32_t line;
+ uint32_t i = aFromIndex;
+ for (; i < end; i = line) {
+ line = i + 1;
+ if (line == aImplicitLine || Contains(i, aName)) {
+ if (--nth == 0) {
+ return line;
+ }
+ }
+ }
+ if (aImplicitLine > i) {
+ // aImplicitLine is after the lines we searched above so it's last.
+ // (grid-template-areas has more tracks than grid-template-[rows|columns])
+ if (--nth == 0) {
+ return aImplicitLine;
+ }
+ }
+ MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
+ *aNth = nth;
+ return 0;
+ }
+
+ /**
+ * @see FindNamedLine, this function searches in reverse.
+ */
+ uint32_t RFindLine(const nsString& aName, int32_t* aNth,
+ uint32_t aFromIndex, uint32_t aImplicitLine) const
+ {
+ MOZ_ASSERT(aNth && *aNth > 0);
+ if (MOZ_UNLIKELY(aFromIndex == 0)) {
+ return 0; // There are no named lines beyond the start of the explicit grid.
+ }
+ --aFromIndex; // (shift aFromIndex so we can treat it as inclusive)
+ int32_t nth = *aNth;
+ // The implicit line may be beyond the explicit grid so we match
+ // this line first if it's within the mTemplateLinesEnd..aFromIndex range.
+ const uint32_t end = mTemplateLinesEnd;
+ if (aImplicitLine > end && aImplicitLine < aFromIndex) {
+ if (--nth == 0) {
+ return aImplicitLine;
+ }
+ }
+ for (uint32_t i = std::min(aFromIndex, end); i; --i) {
+ if (i == aImplicitLine || Contains(i - 1, aName)) {
+ if (--nth == 0) {
+ return i;
+ }
+ }
+ }
+ MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
+ *aNth = nth;
+ return 0;
+ }
+
+ // Return true if aName exists at aIndex.
+ bool Contains(uint32_t aIndex, const nsString& aName) const
+ {
+ if (!mHasRepeatAuto) {
+ return mLineNameLists[aIndex].Contains(aName);
+ }
+ if (aIndex < mRepeatAutoEnd && aIndex >= mRepeatAutoStart &&
+ mRepeatAutoLineNameListBefore.Contains(aName)) {
+ return true;
+ }
+ if (aIndex <= mRepeatAutoEnd && aIndex > mRepeatAutoStart &&
+ mRepeatAutoLineNameListAfter.Contains(aName)) {
+ return true;
+ }
+ if (aIndex <= mRepeatAutoStart) {
+ return mLineNameLists[aIndex].Contains(aName) ||
+ (aIndex == mRepeatAutoEnd &&
+ mLineNameLists[aIndex + 1].Contains(aName));
+ }
+ return aIndex >= mRepeatAutoEnd &&
+ mLineNameLists[aIndex - mRepeatEndDelta].Contains(aName);
+ }
+
+ // Some style data references, for easy access.
+ const nsTArray<nsTArray<nsString>>& mLineNameLists;
+ const nsTArray<nsString>& mRepeatAutoLineNameListBefore;
+ const nsTArray<nsString>& mRepeatAutoLineNameListAfter;
+ // The index of the repeat(auto-fill/fit) track, or zero if there is none.
+ const uint32_t mRepeatAutoStart;
+ // The (hypothetical) index of the last such repeat() track.
+ const uint32_t mRepeatAutoEnd;
+ // The difference between mTemplateLinesEnd and mLineNameLists.Length().
+ const int32_t mRepeatEndDelta;
+ // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
+ // for. It is equal to mLineNameLists.Length() when a repeat() track
+ // generates one track (making mRepeatEndDelta == 0).
+ const uint32_t mTemplateLinesEnd;
+ // True if there is a specified repeat(auto-fill/fit) track.
+ const bool mHasRepeatAuto;
+};
+
+/**
+ * Encapsulates CSS track-sizing functions.
+ */
+struct nsGridContainerFrame::TrackSizingFunctions
+{
+ TrackSizingFunctions(const nsStyleGridTemplate& aGridTemplate,
+ const nsStyleCoord& aAutoMinSizing,
+ const nsStyleCoord& aAutoMaxSizing)
+ : mMinSizingFunctions(aGridTemplate.mMinTrackSizingFunctions)
+ , mMaxSizingFunctions(aGridTemplate.mMaxTrackSizingFunctions)
+ , mAutoMinSizing(aAutoMinSizing)
+ , mAutoMaxSizing(aAutoMaxSizing)
+ , mExplicitGridOffset(0)
+ , mRepeatAutoStart(aGridTemplate.HasRepeatAuto() ?
+ aGridTemplate.mRepeatAutoIndex : 0)
+ , mRepeatAutoEnd(mRepeatAutoStart)
+ , mRepeatEndDelta(0)
+ , mHasRepeatAuto(aGridTemplate.HasRepeatAuto())
+ {
+ MOZ_ASSERT(mMinSizingFunctions.Length() == mMaxSizingFunctions.Length());
+ MOZ_ASSERT(!mHasRepeatAuto ||
+ (mMinSizingFunctions.Length() >= 1 &&
+ mRepeatAutoStart < mMinSizingFunctions.Length()));
+ }
+
+ /**
+ * Initialize the number of auto-fill/fit tracks to use and return that.
+ * (zero if no auto-fill/fit track was specified)
+ */
+ uint32_t InitRepeatTracks(const nsStyleCoord& aGridGap, nscoord aMinSize,
+ nscoord aSize, nscoord aMaxSize)
+ {
+ uint32_t repeatTracks =
+ CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize);
+ SetNumRepeatTracks(repeatTracks);
+ // Blank out the removed flags for each of these tracks.
+ mRemovedRepeatTracks.SetLength(repeatTracks);
+ for (auto& track : mRemovedRepeatTracks) {
+ track = false;
+ }
+ return repeatTracks;
+ }
+
+ uint32_t CalculateRepeatFillCount(const nsStyleCoord& aGridGap,
+ nscoord aMinSize,
+ nscoord aSize,
+ nscoord aMaxSize) const
+ {
+ if (!mHasRepeatAuto) {
+ return 0;
+ }
+ // Spec quotes are from https://drafts.csswg.org/css-grid/#repeat-notation
+ const uint32_t numTracks = mMinSizingFunctions.Length();
+ MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
+ nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
+ if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
+ // "Otherwise, the specified track list repeats only once."
+ return 1;
+ }
+ nscoord repeatTrackSize = 0;
+ // Note that the repeat() track size is included in |sum| in this loop.
+ nscoord sum = 0;
+ const nscoord percentBasis = aSize;
+ for (uint32_t i = 0; i < numTracks; ++i) {
+ // "treating each track as its max track sizing function if that is
+ // definite or as its minimum track sizing function otherwise"
+ // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
+ const auto& maxCoord = mMaxSizingFunctions[i];
+ const auto* coord = &maxCoord;
+ if (!coord->IsCoordPercentCalcUnit()) {
+ coord = &mMinSizingFunctions[i];
+ if (!coord->IsCoordPercentCalcUnit()) {
+ return 1;
+ }
+ }
+ nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
+ if (i == mRepeatAutoStart) {
+ if (percentBasis != NS_UNCONSTRAINEDSIZE) {
+ // Use a minimum 1px for the repeat() track-size.
+ if (trackSize < AppUnitsPerCSSPixel()) {
+ trackSize = AppUnitsPerCSSPixel();
+ }
+ }
+ repeatTrackSize = trackSize;
+ }
+ sum += trackSize;
+ }
+ nscoord gridGap;
+ float percentSum = 0.0f;
+ float gridGapPercent;
+ ResolvePercentSizeParts(aGridGap, percentBasis, &gridGap, &gridGapPercent);
+ if (numTracks > 1) {
+ // Add grid-gaps for all the tracks including the repeat() track.
+ sum += gridGap * (numTracks - 1);
+ percentSum = gridGapPercent * (numTracks - 1);
+ }
+ // Calculate the max number of tracks that fits without overflow.
+ nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
+ nscoord size = nsLayoutUtils::AddPercents(sum, percentSum);
+ if (available - size < 0) {
+ // "if any number of repetitions would overflow, then 1 repetition"
+ return 1;
+ }
+ uint32_t numRepeatTracks = 1;
+ bool exactFit = false;
+ while (true) {
+ sum += gridGap + repeatTrackSize;
+ percentSum += gridGapPercent;
+ nscoord newSize = nsLayoutUtils::AddPercents(sum, percentSum);
+ if (newSize <= size) {
+ // Adding more repeat-tracks won't make forward progress.
+ return numRepeatTracks;
+ }
+ size = newSize;
+ nscoord remaining = available - size;
+ exactFit = remaining == 0;
+ if (remaining >= 0) {
+ ++numRepeatTracks;
+ }
+ if (remaining <= 0) {
+ break;
+ }
+ }
+
+ if (!exactFit && maxFill == NS_UNCONSTRAINEDSIZE) {
+ // "Otherwise, if the grid container has a definite min size in
+ // the relevant axis, the number of repetitions is the largest possible
+ // positive integer that fulfills that minimum requirement."
+ ++numRepeatTracks; // one more to ensure the grid is at least min-size
+ }
+ // Clamp the number of repeat tracks so that the last line <= kMaxLine.
+ // (note that |numTracks| already includes one repeat() track)
+ const uint32_t maxRepeatTracks = nsStyleGridLine::kMaxLine - numTracks;
+ return std::min(numRepeatTracks, maxRepeatTracks);
+ }
+
+ /**
+ * Compute the explicit grid end line number (in a zero-based grid).
+ * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
+ */
+ uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd)
+ {
+ uint32_t end = NumExplicitTracks() + 1;
+ end = std::max(end, aGridTemplateAreasEnd);
+ end = std::min(end, uint32_t(nsStyleGridLine::kMaxLine));
+ return end;
+ }
+
+ const nsStyleCoord& MinSizingFor(uint32_t aTrackIndex) const
+ {
+ if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
+ return mAutoMinSizing;
+ }
+ uint32_t index = aTrackIndex - mExplicitGridOffset;
+ if (index >= mRepeatAutoStart) {
+ if (index < mRepeatAutoEnd) {
+ return mMinSizingFunctions[mRepeatAutoStart];
+ }
+ index -= mRepeatEndDelta;
+ }
+ return index < mMinSizingFunctions.Length() ?
+ mMinSizingFunctions[index] : mAutoMinSizing;
+ }
+ const nsStyleCoord& MaxSizingFor(uint32_t aTrackIndex) const
+ {
+ if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
+ return mAutoMaxSizing;
+ }
+ uint32_t index = aTrackIndex - mExplicitGridOffset;
+ if (index >= mRepeatAutoStart) {
+ if (index < mRepeatAutoEnd) {
+ return mMaxSizingFunctions[mRepeatAutoStart];
+ }
+ index -= mRepeatEndDelta;
+ }
+ return index < mMaxSizingFunctions.Length() ?
+ mMaxSizingFunctions[index] : mAutoMaxSizing;
+ }
+ uint32_t NumExplicitTracks() const
+ {
+ return mMinSizingFunctions.Length() + mRepeatEndDelta;
+ }
+ uint32_t NumRepeatTracks() const
+ {
+ return mRepeatAutoEnd - mRepeatAutoStart;
+ }
+ void SetNumRepeatTracks(uint32_t aNumRepeatTracks)
+ {
+ MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
+ mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
+ mRepeatEndDelta = mHasRepeatAuto ?
+ int32_t(aNumRepeatTracks) - 1 :
+ 0;
+}
+
+ // Some style data references, for easy access.
+ const nsTArray<nsStyleCoord>& mMinSizingFunctions;
+ const nsTArray<nsStyleCoord>& mMaxSizingFunctions;
+ const nsStyleCoord& mAutoMinSizing;
+ const nsStyleCoord& mAutoMaxSizing;
+ // Offset from the start of the implicit grid to the first explicit track.
+ uint32_t mExplicitGridOffset;
+ // The index of the repeat(auto-fill/fit) track, or zero if there is none.
+ const uint32_t mRepeatAutoStart;
+ // The (hypothetical) index of the last such repeat() track.
+ uint32_t mRepeatAutoEnd;
+ // The difference between mExplicitGridEnd and mMinSizingFunctions.Length().
+ int32_t mRepeatEndDelta;
+ // True if there is a specified repeat(auto-fill/fit) track.
+ const bool mHasRepeatAuto;
+ // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
+ nsTArray<bool> mRemovedRepeatTracks;
+};
+
+/**
+ * State for the tracks in one dimension.
+ */
+struct nsGridContainerFrame::Tracks
+{
+ explicit Tracks(LogicalAxis aAxis)
+ : mStateUnion(TrackSize::StateBits(0))
+ , mAxis(aAxis)
+ , mCanResolveLineRangeSize(false)
+ {
+ mBaselineSubtreeAlign[BaselineSharingGroup::eFirst] = NS_STYLE_ALIGN_AUTO;
+ mBaselineSubtreeAlign[BaselineSharingGroup::eLast] = NS_STYLE_ALIGN_AUTO;
+ mBaseline[BaselineSharingGroup::eFirst] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[BaselineSharingGroup::eLast] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ }
+
+ void Initialize(const TrackSizingFunctions& aFunctions,
+ const nsStyleCoord& aGridGap,
+ uint32_t aNumTracks,
+ nscoord aContentBoxSize);
+
+ /**
+ * Return true if aRange spans at least one track with an intrinsic sizing
+ * function and does not span any tracks with a <flex> max-sizing function.
+ * @param aRange the span of tracks to check
+ * @param aState will be set to the union of the state bits of all the spanned
+ * tracks, unless a flex track is found - then it only contains
+ * the union of the tracks up to and including the flex track.
+ */
+ bool HasIntrinsicButNoFlexSizingInRange(const LineRange& aRange,
+ TrackSize::StateBits* aState) const;
+
+ // Some data we collect for aligning baseline-aligned items.
+ struct ItemBaselineData
+ {
+ uint32_t mBaselineTrack;
+ nscoord mBaseline;
+ nscoord mSize;
+ GridItemInfo* mGridItem;
+ static bool IsBaselineTrackLessThan(const ItemBaselineData& a,
+ const ItemBaselineData& b)
+ {
+ return a.mBaselineTrack < b.mBaselineTrack;
+ }
+ };
+
+ /**
+ * Calculate baseline offsets for the given set of items.
+ * Helper for InitialzeItemBaselines.
+ */
+ void CalculateItemBaselines(nsTArray<ItemBaselineData>& aBaselineItems,
+ BaselineSharingGroup aBaselineGroup);
+
+ /**
+ * Initialize grid item baseline state and offsets.
+ */
+ void InitializeItemBaselines(GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems);
+
+ /**
+ * Apply the additional alignment needed to align the baseline-aligned subtree
+ * the item belongs to within its baseline track.
+ */
+ void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
+
+ /**
+ * Resolve Intrinsic Track Sizes.
+ * http://dev.w3.org/csswg/css-grid/#algo-content
+ */
+ void ResolveIntrinsicSize(GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const TrackSizingFunctions& aFunctions,
+ LineRange GridArea::* aRange,
+ nscoord aPercentageBasis,
+ SizingConstraint aConstraint);
+
+ /**
+ * Helper for ResolveIntrinsicSize. It implements step 1 "size tracks to fit
+ * non-spanning items" in the spec. Return true if the track has a <flex>
+ * max-sizing function, false otherwise.
+ */
+ bool ResolveIntrinsicSizeStep1(GridReflowInput& aState,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aPercentageBasis,
+ SizingConstraint aConstraint,
+ const LineRange& aRange,
+ const GridItemInfo& aGridItem);
+ /**
+ * Collect the tracks which are growable (matching aSelector) into
+ * aGrowableTracks, and return the amount of space that can be used
+ * to grow those tracks. Specifically, we return aAvailableSpace minus
+ * the sum of mBase's (and corresponding grid gaps) in aPlan (clamped to 0)
+ * for the tracks in aRange, or zero when there are no growable tracks.
+ * @note aPlan[*].mBase represents a planned new base or limit.
+ */
+ nscoord CollectGrowable(nscoord aAvailableSpace,
+ const nsTArray<TrackSize>& aPlan,
+ const LineRange& aRange,
+ TrackSize::StateBits aSelector,
+ nsTArray<uint32_t>& aGrowableTracks) const
+ {
+ MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
+ nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
+ const uint32_t start = aRange.mStart;
+ const uint32_t end = aRange.mEnd;
+ for (uint32_t i = start; i < end; ++i) {
+ const TrackSize& sz = aPlan[i];
+ space -= sz.mBase;
+ if (space <= 0) {
+ return 0;
+ }
+ if ((sz.mState & aSelector) && !sz.IsFrozen()) {
+ aGrowableTracks.AppendElement(i);
+ }
+ }
+ return aGrowableTracks.IsEmpty() ? 0 : space;
+ }
+
+ void SetupGrowthPlan(nsTArray<TrackSize>& aPlan,
+ const nsTArray<uint32_t>& aTracks) const
+ {
+ for (uint32_t track : aTracks) {
+ aPlan[track] = mSizes[track];
+ }
+ }
+
+ void CopyPlanToBase(const nsTArray<TrackSize>& aPlan,
+ const nsTArray<uint32_t>& aTracks)
+ {
+ for (uint32_t track : aTracks) {
+ MOZ_ASSERT(mSizes[track].mBase <= aPlan[track].mBase);
+ mSizes[track].mBase = aPlan[track].mBase;
+ }
+ }
+
+ void CopyPlanToLimit(const nsTArray<TrackSize>& aPlan,
+ const nsTArray<uint32_t>& aTracks)
+ {
+ for (uint32_t track : aTracks) {
+ MOZ_ASSERT(mSizes[track].mLimit == NS_UNCONSTRAINEDSIZE ||
+ mSizes[track].mLimit <= aPlan[track].mBase);
+ mSizes[track].mLimit = aPlan[track].mBase;
+ }
+ }
+
+ using FitContentClamper =
+ function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
+ /**
+ * Grow the planned size for tracks in aGrowableTracks up to their limit
+ * and then freeze them (all aGrowableTracks must be unfrozen on entry).
+ * Subtract the space added from aAvailableSpace and return that.
+ */
+ nscoord GrowTracksToLimit(nscoord aAvailableSpace,
+ nsTArray<TrackSize>& aPlan,
+ const nsTArray<uint32_t>& aGrowableTracks,
+ FitContentClamper aFitContentClamper) const
+ {
+ MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
+ nscoord space = aAvailableSpace;
+ uint32_t numGrowable = aGrowableTracks.Length();
+ while (true) {
+ nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
+ for (uint32_t track : aGrowableTracks) {
+ TrackSize& sz = aPlan[track];
+ if (sz.IsFrozen()) {
+ continue;
+ }
+ nscoord newBase = sz.mBase + spacePerTrack;
+ nscoord limit = sz.mLimit;
+ if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
+ aFitContentClamper)) {
+ // Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
+ aFitContentClamper(track, sz.mBase, &limit);
+ }
+ if (newBase > limit) {
+ nscoord consumed = limit - sz.mBase;
+ if (consumed > 0) {
+ space -= consumed;
+ sz.mBase = limit;
+ }
+ sz.mState |= TrackSize::eFrozen;
+ if (--numGrowable == 0) {
+ return space;
+ }
+ } else {
+ sz.mBase = newBase;
+ space -= spacePerTrack;
+ }
+ MOZ_ASSERT(space >= 0);
+ if (space == 0) {
+ return 0;
+ }
+ }
+ }
+ MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
+ return 0;
+ }
+
+ /**
+ * Helper for GrowSelectedTracksUnlimited. For the set of tracks (S) that
+ * match aMinSizingSelector: if a track in S doesn't match aMaxSizingSelector
+ * then mark it with aSkipFlag. If all tracks in S were marked then unmark
+ * them. Return aNumGrowable minus the number of tracks marked. It is
+ * assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
+ * on entry to this method.
+ */
+ uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
+ uint32_t aNumGrowable,
+ const nsTArray<uint32_t>& aGrowableTracks,
+ TrackSize::StateBits aMinSizingSelector,
+ TrackSize::StateBits aMaxSizingSelector,
+ TrackSize::StateBits aSkipFlag) const
+ {
+ bool foundOneSelected = false;
+ bool foundOneGrowable = false;
+ uint32_t numGrowable = aNumGrowable;
+ for (uint32_t track : aGrowableTracks) {
+ TrackSize& sz = aPlan[track];
+ const auto state = sz.mState;
+ if (state & aMinSizingSelector) {
+ foundOneSelected = true;
+ if (state & aMaxSizingSelector) {
+ foundOneGrowable = true;
+ continue;
+ }
+ sz.mState |= aSkipFlag;
+ MOZ_ASSERT(numGrowable != 0);
+ --numGrowable;
+ }
+ }
+ // 12.5 "if there are no such tracks, then all affected tracks"
+ if (foundOneSelected && !foundOneGrowable) {
+ for (uint32_t track : aGrowableTracks) {
+ aPlan[track].mState &= ~aSkipFlag;
+ }
+ numGrowable = aNumGrowable;
+ }
+ return numGrowable;
+ }
+
+ /**
+ * Increase the planned size for tracks in aGrowableTracks that match
+ * aSelector (or all tracks if aSelector is zero) beyond their limit.
+ * This implements the "Distribute space beyond growth limits" step in
+ * https://drafts.csswg.org/css-grid/#distribute-extra-space
+ */
+ void GrowSelectedTracksUnlimited(nscoord aAvailableSpace,
+ nsTArray<TrackSize>& aPlan,
+ const nsTArray<uint32_t>& aGrowableTracks,
+ TrackSize::StateBits aSelector,
+ FitContentClamper aFitContentClamper) const
+ {
+ MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
+ uint32_t numGrowable = aGrowableTracks.Length();
+ if (aSelector) {
+ MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
+ (aSelector & TrackSize::eMaxContentMinSizing),
+ "Should only get here for track sizing steps 2.1 to 2.3");
+ // Note that eMaxContentMinSizing is always included. We do those first:
+ numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
+ TrackSize::eMaxContentMinSizing,
+ TrackSize::eMaxContentMaxSizing,
+ TrackSize::eSkipGrowUnlimited1);
+ // Now mark min-content/auto min-sizing tracks if requested.
+ auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
+ if (minOrAutoSelector) {
+ numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
+ minOrAutoSelector,
+ TrackSize::eIntrinsicMaxSizing,
+ TrackSize::eSkipGrowUnlimited2);
+ }
+ }
+ nscoord space = aAvailableSpace;
+ DebugOnly<bool> didClamp = false;
+ while (numGrowable) {
+ nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
+ for (uint32_t track : aGrowableTracks) {
+ TrackSize& sz = aPlan[track];
+ if (sz.mState & TrackSize::eSkipGrowUnlimited) {
+ continue; // an excluded track
+ }
+ nscoord delta = spacePerTrack;
+ nscoord newBase = sz.mBase + delta;
+ if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
+ aFitContentClamper)) {
+ // Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
+ if (aFitContentClamper(track, sz.mBase, &newBase)) {
+ didClamp = true;
+ delta = newBase - sz.mBase;
+ MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
+ sz.mState |= TrackSize::eSkipGrowUnlimited1;
+ --numGrowable;
+ }
+ }
+ sz.mBase = newBase;
+ space -= delta;
+ MOZ_ASSERT(space >= 0);
+ if (space == 0) {
+ return;
+ }
+ }
+ }
+ MOZ_ASSERT(didClamp, "we don't exit the loop above except by return, "
+ "unless we clamped some track's size");
+ }
+
+ /**
+ * Distribute aAvailableSpace to the planned base size for aGrowableTracks
+ * up to their limits, then distribute the remaining space beyond the limits.
+ */
+ void DistributeToTrackBases(nscoord aAvailableSpace,
+ nsTArray<TrackSize>& aPlan,
+ nsTArray<uint32_t>& aGrowableTracks,
+ TrackSize::StateBits aSelector)
+ {
+ SetupGrowthPlan(aPlan, aGrowableTracks);
+ nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks, nullptr);
+ if (space > 0) {
+ GrowSelectedTracksUnlimited(space, aPlan, aGrowableTracks, aSelector, nullptr);
+ }
+ CopyPlanToBase(aPlan, aGrowableTracks);
+ }
+
+ /**
+ * Distribute aAvailableSpace to the planned limits for aGrowableTracks.
+ */
+ void DistributeToTrackLimits(nscoord aAvailableSpace,
+ nsTArray<TrackSize>& aPlan,
+ nsTArray<uint32_t>& aGrowableTracks,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aPercentageBasis)
+ {
+ auto fitContentClamper = [&aFunctions, aPercentageBasis] (uint32_t aTrack,
+ nscoord aMinSize,
+ nscoord* aSize) {
+ nscoord fitContentLimit =
+ ::ResolveToDefiniteSize(aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
+ if (*aSize > fitContentLimit) {
+ *aSize = std::max(aMinSize, fitContentLimit);
+ return true;
+ }
+ return false;
+ };
+ nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks,
+ fitContentClamper);
+ if (space > 0) {
+ GrowSelectedTracksUnlimited(aAvailableSpace, aPlan, aGrowableTracks,
+ TrackSize::StateBits(0), fitContentClamper);
+ }
+ CopyPlanToLimit(aPlan, aGrowableTracks);
+ }
+
+ /**
+ * Distribute aAvailableSize to the tracks. This implements 12.6 at:
+ * http://dev.w3.org/csswg/css-grid/#algo-grow-tracks
+ */
+ void DistributeFreeSpace(nscoord aAvailableSize)
+ {
+ const uint32_t numTracks = mSizes.Length();
+ if (MOZ_UNLIKELY(numTracks == 0 || aAvailableSize <= 0)) {
+ return;
+ }
+ if (aAvailableSize == NS_UNCONSTRAINEDSIZE) {
+ for (TrackSize& sz : mSizes) {
+ sz.mBase = sz.mLimit;
+ }
+ } else {
+ // Compute free space and count growable tracks.
+ nscoord space = aAvailableSize;
+ uint32_t numGrowable = numTracks;
+ for (const TrackSize& sz : mSizes) {
+ space -= sz.mBase;
+ MOZ_ASSERT(sz.mBase <= sz.mLimit);
+ if (sz.mBase == sz.mLimit) {
+ --numGrowable;
+ }
+ }
+ // Distribute the free space evenly to the growable tracks. If not exactly
+ // divisable the remainder is added to the leading tracks.
+ while (space > 0 && numGrowable) {
+ nscoord spacePerTrack =
+ std::max<nscoord>(space / numGrowable, 1);
+ for (uint32_t i = 0; i < numTracks && space > 0; ++i) {
+ TrackSize& sz = mSizes[i];
+ if (sz.mBase == sz.mLimit) {
+ continue;
+ }
+ nscoord newBase = sz.mBase + spacePerTrack;
+ if (newBase >= sz.mLimit) {
+ space -= sz.mLimit - sz.mBase;
+ sz.mBase = sz.mLimit;
+ --numGrowable;
+ } else {
+ space -= spacePerTrack;
+ sz.mBase = newBase;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Implements "12.7.1. Find the Size of an 'fr'".
+ * http://dev.w3.org/csswg/css-grid/#algo-find-fr-size
+ * (The returned value is a 'nscoord' divided by a factor - a floating type
+ * is used to avoid intermediary rounding errors.)
+ */
+ float FindFrUnitSize(const LineRange& aRange,
+ const nsTArray<uint32_t>& aFlexTracks,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aSpaceToFill) const;
+
+ /**
+ * Implements the "find the used flex fraction" part of StretchFlexibleTracks.
+ * (The returned value is a 'nscoord' divided by a factor - a floating type
+ * is used to avoid intermediary rounding errors.)
+ */
+ float FindUsedFlexFraction(GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const nsTArray<uint32_t>& aFlexTracks,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aAvailableSize) const;
+
+ /**
+ * Implements "12.7. Stretch Flexible Tracks"
+ * http://dev.w3.org/csswg/css-grid/#algo-flex-tracks
+ */
+ void StretchFlexibleTracks(GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aAvailableSize);
+
+ /**
+ * Implements "12.3. Track Sizing Algorithm"
+ * http://dev.w3.org/csswg/css-grid/#algo-track-sizing
+ */
+ void CalculateSizes(GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aContentSize,
+ LineRange GridArea::* aRange,
+ SizingConstraint aConstraint);
+
+ /**
+ * Apply 'align/justify-content', whichever is relevant for this axis.
+ * https://drafts.csswg.org/css-align-3/#propdef-align-content
+ */
+ void AlignJustifyContent(const nsStylePosition* aStyle,
+ WritingMode aWM,
+ const LogicalSize& aContainerSize);
+
+ /**
+ * Return the intrinsic size by back-computing percentages as:
+ * IntrinsicSize = SumOfCoordSizes / (1 - SumOfPercentages).
+ */
+ nscoord BackComputedIntrinsicSize(const TrackSizingFunctions& aFunctions,
+ const nsStyleCoord& aGridGap) const;
+
+ nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const
+ {
+ if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
+ // https://drafts.csswg.org/css-grid/#grid-definition
+ // "... the explicit grid still contains one grid line in each axis."
+ MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
+ return nscoord(0);
+ }
+ MOZ_ASSERT(aLine <= mSizes.Length(), "mSizes is too small");
+ if (aSide == GridLineSide::eBeforeGridGap) {
+ if (aLine == 0) {
+ return nscoord(0);
+ }
+ const TrackSize& sz = mSizes[aLine - 1];
+ return sz.mPosition + sz.mBase;
+ }
+ if (aLine == mSizes.Length()) {
+ return mContentBoxSize;
+ }
+ return mSizes[aLine].mPosition;
+ }
+
+ nscoord SumOfGridGaps() const
+ {
+ auto len = mSizes.Length();
+ return MOZ_LIKELY(len > 1) ? (len - 1) * mGridGap : 0;
+ }
+
+ /**
+ * Break before aRow, i.e. set the eBreakBefore flag on aRow and set the grid
+ * gap before aRow to zero (and shift all rows after it by the removed gap).
+ */
+ void BreakBeforeRow(uint32_t aRow)
+ {
+ MOZ_ASSERT(mAxis == eLogicalAxisBlock,
+ "Should only be fragmenting in the block axis (between rows)");
+ nscoord prevRowEndPos = 0;
+ if (aRow != 0) {
+ auto& prevSz = mSizes[aRow - 1];
+ prevRowEndPos = prevSz.mPosition + prevSz.mBase;
+ }
+ auto& sz = mSizes[aRow];
+ const nscoord gap = sz.mPosition - prevRowEndPos;
+ sz.mState |= TrackSize::eBreakBefore;
+ if (gap != 0) {
+ for (uint32_t i = aRow, len = mSizes.Length(); i < len; ++i) {
+ mSizes[i].mPosition -= gap;
+ }
+ }
+ }
+
+ /**
+ * Set the size of aRow to aSize and adjust the position of all rows after it.
+ */
+ void ResizeRow(uint32_t aRow, nscoord aNewSize)
+ {
+ MOZ_ASSERT(mAxis == eLogicalAxisBlock,
+ "Should only be fragmenting in the block axis (between rows)");
+ MOZ_ASSERT(aNewSize >= 0);
+ auto& sz = mSizes[aRow];
+ nscoord delta = aNewSize - sz.mBase;
+ NS_WARNING_ASSERTION(delta != nscoord(0), "Useless call to ResizeRow");
+ sz.mBase = aNewSize;
+ const uint32_t numRows = mSizes.Length();
+ for (uint32_t r = aRow + 1; r < numRows; ++r) {
+ mSizes[r].mPosition += delta;
+ }
+ }
+
+ nscoord ResolveSize(const LineRange& aRange) const
+ {
+ MOZ_ASSERT(mCanResolveLineRangeSize);
+ MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
+ nscoord pos, size;
+ aRange.ToPositionAndLength(mSizes, &pos, &size);
+ return size;
+ }
+
+ nsTArray<nsString> GetExplicitLineNamesAtIndex(
+ const nsStyleGridTemplate& aGridTemplate,
+ const TrackSizingFunctions& aFunctions,
+ uint32_t aIndex)
+ {
+ nsTArray<nsString> lineNames;
+
+ bool hasRepeatAuto = aGridTemplate.HasRepeatAuto();
+ const nsTArray<nsTArray<nsString>>& lineNameLists(
+ aGridTemplate.mLineNameLists);
+
+ if (!hasRepeatAuto) {
+ if (aIndex < lineNameLists.Length()) {
+ lineNames.AppendElements(lineNameLists[aIndex]);
+ }
+ } else {
+ const uint32_t repeatTrackCount = aFunctions.NumRepeatTracks();
+ const uint32_t repeatAutoStart = aGridTemplate.mRepeatAutoIndex;
+ const uint32_t repeatAutoEnd = (repeatAutoStart + repeatTrackCount);
+ const int32_t repeatEndDelta = int32_t(repeatTrackCount - 1);
+
+ if (aIndex <= repeatAutoStart) {
+ if (aIndex < lineNameLists.Length()) {
+ lineNames.AppendElements(lineNameLists[aIndex]);
+ }
+ if (aIndex == repeatAutoEnd) {
+ uint32_t i = aIndex + 1;
+ if (i < lineNameLists.Length()) {
+ lineNames.AppendElements(lineNameLists[i]);
+ }
+ }
+ }
+ if (aIndex <= repeatAutoEnd && aIndex > repeatAutoStart) {
+ lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListAfter);
+ }
+ if (aIndex < repeatAutoEnd && aIndex >= repeatAutoStart) {
+ lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListBefore);
+ }
+ if (aIndex >= repeatAutoEnd && aIndex > repeatAutoStart) {
+ uint32_t i = aIndex - repeatEndDelta;
+ if (i < lineNameLists.Length()) {
+ lineNames.AppendElements(lineNameLists[i]);
+ }
+ }
+ }
+
+ return lineNames;
+ }
+
+#ifdef DEBUG
+ void Dump() const
+ {
+ for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
+ printf(" %d: ", i);
+ mSizes[i].Dump();
+ printf("\n");
+ }
+ }
+#endif
+
+ AutoTArray<TrackSize, 32> mSizes;
+ nscoord mContentBoxSize;
+ nscoord mGridGap;
+ // The first(last)-baseline for the first(last) track in this axis.
+ nscoord mBaseline[2]; // index by BaselineSharingGroup
+ // The union of the track min/max-sizing state bits in this axis.
+ TrackSize::StateBits mStateUnion;
+ LogicalAxis mAxis;
+ // Used for aligning a baseline-aligned subtree of items. The only possible
+ // values are NS_STYLE_ALIGN_{START,END,CENTER,AUTO}. AUTO means there are
+ // no baseline-aligned items in any track in that axis.
+ // There is one alignment value for each BaselineSharingGroup.
+ uint8_t mBaselineSubtreeAlign[2];
+ // True if track positions and sizes are final in this axis.
+ bool mCanResolveLineRangeSize;
+};
+
+/**
+ * Grid data shared by all continuations, owned by the first-in-flow.
+ * The data is initialized from the first-in-flow's GridReflowInput at
+ * the end of its reflow. Fragmentation will modify mRows.mSizes -
+ * the mPosition to remove the row gap at the break boundary, the mState
+ * by setting the eBreakBefore flag, and mBase is modified when we decide
+ * to grow a row. mOriginalRowData is setup by the first-in-flow and
+ * not modified after that. It's used for undoing the changes to mRows.
+ * mCols, mGridItems, mAbsPosItems are used for initializing the grid
+ * reflow state for continuations, see GridReflowInput::Initialize below.
+ */
+struct nsGridContainerFrame::SharedGridData
+{
+ SharedGridData() :
+ mCols(eLogicalAxisInline),
+ mRows(eLogicalAxisBlock),
+ mGenerateComputedGridInfo(false) {}
+ Tracks mCols;
+ Tracks mRows;
+ struct RowData {
+ nscoord mBase; // the original track size
+ nscoord mGap; // the original gap before a track
+ };
+ nsTArray<RowData> mOriginalRowData;
+ nsTArray<GridItemInfo> mGridItems;
+ nsTArray<GridItemInfo> mAbsPosItems;
+ bool mGenerateComputedGridInfo;
+
+ /**
+ * Only set on the first-in-flow. Continuations will Initialize() their
+ * GridReflowInput from it.
+ */
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedGridData)
+};
+
+struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput
+{
+ GridReflowInput(nsGridContainerFrame* aFrame,
+ const ReflowInput& aRI)
+ : GridReflowInput(aFrame, *aRI.mRenderingContext, &aRI, aRI.mStylePosition,
+ aRI.GetWritingMode())
+ {}
+ GridReflowInput(nsGridContainerFrame* aFrame,
+ nsRenderingContext& aRC)
+ : GridReflowInput(aFrame, aRC, nullptr, aFrame->StylePosition(),
+ aFrame->GetWritingMode())
+ {}
+
+ /**
+ * Initialize our track sizes and grid item info using the shared
+ * state from aGridContainerFrame first-in-flow.
+ */
+ void InitializeForContinuation(nsGridContainerFrame* aGridContainerFrame,
+ nscoord aConsumedBSize)
+ {
+ MOZ_ASSERT(aGridContainerFrame->GetPrevInFlow(),
+ "don't call this on the first-in-flow");
+ MOZ_ASSERT(mGridItems.IsEmpty() && mAbsPosItems.IsEmpty(),
+ "shouldn't have any item data yet");
+
+ // Get the SharedGridData from the first-in-flow. Also calculate the number
+ // of fragments before this so that we can figure out our start row below.
+ uint32_t fragment = 0;
+ nsIFrame* firstInFlow = aGridContainerFrame;
+ for (auto pif = aGridContainerFrame->GetPrevInFlow();
+ pif; pif = pif->GetPrevInFlow()) {
+ ++fragment;
+ firstInFlow = pif;
+ }
+ mSharedGridData = firstInFlow->Properties().Get(SharedGridData::Prop());
+ MOZ_ASSERT(mSharedGridData, "first-in-flow must have SharedGridData");
+
+ // Find the start row for this fragment and undo breaks after that row
+ // since the breaks might be different from the last reflow.
+ auto& rowSizes = mSharedGridData->mRows.mSizes;
+ const uint32_t numRows = rowSizes.Length();
+ mStartRow = numRows;
+ for (uint32_t row = 0, breakCount = 0; row < numRows; ++row) {
+ if (rowSizes[row].mState & TrackSize::eBreakBefore) {
+ if (fragment == ++breakCount) {
+ mStartRow = row;
+ mFragBStart = rowSizes[row].mPosition;
+ // Restore the original size for |row| and grid gaps / state after it.
+ const auto& origRowData = mSharedGridData->mOriginalRowData;
+ rowSizes[row].mBase = origRowData[row].mBase;
+ nscoord prevEndPos = rowSizes[row].mPosition + rowSizes[row].mBase;
+ while (++row < numRows) {
+ auto& sz = rowSizes[row];
+ const auto& orig = origRowData[row];
+ sz.mPosition = prevEndPos + orig.mGap;
+ sz.mBase = orig.mBase;
+ sz.mState &= ~TrackSize::eBreakBefore;
+ prevEndPos = sz.mPosition + sz.mBase;
+ }
+ break;
+ }
+ }
+ }
+ if (mStartRow == numRows) {
+ // All of the grid's rows fit inside of previous grid-container fragments.
+ mFragBStart = aConsumedBSize;
+ }
+
+ // Copy the shared track state.
+ // XXX consider temporarily swapping the array elements instead and swapping
+ // XXX them back after we're done reflowing, for better performance.
+ // XXX (bug 1252002)
+ mCols = mSharedGridData->mCols;
+ mRows = mSharedGridData->mRows;
+
+ // Copy item data from each child's first-in-flow data in mSharedGridData.
+ // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
+ mIter.Reset();
+ for (; !mIter.AtEnd(); mIter.Next()) {
+ nsIFrame* child = *mIter;
+ nsIFrame* childFirstInFlow = child->FirstInFlow();
+ DebugOnly<size_t> len = mGridItems.Length();
+ for (auto& itemInfo : mSharedGridData->mGridItems) {
+ if (itemInfo.mFrame == childFirstInFlow) {
+ auto item = mGridItems.AppendElement(GridItemInfo(child, itemInfo.mArea));
+ // Copy the item's baseline data so that the item's last fragment can do
+ // 'last baseline' alignment if necessary.
+ item->mState[0] |= itemInfo.mState[0] & ItemState::eAllBaselineBits;
+ item->mState[1] |= itemInfo.mState[1] & ItemState::eAllBaselineBits;
+ item->mBaselineOffset[0] = itemInfo.mBaselineOffset[0];
+ item->mBaselineOffset[1] = itemInfo.mBaselineOffset[1];
+ break;
+ }
+ }
+ MOZ_ASSERT(mGridItems.Length() == len + 1, "can't find GridItemInfo");
+ }
+
+ // XXX NOTE: This is O(n^2) in the number of abs.pos. items. (bug 1252186)
+ nsFrameList absPosChildren(aGridContainerFrame->GetChildList(
+ aGridContainerFrame->GetAbsoluteListID()));
+ for (auto f : absPosChildren) {
+ nsIFrame* childFirstInFlow = f->FirstInFlow();
+ DebugOnly<size_t> len = mAbsPosItems.Length();
+ for (auto& itemInfo : mSharedGridData->mAbsPosItems) {
+ if (itemInfo.mFrame == childFirstInFlow) {
+ mAbsPosItems.AppendElement(GridItemInfo(f, itemInfo.mArea));
+ break;
+ }
+ }
+ MOZ_ASSERT(mAbsPosItems.Length() == len + 1, "can't find GridItemInfo");
+ }
+
+ // Copy in the computed grid info state bit
+ if (mSharedGridData->mGenerateComputedGridInfo) {
+ aGridContainerFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
+ }
+ }
+
+ /**
+ * Calculate our track sizes. If the given aContentBox block-axis size is
+ * unconstrained, it is assigned to the resulting intrinsic block-axis size.
+ */
+ void CalculateTrackSizes(const Grid& aGrid,
+ LogicalSize& aContentBox,
+ SizingConstraint aConstraint);
+
+ /**
+ * Return the containing block for a grid item occupying aArea.
+ */
+ LogicalRect ContainingBlockFor(const GridArea& aArea) const;
+
+ /**
+ * Return the containing block for an abs.pos. grid item occupying aArea.
+ * Any 'auto' lines in the grid area will be aligned with grid container
+ * containing block on that side.
+ * @param aGridOrigin the origin of the grid
+ * @param aGridCB the grid container containing block (its padding area)
+ */
+ LogicalRect ContainingBlockForAbsPos(const GridArea& aArea,
+ const LogicalPoint& aGridOrigin,
+ const LogicalRect& aGridCB) const;
+
+ GridItemCSSOrderIterator mIter;
+ const nsStylePosition* const mGridStyle;
+ Tracks mCols;
+ Tracks mRows;
+ TrackSizingFunctions mColFunctions;
+ TrackSizingFunctions mRowFunctions;
+ /**
+ * Info about each (normal flow) grid item.
+ */
+ nsTArray<GridItemInfo> mGridItems;
+ /**
+ * Info about each grid-aligned abs.pos. child.
+ */
+ nsTArray<GridItemInfo> mAbsPosItems;
+
+ /**
+ * @note mReflowInput may be null when using the 2nd ctor above. In this case
+ * we'll construct a dummy parent reflow state if we need it to calculate
+ * min/max-content contributions when sizing tracks.
+ */
+ const ReflowInput* const mReflowInput;
+ nsRenderingContext& mRenderingContext;
+ nsGridContainerFrame* const mFrame;
+ SharedGridData* mSharedGridData; // [weak] owned by mFrame's first-in-flow.
+ /** Computed border+padding with mSkipSides applied. */
+ LogicalMargin mBorderPadding;
+ /**
+ * BStart of this fragment in "grid space" (i.e. the concatenation of content
+ * areas of all fragments). Equal to mRows.mSizes[mStartRow].mPosition,
+ * or, if this fragment starts after the last row, the GetConsumedBSize().
+ */
+ nscoord mFragBStart;
+ /** The start row for this fragment. */
+ uint32_t mStartRow;
+ /**
+ * The start row for the next fragment, if any. If mNextFragmentStartRow ==
+ * mStartRow then there are no rows in this fragment.
+ */
+ uint32_t mNextFragmentStartRow;
+ /** Our tentative ApplySkipSides bits. */
+ LogicalSides mSkipSides;
+ const WritingMode mWM;
+ /** Initialized lazily, when we find the fragmentainer. */
+ bool mInFragmentainer;
+
+private:
+ GridReflowInput(nsGridContainerFrame* aFrame,
+ nsRenderingContext& aRenderingContext,
+ const ReflowInput* aReflowInput,
+ const nsStylePosition* aGridStyle,
+ const WritingMode& aWM)
+ : mIter(aFrame, kPrincipalList)
+ , mGridStyle(aGridStyle)
+ , mCols(eLogicalAxisInline)
+ , mRows(eLogicalAxisBlock)
+ , mColFunctions(mGridStyle->mGridTemplateColumns,
+ mGridStyle->mGridAutoColumnsMin,
+ mGridStyle->mGridAutoColumnsMax)
+ , mRowFunctions(mGridStyle->mGridTemplateRows,
+ mGridStyle->mGridAutoRowsMin,
+ mGridStyle->mGridAutoRowsMax)
+ , mReflowInput(aReflowInput)
+ , mRenderingContext(aRenderingContext)
+ , mFrame(aFrame)
+ , mSharedGridData(nullptr)
+ , mBorderPadding(aWM)
+ , mFragBStart(0)
+ , mStartRow(0)
+ , mNextFragmentStartRow(0)
+ , mWM(aWM)
+ , mInFragmentainer(false)
+ {
+ MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == mFrame);
+ if (aReflowInput) {
+ mBorderPadding = aReflowInput->ComputedLogicalBorderPadding();
+ mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides();
+ mBorderPadding.ApplySkipSides(mSkipSides);
+ }
+ }
+};
+
+using GridReflowInput = nsGridContainerFrame::GridReflowInput;
+
+/**
+ * The Grid implements grid item placement and the state of the grid -
+ * the size of the explicit/implicit grid, which cells are occupied etc.
+ */
+struct MOZ_STACK_CLASS nsGridContainerFrame::Grid
+{
+ /**
+ * Place all child frames into the grid and expand the (implicit) grid as
+ * needed. The allocated GridAreas are stored in the GridAreaProperty
+ * frame property on the child frame.
+ * @param aComputedMinSize the container's min-size - used to determine
+ * the number of repeat(auto-fill/fit) tracks.
+ * @param aComputedSize the container's size - used to determine
+ * the number of repeat(auto-fill/fit) tracks.
+ * @param aComputedMaxSize the container's max-size - used to determine
+ * the number of repeat(auto-fill/fit) tracks.
+ */
+ void PlaceGridItems(GridReflowInput& aState,
+ const LogicalSize& aComputedMinSize,
+ const LogicalSize& aComputedSize,
+ const LogicalSize& aComputedMaxSize);
+
+ /**
+ * As above but for an abs.pos. child. Any 'auto' lines will be represented
+ * by kAutoLine in the LineRange result.
+ * @param aGridStart the first line in the final, but untranslated grid
+ * @param aGridEnd the last line in the final, but untranslated grid
+ */
+ LineRange ResolveAbsPosLineRange(const nsStyleGridLine& aStart,
+ const nsStyleGridLine& aEnd,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ int32_t aGridStart,
+ int32_t aGridEnd,
+ const nsStylePosition* aStyle);
+
+ /**
+ * Return a GridArea for abs.pos. item with non-auto lines placed at
+ * a definite line (1-based) with placement errors resolved. One or both
+ * positions may still be 'auto'.
+ * @param aChild the abs.pos. grid item to place
+ * @param aStyle the StylePosition() for the grid container
+ */
+ GridArea PlaceAbsPos(nsIFrame* aChild,
+ const LineNameMap& aColLineNameMap,
+ const LineNameMap& aRowLineNameMap,
+ const nsStylePosition* aStyle);
+
+ /**
+ * Find the first column in row aLockedRow starting at aStartCol where aArea
+ * could be placed without overlapping other items. The returned column may
+ * cause aArea to overflow the current implicit grid bounds if placed there.
+ */
+ uint32_t FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
+ const GridArea* aArea) const;
+
+ /**
+ * Place aArea in the first column (in row aArea->mRows.mStart) starting at
+ * aStartCol without overlapping other items. The resulting aArea may
+ * overflow the current implicit grid bounds.
+ * Pre-condition: aArea->mRows.IsDefinite() is true.
+ * Post-condition: aArea->IsDefinite() is true.
+ */
+ void PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const;
+
+ /**
+ * Find the first row in column aLockedCol starting at aStartRow where aArea
+ * could be placed without overlapping other items. The returned row may
+ * cause aArea to overflow the current implicit grid bounds if placed there.
+ */
+ uint32_t FindAutoRow(uint32_t aLockedCol, uint32_t aStartRow,
+ const GridArea* aArea) const;
+
+ /**
+ * Place aArea in the first row (in column aArea->mCols.mStart) starting at
+ * aStartRow without overlapping other items. The resulting aArea may
+ * overflow the current implicit grid bounds.
+ * Pre-condition: aArea->mCols.IsDefinite() is true.
+ * Post-condition: aArea->IsDefinite() is true.
+ */
+ void PlaceAutoRow(uint32_t aStartRow, GridArea* aArea) const;
+
+ /**
+ * Place aArea in the first column starting at aStartCol,aStartRow without
+ * causing it to overlap other items or overflow mGridColEnd.
+ * If there's no such column in aStartRow, continue in position 1,aStartRow+1.
+ * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
+ * Post-condition: aArea->IsDefinite() is true.
+ */
+ void PlaceAutoAutoInRowOrder(uint32_t aStartCol,
+ uint32_t aStartRow,
+ GridArea* aArea) const;
+
+ /**
+ * Place aArea in the first row starting at aStartCol,aStartRow without
+ * causing it to overlap other items or overflow mGridRowEnd.
+ * If there's no such row in aStartCol, continue in position aStartCol+1,1.
+ * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
+ * Post-condition: aArea->IsDefinite() is true.
+ */
+ void PlaceAutoAutoInColOrder(uint32_t aStartCol,
+ uint32_t aStartRow,
+ GridArea* aArea) const;
+
+ /**
+ * Return aLine if it's inside the aMin..aMax range (inclusive),
+ * otherwise return kAutoLine.
+ */
+ static int32_t
+ AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax)
+ {
+ MOZ_ASSERT(aMin <= aMax);
+ if (aLine < aMin || aLine > aMax) {
+ return kAutoLine;
+ }
+ return aLine;
+ }
+
+ /**
+ * Inflate the implicit grid to include aArea.
+ * @param aArea may be definite or auto
+ */
+ void InflateGridFor(const GridArea& aArea)
+ {
+ mGridColEnd = std::max(mGridColEnd, aArea.mCols.HypotheticalEnd());
+ mGridRowEnd = std::max(mGridRowEnd, aArea.mRows.HypotheticalEnd());
+ MOZ_ASSERT(mGridColEnd <= kTranslatedMaxLine &&
+ mGridRowEnd <= kTranslatedMaxLine);
+ }
+
+ enum LineRangeSide {
+ eLineRangeSideStart, eLineRangeSideEnd
+ };
+ /**
+ * Return a line number for (non-auto) aLine, per:
+ * http://dev.w3.org/csswg/css-grid/#line-placement
+ * @param aLine style data for the line (must be non-auto)
+ * @param aNth a number of lines to find from aFromIndex, negative if the
+ * search should be in reverse order. In the case aLine has
+ * a specified line name, it's permitted to pass in zero which
+ * will be treated as one.
+ * @param aFromIndex the zero-based index to start counting from
+ * @param aLineNameList the explicit named lines
+ * @param aAreaStart a pointer to GridNamedArea::mColumnStart/mRowStart
+ * @param aAreaEnd a pointer to GridNamedArea::mColumnEnd/mRowEnd
+ * @param aExplicitGridEnd the last line in the explicit grid
+ * @param aEdge indicates whether we are resolving a start or end line
+ * @param aStyle the StylePosition() for the grid container
+ * @return a definite line (1-based), clamped to the kMinLine..kMaxLine range
+ */
+ int32_t ResolveLine(const nsStyleGridLine& aLine,
+ int32_t aNth,
+ uint32_t aFromIndex,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ LineRangeSide aSide,
+ const nsStylePosition* aStyle);
+
+ /**
+ * Helper method for ResolveLineRange.
+ * @see ResolveLineRange
+ * @return a pair (start,end) of lines
+ */
+ typedef std::pair<int32_t, int32_t> LinePair;
+ LinePair ResolveLineRangeHelper(const nsStyleGridLine& aStart,
+ const nsStyleGridLine& aEnd,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ const nsStylePosition* aStyle);
+
+ /**
+ * Return a LineRange based on the given style data. Non-auto lines
+ * are resolved to a definite line number (1-based) per:
+ * http://dev.w3.org/csswg/css-grid/#line-placement
+ * with placement errors corrected per:
+ * http://dev.w3.org/csswg/css-grid/#grid-placement-errors
+ * @param aStyle the StylePosition() for the grid container
+ * @param aStart style data for the start line
+ * @param aEnd style data for the end line
+ * @param aLineNameList the explicit named lines
+ * @param aAreaStart a pointer to GridNamedArea::mColumnStart/mRowStart
+ * @param aAreaEnd a pointer to GridNamedArea::mColumnEnd/mRowEnd
+ * @param aExplicitGridEnd the last line in the explicit grid
+ * @param aStyle the StylePosition() for the grid container
+ */
+ LineRange ResolveLineRange(const nsStyleGridLine& aStart,
+ const nsStyleGridLine& aEnd,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ const nsStylePosition* aStyle);
+
+ /**
+ * Return a GridArea with non-auto lines placed at a definite line (1-based)
+ * with placement errors resolved. One or both positions may still
+ * be 'auto'.
+ * @param aChild the grid item
+ * @param aStyle the StylePosition() for the grid container
+ */
+ GridArea PlaceDefinite(nsIFrame* aChild,
+ const LineNameMap& aColLineNameMap,
+ const LineNameMap& aRowLineNameMap,
+ const nsStylePosition* aStyle);
+
+ bool HasImplicitNamedArea(const nsString& aName) const
+ {
+ return mAreas && mAreas->Contains(aName);
+ }
+
+ /**
+ * A convenience method to lookup a name in 'grid-template-areas'.
+ * @param aStyle the StylePosition() for the grid container
+ * @return null if not found
+ */
+ static const css::GridNamedArea*
+ FindNamedArea(const nsSubstring& aName, const nsStylePosition* aStyle)
+ {
+ if (!aStyle->mGridTemplateAreas) {
+ return nullptr;
+ }
+ const nsTArray<css::GridNamedArea>& areas =
+ aStyle->mGridTemplateAreas->mNamedAreas;
+ size_t len = areas.Length();
+ for (size_t i = 0; i < len; ++i) {
+ const css::GridNamedArea& area = areas[i];
+ if (area.mName == aName) {
+ return &area;
+ }
+ }
+ return nullptr;
+ }
+
+ // Return true if aString ends in aSuffix and has at least one character before
+ // the suffix. Assign aIndex to where the suffix starts.
+ static bool
+ IsNameWithSuffix(const nsString& aString, const nsString& aSuffix,
+ uint32_t* aIndex)
+ {
+ if (StringEndsWith(aString, aSuffix)) {
+ *aIndex = aString.Length() - aSuffix.Length();
+ return *aIndex != 0;
+ }
+ return false;
+ }
+
+ static bool
+ IsNameWithEndSuffix(const nsString& aString, uint32_t* aIndex)
+ {
+ return IsNameWithSuffix(aString, NS_LITERAL_STRING("-end"), aIndex);
+ }
+
+ static bool
+ IsNameWithStartSuffix(const nsString& aString, uint32_t* aIndex)
+ {
+ return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex);
+ }
+
+ /**
+ * A CellMap holds state for each cell in the grid.
+ * It's row major. It's sparse in the sense that it only has enough rows to
+ * cover the last row that has a grid item. Each row only has enough entries
+ * to cover columns that are occupied *on that row*, i.e. it's not a full
+ * matrix covering the entire implicit grid. An absent Cell means that it's
+ * unoccupied by any grid item.
+ */
+ struct CellMap {
+ struct Cell {
+ Cell() : mIsOccupied(false) {}
+ bool mIsOccupied : 1;
+ };
+
+ void Fill(const GridArea& aGridArea)
+ {
+ MOZ_ASSERT(aGridArea.IsDefinite());
+ MOZ_ASSERT(aGridArea.mRows.mStart < aGridArea.mRows.mEnd);
+ MOZ_ASSERT(aGridArea.mCols.mStart < aGridArea.mCols.mEnd);
+ const auto numRows = aGridArea.mRows.mEnd;
+ const auto numCols = aGridArea.mCols.mEnd;
+ mCells.EnsureLengthAtLeast(numRows);
+ for (auto i = aGridArea.mRows.mStart; i < numRows; ++i) {
+ nsTArray<Cell>& cellsInRow = mCells[i];
+ cellsInRow.EnsureLengthAtLeast(numCols);
+ for (auto j = aGridArea.mCols.mStart; j < numCols; ++j) {
+ cellsInRow[j].mIsOccupied = true;
+ }
+ }
+ }
+
+ uint32_t IsEmptyCol(uint32_t aCol) const
+ {
+ for (auto& row : mCells) {
+ if (aCol < row.Length() && row[aCol].mIsOccupied) {
+ return false;
+ }
+ }
+ return true;
+ }
+ uint32_t IsEmptyRow(uint32_t aRow) const
+ {
+ if (aRow >= mCells.Length()) {
+ return true;
+ }
+ for (const Cell& cell : mCells[aRow]) {
+ if (cell.mIsOccupied) {
+ return false;
+ }
+ }
+ return true;
+ }
+#ifdef DEBUG
+ void Dump() const
+ {
+ const size_t numRows = mCells.Length();
+ for (size_t i = 0; i < numRows; ++i) {
+ const nsTArray<Cell>& cellsInRow = mCells[i];
+ const size_t numCols = cellsInRow.Length();
+ printf("%lu:\t", (unsigned long)i + 1);
+ for (size_t j = 0; j < numCols; ++j) {
+ printf(cellsInRow[j].mIsOccupied ? "X " : ". ");
+ }
+ printf("\n");
+ }
+ }
+#endif
+
+ nsTArray<nsTArray<Cell>> mCells;
+ };
+
+ /**
+ * State for each cell in the grid.
+ */
+ CellMap mCellMap;
+ /**
+ * @see HasImplicitNamedArea.
+ */
+ ImplicitNamedAreas* mAreas;
+ /**
+ * The last column grid line (1-based) in the explicit grid.
+ * (i.e. the number of explicit columns + 1)
+ */
+ uint32_t mExplicitGridColEnd;
+ /**
+ * The last row grid line (1-based) in the explicit grid.
+ * (i.e. the number of explicit rows + 1)
+ */
+ uint32_t mExplicitGridRowEnd;
+ // Same for the implicit grid, except these become zero-based after
+ // resolving definite lines.
+ uint32_t mGridColEnd;
+ uint32_t mGridRowEnd;
+
+ /**
+ * Offsets from the start of the implicit grid to the start of the translated
+ * explicit grid. They are zero if there are no implicit lines before 1,1.
+ * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
+ * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
+ * grid.
+ */
+ uint32_t mExplicitGridOffsetCol;
+ uint32_t mExplicitGridOffsetRow;
+};
+
+void
+nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
+ const Grid& aGrid,
+ LogicalSize& aContentBox,
+ SizingConstraint aConstraint)
+{
+ mCols.Initialize(mColFunctions, mGridStyle->mGridColumnGap,
+ aGrid.mGridColEnd, aContentBox.ISize(mWM));
+ mRows.Initialize(mRowFunctions, mGridStyle->mGridRowGap,
+ aGrid.mGridRowEnd, aContentBox.BSize(mWM));
+
+ mCols.CalculateSizes(*this, mGridItems, mColFunctions,
+ aContentBox.ISize(mWM), &GridArea::mCols,
+ aConstraint);
+ mCols.AlignJustifyContent(mGridStyle, mWM, aContentBox);
+ // Column positions and sizes are now final.
+ mCols.mCanResolveLineRangeSize = true;
+
+ mRows.CalculateSizes(*this, mGridItems, mRowFunctions,
+ aContentBox.BSize(mWM), &GridArea::mRows,
+ aConstraint);
+ if (aContentBox.BSize(mWM) == NS_AUTOHEIGHT) {
+ aContentBox.BSize(mWM) =
+ mRows.BackComputedIntrinsicSize(mRowFunctions, mGridStyle->mGridRowGap);
+ mRows.mGridGap =
+ ::ResolveToDefiniteSize(mGridStyle->mGridRowGap, aContentBox.BSize(mWM));
+ }
+}
+
+/**
+ * (XXX share this utility function with nsFlexContainerFrame at some point)
+ *
+ * Helper for BuildDisplayList, to implement this special-case for grid
+ * items from the spec:
+ * The painting order of grid items is exactly the same as inline blocks,
+ * except that [...] 'z-index' values other than 'auto' create a stacking
+ * context even if 'position' is 'static'.
+ * http://dev.w3.org/csswg/css-grid/#z-order
+ */
+static uint32_t
+GetDisplayFlagsForGridItem(nsIFrame* aFrame)
+{
+ const nsStylePosition* pos = aFrame->StylePosition();
+ if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) {
+ return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT;
+ }
+ return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
+}
+
+// Align an item's margin box in its aAxis inside aCBSize.
+static void
+AlignJustifySelf(uint8_t aAlignment, LogicalAxis aAxis,
+ AlignJustifyFlags aFlags,
+ nscoord aBaselineAdjust, nscoord aCBSize,
+ const ReflowInput& aRI, const LogicalSize& aChildSize,
+ LogicalPoint* aPos)
+{
+ MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_AUTO, "unexpected 'auto' "
+ "computed value for normal flow grid item");
+
+ // NOTE: this is the resulting frame offset (border box).
+ nscoord offset =
+ CSSAlignUtils::AlignJustifySelf(aAlignment, aAxis, aFlags,
+ aBaselineAdjust, aCBSize,
+ aRI, aChildSize);
+
+ // Set the position (aPos) for the requested alignment.
+ if (offset != 0) {
+ WritingMode wm = aRI.GetWritingMode();
+ nscoord& pos = aAxis == eLogicalAxisBlock ? aPos->B(wm) : aPos->I(wm);
+ pos += MOZ_LIKELY(aFlags & AlignJustifyFlags::eSameSide) ? offset : -offset;
+ }
+}
+
+static void
+AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
+ uint8_t aAlignSelf, nscoord aCBSize, const WritingMode aCBWM,
+ const ReflowInput& aRI, const LogicalSize& aSize,
+ LogicalPoint* aPos)
+{
+ auto alignSelf = aAlignSelf;
+
+ AlignJustifyFlags flags = AlignJustifyFlags::eNoFlags;
+ if (alignSelf & NS_STYLE_ALIGN_SAFE) {
+ flags |= AlignJustifyFlags::eOverflowSafe;
+ }
+ alignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
+ WritingMode childWM = aRI.GetWritingMode();
+ if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, childWM)) {
+ flags |= AlignJustifyFlags::eSameSide;
+ }
+
+ // Grid's 'align-self' axis is never parallel to the container's inline axis.
+ if (alignSelf == NS_STYLE_ALIGN_LEFT || alignSelf == NS_STYLE_ALIGN_RIGHT) {
+ alignSelf = NS_STYLE_ALIGN_START;
+ }
+ if (MOZ_LIKELY(alignSelf == NS_STYLE_ALIGN_NORMAL)) {
+ alignSelf = NS_STYLE_ALIGN_STRETCH;
+ }
+
+ nscoord baselineAdjust = 0;
+ if (alignSelf == NS_STYLE_ALIGN_BASELINE ||
+ alignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
+ alignSelf = aGridItem.GetSelfBaseline(alignSelf, eLogicalAxisBlock,
+ &baselineAdjust);
+ }
+
+ bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
+ LogicalAxis axis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock;
+ AlignJustifySelf(alignSelf, axis, flags, baselineAdjust,
+ aCBSize, aRI, aSize, aPos);
+}
+
+static void
+JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
+ uint8_t aJustifySelf, nscoord aCBSize, const WritingMode aCBWM,
+ const ReflowInput& aRI, const LogicalSize& aSize,
+ LogicalPoint* aPos)
+{
+ auto justifySelf = aJustifySelf;
+
+ AlignJustifyFlags flags = AlignJustifyFlags::eNoFlags;
+ if (justifySelf & NS_STYLE_JUSTIFY_SAFE) {
+ flags |= AlignJustifyFlags::eOverflowSafe;
+ }
+ justifySelf &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
+
+ WritingMode childWM = aRI.GetWritingMode();
+ if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, childWM)) {
+ flags |= AlignJustifyFlags::eSameSide;
+ }
+
+ if (MOZ_LIKELY(justifySelf == NS_STYLE_ALIGN_NORMAL)) {
+ justifySelf = NS_STYLE_ALIGN_STRETCH;
+ }
+
+ nscoord baselineAdjust = 0;
+ // Grid's 'justify-self' axis is always parallel to the container's inline
+ // axis, so justify-self:left|right always applies.
+ switch (justifySelf) {
+ case NS_STYLE_JUSTIFY_LEFT:
+ justifySelf = aCBWM.IsBidiLTR() ? NS_STYLE_JUSTIFY_START
+ : NS_STYLE_JUSTIFY_END;
+ break;
+ case NS_STYLE_JUSTIFY_RIGHT:
+ justifySelf = aCBWM.IsBidiLTR() ? NS_STYLE_JUSTIFY_END
+ : NS_STYLE_JUSTIFY_START;
+ break;
+ case NS_STYLE_JUSTIFY_BASELINE:
+ case NS_STYLE_JUSTIFY_LAST_BASELINE:
+ justifySelf = aGridItem.GetSelfBaseline(justifySelf, eLogicalAxisInline,
+ &baselineAdjust);
+ break;
+ }
+
+ bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
+ LogicalAxis axis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
+ AlignJustifySelf(justifySelf, axis, flags, baselineAdjust,
+ aCBSize, aRI, aSize, aPos);
+}
+
+static uint16_t
+GetAlignJustifyValue(uint16_t aAlignment, const WritingMode aWM,
+ const bool aIsAlign, bool* aOverflowSafe)
+{
+ *aOverflowSafe = aAlignment & NS_STYLE_ALIGN_SAFE;
+ aAlignment &= (NS_STYLE_ALIGN_ALL_BITS & ~NS_STYLE_ALIGN_FLAG_BITS);
+
+ // Map some alignment values to 'start' / 'end'.
+ switch (aAlignment) {
+ case NS_STYLE_ALIGN_LEFT:
+ case NS_STYLE_ALIGN_RIGHT: {
+ if (aIsAlign) {
+ // Grid's 'align-content' axis is never parallel to the inline axis.
+ return NS_STYLE_ALIGN_START;
+ }
+ bool isStart = aWM.IsBidiLTR() == (aAlignment == NS_STYLE_ALIGN_LEFT);
+ return isStart ? NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_END;
+ }
+ case NS_STYLE_ALIGN_FLEX_START: // same as 'start' for Grid
+ return NS_STYLE_ALIGN_START;
+ case NS_STYLE_ALIGN_FLEX_END: // same as 'end' for Grid
+ return NS_STYLE_ALIGN_END;
+ }
+ return aAlignment;
+}
+
+static uint16_t
+GetAlignJustifyFallbackIfAny(uint16_t aAlignment, const WritingMode aWM,
+ const bool aIsAlign, bool* aOverflowSafe)
+{
+ uint16_t fallback = aAlignment >> NS_STYLE_ALIGN_ALL_SHIFT;
+ if (fallback) {
+ return GetAlignJustifyValue(fallback, aWM, aIsAlign, aOverflowSafe);
+ }
+ // https://drafts.csswg.org/css-align-3/#fallback-alignment
+ switch (aAlignment) {
+ case NS_STYLE_ALIGN_STRETCH:
+ case NS_STYLE_ALIGN_SPACE_BETWEEN:
+ return NS_STYLE_ALIGN_START;
+ case NS_STYLE_ALIGN_SPACE_AROUND:
+ case NS_STYLE_ALIGN_SPACE_EVENLY:
+ return NS_STYLE_ALIGN_CENTER;
+ }
+ return 0;
+}
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsGridContainerFrame)
+ NS_QUERYFRAME_ENTRY(nsGridContainerFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsGridContainerFrame)
+
+nsContainerFrame*
+NS_NewGridContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsGridContainerFrame(aContext);
+}
+
+
+//----------------------------------------------------------------------
+
+// nsGridContainerFrame Method Implementations
+// ===========================================
+
+/*static*/ const nsRect&
+nsGridContainerFrame::GridItemCB(nsIFrame* aChild)
+{
+ MOZ_ASSERT((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
+ aChild->IsAbsolutelyPositioned());
+ nsRect* cb = aChild->Properties().Get(GridItemContainingBlockRect());
+ MOZ_ASSERT(cb, "this method must only be called on grid items, and the grid "
+ "container should've reflowed this item by now and set up cb");
+ return *cb;
+}
+
+void
+nsGridContainerFrame::AddImplicitNamedAreas(
+ const nsTArray<nsTArray<nsString>>& aLineNameLists)
+{
+ // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
+ // Note: recording these names for fast lookup later is just an optimization.
+ const uint32_t len =
+ std::min(aLineNameLists.Length(), size_t(nsStyleGridLine::kMaxLine));
+ nsTHashtable<nsStringHashKey> currentStarts;
+ ImplicitNamedAreas* areas = GetImplicitNamedAreas();
+ for (uint32_t i = 0; i < len; ++i) {
+ for (const nsString& name : aLineNameLists[i]) {
+ uint32_t indexOfSuffix;
+ if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
+ Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
+ // Extract the name that was found earlier.
+ nsDependentSubstring areaName(name, 0, indexOfSuffix);
+
+ // Lazily create the ImplicitNamedAreas.
+ if (!areas) {
+ areas = new ImplicitNamedAreas;
+ Properties().Set(ImplicitNamedAreasProperty(), areas);
+ }
+
+ mozilla::css::GridNamedArea area;
+ if (!areas->Get(areaName, &area)) {
+ // Not found, so prep the newly-seen area with a name and empty
+ // boundary information, which will get filled in later.
+ area.mName = areaName;
+ area.mRowStart = 0;
+ area.mRowEnd = 0;
+ area.mColumnStart = 0;
+ area.mColumnEnd = 0;
+
+ areas->Put(areaName, area);
+ }
+ }
+ }
+ }
+}
+
+void
+nsGridContainerFrame::InitImplicitNamedAreas(const nsStylePosition* aStyle)
+{
+ ImplicitNamedAreas* areas = GetImplicitNamedAreas();
+ if (areas) {
+ // Clear it, but reuse the hashtable itself for now. We'll remove it
+ // below if it isn't needed anymore.
+ areas->Clear();
+ }
+ AddImplicitNamedAreas(aStyle->mGridTemplateColumns.mLineNameLists);
+ AddImplicitNamedAreas(aStyle->mGridTemplateRows.mLineNameLists);
+ if (areas && areas->Count() == 0) {
+ Properties().Delete(ImplicitNamedAreasProperty());
+ }
+}
+
+int32_t
+nsGridContainerFrame::Grid::ResolveLine(const nsStyleGridLine& aLine,
+ int32_t aNth,
+ uint32_t aFromIndex,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ LineRangeSide aSide,
+ const nsStylePosition* aStyle)
+{
+ MOZ_ASSERT(!aLine.IsAuto());
+ int32_t line = 0;
+ if (aLine.mLineName.IsEmpty()) {
+ MOZ_ASSERT(aNth != 0, "css-grid 9.2: <integer> must not be zero.");
+ line = int32_t(aFromIndex) + aNth;
+ } else {
+ if (aNth == 0) {
+ // <integer> was omitted; treat it as 1.
+ aNth = 1;
+ }
+ bool isNameOnly = !aLine.mHasSpan && aLine.mInteger == 0;
+ if (isNameOnly) {
+ const GridNamedArea* area = FindNamedArea(aLine.mLineName, aStyle);
+ if (area || HasImplicitNamedArea(aLine.mLineName)) {
+ // The given name is a named area - look for explicit lines named
+ // <name>-start/-end depending on which side we're resolving.
+ // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
+ uint32_t implicitLine = 0;
+ nsAutoString lineName(aLine.mLineName);
+ if (aSide == eLineRangeSideStart) {
+ lineName.AppendLiteral("-start");
+ implicitLine = area ? area->*aAreaStart : 0;
+ } else {
+ lineName.AppendLiteral("-end");
+ implicitLine = area ? area->*aAreaEnd : 0;
+ }
+ line = aNameMap.FindNamedLine(lineName, &aNth, aFromIndex,
+ implicitLine);
+ }
+ }
+
+ if (line == 0) {
+ // If mLineName ends in -start/-end, try the prefix as a named area.
+ uint32_t implicitLine = 0;
+ uint32_t index;
+ auto GridNamedArea::* areaEdge = aAreaStart;
+ bool found = IsNameWithStartSuffix(aLine.mLineName, &index);
+ if (!found) {
+ found = IsNameWithEndSuffix(aLine.mLineName, &index);
+ areaEdge = aAreaEnd;
+ }
+ if (found) {
+ const GridNamedArea* area =
+ FindNamedArea(nsDependentSubstring(aLine.mLineName, 0, index),
+ aStyle);
+ if (area) {
+ implicitLine = area->*areaEdge;
+ }
+ }
+ line = aNameMap.FindNamedLine(aLine.mLineName, &aNth, aFromIndex,
+ implicitLine);
+ }
+
+ if (line == 0) {
+ MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
+ int32_t edgeLine;
+ if (aLine.mHasSpan) {
+ // http://dev.w3.org/csswg/css-grid/#grid-placement-span-int
+ // 'span <custom-ident> N'
+ edgeLine = aSide == eLineRangeSideStart ? 1 : aExplicitGridEnd;
+ } else {
+ // http://dev.w3.org/csswg/css-grid/#grid-placement-int
+ // '<custom-ident> N'
+ edgeLine = aNth < 0 ? 1 : aExplicitGridEnd;
+ }
+ // "If not enough lines with that name exist, all lines in the implicit
+ // grid are assumed to have that name..."
+ line = edgeLine + aNth;
+ }
+ }
+ return clamped(line, nsStyleGridLine::kMinLine, nsStyleGridLine::kMaxLine);
+}
+
+nsGridContainerFrame::Grid::LinePair
+nsGridContainerFrame::Grid::ResolveLineRangeHelper(
+ const nsStyleGridLine& aStart,
+ const nsStyleGridLine& aEnd,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ const nsStylePosition* aStyle)
+{
+ MOZ_ASSERT(int32_t(nsGridContainerFrame::kAutoLine) > nsStyleGridLine::kMaxLine);
+
+ if (aStart.mHasSpan) {
+ if (aEnd.mHasSpan || aEnd.IsAuto()) {
+ // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
+ if (aStart.mLineName.IsEmpty()) {
+ // span <integer> / span *
+ // span <integer> / auto
+ return LinePair(kAutoLine, aStart.mInteger);
+ }
+ // span <custom-ident> / span *
+ // span <custom-ident> / auto
+ return LinePair(kAutoLine, 1); // XXX subgrid explicit size instead of 1?
+ }
+
+ uint32_t from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1: 0;
+ auto end = ResolveLine(aEnd, aEnd.mInteger, from, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, eLineRangeSideEnd,
+ aStyle);
+ int32_t span = aStart.mInteger == 0 ? 1 : aStart.mInteger;
+ if (end <= 1) {
+ // The end is at or before the first explicit line, thus all lines before
+ // it match <custom-ident> since they're implicit.
+ int32_t start = std::max(end - span, nsStyleGridLine::kMinLine);
+ return LinePair(start, end);
+ }
+ auto start = ResolveLine(aStart, -span, end, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, eLineRangeSideStart,
+ aStyle);
+ return LinePair(start, end);
+ }
+
+ int32_t start = kAutoLine;
+ if (aStart.IsAuto()) {
+ if (aEnd.IsAuto()) {
+ // auto / auto
+ return LinePair(start, 1); // XXX subgrid explicit size instead of 1?
+ }
+ if (aEnd.mHasSpan) {
+ if (aEnd.mLineName.IsEmpty()) {
+ // auto / span <integer>
+ MOZ_ASSERT(aEnd.mInteger != 0);
+ return LinePair(start, aEnd.mInteger);
+ }
+ // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
+ // auto / span <custom-ident>
+ return LinePair(start, 1); // XXX subgrid explicit size instead of 1?
+ }
+ } else {
+ uint32_t from = aStart.mInteger < 0 ? aExplicitGridEnd + 1: 0;
+ start = ResolveLine(aStart, aStart.mInteger, from, aNameMap,
+ aAreaStart, aAreaEnd, aExplicitGridEnd,
+ eLineRangeSideStart, aStyle);
+ if (aEnd.IsAuto()) {
+ // A "definite line / auto" should resolve the auto to 'span 1'.
+ // The error handling in ResolveLineRange will make that happen and also
+ // clamp the end line correctly if we return "start / start".
+ return LinePair(start, start);
+ }
+ }
+
+ uint32_t from;
+ int32_t nth = aEnd.mInteger == 0 ? 1 : aEnd.mInteger;
+ if (aEnd.mHasSpan) {
+ if (MOZ_UNLIKELY(start < 0)) {
+ if (aEnd.mLineName.IsEmpty()) {
+ return LinePair(start, start + nth);
+ }
+ from = 0;
+ } else {
+ if (start >= int32_t(aExplicitGridEnd)) {
+ // The start is at or after the last explicit line, thus all lines
+ // after it match <custom-ident> since they're implicit.
+ return LinePair(start, std::min(start + nth, nsStyleGridLine::kMaxLine));
+ }
+ from = start;
+ }
+ } else {
+ from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1: 0;
+ }
+ auto end = ResolveLine(aEnd, nth, from, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, eLineRangeSideEnd, aStyle);
+ if (start == int32_t(kAutoLine)) {
+ // auto / definite line
+ start = std::max(nsStyleGridLine::kMinLine, end - 1);
+ }
+ return LinePair(start, end);
+}
+
+nsGridContainerFrame::LineRange
+nsGridContainerFrame::Grid::ResolveLineRange(
+ const nsStyleGridLine& aStart,
+ const nsStyleGridLine& aEnd,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ const nsStylePosition* aStyle)
+{
+ LinePair r = ResolveLineRangeHelper(aStart, aEnd, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, aStyle);
+ MOZ_ASSERT(r.second != int32_t(kAutoLine));
+
+ if (r.first == int32_t(kAutoLine)) {
+ // r.second is a span, clamp it to kMaxLine - 1 so that the returned
+ // range has a HypotheticalEnd <= kMaxLine.
+ // http://dev.w3.org/csswg/css-grid/#overlarge-grids
+ r.second = std::min(r.second, nsStyleGridLine::kMaxLine - 1);
+ } else {
+ // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
+ if (r.first > r.second) {
+ Swap(r.first, r.second);
+ } else if (r.first == r.second) {
+ if (MOZ_UNLIKELY(r.first == nsStyleGridLine::kMaxLine)) {
+ r.first = nsStyleGridLine::kMaxLine - 1;
+ }
+ r.second = r.first + 1; // XXX subgrid explicit size instead of 1?
+ }
+ }
+ return LineRange(r.first, r.second);
+}
+
+nsGridContainerFrame::GridArea
+nsGridContainerFrame::Grid::PlaceDefinite(nsIFrame* aChild,
+ const LineNameMap& aColLineNameMap,
+ const LineNameMap& aRowLineNameMap,
+ const nsStylePosition* aStyle)
+{
+ const nsStylePosition* itemStyle = aChild->StylePosition();
+ return GridArea(
+ ResolveLineRange(itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
+ aColLineNameMap,
+ &GridNamedArea::mColumnStart, &GridNamedArea::mColumnEnd,
+ mExplicitGridColEnd, aStyle),
+ ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
+ aRowLineNameMap,
+ &GridNamedArea::mRowStart, &GridNamedArea::mRowEnd,
+ mExplicitGridRowEnd, aStyle));
+}
+
+nsGridContainerFrame::LineRange
+nsGridContainerFrame::Grid::ResolveAbsPosLineRange(
+ const nsStyleGridLine& aStart,
+ const nsStyleGridLine& aEnd,
+ const LineNameMap& aNameMap,
+ uint32_t GridNamedArea::* aAreaStart,
+ uint32_t GridNamedArea::* aAreaEnd,
+ uint32_t aExplicitGridEnd,
+ int32_t aGridStart,
+ int32_t aGridEnd,
+ const nsStylePosition* aStyle)
+{
+ if (aStart.IsAuto()) {
+ if (aEnd.IsAuto()) {
+ return LineRange(kAutoLine, kAutoLine);
+ }
+ uint32_t from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1: 0;
+ int32_t end =
+ ResolveLine(aEnd, aEnd.mInteger, from, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, eLineRangeSideEnd, aStyle);
+ if (aEnd.mHasSpan) {
+ ++end;
+ }
+ // A line outside the existing grid is treated as 'auto' for abs.pos (10.1).
+ end = AutoIfOutside(end, aGridStart, aGridEnd);
+ return LineRange(kAutoLine, end);
+ }
+
+ if (aEnd.IsAuto()) {
+ uint32_t from = aStart.mInteger < 0 ? aExplicitGridEnd + 1: 0;
+ int32_t start =
+ ResolveLine(aStart, aStart.mInteger, from, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, eLineRangeSideStart, aStyle);
+ if (aStart.mHasSpan) {
+ start = std::max(aGridEnd - start, aGridStart);
+ }
+ start = AutoIfOutside(start, aGridStart, aGridEnd);
+ return LineRange(start, kAutoLine);
+ }
+
+ LineRange r = ResolveLineRange(aStart, aEnd, aNameMap, aAreaStart,
+ aAreaEnd, aExplicitGridEnd, aStyle);
+ if (r.IsAuto()) {
+ MOZ_ASSERT(aStart.mHasSpan && aEnd.mHasSpan, "span / span is the only case "
+ "leading to IsAuto here -- we dealt with the other cases above");
+ // The second span was ignored per 9.2.1. For abs.pos., 10.1 says that this
+ // case should result in "auto / auto" unlike normal flow grid items.
+ return LineRange(kAutoLine, kAutoLine);
+ }
+
+ return LineRange(AutoIfOutside(r.mUntranslatedStart, aGridStart, aGridEnd),
+ AutoIfOutside(r.mUntranslatedEnd, aGridStart, aGridEnd));
+}
+
+nsGridContainerFrame::GridArea
+nsGridContainerFrame::Grid::PlaceAbsPos(nsIFrame* aChild,
+ const LineNameMap& aColLineNameMap,
+ const LineNameMap& aRowLineNameMap,
+ const nsStylePosition* aStyle)
+{
+ const nsStylePosition* itemStyle = aChild->StylePosition();
+ int32_t gridColStart = 1 - mExplicitGridOffsetCol;
+ int32_t gridRowStart = 1 - mExplicitGridOffsetRow;
+ return GridArea(
+ ResolveAbsPosLineRange(itemStyle->mGridColumnStart,
+ itemStyle->mGridColumnEnd,
+ aColLineNameMap,
+ &GridNamedArea::mColumnStart,
+ &GridNamedArea::mColumnEnd,
+ mExplicitGridColEnd, gridColStart, mGridColEnd,
+ aStyle),
+ ResolveAbsPosLineRange(itemStyle->mGridRowStart,
+ itemStyle->mGridRowEnd,
+ aRowLineNameMap,
+ &GridNamedArea::mRowStart,
+ &GridNamedArea::mRowEnd,
+ mExplicitGridRowEnd, gridRowStart, mGridRowEnd,
+ aStyle));
+}
+
+uint32_t
+nsGridContainerFrame::Grid::FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
+ const GridArea* aArea) const
+{
+ const uint32_t extent = aArea->mCols.Extent();
+ const uint32_t iStart = aLockedRow;
+ const uint32_t iEnd = iStart + aArea->mRows.Extent();
+ uint32_t candidate = aStartCol;
+ for (uint32_t i = iStart; i < iEnd; ) {
+ if (i >= mCellMap.mCells.Length()) {
+ break;
+ }
+ const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
+ const uint32_t len = cellsInRow.Length();
+ const uint32_t lastCandidate = candidate;
+ // Find the first gap in the current row that's at least 'extent' wide.
+ // ('gap' tracks how wide the current column gap is.)
+ for (uint32_t j = candidate, gap = 0; j < len && gap < extent; ++j) {
+ if (!cellsInRow[j].mIsOccupied) {
+ ++gap;
+ continue;
+ }
+ candidate = j + 1;
+ gap = 0;
+ }
+ if (lastCandidate < candidate && i != iStart) {
+ // Couldn't fit 'extent' tracks at 'lastCandidate' here so we must
+ // restart from the beginning with the new 'candidate'.
+ i = iStart;
+ } else {
+ ++i;
+ }
+ }
+ return candidate;
+}
+
+void
+nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol,
+ GridArea* aArea) const
+{
+ MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto());
+ uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea);
+ aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol);
+ MOZ_ASSERT(aArea->IsDefinite());
+}
+
+uint32_t
+nsGridContainerFrame::Grid::FindAutoRow(uint32_t aLockedCol, uint32_t aStartRow,
+ const GridArea* aArea) const
+{
+ const uint32_t extent = aArea->mRows.Extent();
+ const uint32_t jStart = aLockedCol;
+ const uint32_t jEnd = jStart + aArea->mCols.Extent();
+ const uint32_t iEnd = mCellMap.mCells.Length();
+ uint32_t candidate = aStartRow;
+ // Find the first gap in the rows that's at least 'extent' tall.
+ // ('gap' tracks how tall the current row gap is.)
+ for (uint32_t i = candidate, gap = 0; i < iEnd && gap < extent; ++i) {
+ ++gap; // tentative, but we may reset it below if a column is occupied
+ const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
+ const uint32_t clampedJEnd = std::min<uint32_t>(jEnd, cellsInRow.Length());
+ // Check if the current row is unoccupied from jStart to jEnd.
+ for (uint32_t j = jStart; j < clampedJEnd; ++j) {
+ if (cellsInRow[j].mIsOccupied) {
+ // Couldn't fit 'extent' rows at 'candidate' here; we hit something
+ // at row 'i'. So, try the row after 'i' as our next candidate.
+ candidate = i + 1;
+ gap = 0;
+ break;
+ }
+ }
+ }
+ return candidate;
+}
+
+void
+nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow,
+ GridArea* aArea) const
+{
+ MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto());
+ uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea);
+ aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow);
+ MOZ_ASSERT(aArea->IsDefinite());
+}
+
+void
+nsGridContainerFrame::Grid::PlaceAutoAutoInRowOrder(uint32_t aStartCol,
+ uint32_t aStartRow,
+ GridArea* aArea) const
+{
+ MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
+ const uint32_t colExtent = aArea->mCols.Extent();
+ const uint32_t gridRowEnd = mGridRowEnd;
+ const uint32_t gridColEnd = mGridColEnd;
+ uint32_t col = aStartCol;
+ uint32_t row = aStartRow;
+ for (; row < gridRowEnd; ++row) {
+ col = FindAutoCol(col, row, aArea);
+ if (col + colExtent <= gridColEnd) {
+ break;
+ }
+ col = 0;
+ }
+ MOZ_ASSERT(row < gridRowEnd || col == 0,
+ "expected column 0 for placing in a new row");
+ aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol);
+ aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow);
+ MOZ_ASSERT(aArea->IsDefinite());
+}
+
+void
+nsGridContainerFrame::Grid::PlaceAutoAutoInColOrder(uint32_t aStartCol,
+ uint32_t aStartRow,
+ GridArea* aArea) const
+{
+ MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
+ const uint32_t rowExtent = aArea->mRows.Extent();
+ const uint32_t gridRowEnd = mGridRowEnd;
+ const uint32_t gridColEnd = mGridColEnd;
+ uint32_t col = aStartCol;
+ uint32_t row = aStartRow;
+ for (; col < gridColEnd; ++col) {
+ row = FindAutoRow(col, row, aArea);
+ if (row + rowExtent <= gridRowEnd) {
+ break;
+ }
+ row = 0;
+ }
+ MOZ_ASSERT(col < gridColEnd || row == 0,
+ "expected row 0 for placing in a new column");
+ aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol);
+ aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow);
+ MOZ_ASSERT(aArea->IsDefinite());
+}
+
+void
+nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState,
+ const LogicalSize& aComputedMinSize,
+ const LogicalSize& aComputedSize,
+ const LogicalSize& aComputedMaxSize)
+{
+ mAreas = aState.mFrame->GetImplicitNamedAreas();
+ const nsStylePosition* const gridStyle = aState.mGridStyle;
+ MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
+
+ // http://dev.w3.org/csswg/css-grid/#grid-definition
+ // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
+ // This is determined by the larger of the number of rows/columns defined
+ // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
+ // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
+ // Note that this is for a grid with a 1,1 origin. We'll change that
+ // to a 0,0 based grid after placing definite lines.
+ auto areas = gridStyle->mGridTemplateAreas.get();
+ uint32_t numRepeatCols = aState.mColFunctions.InitRepeatTracks(
+ gridStyle->mGridColumnGap,
+ aComputedMinSize.ISize(aState.mWM),
+ aComputedSize.ISize(aState.mWM),
+ aComputedMaxSize.ISize(aState.mWM));
+ mGridColEnd = mExplicitGridColEnd =
+ aState.mColFunctions.ComputeExplicitGridEnd(areas ? areas->mNColumns + 1 : 1);
+ LineNameMap colLineNameMap(gridStyle->mGridTemplateColumns, numRepeatCols);
+
+ uint32_t numRepeatRows = aState.mRowFunctions.InitRepeatTracks(
+ gridStyle->mGridRowGap,
+ aComputedMinSize.BSize(aState.mWM),
+ aComputedSize.BSize(aState.mWM),
+ aComputedMaxSize.BSize(aState.mWM));
+ mGridRowEnd = mExplicitGridRowEnd =
+ aState.mRowFunctions.ComputeExplicitGridEnd(areas ? areas->NRows() + 1 : 1);
+ LineNameMap rowLineNameMap(gridStyle->mGridTemplateRows, numRepeatRows);
+
+ // http://dev.w3.org/csswg/css-grid/#line-placement
+ // Resolve definite positions per spec chap 9.2.
+ int32_t minCol = 1;
+ int32_t minRow = 1;
+ aState.mGridItems.ClearAndRetainStorage();
+ aState.mIter.Reset();
+ for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
+ nsIFrame* child = *aState.mIter;
+ GridItemInfo* info =
+ aState.mGridItems.AppendElement(GridItemInfo(child,
+ PlaceDefinite(child,
+ colLineNameMap,
+ rowLineNameMap,
+ gridStyle)));
+ MOZ_ASSERT(aState.mIter.GridItemIndex() == aState.mGridItems.Length() - 1,
+ "GridItemIndex() is broken");
+ GridArea& area = info->mArea;
+ if (area.mCols.IsDefinite()) {
+ minCol = std::min(minCol, area.mCols.mUntranslatedStart);
+ }
+ if (area.mRows.IsDefinite()) {
+ minRow = std::min(minRow, area.mRows.mUntranslatedStart);
+ }
+ }
+
+ // Translate the whole grid so that the top-/left-most area is at 0,0.
+ mExplicitGridOffsetCol = 1 - minCol; // minCol/Row is always <= 1, see above
+ mExplicitGridOffsetRow = 1 - minRow;
+ aState.mColFunctions.mExplicitGridOffset = mExplicitGridOffsetCol;
+ aState.mRowFunctions.mExplicitGridOffset = mExplicitGridOffsetRow;
+ const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
+ const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
+ mGridColEnd += offsetToColZero;
+ mGridRowEnd += offsetToRowZero;
+ aState.mIter.Reset();
+ for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
+ GridArea& area = aState.mGridItems[aState.mIter.GridItemIndex()].mArea;
+ if (area.mCols.IsDefinite()) {
+ area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
+ area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
+ }
+ if (area.mRows.IsDefinite()) {
+ area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
+ area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
+ }
+ if (area.IsDefinite()) {
+ mCellMap.Fill(area);
+ InflateGridFor(area);
+ }
+ }
+
+ // http://dev.w3.org/csswg/css-grid/#auto-placement-algo
+ // Step 1, place 'auto' items that have one definite position -
+ // definite row (column) for grid-auto-flow:row (column).
+ auto flowStyle = gridStyle->mGridAutoFlow;
+ const bool isRowOrder = (flowStyle & NS_STYLE_GRID_AUTO_FLOW_ROW);
+ const bool isSparse = !(flowStyle & NS_STYLE_GRID_AUTO_FLOW_DENSE);
+ // We need 1 cursor per row (or column) if placement is sparse.
+ {
+ Maybe<nsDataHashtable<nsUint32HashKey, uint32_t>> cursors;
+ if (isSparse) {
+ cursors.emplace();
+ }
+ auto placeAutoMinorFunc = isRowOrder ? &Grid::PlaceAutoCol
+ : &Grid::PlaceAutoRow;
+ aState.mIter.Reset();
+ for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
+ GridArea& area = aState.mGridItems[aState.mIter.GridItemIndex()].mArea;
+ LineRange& major = isRowOrder ? area.mRows : area.mCols;
+ LineRange& minor = isRowOrder ? area.mCols : area.mRows;
+ if (major.IsDefinite() && minor.IsAuto()) {
+ // Items with 'auto' in the minor dimension only.
+ uint32_t cursor = 0;
+ if (isSparse) {
+ cursors->Get(major.mStart, &cursor);
+ }
+ (this->*placeAutoMinorFunc)(cursor, &area);
+ mCellMap.Fill(area);
+ if (isSparse) {
+ cursors->Put(major.mStart, minor.mEnd);
+ }
+ }
+ InflateGridFor(area); // Step 2, inflating for auto items too
+ }
+ }
+
+ // XXX NOTE possible spec issue.
+ // XXX It's unclear if the remaining major-dimension auto and
+ // XXX auto in both dimensions should use the same cursor or not,
+ // XXX https://www.w3.org/Bugs/Public/show_bug.cgi?id=16044
+ // XXX seems to indicate it shouldn't.
+ // XXX http://dev.w3.org/csswg/css-grid/#auto-placement-cursor
+ // XXX now says it should (but didn't in earlier versions)
+
+ // Step 3, place the remaining grid items
+ uint32_t cursorMajor = 0; // for 'dense' these two cursors will stay at 0,0
+ uint32_t cursorMinor = 0;
+ auto placeAutoMajorFunc = isRowOrder ? &Grid::PlaceAutoRow
+ : &Grid::PlaceAutoCol;
+ aState.mIter.Reset();
+ for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
+ GridArea& area = aState.mGridItems[aState.mIter.GridItemIndex()].mArea;
+ MOZ_ASSERT(*aState.mIter == aState.mGridItems[aState.mIter.GridItemIndex()].mFrame,
+ "iterator out of sync with aState.mGridItems");
+ LineRange& major = isRowOrder ? area.mRows : area.mCols;
+ LineRange& minor = isRowOrder ? area.mCols : area.mRows;
+ if (major.IsAuto()) {
+ if (minor.IsDefinite()) {
+ // Items with 'auto' in the major dimension only.
+ if (isSparse) {
+ if (minor.mStart < cursorMinor) {
+ ++cursorMajor;
+ }
+ cursorMinor = minor.mStart;
+ }
+ (this->*placeAutoMajorFunc)(cursorMajor, &area);
+ if (isSparse) {
+ cursorMajor = major.mStart;
+ }
+ } else {
+ // Items with 'auto' in both dimensions.
+ if (isRowOrder) {
+ PlaceAutoAutoInRowOrder(cursorMinor, cursorMajor, &area);
+ } else {
+ PlaceAutoAutoInColOrder(cursorMajor, cursorMinor, &area);
+ }
+ if (isSparse) {
+ cursorMajor = major.mStart;
+ cursorMinor = minor.mEnd;
+#ifdef DEBUG
+ uint32_t gridMajorEnd = isRowOrder ? mGridRowEnd : mGridColEnd;
+ uint32_t gridMinorEnd = isRowOrder ? mGridColEnd : mGridRowEnd;
+ MOZ_ASSERT(cursorMajor <= gridMajorEnd,
+ "we shouldn't need to place items further than 1 track "
+ "past the current end of the grid, in major dimension");
+ MOZ_ASSERT(cursorMinor <= gridMinorEnd,
+ "we shouldn't add implicit minor tracks for auto/auto");
+#endif
+ }
+ }
+ mCellMap.Fill(area);
+ InflateGridFor(area);
+ }
+ }
+
+ if (aState.mFrame->IsAbsoluteContainer()) {
+ // 9.4 Absolutely-positioned Grid Items
+ // http://dev.w3.org/csswg/css-grid/#abspos-items
+ // We only resolve definite lines here; we'll align auto positions to the
+ // grid container later during reflow.
+ nsFrameList children(aState.mFrame->GetChildList(
+ aState.mFrame->GetAbsoluteListID()));
+ const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
+ const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
+ // Untranslate the grid again temporarily while resolving abs.pos. lines.
+ AutoRestore<uint32_t> save1(mGridColEnd);
+ AutoRestore<uint32_t> save2(mGridRowEnd);
+ mGridColEnd -= offsetToColZero;
+ mGridRowEnd -= offsetToRowZero;
+ aState.mAbsPosItems.ClearAndRetainStorage();
+ size_t i = 0;
+ for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
+ nsIFrame* child = e.get();
+ GridItemInfo* info =
+ aState.mAbsPosItems.AppendElement(GridItemInfo(child,
+ PlaceAbsPos(child,
+ colLineNameMap,
+ rowLineNameMap,
+ gridStyle)));
+ GridArea& area = info->mArea;
+ if (area.mCols.mUntranslatedStart != int32_t(kAutoLine)) {
+ area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
+ }
+ if (area.mCols.mUntranslatedEnd != int32_t(kAutoLine)) {
+ area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
+ }
+ if (area.mRows.mUntranslatedStart != int32_t(kAutoLine)) {
+ area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
+ }
+ if (area.mRows.mUntranslatedEnd != int32_t(kAutoLine)) {
+ area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
+ }
+ }
+ }
+
+ // Count empty 'auto-fit' tracks in the repeat() range.
+ // |colAdjust| will have a count for each line in the grid of how many
+ // tracks were empty between the start of the grid and that line.
+ Maybe<nsTArray<uint32_t>> colAdjust;
+ uint32_t numEmptyCols = 0;
+ if (aState.mColFunctions.mHasRepeatAuto &&
+ !gridStyle->mGridTemplateColumns.mIsAutoFill &&
+ aState.mColFunctions.NumRepeatTracks() > 0) {
+ for (uint32_t col = aState.mColFunctions.mRepeatAutoStart,
+ endRepeat = aState.mColFunctions.mRepeatAutoEnd,
+ numColLines = mGridColEnd + 1;
+ col < numColLines; ++col) {
+ if (numEmptyCols) {
+ (*colAdjust)[col] = numEmptyCols;
+ }
+ if (col < endRepeat && mCellMap.IsEmptyCol(col)) {
+ ++numEmptyCols;
+ if (colAdjust.isNothing()) {
+ colAdjust.emplace(numColLines);
+ colAdjust->SetLength(numColLines);
+ PodZero(colAdjust->Elements(), colAdjust->Length());
+ }
+
+ uint32_t repeatIndex = col - aState.mColFunctions.mRepeatAutoStart;
+ MOZ_ASSERT(aState.mColFunctions.mRemovedRepeatTracks.Length() >
+ repeatIndex);
+ aState.mColFunctions.mRemovedRepeatTracks[repeatIndex] = true;
+ }
+ }
+ }
+ Maybe<nsTArray<uint32_t>> rowAdjust;
+ uint32_t numEmptyRows = 0;
+ if (aState.mRowFunctions.mHasRepeatAuto &&
+ !gridStyle->mGridTemplateRows.mIsAutoFill &&
+ aState.mRowFunctions.NumRepeatTracks() > 0) {
+ for (uint32_t row = aState.mRowFunctions.mRepeatAutoStart,
+ endRepeat = aState.mRowFunctions.mRepeatAutoEnd,
+ numRowLines = mGridRowEnd + 1;
+ row < numRowLines; ++row) {
+ if (numEmptyRows) {
+ (*rowAdjust)[row] = numEmptyRows;
+ }
+ if (row < endRepeat && mCellMap.IsEmptyRow(row)) {
+ ++numEmptyRows;
+ if (rowAdjust.isNothing()) {
+ rowAdjust.emplace(numRowLines);
+ rowAdjust->SetLength(numRowLines);
+ PodZero(rowAdjust->Elements(), rowAdjust->Length());
+ }
+
+ uint32_t repeatIndex = row - aState.mRowFunctions.mRepeatAutoStart;
+ MOZ_ASSERT(aState.mRowFunctions.mRemovedRepeatTracks.Length() >
+ repeatIndex);
+ aState.mRowFunctions.mRemovedRepeatTracks[repeatIndex] = true;
+ }
+ }
+ }
+ // Remove the empty 'auto-fit' tracks we found above, if any.
+ if (numEmptyCols || numEmptyRows) {
+ // Adjust the line numbers in the grid areas.
+ for (auto& item : aState.mGridItems) {
+ GridArea& area = item.mArea;
+ if (numEmptyCols) {
+ area.mCols.AdjustForRemovedTracks(*colAdjust);
+ }
+ if (numEmptyRows) {
+ area.mRows.AdjustForRemovedTracks(*rowAdjust);
+ }
+ }
+ for (auto& item : aState.mAbsPosItems) {
+ GridArea& area = item.mArea;
+ if (numEmptyCols) {
+ area.mCols.AdjustAbsPosForRemovedTracks(*colAdjust);
+ }
+ if (numEmptyRows) {
+ area.mRows.AdjustAbsPosForRemovedTracks(*rowAdjust);
+ }
+ }
+ // Adjust the grid size.
+ mGridColEnd -= numEmptyCols;
+ mExplicitGridColEnd -= numEmptyCols;
+ mGridRowEnd -= numEmptyRows;
+ mExplicitGridRowEnd -= numEmptyRows;
+ // Adjust the track mapping to unmap the removed tracks.
+ auto colRepeatCount = aState.mColFunctions.NumRepeatTracks();
+ aState.mColFunctions.SetNumRepeatTracks(colRepeatCount - numEmptyCols);
+ auto rowRepeatCount = aState.mRowFunctions.NumRepeatTracks();
+ aState.mRowFunctions.SetNumRepeatTracks(rowRepeatCount - numEmptyRows);
+ }
+
+ // Update the line boundaries of the implicit grid areas, if needed.
+ if (mAreas &&
+ aState.mFrame->HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
+ for (auto iter = mAreas->Iter(); !iter.Done(); iter.Next()) {
+ auto& areaInfo = iter.Data();
+
+ // Resolve the lines for the area. We use the name of the area as the
+ // name of the lines, knowing that the line placement algorithm will
+ // add the -start and -end suffixes as appropriate for layout.
+ nsStyleGridLine lineStartAndEnd;
+ lineStartAndEnd.mLineName = areaInfo.mName;
+
+ LineRange columnLines = ResolveLineRange(
+ lineStartAndEnd, lineStartAndEnd,
+ colLineNameMap,
+ &GridNamedArea::mColumnStart, &GridNamedArea::mColumnEnd,
+ mExplicitGridColEnd, gridStyle);
+
+ LineRange rowLines = ResolveLineRange(
+ lineStartAndEnd, lineStartAndEnd,
+ rowLineNameMap,
+ &GridNamedArea::mRowStart, &GridNamedArea::mRowEnd,
+ mExplicitGridRowEnd, gridStyle);
+
+ // Put the resolved line indices back into the area structure.
+ areaInfo.mColumnStart = columnLines.mStart + mExplicitGridOffsetCol;
+ areaInfo.mColumnEnd = columnLines.mEnd + mExplicitGridOffsetCol;
+ areaInfo.mRowStart = rowLines.mStart + mExplicitGridOffsetRow;
+ areaInfo.mRowEnd = rowLines.mEnd + mExplicitGridOffsetRow;
+ }
+ }
+}
+
+void
+nsGridContainerFrame::Tracks::Initialize(
+ const TrackSizingFunctions& aFunctions,
+ const nsStyleCoord& aGridGap,
+ uint32_t aNumTracks,
+ nscoord aContentBoxSize)
+{
+ MOZ_ASSERT(aNumTracks >= aFunctions.mExplicitGridOffset +
+ aFunctions.NumExplicitTracks());
+ mSizes.SetLength(aNumTracks);
+ PodZero(mSizes.Elements(), mSizes.Length());
+ for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
+ mStateUnion |= mSizes[i].Initialize(aContentBoxSize,
+ aFunctions.MinSizingFor(i),
+ aFunctions.MaxSizingFor(i));
+ }
+ mGridGap = ::ResolveToDefiniteSize(aGridGap, aContentBoxSize);
+ mContentBoxSize = aContentBoxSize;
+}
+
+/**
+ * Reflow aChild in the given aAvailableSize.
+ */
+static nscoord
+MeasuringReflow(nsIFrame* aChild,
+ const ReflowInput* aReflowInput,
+ nsRenderingContext* aRC,
+ const LogicalSize& aAvailableSize,
+ const LogicalSize& aCBSize,
+ nscoord aIMinSizeClamp = NS_MAXSIZE,
+ nscoord aBMinSizeClamp = NS_MAXSIZE)
+{
+ nsContainerFrame* parent = aChild->GetParent();
+ nsPresContext* pc = aChild->PresContext();
+ Maybe<ReflowInput> dummyParentState;
+ const ReflowInput* rs = aReflowInput;
+ if (!aReflowInput) {
+ MOZ_ASSERT(!parent->HasAnyStateBits(NS_FRAME_IN_REFLOW));
+ dummyParentState.emplace(pc, parent, aRC,
+ LogicalSize(parent->GetWritingMode(), 0,
+ NS_UNCONSTRAINEDSIZE),
+ ReflowInput::DUMMY_PARENT_REFLOW_STATE);
+ rs = dummyParentState.ptr();
+ }
+#ifdef DEBUG
+ // This will suppress various CRAZY_SIZE warnings for this reflow.
+ parent->Properties().Set(
+ nsContainerFrame::DebugReflowingWithInfiniteISize(), true);
+#endif
+ uint32_t riFlags = ReflowInput::COMPUTE_SIZE_SHRINK_WRAP |
+ ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE;
+ if (aIMinSizeClamp != NS_MAXSIZE) {
+ riFlags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE;
+ }
+ if (aBMinSizeClamp != NS_MAXSIZE) {
+ riFlags |= ReflowInput::B_CLAMP_MARGIN_BOX_MIN_SIZE;
+ aChild->Properties().Set(nsIFrame::BClampMarginBoxMinSizeProperty(),
+ aBMinSizeClamp);
+ } else {
+ aChild->Properties().Delete(nsIFrame::BClampMarginBoxMinSizeProperty());
+ }
+ ReflowInput childRI(pc, *rs, aChild, aAvailableSize, &aCBSize, riFlags);
+ ReflowOutput childSize(childRI);
+ nsReflowStatus childStatus;
+ const uint32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW;
+ WritingMode wm = childRI.GetWritingMode();
+ parent->ReflowChild(aChild, pc, childSize, childRI, wm,
+ LogicalPoint(wm), nsSize(), flags, childStatus);
+ parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm,
+ LogicalPoint(wm), nsSize(), flags);
+#ifdef DEBUG
+ parent->Properties().Delete(nsContainerFrame::DebugReflowingWithInfiniteISize());
+#endif
+ return childSize.BSize(wm);
+}
+
+/**
+ * Return the [min|max]-content contribution of aChild to its parent (i.e.
+ * the child's margin-box) in aAxis.
+ */
+static nscoord
+ContentContribution(const GridItemInfo& aGridItem,
+ const GridReflowInput& aState,
+ nsRenderingContext* aRC,
+ WritingMode aCBWM,
+ LogicalAxis aAxis,
+ IntrinsicISizeType aConstraint,
+ nscoord aMinSizeClamp = NS_MAXSIZE,
+ uint32_t aFlags = 0)
+{
+ nsIFrame* child = aGridItem.mFrame;
+ PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
+ nscoord size = nsLayoutUtils::IntrinsicForAxis(axis, aRC, child, aConstraint,
+ aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED |
+ nsLayoutUtils::ADD_PERCENTS,
+ aMinSizeClamp);
+ if (size == NS_INTRINSIC_WIDTH_UNKNOWN) {
+ // We need to reflow the child to find its BSize contribution.
+ // XXX this will give mostly correct results for now (until bug 1174569).
+ nscoord availISize = INFINITE_ISIZE_COORD;
+ nscoord availBSize = NS_UNCONSTRAINEDSIZE;
+ auto childWM = child->GetWritingMode();
+ const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
+ // The next two variables are MinSizeClamp values in the child's axes.
+ nscoord iMinSizeClamp = NS_MAXSIZE;
+ nscoord bMinSizeClamp = NS_MAXSIZE;
+ LogicalSize cbSize(childWM, 0, 0);
+ if (aState.mCols.mCanResolveLineRangeSize) {
+ nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
+ if (isOrthogonal) {
+ availBSize = sz;
+ cbSize.BSize(childWM) = sz;
+ if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
+ bMinSizeClamp = sz;
+ }
+ } else {
+ availISize = sz;
+ cbSize.ISize(childWM) = sz;
+ if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
+ iMinSizeClamp = sz;
+ }
+ }
+ }
+ if (isOrthogonal == (aAxis == eLogicalAxisInline)) {
+ bMinSizeClamp = aMinSizeClamp;
+ } else {
+ iMinSizeClamp = aMinSizeClamp;
+ }
+ LogicalSize availableSize(childWM, availISize, availBSize);
+ size = ::MeasuringReflow(child, aState.mReflowInput, aRC, availableSize,
+ cbSize, iMinSizeClamp, bMinSizeClamp);
+ nsIFrame::IntrinsicISizeOffsetData offsets = child->IntrinsicBSizeOffsets();
+ size += offsets.hMargin;
+ auto percent = offsets.hPctMargin;
+ if (availBSize == NS_UNCONSTRAINEDSIZE) {
+ // We always want to add in percent padding too, unless we already did so
+ // using a resolved column size above.
+ percent += offsets.hPctPadding;
+ }
+ size = nsLayoutUtils::AddPercents(size, percent);
+ nscoord overflow = size - aMinSizeClamp;
+ if (MOZ_UNLIKELY(overflow > 0)) {
+ nscoord contentSize = child->ContentBSize(childWM);
+ nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
+ // XXXmats deal with percentages better, see bug 1300369 comment 27.
+ size -= contentSize - newContentSize;
+ }
+ }
+ MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
+ "baseline offset should be non-negative at this point");
+ MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
+ aGridItem.mBaselineOffset[aAxis] == nscoord(0),
+ "baseline offset should be zero when not baseline-aligned");
+ size += aGridItem.mBaselineOffset[aAxis];
+ return std::max(size, 0);
+}
+
+struct CachedIntrinsicSizes
+{
+ Maybe<nscoord> mMinSize;
+ Maybe<nscoord> mMinContentContribution;
+ Maybe<nscoord> mMaxContentContribution;
+ // "if the grid item spans only grid tracks that have a fixed max track
+ // sizing function, its automatic minimum size in that dimension is
+ // further clamped to less than or equal to the size necessary to fit its
+ // margin box within the resulting grid area (flooring at zero)"
+ // https://drafts.csswg.org/css-grid/#min-size-auto
+ // This is the clamp value to use for that:
+ nscoord mMinSizeClamp = NS_MAXSIZE;
+};
+
+static nscoord
+MinContentContribution(const GridItemInfo& aGridItem,
+ const GridReflowInput& aState,
+ nsRenderingContext* aRC,
+ WritingMode aCBWM,
+ LogicalAxis aAxis,
+ CachedIntrinsicSizes* aCache)
+{
+ if (aCache->mMinContentContribution.isSome()) {
+ return aCache->mMinContentContribution.value();
+ }
+ nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
+ nsLayoutUtils::MIN_ISIZE,
+ aCache->mMinSizeClamp);
+ aCache->mMinContentContribution.emplace(s);
+ return s;
+}
+
+static nscoord
+MaxContentContribution(const GridItemInfo& aGridItem,
+ const GridReflowInput& aState,
+ nsRenderingContext* aRC,
+ WritingMode aCBWM,
+ LogicalAxis aAxis,
+ CachedIntrinsicSizes* aCache)
+{
+ if (aCache->mMaxContentContribution.isSome()) {
+ return aCache->mMaxContentContribution.value();
+ }
+ nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
+ nsLayoutUtils::PREF_ISIZE,
+ aCache->mMinSizeClamp);
+ aCache->mMaxContentContribution.emplace(s);
+ return s;
+}
+
+// Computes the min-size contribution for a grid item, as defined at
+// https://drafts.csswg.org/css-grid/#min-size-contributions
+static nscoord
+MinSize(const GridItemInfo& aGridItem,
+ const GridReflowInput& aState,
+ nsRenderingContext* aRC,
+ WritingMode aCBWM,
+ LogicalAxis aAxis,
+ CachedIntrinsicSizes* aCache)
+{
+ if (aCache->mMinSize.isSome()) {
+ return aCache->mMinSize.value();
+ }
+ nsIFrame* child = aGridItem.mFrame;
+ PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
+ const nsStylePosition* stylePos = child->StylePosition();
+ const nsStyleCoord& sizeStyle =
+ axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
+ if (sizeStyle.GetUnit() != eStyleUnit_Auto) {
+ nscoord s =
+ MinContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, aCache);
+ aCache->mMinSize.emplace(s);
+ return s;
+ }
+
+ // https://drafts.csswg.org/css-grid/#min-size-auto
+ // This calculates the min-content contribution from either a definite
+ // min-width (or min-height depending on aAxis), or the "specified /
+ // transferred size" for min-width:auto if overflow == visible (as min-width:0
+ // otherwise), or NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
+ // (which results in always taking the "content size" part below).
+ MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
+ "baseline offset should be non-negative at this point");
+ MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
+ aGridItem.mBaselineOffset[aAxis] == nscoord(0),
+ "baseline offset should be zero when not baseline-aligned");
+ nscoord sz = aGridItem.mBaselineOffset[aAxis] +
+ nsLayoutUtils::MinSizeContributionForAxis(axis, aRC, child,
+ nsLayoutUtils::MIN_ISIZE);
+ const nsStyleCoord& style = axis == eAxisHorizontal ? stylePos->mMinWidth
+ : stylePos->mMinHeight;
+ auto unit = style.GetUnit();
+ if (unit == eStyleUnit_Enumerated ||
+ (unit == eStyleUnit_Auto &&
+ child->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)) {
+ // Now calculate the "content size" part and return whichever is smaller.
+ MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE);
+ sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
+ nsLayoutUtils::MIN_ISIZE,
+ aCache->mMinSizeClamp,
+ nsLayoutUtils::MIN_INTRINSIC_ISIZE));
+ }
+ aCache->mMinSize.emplace(sz);
+ return sz;
+}
+
+void
+nsGridContainerFrame::Tracks::CalculateSizes(
+ GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aContentBoxSize,
+ LineRange GridArea::* aRange,
+ SizingConstraint aConstraint)
+{
+ nscoord percentageBasis = aContentBoxSize;
+ if (percentageBasis == NS_UNCONSTRAINEDSIZE) {
+ percentageBasis = 0;
+ }
+ InitializeItemBaselines(aState, aGridItems);
+ ResolveIntrinsicSize(aState, aGridItems, aFunctions, aRange, percentageBasis,
+ aConstraint);
+ if (aConstraint != SizingConstraint::eMinContent) {
+ nscoord freeSpace = aContentBoxSize;
+ if (freeSpace != NS_UNCONSTRAINEDSIZE) {
+ freeSpace -= SumOfGridGaps();
+ }
+ DistributeFreeSpace(freeSpace);
+ StretchFlexibleTracks(aState, aGridItems, aFunctions, freeSpace);
+ }
+}
+
+bool
+nsGridContainerFrame::Tracks::HasIntrinsicButNoFlexSizingInRange(
+ const LineRange& aRange,
+ TrackSize::StateBits* aState) const
+{
+ MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
+ const uint32_t start = aRange.mStart;
+ const uint32_t end = aRange.mEnd;
+ const TrackSize::StateBits selector =
+ TrackSize::eIntrinsicMinSizing | TrackSize::eIntrinsicMaxSizing;
+ bool foundIntrinsic = false;
+ for (uint32_t i = start; i < end; ++i) {
+ TrackSize::StateBits state = mSizes[i].mState;
+ *aState |= state;
+ if (state & TrackSize::eFlexMaxSizing) {
+ return false;
+ }
+ if (state & selector) {
+ foundIntrinsic = true;
+ }
+ }
+ return foundIntrinsic;
+}
+
+bool
+nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1(
+ GridReflowInput& aState,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aPercentageBasis,
+ SizingConstraint aConstraint,
+ const LineRange& aRange,
+ const GridItemInfo& aGridItem)
+{
+ CachedIntrinsicSizes cache;
+ TrackSize& sz = mSizes[aRange.mStart];
+ WritingMode wm = aState.mWM;
+ // Calculate data for "Automatic Minimum Size" clamping, if needed.
+ bool needed = ((sz.mState & TrackSize::eIntrinsicMinSizing) ||
+ aConstraint == SizingConstraint::eNoConstraint);
+ if (needed && TrackSize::IsDefiniteMaxSizing(sz.mState) &&
+ aGridItem.ShouldClampMinSize(wm, mAxis, aPercentageBasis)) {
+ if (sz.mState & TrackSize::eIntrinsicMinSizing) {
+ auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
+ cache.mMinSizeClamp =
+ nsRuleNode::ComputeCoordPercentCalc(maxCoord, aPercentageBasis);
+ }
+ aGridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
+ }
+ // min sizing
+ nsRenderingContext* rc = &aState.mRenderingContext;
+ if (sz.mState & TrackSize::eAutoMinSizing) {
+ nscoord s;
+ if (aConstraint == SizingConstraint::eMinContent) {
+ s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
+ } else if (aConstraint == SizingConstraint::eMaxContent) {
+ s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
+ } else {
+ MOZ_ASSERT(aConstraint == SizingConstraint::eNoConstraint);
+ s = MinSize(aGridItem, aState, rc, wm, mAxis, &cache);
+ }
+ sz.mBase = std::max(sz.mBase, s);
+ } else if (sz.mState & TrackSize::eMinContentMinSizing) {
+ auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
+ sz.mBase = std::max(sz.mBase, s);
+ } else if (sz.mState & TrackSize::eMaxContentMinSizing) {
+ auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
+ sz.mBase = std::max(sz.mBase, s);
+ }
+ // max sizing
+ if (sz.mState & TrackSize::eMinContentMaxSizing) {
+ auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
+ if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
+ sz.mLimit = s;
+ } else {
+ sz.mLimit = std::max(sz.mLimit, s);
+ }
+ } else if (sz.mState & (TrackSize::eAutoMaxSizing |
+ TrackSize::eMaxContentMaxSizing)) {
+ auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
+ if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
+ sz.mLimit = s;
+ } else {
+ sz.mLimit = std::max(sz.mLimit, s);
+ }
+ if (MOZ_UNLIKELY(sz.mState & TrackSize::eFitContent)) {
+ // Clamp mLimit to the fit-content() size, for §12.5.1.
+ auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
+ nscoord fitContentClamp =
+ nsRuleNode::ComputeCoordPercentCalc(maxCoord, aPercentageBasis);
+ sz.mLimit = std::min(sz.mLimit, fitContentClamp);
+ }
+ }
+ if (sz.mLimit < sz.mBase) {
+ sz.mLimit = sz.mBase;
+ }
+ return sz.mState & TrackSize::eFlexMaxSizing;
+}
+
+void
+nsGridContainerFrame::Tracks::CalculateItemBaselines(
+ nsTArray<ItemBaselineData>& aBaselineItems,
+ BaselineSharingGroup aBaselineGroup)
+{
+ if (aBaselineItems.IsEmpty()) {
+ return;
+ }
+
+ // Sort the collected items on their baseline track.
+ std::sort(aBaselineItems.begin(), aBaselineItems.end(),
+ ItemBaselineData::IsBaselineTrackLessThan);
+
+ MOZ_ASSERT(mSizes.Length() > 0, "having an item implies at least one track");
+ const uint32_t lastTrack = mSizes.Length() - 1;
+ nscoord maxBaseline = 0;
+ nscoord maxDescent = 0;
+ uint32_t currentTrack = kAutoLine; // guaranteed to not match any item
+ uint32_t trackStartIndex = 0;
+ for (uint32_t i = 0, len = aBaselineItems.Length(); true ; ++i) {
+ // Find the maximum baseline and descent in the current track.
+ if (i != len) {
+ const ItemBaselineData& item = aBaselineItems[i];
+ if (currentTrack == item.mBaselineTrack) {
+ maxBaseline = std::max(maxBaseline, item.mBaseline);
+ maxDescent = std::max(maxDescent, item.mSize - item.mBaseline);
+ continue;
+ }
+ }
+ // Iterate the current track again and update the baseline offsets making
+ // all items baseline-aligned within this group in this track.
+ for (uint32_t j = trackStartIndex; j < i; ++j) {
+ const ItemBaselineData& item = aBaselineItems[j];
+ item.mGridItem->mBaselineOffset[mAxis] = maxBaseline - item.mBaseline;
+ MOZ_ASSERT(item.mGridItem->mBaselineOffset[mAxis] >= 0);
+ }
+ if (i != 0) {
+ // Store the size of this baseline-aligned subtree.
+ mSizes[currentTrack].mBaselineSubtreeSize[aBaselineGroup] =
+ maxBaseline + maxDescent;
+ // Record the first(last) baseline for the first(last) track.
+ if (currentTrack == 0 && aBaselineGroup == BaselineSharingGroup::eFirst) {
+ mBaseline[aBaselineGroup] = maxBaseline;
+ }
+ if (currentTrack == lastTrack &&
+ aBaselineGroup == BaselineSharingGroup::eLast) {
+ mBaseline[aBaselineGroup] = maxBaseline;
+ }
+ }
+ if (i == len) {
+ break;
+ }
+ // Initialize data for the next track with baseline-aligned items.
+ const ItemBaselineData& item = aBaselineItems[i];
+ currentTrack = item.mBaselineTrack;
+ trackStartIndex = i;
+ maxBaseline = item.mBaseline;
+ maxDescent = item.mSize - item.mBaseline;
+ }
+}
+
+void
+nsGridContainerFrame::Tracks::InitializeItemBaselines(
+ GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems)
+{
+
+ nsTArray<ItemBaselineData> firstBaselineItems;
+ nsTArray<ItemBaselineData> lastBaselineItems;
+ WritingMode wm = aState.mWM;
+ nsStyleContext* containerSC = aState.mFrame->StyleContext();
+ GridItemCSSOrderIterator& iter = aState.mIter;
+ iter.Reset();
+ for (; !iter.AtEnd(); iter.Next()) {
+ nsIFrame* child = *iter;
+ GridItemInfo& gridItem = aGridItems[iter.GridItemIndex()];
+ uint32_t baselineTrack = kAutoLine;
+ auto state = ItemState(0);
+ auto childWM = child->GetWritingMode();
+ const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
+ const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns
+ // XXX update the line below to include orthogonal grid/table boxes
+ // XXX since they have baselines in both dimensions. And flexbox with
+ // XXX reversed main/cross axis?
+ const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
+ if (itemHasBaselineParallelToTrack) {
+ // [align|justify]-self:[last ]baseline.
+ auto selfAlignment = isOrthogonal ?
+ child->StylePosition()->UsedJustifySelf(containerSC) :
+ child->StylePosition()->UsedAlignSelf(containerSC);
+ selfAlignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
+ if (selfAlignment == NS_STYLE_ALIGN_BASELINE) {
+ state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
+ const GridArea& area = gridItem.mArea;
+ baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
+ } else if (selfAlignment == NS_STYLE_ALIGN_LAST_BASELINE) {
+ state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
+ const GridArea& area = gridItem.mArea;
+ baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
+ }
+
+ // [align|justify]-content:[last ]baseline.
+ // https://drafts.csswg.org/css-align-3/#baseline-align-content
+ // "[...] and its computed 'align-self' or 'justify-self' (whichever
+ // affects its block axis) is 'stretch' or 'self-start' ('self-end').
+ // For this purpose, the 'start', 'end', 'flex-start', and 'flex-end'
+ // values of 'align-self' are treated as either 'self-start' or
+ // 'self-end', whichever they end up equivalent to.
+ auto alignContent = child->StylePosition()->mAlignContent;
+ alignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
+ if (alignContent == NS_STYLE_ALIGN_BASELINE ||
+ alignContent == NS_STYLE_ALIGN_LAST_BASELINE) {
+ const auto selfAlignEdge = alignContent == NS_STYLE_ALIGN_BASELINE ?
+ NS_STYLE_ALIGN_SELF_START : NS_STYLE_ALIGN_SELF_END;
+ bool validCombo = selfAlignment == NS_STYLE_ALIGN_NORMAL ||
+ selfAlignment == NS_STYLE_ALIGN_STRETCH ||
+ selfAlignment == selfAlignEdge;
+ if (!validCombo) {
+ // We're doing alignment in the axis that's orthogonal to mAxis here.
+ LogicalAxis alignAxis = GetOrthogonalAxis(mAxis);
+ // |sameSide| is true if the container's start side in this axis is
+ // the same as the child's start side, in the child's parallel axis.
+ bool sameSide = wm.ParallelAxisStartsOnSameSide(alignAxis, childWM);
+ switch (selfAlignment) {
+ case NS_STYLE_ALIGN_LEFT:
+ selfAlignment = !isInlineAxis || wm.IsBidiLTR() ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END;
+ break;
+ case NS_STYLE_ALIGN_RIGHT:
+ selfAlignment = isInlineAxis && wm.IsBidiLTR() ? NS_STYLE_ALIGN_END
+ : NS_STYLE_ALIGN_START;
+ break;
+ }
+ switch (selfAlignment) {
+ case NS_STYLE_ALIGN_START:
+ case NS_STYLE_ALIGN_FLEX_START:
+ validCombo = sameSide ==
+ (alignContent == NS_STYLE_ALIGN_BASELINE);
+ break;
+ case NS_STYLE_ALIGN_END:
+ case NS_STYLE_ALIGN_FLEX_END:
+ validCombo = sameSide ==
+ (alignContent == NS_STYLE_ALIGN_LAST_BASELINE);
+ break;
+ }
+ }
+ if (validCombo) {
+ const GridArea& area = gridItem.mArea;
+ if (alignContent == NS_STYLE_ALIGN_BASELINE) {
+ state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
+ baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
+ } else if (alignContent == NS_STYLE_ALIGN_LAST_BASELINE) {
+ state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
+ baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
+ }
+ }
+ }
+ }
+
+ if (state & ItemState::eIsBaselineAligned) {
+ // XXX available size issue
+ LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
+ auto* rc = &aState.mRenderingContext;
+ // XXX figure out if we can avoid/merge this reflow with the main reflow.
+ // XXX (after bug 1174569 is sorted out)
+ //
+ // XXX How should we handle percentage padding here? (bug 1330866)
+ // XXX (see ::ContentContribution and how it deals with percentages)
+ // XXX What if the true baseline after line-breaking differs from this
+ // XXX hypothetical baseline based on an infinite inline size?
+ // XXX Maybe we should just call ::ContentContribution here instead?
+ // XXX For now we just pass a zero-sized CB:
+ LogicalSize cbSize(childWM, 0, 0);
+ ::MeasuringReflow(child, aState.mReflowInput, rc, avail, cbSize);
+ nscoord baseline;
+ nsGridContainerFrame* grid = do_QueryFrame(child);
+ if (state & ItemState::eFirstBaseline) {
+ if (grid) {
+ if (isOrthogonal == isInlineAxis) {
+ grid->GetBBaseline(BaselineSharingGroup::eFirst, &baseline);
+ } else {
+ grid->GetIBaseline(BaselineSharingGroup::eFirst, &baseline);
+ }
+ }
+ if (grid ||
+ nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
+ NS_ASSERTION(baseline != NS_INTRINSIC_WIDTH_UNKNOWN,
+ "about to use an unknown baseline");
+ auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
+ auto m = child->GetLogicalUsedMargin(wm);
+ baseline += isInlineAxis ? m.IStart(wm) : m.BStart(wm);
+ auto alignSize = frameSize + (isInlineAxis ? m.IStartEnd(wm)
+ : m.BStartEnd(wm));
+ firstBaselineItems.AppendElement(ItemBaselineData(
+ { baselineTrack, baseline, alignSize, &gridItem }));
+ } else {
+ state &= ~ItemState::eAllBaselineBits;
+ }
+ } else {
+ if (grid) {
+ if (isOrthogonal == isInlineAxis) {
+ grid->GetBBaseline(BaselineSharingGroup::eLast, &baseline);
+ } else {
+ grid->GetIBaseline(BaselineSharingGroup::eLast, &baseline);
+ }
+ }
+ if (grid ||
+ nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
+ NS_ASSERTION(baseline != NS_INTRINSIC_WIDTH_UNKNOWN,
+ "about to use an unknown baseline");
+ auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
+ auto m = child->GetLogicalUsedMargin(wm);
+ if (!grid) {
+ // Convert to distance from border-box end.
+ baseline = frameSize - baseline;
+ }
+ auto descent = baseline + (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm));
+ auto alignSize = frameSize + (isInlineAxis ? m.IStartEnd(wm)
+ : m.BStartEnd(wm));
+ lastBaselineItems.AppendElement(ItemBaselineData(
+ { baselineTrack, descent, alignSize, &gridItem }));
+ } else {
+ state &= ~ItemState::eAllBaselineBits;
+ }
+ }
+ }
+ MOZ_ASSERT((state &
+ (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
+ (ItemState::eFirstBaseline | ItemState::eLastBaseline),
+ "first/last baseline bits are mutually exclusive");
+ MOZ_ASSERT((state &
+ (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
+ (ItemState::eSelfBaseline | ItemState::eContentBaseline),
+ "*-self and *-content baseline bits are mutually exclusive");
+ MOZ_ASSERT(!(state &
+ (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
+ !(state &
+ (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
+ "first/last bit requires self/content bit and vice versa");
+ gridItem.mState[mAxis] = state;
+ gridItem.mBaselineOffset[mAxis] = nscoord(0);
+ }
+
+ if (firstBaselineItems.IsEmpty() && lastBaselineItems.IsEmpty()) {
+ return;
+ }
+
+ // TODO: CSS Align spec issue - how to align a baseline subtree in a track?
+ // https://lists.w3.org/Archives/Public/www-style/2016May/0141.html
+ mBaselineSubtreeAlign[BaselineSharingGroup::eFirst] = NS_STYLE_ALIGN_START;
+ mBaselineSubtreeAlign[BaselineSharingGroup::eLast] = NS_STYLE_ALIGN_END;
+
+ CalculateItemBaselines(firstBaselineItems, BaselineSharingGroup::eFirst);
+ CalculateItemBaselines(lastBaselineItems, BaselineSharingGroup::eLast);
+}
+
+void
+nsGridContainerFrame::Tracks::AlignBaselineSubtree(
+ const GridItemInfo& aGridItem) const
+{
+ auto state = aGridItem.mState[mAxis];
+ if (!(state & ItemState::eIsBaselineAligned)) {
+ return;
+ }
+ const GridArea& area = aGridItem.mArea;
+ int32_t baselineTrack;
+ const bool isFirstBaseline = state & ItemState::eFirstBaseline;
+ if (isFirstBaseline) {
+ baselineTrack = mAxis == eLogicalAxisBlock ? area.mRows.mStart
+ : area.mCols.mStart;
+ } else {
+ baselineTrack = (mAxis == eLogicalAxisBlock ? area.mRows.mEnd
+ : area.mCols.mEnd) - 1;
+ }
+ const TrackSize& sz = mSizes[baselineTrack];
+ auto baselineGroup = isFirstBaseline ? BaselineSharingGroup::eFirst
+ : BaselineSharingGroup::eLast;
+ nscoord delta = sz.mBase - sz.mBaselineSubtreeSize[baselineGroup];
+ const auto subtreeAlign = mBaselineSubtreeAlign[baselineGroup];
+ switch (subtreeAlign) {
+ case NS_STYLE_ALIGN_START:
+ if (state & ItemState::eLastBaseline) {
+ aGridItem.mBaselineOffset[mAxis] += delta;
+ }
+ break;
+ case NS_STYLE_ALIGN_END:
+ if (isFirstBaseline) {
+ aGridItem.mBaselineOffset[mAxis] += delta;
+ }
+ break;
+ case NS_STYLE_ALIGN_CENTER:
+ aGridItem.mBaselineOffset[mAxis] += delta / 2;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected baseline subtree alignment");
+ }
+}
+
+void
+nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
+ GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const TrackSizingFunctions& aFunctions,
+ LineRange GridArea::* aRange,
+ nscoord aPercentageBasis,
+ SizingConstraint aConstraint)
+{
+ // Some data we collect on each item for Step 2 of the algorithm below.
+ struct Step2ItemData
+ {
+ uint32_t mSpan;
+ TrackSize::StateBits mState;
+ LineRange mLineRange;
+ nscoord mMinSize;
+ nscoord mMinContentContribution;
+ nscoord mMaxContentContribution;
+ nsIFrame* mFrame;
+ static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b)
+ {
+ return a.mSpan < b.mSpan;
+ }
+ };
+
+ // Resolve Intrinsic Track Sizes
+ // http://dev.w3.org/csswg/css-grid/#algo-content
+ // We're also setting eIsFlexing on the item state here to speed up
+ // FindUsedFlexFraction later.
+ AutoTArray<TrackSize::StateBits, 16> stateBitsPerSpan;
+ nsTArray<Step2ItemData> step2Items;
+ GridItemCSSOrderIterator& iter = aState.mIter;
+ nsRenderingContext* rc = &aState.mRenderingContext;
+ WritingMode wm = aState.mWM;
+ uint32_t maxSpan = 0; // max span of the step2Items items
+ // Setup track selector for step 2.2:
+ const auto contentBasedMinSelector =
+ aConstraint == SizingConstraint::eMinContent ?
+ TrackSize::eIntrinsicMinSizing : TrackSize::eMinOrMaxContentMinSizing;
+ // Setup track selector for step 2.3:
+ const auto maxContentMinSelector =
+ aConstraint == SizingConstraint::eMaxContent ?
+ (TrackSize::eMaxContentMinSizing | TrackSize::eAutoMinSizing) :
+ TrackSize::eMaxContentMinSizing;
+ iter.Reset();
+ for (; !iter.AtEnd(); iter.Next()) {
+ auto& gridItem = aGridItems[iter.GridItemIndex()];
+ const GridArea& area = gridItem.mArea;
+ const LineRange& lineRange = area.*aRange;
+ uint32_t span = lineRange.Extent();
+ if (span == 1) {
+ // Step 1. Size tracks to fit non-spanning items.
+ if (ResolveIntrinsicSizeStep1(aState, aFunctions, aPercentageBasis,
+ aConstraint, lineRange, gridItem)) {
+ gridItem.mState[mAxis] |= ItemState::eIsFlexing;
+ }
+ } else {
+ TrackSize::StateBits state = TrackSize::StateBits(0);
+ if (HasIntrinsicButNoFlexSizingInRange(lineRange, &state)) {
+ // Collect data for Step 2.
+ maxSpan = std::max(maxSpan, span);
+ if (span >= stateBitsPerSpan.Length()) {
+ uint32_t len = 2 * span;
+ stateBitsPerSpan.SetCapacity(len);
+ for (uint32_t i = stateBitsPerSpan.Length(); i < len; ++i) {
+ stateBitsPerSpan.AppendElement(TrackSize::StateBits(0));
+ }
+ }
+ stateBitsPerSpan[span] |= state;
+ CachedIntrinsicSizes cache;
+ // Calculate data for "Automatic Minimum Size" clamping, if needed.
+ bool needed = ((state & TrackSize::eIntrinsicMinSizing) ||
+ aConstraint == SizingConstraint::eNoConstraint);
+ if (needed && TrackSize::IsDefiniteMaxSizing(state) &&
+ gridItem.ShouldClampMinSize(wm, mAxis, aPercentageBasis)) {
+ nscoord minSizeClamp = 0;
+ for (auto i = lineRange.mStart, end = lineRange.mEnd; i < end; ++i) {
+ auto maxCoord = aFunctions.MaxSizingFor(i);
+ minSizeClamp +=
+ nsRuleNode::ComputeCoordPercentCalc(maxCoord, aPercentageBasis);
+ }
+ minSizeClamp += mGridGap * (span - 1);
+ cache.mMinSizeClamp = minSizeClamp;
+ gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
+ }
+ // Collect the various grid item size contributions we need.
+ nscoord minSize = 0;
+ if (state & (TrackSize::eIntrinsicMinSizing | // for 2.1
+ TrackSize::eIntrinsicMaxSizing)) { // for 2.5
+ minSize = MinSize(gridItem, aState, rc, wm, mAxis, &cache);
+ }
+ nscoord minContent = 0;
+ if (state & contentBasedMinSelector) { // for 2.2
+ minContent = MinContentContribution(gridItem, aState,
+ rc, wm, mAxis, &cache);
+ }
+ nscoord maxContent = 0;
+ if (state & (maxContentMinSelector | // for 2.3
+ TrackSize::eAutoOrMaxContentMaxSizing)) { // for 2.6
+ maxContent = MaxContentContribution(gridItem, aState,
+ rc, wm, mAxis, &cache);
+ }
+ step2Items.AppendElement(
+ Step2ItemData({span, state, lineRange, minSize,
+ minContent, maxContent, *iter}));
+ } else {
+ if (state & TrackSize::eFlexMaxSizing) {
+ gridItem.mState[mAxis] |= ItemState::eIsFlexing;
+ } else if (aConstraint == SizingConstraint::eNoConstraint &&
+ TrackSize::IsDefiniteMaxSizing(state) &&
+ gridItem.ShouldClampMinSize(wm, mAxis, aPercentageBasis)) {
+ gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
+ }
+ }
+ }
+ }
+
+ // Step 2.
+ if (maxSpan) {
+ // Sort the collected items on span length, shortest first.
+ std::stable_sort(step2Items.begin(), step2Items.end(),
+ Step2ItemData::IsSpanLessThan);
+
+ nsTArray<uint32_t> tracks(maxSpan);
+ nsTArray<TrackSize> plan(mSizes.Length());
+ plan.SetLength(mSizes.Length());
+ for (uint32_t i = 0, len = step2Items.Length(); i < len; ) {
+ // Start / end index for items of the same span length:
+ const uint32_t spanGroupStartIndex = i;
+ uint32_t spanGroupEndIndex = len;
+ const uint32_t span = step2Items[i].mSpan;
+ for (++i; i < len; ++i) {
+ if (step2Items[i].mSpan != span) {
+ spanGroupEndIndex = i;
+ break;
+ }
+ }
+
+ bool updatedBase = false; // Did we update any mBase in step 2.1 - 2.3?
+ TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing);
+ if (stateBitsPerSpan[span] & selector) {
+ // Step 2.1 MinSize to intrinsic min-sizing.
+ for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
+ Step2ItemData& item = step2Items[i];
+ if (!(item.mState & selector)) {
+ continue;
+ }
+ nscoord space = item.mMinSize;
+ if (space <= 0) {
+ continue;
+ }
+ tracks.ClearAndRetainStorage();
+ space = CollectGrowable(space, mSizes, item.mLineRange, selector,
+ tracks);
+ if (space > 0) {
+ DistributeToTrackBases(space, plan, tracks, selector);
+ updatedBase = true;
+ }
+ }
+ }
+
+ selector = contentBasedMinSelector;
+ if (stateBitsPerSpan[span] & selector) {
+ // Step 2.2 MinContentContribution to min-/max-content (and 'auto' when
+ // sizing under a min-content constraint) min-sizing.
+ for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
+ Step2ItemData& item = step2Items[i];
+ if (!(item.mState & selector)) {
+ continue;
+ }
+ nscoord space = item.mMinContentContribution;
+ if (space <= 0) {
+ continue;
+ }
+ tracks.ClearAndRetainStorage();
+ space = CollectGrowable(space, mSizes, item.mLineRange, selector,
+ tracks);
+ if (space > 0) {
+ DistributeToTrackBases(space, plan, tracks, selector);
+ updatedBase = true;
+ }
+ }
+ }
+
+ selector = maxContentMinSelector;
+ if (stateBitsPerSpan[span] & selector) {
+ // Step 2.3 MaxContentContribution to max-content (and 'auto' when
+ // sizing under a max-content constraint) min-sizing.
+ for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
+ Step2ItemData& item = step2Items[i];
+ if (!(item.mState & selector)) {
+ continue;
+ }
+ nscoord space = item.mMaxContentContribution;
+ if (space <= 0) {
+ continue;
+ }
+ tracks.ClearAndRetainStorage();
+ space = CollectGrowable(space, mSizes, item.mLineRange, selector,
+ tracks);
+ if (space > 0) {
+ DistributeToTrackBases(space, plan, tracks, selector);
+ updatedBase = true;
+ }
+ }
+ }
+
+ if (updatedBase) {
+ // Step 2.4
+ for (TrackSize& sz : mSizes) {
+ if (sz.mBase > sz.mLimit) {
+ sz.mLimit = sz.mBase;
+ }
+ }
+ }
+ if (stateBitsPerSpan[span] & TrackSize::eIntrinsicMaxSizing) {
+ plan = mSizes;
+ for (TrackSize& sz : plan) {
+ if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
+ // use mBase as the planned limit
+ } else {
+ sz.mBase = sz.mLimit;
+ }
+ }
+
+ // Step 2.5 MinSize to intrinsic max-sizing.
+ for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
+ Step2ItemData& item = step2Items[i];
+ if (!(item.mState & TrackSize::eIntrinsicMaxSizing)) {
+ continue;
+ }
+ nscoord space = item.mMinSize;
+ if (space <= 0) {
+ continue;
+ }
+ tracks.ClearAndRetainStorage();
+ space = CollectGrowable(space, plan, item.mLineRange,
+ TrackSize::eIntrinsicMaxSizing,
+ tracks);
+ if (space > 0) {
+ DistributeToTrackLimits(space, plan, tracks, aFunctions,
+ aPercentageBasis);
+ }
+ }
+ for (size_t j = 0, len = mSizes.Length(); j < len; ++j) {
+ TrackSize& sz = plan[j];
+ sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited);
+ if (sz.mLimit != NS_UNCONSTRAINEDSIZE) {
+ sz.mLimit = sz.mBase; // collect the results from 2.5
+ }
+ }
+
+ if (stateBitsPerSpan[span] & TrackSize::eAutoOrMaxContentMaxSizing) {
+ // Step 2.6 MaxContentContribution to max-content max-sizing.
+ for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
+ Step2ItemData& item = step2Items[i];
+ if (!(item.mState & TrackSize::eAutoOrMaxContentMaxSizing)) {
+ continue;
+ }
+ nscoord space = item.mMaxContentContribution;
+ if (space <= 0) {
+ continue;
+ }
+ tracks.ClearAndRetainStorage();
+ space = CollectGrowable(space, plan, item.mLineRange,
+ TrackSize::eAutoOrMaxContentMaxSizing,
+ tracks);
+ if (space > 0) {
+ DistributeToTrackLimits(space, plan, tracks, aFunctions,
+ aPercentageBasis);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Step 3.
+ for (TrackSize& sz : mSizes) {
+ if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
+ sz.mLimit = sz.mBase;
+ }
+ }
+}
+
+float
+nsGridContainerFrame::Tracks::FindFrUnitSize(
+ const LineRange& aRange,
+ const nsTArray<uint32_t>& aFlexTracks,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aSpaceToFill) const
+{
+ MOZ_ASSERT(aSpaceToFill > 0 && !aFlexTracks.IsEmpty());
+ float flexFactorSum = 0.0f;
+ nscoord leftOverSpace = aSpaceToFill;
+ for (uint32_t i = aRange.mStart, end = aRange.mEnd; i < end; ++i) {
+ const TrackSize& sz = mSizes[i];
+ if (sz.mState & TrackSize::eFlexMaxSizing) {
+ flexFactorSum += aFunctions.MaxSizingFor(i).GetFlexFractionValue();
+ } else {
+ leftOverSpace -= sz.mBase;
+ if (leftOverSpace <= 0) {
+ return 0.0f;
+ }
+ }
+ }
+ bool restart;
+ float hypotheticalFrSize;
+ nsTArray<uint32_t> flexTracks(aFlexTracks);
+ uint32_t numFlexTracks = flexTracks.Length();
+ do {
+ restart = false;
+ hypotheticalFrSize = leftOverSpace / std::max(flexFactorSum, 1.0f);
+ for (uint32_t i = 0, len = flexTracks.Length(); i < len; ++i) {
+ uint32_t track = flexTracks[i];
+ if (track == kAutoLine) {
+ continue; // Track marked as inflexible in a prev. iter of this loop.
+ }
+ float flexFactor = aFunctions.MaxSizingFor(track).GetFlexFractionValue();
+ const nscoord base = mSizes[track].mBase;
+ if (flexFactor * hypotheticalFrSize < base) {
+ // 12.7.1.4: Treat this track as inflexible.
+ flexTracks[i] = kAutoLine;
+ flexFactorSum -= flexFactor;
+ leftOverSpace -= base;
+ --numFlexTracks;
+ if (numFlexTracks == 0 || leftOverSpace <= 0) {
+ return 0.0f;
+ }
+ restart = true;
+ // break; XXX (bug 1176621 comment 16) measure which is more common
+ }
+ }
+ } while (restart);
+ return hypotheticalFrSize;
+}
+
+float
+nsGridContainerFrame::Tracks::FindUsedFlexFraction(
+ GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const nsTArray<uint32_t>& aFlexTracks,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aAvailableSize) const
+{
+ if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
+ // Use all of the grid tracks and a 'space to fill' of the available space.
+ const TranslatedLineRange range(0, mSizes.Length());
+ return FindFrUnitSize(range, aFlexTracks, aFunctions, aAvailableSize);
+ }
+
+ // The used flex fraction is the maximum of:
+ // ... each flexible track's base size divided by its flex factor (which is
+ // floored at 1).
+ float fr = 0.0f;
+ for (uint32_t track : aFlexTracks) {
+ float flexFactor = aFunctions.MaxSizingFor(track).GetFlexFractionValue();
+ float possiblyDividedBaseSize = (flexFactor > 1.0f)
+ ? mSizes[track].mBase / flexFactor
+ : mSizes[track].mBase;
+ fr = std::max(fr, possiblyDividedBaseSize);
+ }
+ WritingMode wm = aState.mWM;
+ nsRenderingContext* rc = &aState.mRenderingContext;
+ GridItemCSSOrderIterator& iter = aState.mIter;
+ iter.Reset();
+ // ... the result of 'finding the size of an fr' for each item that spans
+ // a flex track with its max-content contribution as 'space to fill'
+ for (; !iter.AtEnd(); iter.Next()) {
+ const GridItemInfo& item = aGridItems[iter.GridItemIndex()];
+ if (item.mState[mAxis] & ItemState::eIsFlexing) {
+ // XXX optimize: bug 1194446
+ nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis,
+ nsLayoutUtils::PREF_ISIZE);
+ if (spaceToFill <= 0) {
+ continue;
+ }
+ // ... and all its spanned tracks as input.
+ const LineRange& range =
+ mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows;
+ nsTArray<uint32_t> itemFlexTracks;
+ for (uint32_t i = range.mStart, end = range.mEnd; i < end; ++i) {
+ if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
+ itemFlexTracks.AppendElement(i);
+ }
+ }
+ float itemFr =
+ FindFrUnitSize(range, itemFlexTracks, aFunctions, spaceToFill);
+ fr = std::max(fr, itemFr);
+ }
+ }
+ return fr;
+}
+
+void
+nsGridContainerFrame::Tracks::StretchFlexibleTracks(
+ GridReflowInput& aState,
+ nsTArray<GridItemInfo>& aGridItems,
+ const TrackSizingFunctions& aFunctions,
+ nscoord aAvailableSize)
+{
+ if (aAvailableSize <= 0) {
+ return;
+ }
+ nsTArray<uint32_t> flexTracks(mSizes.Length());
+ for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
+ if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
+ flexTracks.AppendElement(i);
+ }
+ }
+ if (flexTracks.IsEmpty()) {
+ return;
+ }
+ nscoord minSize = 0;
+ nscoord maxSize = NS_UNCONSTRAINEDSIZE;
+ if (aState.mReflowInput) {
+ auto* ri = aState.mReflowInput;
+ minSize = mAxis == eLogicalAxisBlock ? ri->ComputedMinBSize()
+ : ri->ComputedMinISize();
+ maxSize = mAxis == eLogicalAxisBlock ? ri->ComputedMaxBSize()
+ : ri->ComputedMaxISize();
+ }
+ Maybe<nsTArray<TrackSize>> origSizes;
+ // We iterate twice at most. The 2nd time if the grid size changed after
+ // applying a min/max-size (can only occur if aAvailableSize is indefinite).
+ while (true) {
+ float fr = FindUsedFlexFraction(aState, aGridItems, flexTracks,
+ aFunctions, aAvailableSize);
+ if (fr != 0.0f) {
+ bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
+ aAvailableSize == NS_UNCONSTRAINEDSIZE;
+ for (uint32_t i : flexTracks) {
+ float flexFactor = aFunctions.MaxSizingFor(i).GetFlexFractionValue();
+ nscoord flexLength = NSToCoordRound(flexFactor * fr);
+ nscoord& base = mSizes[i].mBase;
+ if (flexLength > base) {
+ if (applyMinMax && origSizes.isNothing()) {
+ origSizes.emplace(mSizes);
+ }
+ base = flexLength;
+ }
+ }
+ if (applyMinMax && origSizes.isSome()) {
+ // https://drafts.csswg.org/css-grid/#algo-flex-tracks
+ // "If using this flex fraction would cause the grid to be smaller than
+ // the grid container’s min-width/height (or larger than the grid
+ // container’s max-width/height), then redo this step, treating the free
+ // space as definite [...]"
+ nscoord newSize = 0;
+ for (auto& sz : mSizes) {
+ newSize += sz.mBase;
+ }
+ const auto sumOfGridGaps = SumOfGridGaps();
+ newSize += sumOfGridGaps;
+ if (newSize > maxSize) {
+ aAvailableSize = maxSize;
+ } else if (newSize < minSize) {
+ aAvailableSize = minSize;
+ }
+ if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
+ // Reset min/max-size to ensure 'applyMinMax' becomes false next time.
+ minSize = 0;
+ maxSize = NS_UNCONSTRAINEDSIZE;
+ aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
+ // Restart with the original track sizes and definite aAvailableSize.
+ mSizes = Move(*origSizes);
+ origSizes.reset();
+ if (aAvailableSize == 0) {
+ break; // zero available size wouldn't change any sizes though...
+ }
+ continue;
+ }
+ }
+ }
+ break;
+ }
+}
+
+void
+nsGridContainerFrame::Tracks::AlignJustifyContent(
+ const nsStylePosition* aStyle,
+ WritingMode aWM,
+ const LogicalSize& aContainerSize)
+{
+ if (mSizes.IsEmpty()) {
+ return;
+ }
+
+ const bool isAlign = mAxis == eLogicalAxisBlock;
+ auto valueAndFallback = isAlign ? aStyle->mAlignContent :
+ aStyle->mJustifyContent;
+ bool overflowSafe;
+ auto alignment = ::GetAlignJustifyValue(valueAndFallback, aWM, isAlign,
+ &overflowSafe);
+ if (alignment == NS_STYLE_ALIGN_NORMAL) {
+ MOZ_ASSERT(valueAndFallback == NS_STYLE_ALIGN_NORMAL,
+ "*-content:normal cannot be specified with explicit fallback");
+ alignment = NS_STYLE_ALIGN_STRETCH;
+ valueAndFallback = alignment; // we may need a fallback for 'stretch' below
+ }
+
+ // Compute the free space and count auto-sized tracks.
+ size_t numAutoTracks = 0;
+ nscoord space;
+ if (alignment != NS_STYLE_ALIGN_START) {
+ nscoord trackSizeSum = 0;
+ for (const TrackSize& sz : mSizes) {
+ trackSizeSum += sz.mBase;
+ if (sz.mState & TrackSize::eAutoMaxSizing) {
+ ++numAutoTracks;
+ }
+ }
+ nscoord cbSize = isAlign ? aContainerSize.BSize(aWM)
+ : aContainerSize.ISize(aWM);
+ space = cbSize - trackSizeSum - SumOfGridGaps();
+ // Use the fallback value instead when applicable.
+ if (space < 0 ||
+ (alignment == NS_STYLE_ALIGN_SPACE_BETWEEN && mSizes.Length() == 1)) {
+ auto fallback = ::GetAlignJustifyFallbackIfAny(valueAndFallback, aWM,
+ isAlign, &overflowSafe);
+ if (fallback) {
+ alignment = fallback;
+ }
+ }
+ if (space == 0 || (space < 0 && overflowSafe)) {
+ // XXX check that this makes sense also for [last ]baseline (bug 1151204).
+ alignment = NS_STYLE_ALIGN_START;
+ }
+ }
+
+ // Optimize the cases where we just need to set each track's position.
+ nscoord pos = 0;
+ bool distribute = true;
+ switch (alignment) {
+ case NS_STYLE_ALIGN_BASELINE:
+ case NS_STYLE_ALIGN_LAST_BASELINE:
+ NS_WARNING("NYI: 'first/last baseline' (bug 1151204)"); // XXX
+ MOZ_FALLTHROUGH;
+ case NS_STYLE_ALIGN_START:
+ distribute = false;
+ break;
+ case NS_STYLE_ALIGN_END:
+ pos = space;
+ distribute = false;
+ break;
+ case NS_STYLE_ALIGN_CENTER:
+ pos = space / 2;
+ distribute = false;
+ break;
+ case NS_STYLE_ALIGN_STRETCH:
+ distribute = numAutoTracks != 0;
+ break;
+ }
+ if (!distribute) {
+ for (TrackSize& sz : mSizes) {
+ sz.mPosition = pos;
+ pos += sz.mBase + mGridGap;
+ }
+ return;
+ }
+
+ // Distribute free space to/between tracks and set their position.
+ MOZ_ASSERT(space > 0, "should've handled that on the fallback path above");
+ nscoord between, roundingError;
+ switch (alignment) {
+ case NS_STYLE_ALIGN_STRETCH: {
+ MOZ_ASSERT(numAutoTracks > 0, "we handled numAutoTracks == 0 above");
+ nscoord spacePerTrack;
+ roundingError = NSCoordDivRem(space, numAutoTracks, &spacePerTrack);
+ for (TrackSize& sz : mSizes) {
+ sz.mPosition = pos;
+ if (!(sz.mState & TrackSize::eAutoMaxSizing)) {
+ pos += sz.mBase + mGridGap;
+ continue;
+ }
+ nscoord stretch = spacePerTrack;
+ if (roundingError) {
+ roundingError -= 1;
+ stretch += 1;
+ }
+ nscoord newBase = sz.mBase + stretch;
+ sz.mBase = newBase;
+ pos += newBase + mGridGap;
+ }
+ MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
+ return;
+ }
+ case NS_STYLE_ALIGN_SPACE_BETWEEN:
+ MOZ_ASSERT(mSizes.Length() > 1, "should've used a fallback above");
+ roundingError = NSCoordDivRem(space, mSizes.Length() - 1, &between);
+ break;
+ case NS_STYLE_ALIGN_SPACE_AROUND:
+ roundingError = NSCoordDivRem(space, mSizes.Length(), &between);
+ pos = between / 2;
+ break;
+ case NS_STYLE_ALIGN_SPACE_EVENLY:
+ roundingError = NSCoordDivRem(space, mSizes.Length() + 1, &between);
+ pos = between;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
+ between = 0; // just to avoid a compiler warning
+ }
+ between += mGridGap;
+ for (TrackSize& sz : mSizes) {
+ sz.mPosition = pos;
+ nscoord spacing = between;
+ if (roundingError) {
+ roundingError -= 1;
+ spacing += 1;
+ }
+ pos += sz.mBase + spacing;
+ }
+ MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
+}
+
+nscoord
+nsGridContainerFrame::Tracks::BackComputedIntrinsicSize(
+ const TrackSizingFunctions& aFunctions,
+ const nsStyleCoord& aGridGap) const
+{
+ // Sum up the current sizes (where percentage tracks were treated as 'auto')
+ // in 'size'.
+ nscoord size = 0;
+ for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
+ size += mSizes[i].mBase;
+ }
+
+ // Add grid-gap contributions to 'size' and calculate a 'percent' sum.
+ float percent = 0.0f;
+ size_t numTracks = mSizes.Length();
+ if (numTracks > 1) {
+ const size_t gridGapCount = numTracks - 1;
+ nscoord gridGapLength;
+ float gridGapPercent;
+ if (::GetPercentSizeParts(aGridGap, &gridGapLength, &gridGapPercent)) {
+ percent = gridGapCount * gridGapPercent;
+ } else {
+ gridGapLength = aGridGap.ToLength();
+ }
+ size += gridGapCount * gridGapLength;
+ }
+
+ return std::max(0, nsLayoutUtils::AddPercents(size, percent));
+}
+
+void
+nsGridContainerFrame::LineRange::ToPositionAndLength(
+ const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos, nscoord* aLength) const
+{
+ MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
+ "expected a definite LineRange");
+ MOZ_ASSERT(mStart < mEnd);
+ nscoord startPos = aTrackSizes[mStart].mPosition;
+ const TrackSize& sz = aTrackSizes[mEnd - 1];
+ *aPos = startPos;
+ *aLength = (sz.mPosition + sz.mBase) - startPos;
+}
+
+nscoord
+nsGridContainerFrame::LineRange::ToLength(
+ const nsTArray<TrackSize>& aTrackSizes) const
+{
+ MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
+ "expected a definite LineRange");
+ MOZ_ASSERT(mStart < mEnd);
+ nscoord startPos = aTrackSizes[mStart].mPosition;
+ const TrackSize& sz = aTrackSizes[mEnd - 1];
+ return (sz.mPosition + sz.mBase) - startPos;
+}
+
+void
+nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
+ const Tracks& aTracks, nscoord aGridOrigin,
+ nscoord* aPos, nscoord* aLength) const
+{
+ // kAutoLine for abspos children contributes the corresponding edge
+ // of the grid container's padding-box.
+ if (mEnd == kAutoLine) {
+ if (mStart == kAutoLine) {
+ // done
+ } else {
+ const nscoord endPos = *aPos + *aLength;
+ auto side = mStart == aTracks.mSizes.Length() ? GridLineSide::eBeforeGridGap
+ : GridLineSide::eAfterGridGap;
+ nscoord startPos = aTracks.GridLineEdge(mStart, side);
+ *aPos = aGridOrigin + startPos;
+ *aLength = std::max(endPos - *aPos, 0);
+ }
+ } else {
+ if (mStart == kAutoLine) {
+ auto side = mEnd == 0 ? GridLineSide::eAfterGridGap
+ : GridLineSide::eBeforeGridGap;
+ nscoord endPos = aTracks.GridLineEdge(mEnd, side);
+ *aLength = std::max(aGridOrigin + endPos, 0);
+ } else {
+ nscoord pos;
+ ToPositionAndLength(aTracks.mSizes, &pos, aLength);
+ *aPos = aGridOrigin + pos;
+ }
+ }
+}
+
+LogicalRect
+nsGridContainerFrame::GridReflowInput::ContainingBlockFor(const GridArea& aArea) const
+{
+ nscoord i, b, iSize, bSize;
+ MOZ_ASSERT(aArea.mCols.Extent() > 0, "grid items cover at least one track");
+ MOZ_ASSERT(aArea.mRows.Extent() > 0, "grid items cover at least one track");
+ aArea.mCols.ToPositionAndLength(mCols.mSizes, &i, &iSize);
+ aArea.mRows.ToPositionAndLength(mRows.mSizes, &b, &bSize);
+ return LogicalRect(mWM, i, b, iSize, bSize);
+}
+
+LogicalRect
+nsGridContainerFrame::GridReflowInput::ContainingBlockForAbsPos(
+ const GridArea& aArea,
+ const LogicalPoint& aGridOrigin,
+ const LogicalRect& aGridCB) const
+{
+ nscoord i = aGridCB.IStart(mWM);
+ nscoord b = aGridCB.BStart(mWM);
+ nscoord iSize = aGridCB.ISize(mWM);
+ nscoord bSize = aGridCB.BSize(mWM);
+ aArea.mCols.ToPositionAndLengthForAbsPos(mCols, aGridOrigin.I(mWM),
+ &i, &iSize);
+ aArea.mRows.ToPositionAndLengthForAbsPos(mRows, aGridOrigin.B(mWM),
+ &b, &bSize);
+ return LogicalRect(mWM, i, b, iSize, bSize);
+}
+
+/**
+ * Return a Fragmentainer object if we have a fragmentainer frame in our
+ * ancestor chain of containing block (CB) reflow states. We'll only
+ * continue traversing the ancestor chain as long as the CBs have
+ * the same writing-mode and have overflow:visible.
+ */
+Maybe<nsGridContainerFrame::Fragmentainer>
+nsGridContainerFrame::GetNearestFragmentainer(const GridReflowInput& aState) const
+{
+ Maybe<nsGridContainerFrame::Fragmentainer> data;
+ const ReflowInput* gridRI = aState.mReflowInput;
+ if (gridRI->AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
+ return data;
+ }
+ WritingMode wm = aState.mWM;
+ const ReflowInput* cbRI = gridRI->mCBReflowInput;
+ for ( ; cbRI; cbRI = cbRI->mCBReflowInput) {
+ nsIScrollableFrame* sf = do_QueryFrame(cbRI->mFrame);
+ if (sf) {
+ break;
+ }
+ if (wm.IsOrthogonalTo(cbRI->GetWritingMode())) {
+ break;
+ }
+ nsIAtom* frameType = cbRI->mFrame->GetType();
+ if ((frameType == nsGkAtoms::canvasFrame &&
+ PresContext()->IsPaginated()) ||
+ frameType == nsGkAtoms::columnSetFrame) {
+ data.emplace();
+ data->mIsTopOfPage = gridRI->mFlags.mIsTopOfPage;
+ data->mToFragmentainerEnd = aState.mFragBStart +
+ gridRI->AvailableBSize() - aState.mBorderPadding.BStart(wm);
+ const auto numRows = aState.mRows.mSizes.Length();
+ data->mCanBreakAtStart =
+ numRows > 0 && aState.mRows.mSizes[0].mPosition > 0;
+ nscoord bSize = gridRI->ComputedBSize();
+ data->mIsAutoBSize = bSize == NS_AUTOHEIGHT;
+ if (data->mIsAutoBSize) {
+ bSize = gridRI->ComputedMinBSize();
+ } else {
+ bSize = NS_CSS_MINMAX(bSize,
+ gridRI->ComputedMinBSize(),
+ gridRI->ComputedMaxBSize());
+ }
+ nscoord gridEnd =
+ aState.mRows.GridLineEdge(numRows, GridLineSide::eBeforeGridGap);
+ data->mCanBreakAtEnd = bSize > gridEnd &&
+ bSize > aState.mFragBStart;
+ break;
+ }
+ }
+ return data;
+}
+
+void
+nsGridContainerFrame::ReflowInFlowChild(nsIFrame* aChild,
+ const GridItemInfo* aGridItemInfo,
+ nsSize aContainerSize,
+ Maybe<nscoord> aStretchBSize,
+ const Fragmentainer* aFragmentainer,
+ const GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus)
+{
+ nsPresContext* pc = PresContext();
+ nsStyleContext* containerSC = StyleContext();
+ WritingMode wm = aState.mReflowInput->GetWritingMode();
+ LogicalMargin pad(aState.mReflowInput->ComputedLogicalPadding());
+ const LogicalPoint padStart(wm, pad.IStart(wm), pad.BStart(wm));
+ const bool isGridItem = !!aGridItemInfo;
+ auto childType = aChild->GetType();
+ MOZ_ASSERT(isGridItem == (childType != nsGkAtoms::placeholderFrame));
+ LogicalRect cb(wm);
+ WritingMode childWM = aChild->GetWritingMode();
+ bool isConstrainedBSize = false;
+ nscoord toFragmentainerEnd;
+ // The part of the child's grid area that's in previous container fragments.
+ nscoord consumedGridAreaBSize = 0;
+ const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
+ if (MOZ_LIKELY(isGridItem)) {
+ MOZ_ASSERT(aGridItemInfo->mFrame == aChild);
+ const GridArea& area = aGridItemInfo->mArea;
+ MOZ_ASSERT(area.IsDefinite());
+ cb = aState.ContainingBlockFor(area);
+ isConstrainedBSize = aFragmentainer && !wm.IsOrthogonalTo(childWM);
+ if (isConstrainedBSize) {
+ // |gridAreaBOffset| is the offset of the child's grid area in this
+ // container fragment (if negative, that distance is the child CB size
+ // consumed in previous container fragments). Note that cb.BStart
+ // (initially) and aState.mFragBStart are in "global" grid coordinates
+ // (like all track positions).
+ nscoord gridAreaBOffset = cb.BStart(wm) - aState.mFragBStart;
+ consumedGridAreaBSize = std::max(0, -gridAreaBOffset);
+ cb.BStart(wm) = std::max(0, gridAreaBOffset);
+ toFragmentainerEnd = aFragmentainer->mToFragmentainerEnd -
+ aState.mFragBStart - cb.BStart(wm);
+ toFragmentainerEnd = std::max(toFragmentainerEnd, 0);
+ }
+ cb += aContentArea.Origin(wm);
+ aState.mRows.AlignBaselineSubtree(*aGridItemInfo);
+ aState.mCols.AlignBaselineSubtree(*aGridItemInfo);
+ // Setup [align|justify]-content:[last ]baseline related frame properties.
+ // These are added to the padding in SizeComputationInput::InitOffsets.
+ // (a negative value signals the value is for 'last baseline' and should be
+ // added to the (logical) end padding)
+ typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
+ auto SetProp = [aGridItemInfo, aChild] (LogicalAxis aGridAxis,
+ Prop aProp) {
+ auto state = aGridItemInfo->mState[aGridAxis];
+ auto baselineAdjust = (state & ItemState::eContentBaseline) ?
+ aGridItemInfo->mBaselineOffset[aGridAxis] : nscoord(0);
+ if (baselineAdjust < nscoord(0)) {
+ // This happens when the subtree overflows its track.
+ // XXX spec issue? it's unclear how to handle this.
+ baselineAdjust = nscoord(0);
+ } else if (baselineAdjust > nscoord(0) &&
+ (state & ItemState::eLastBaseline)) {
+ baselineAdjust = -baselineAdjust;
+ }
+ if (baselineAdjust != nscoord(0)) {
+ aChild->Properties().Set(aProp, baselineAdjust);
+ } else {
+ aChild->Properties().Delete(aProp);
+ }
+ };
+ SetProp(eLogicalAxisBlock, isOrthogonal ? IBaselinePadProperty() :
+ BBaselinePadProperty());
+ SetProp(eLogicalAxisInline, isOrthogonal ? BBaselinePadProperty() :
+ IBaselinePadProperty());
+ } else {
+ // By convention, for frames that perform CSS Box Alignment, we position
+ // placeholder children at the start corner of their alignment container,
+ // and in this case that's usually the grid's padding box.
+ // ("Usually" - the exception is when the grid *also* forms the
+ // abs.pos. containing block. In that case, the alignment container isn't
+ // the padding box -- it's some grid area instead. But that case doesn't
+ // require any special handling here, because we handle it later using a
+ // special flag (STATIC_POS_IS_CB_ORIGIN) which will make us ignore the
+ // placeholder's position entirely.)
+ cb = aContentArea - padStart;
+ aChild->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
+ }
+
+ LogicalSize reflowSize(cb.Size(wm));
+ if (isConstrainedBSize) {
+ reflowSize.BSize(wm) = toFragmentainerEnd;
+ }
+ LogicalSize childCBSize = reflowSize.ConvertTo(childWM, wm);
+
+ // Setup the ClampMarginBoxMinSize reflow flags and property, if needed.
+ uint32_t flags = 0;
+ if (aGridItemInfo) {
+ auto childIAxis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
+ if (aGridItemInfo->mState[childIAxis] & ItemState::eClampMarginBoxMinSize) {
+ flags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE;
+ }
+ auto childBAxis = GetOrthogonalAxis(childIAxis);
+ if (aGridItemInfo->mState[childBAxis] & ItemState::eClampMarginBoxMinSize) {
+ flags |= ReflowInput::B_CLAMP_MARGIN_BOX_MIN_SIZE;
+ aChild->Properties().Set(BClampMarginBoxMinSizeProperty(),
+ childCBSize.BSize(childWM));
+ } else {
+ aChild->Properties().Delete(BClampMarginBoxMinSizeProperty());
+ }
+ }
+
+ if (!isConstrainedBSize) {
+ childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
+ }
+ LogicalSize percentBasis(cb.Size(wm).ConvertTo(childWM, wm));
+ ReflowInput childRI(pc, *aState.mReflowInput, aChild, childCBSize,
+ &percentBasis, flags);
+ childRI.mFlags.mIsTopOfPage = aFragmentainer ? aFragmentainer->mIsTopOfPage : false;
+
+ // A table-wrapper needs to propagate the CB size we give it to its
+ // inner table frame later. @see nsTableWrapperFrame::InitChildReflowInput.
+ if (childType == nsGkAtoms::tableWrapperFrame) {
+ const auto& props = aChild->Properties();
+ LogicalSize* cb = props.Get(nsTableWrapperFrame::GridItemCBSizeProperty());
+ if (!cb) {
+ cb = new LogicalSize(childWM);
+ props.Set(nsTableWrapperFrame::GridItemCBSizeProperty(), cb);
+ }
+ *cb = percentBasis;
+ }
+
+ // If the child is stretching in its block axis, and we might be fragmenting
+ // it in that axis, then setup a frame property to tell
+ // nsBlockFrame::ComputeFinalSize the size.
+ if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
+ bool stretch = false;
+ if (!childRI.mStyleMargin->HasBlockAxisAuto(childWM) &&
+ childRI.mStylePosition->BSize(childWM).GetUnit() == eStyleUnit_Auto) {
+ auto blockAxisAlignment =
+ childRI.mStylePosition->UsedAlignSelf(StyleContext());
+ if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
+ blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
+ stretch = true;
+ }
+ }
+ if (stretch) {
+ aChild->Properties().Set(FragStretchBSizeProperty(), *aStretchBSize);
+ } else {
+ aChild->Properties().Delete(FragStretchBSizeProperty());
+ }
+ }
+
+ // We need the width of the child before we can correctly convert
+ // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
+ // aContainerSize, and then pass the correct position to FinishReflowChild.
+ ReflowOutput childSize(childRI);
+ const nsSize dummyContainerSize;
+ ReflowChild(aChild, pc, childSize, childRI, childWM, LogicalPoint(childWM),
+ dummyContainerSize, 0, aStatus);
+ LogicalPoint childPos =
+ cb.Origin(wm).ConvertTo(childWM, wm,
+ aContainerSize - childSize.PhysicalSize());
+ // Apply align/justify-self and reflow again if that affects the size.
+ if (MOZ_LIKELY(isGridItem)) {
+ LogicalSize size = childSize.Size(childWM); // from the ReflowChild()
+ if (NS_FRAME_IS_COMPLETE(aStatus)) {
+ auto align = childRI.mStylePosition->UsedAlignSelf(containerSC);
+ auto state = aGridItemInfo->mState[eLogicalAxisBlock];
+ if (state & ItemState::eContentBaseline) {
+ align = (state & ItemState::eFirstBaseline) ? NS_STYLE_ALIGN_SELF_START
+ : NS_STYLE_ALIGN_SELF_END;
+ }
+ nscoord cbsz = cb.BSize(wm) - consumedGridAreaBSize;
+ AlignSelf(*aGridItemInfo, align, cbsz, wm, childRI, size, &childPos);
+ }
+ auto justify = childRI.mStylePosition->UsedJustifySelf(containerSC);
+ auto state = aGridItemInfo->mState[eLogicalAxisInline];
+ if (state & ItemState::eContentBaseline) {
+ justify = (state & ItemState::eFirstBaseline) ? NS_STYLE_JUSTIFY_SELF_START
+ : NS_STYLE_JUSTIFY_SELF_END;
+ }
+ nscoord cbsz = cb.ISize(wm);
+ JustifySelf(*aGridItemInfo, justify, cbsz, wm, childRI, size, &childPos);
+ } // else, nsAbsoluteContainingBlock.cpp will handle align/justify-self.
+
+ childRI.ApplyRelativePositioning(&childPos, aContainerSize);
+ FinishReflowChild(aChild, pc, childSize, &childRI, childWM, childPos,
+ aContainerSize, 0);
+ ConsiderChildOverflow(aDesiredSize.mOverflowAreas, aChild);
+}
+
+nscoord
+nsGridContainerFrame::ReflowInFragmentainer(GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus,
+ Fragmentainer& aFragmentainer,
+ const nsSize& aContainerSize)
+{
+ MOZ_ASSERT(aStatus == NS_FRAME_COMPLETE);
+ MOZ_ASSERT(aState.mReflowInput);
+
+ // Collect our grid items and sort them in row order. Collect placeholders
+ // and put them in a separate array.
+ nsTArray<const GridItemInfo*> sortedItems(aState.mGridItems.Length());
+ nsTArray<nsIFrame*> placeholders(aState.mAbsPosItems.Length());
+ aState.mIter.Reset(GridItemCSSOrderIterator::eIncludeAll);
+ for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
+ nsIFrame* child = *aState.mIter;
+ if (child->GetType() != nsGkAtoms::placeholderFrame) {
+ const GridItemInfo* info = &aState.mGridItems[aState.mIter.GridItemIndex()];
+ sortedItems.AppendElement(info);
+ } else {
+ placeholders.AppendElement(child);
+ }
+ }
+ // NOTE: no need to use stable_sort here, there are no dependencies on
+ // having content order between items on the same row in the code below.
+ std::sort(sortedItems.begin(), sortedItems.end(),
+ GridItemInfo::IsStartRowLessThan);
+
+ // Reflow our placeholder children; they must all be complete.
+ for (auto child : placeholders) {
+ nsReflowStatus childStatus;
+ ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), &aFragmentainer,
+ aState, aContentArea, aDesiredSize, childStatus);
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childStatus),
+ "nsPlaceholderFrame should never need to be fragmented");
+ }
+
+ // The available size for children - we'll set this to the edge of the last
+ // row in most cases below, but for now use the full size.
+ nscoord childAvailableSize = aFragmentainer.mToFragmentainerEnd;
+ const uint32_t startRow = aState.mStartRow;
+ const uint32_t numRows = aState.mRows.mSizes.Length();
+ bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone;
+ nscoord bpBEnd = aState.mBorderPadding.BEnd(aState.mWM);
+
+ // Set |endRow| to the first row that doesn't fit.
+ uint32_t endRow = numRows;
+ for (uint32_t row = startRow; row < numRows; ++row) {
+ auto& sz = aState.mRows.mSizes[row];
+ const nscoord bEnd = sz.mPosition + sz.mBase;
+ nscoord remainingAvailableSize = childAvailableSize - bEnd;
+ if (remainingAvailableSize < 0 ||
+ (isBDBClone && remainingAvailableSize < bpBEnd)) {
+ endRow = row;
+ break;
+ }
+ }
+
+ // Check for forced breaks on the items.
+ const bool isTopOfPage = aFragmentainer.mIsTopOfPage;
+ bool isForcedBreak = false;
+ const bool avoidBreakInside = ShouldAvoidBreakInside(*aState.mReflowInput);
+ for (const GridItemInfo* info : sortedItems) {
+ uint32_t itemStartRow = info->mArea.mRows.mStart;
+ if (itemStartRow == endRow) {
+ break;
+ }
+ auto disp = info->mFrame->StyleDisplay();
+ if (disp->mBreakBefore) {
+ // Propagate break-before on the first row to the container unless we're
+ // already at top-of-page.
+ if ((itemStartRow == 0 && !isTopOfPage) || avoidBreakInside) {
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return aState.mFragBStart;
+ }
+ if ((itemStartRow > startRow ||
+ (itemStartRow == startRow && !isTopOfPage)) &&
+ itemStartRow < endRow) {
+ endRow = itemStartRow;
+ isForcedBreak = true;
+ // reset any BREAK_AFTER we found on an earlier item
+ aStatus = NS_FRAME_COMPLETE;
+ break; // we're done since the items are sorted in row order
+ }
+ }
+ uint32_t itemEndRow = info->mArea.mRows.mEnd;
+ if (disp->mBreakAfter) {
+ if (itemEndRow != numRows) {
+ if (itemEndRow > startRow && itemEndRow < endRow) {
+ endRow = itemEndRow;
+ isForcedBreak = true;
+ // No "break;" here since later items with break-after may have
+ // a shorter span.
+ }
+ } else {
+ // Propagate break-after on the last row to the container, we may still
+ // find a break-before on this row though (and reset aStatus).
+ aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); // tentative
+ }
+ }
+ }
+
+ // Consume at least one row in each fragment until we have consumed them all.
+ // Except for the first row if there's a break opportunity before it.
+ if (startRow == endRow && startRow != numRows &&
+ (startRow != 0 || !aFragmentainer.mCanBreakAtStart)) {
+ ++endRow;
+ }
+
+ // Honor break-inside:avoid if we can't fit all rows.
+ if (avoidBreakInside && endRow < numRows) {
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return aState.mFragBStart;
+ }
+
+ // Calculate the block-size including this fragment.
+ nscoord bEndRow =
+ aState.mRows.GridLineEdge(endRow, GridLineSide::eBeforeGridGap);
+ nscoord bSize;
+ if (aFragmentainer.mIsAutoBSize) {
+ // We only apply min-bsize once all rows are complete (when bsize is auto).
+ if (endRow < numRows) {
+ bSize = bEndRow;
+ auto clampedBSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
+ if (MOZ_UNLIKELY(clampedBSize != bSize)) {
+ // We apply max-bsize in all fragments though.
+ bSize = clampedBSize;
+ } else if (!isBDBClone) {
+ // The max-bsize won't make this fragment COMPLETE, so the block-end
+ // border will be in a later fragment.
+ bpBEnd = 0;
+ }
+ } else {
+ bSize = NS_CSS_MINMAX(bEndRow,
+ aState.mReflowInput->ComputedMinBSize(),
+ aState.mReflowInput->ComputedMaxBSize());
+ }
+ } else {
+ bSize = NS_CSS_MINMAX(aState.mReflowInput->ComputedBSize(),
+ aState.mReflowInput->ComputedMinBSize(),
+ aState.mReflowInput->ComputedMaxBSize());
+ }
+
+ // Check for overflow and set aStatus INCOMPLETE if so.
+ bool overflow = bSize + bpBEnd > childAvailableSize;
+ if (overflow) {
+ if (avoidBreakInside) {
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return aState.mFragBStart;
+ }
+ bool breakAfterLastRow = endRow == numRows && aFragmentainer.mCanBreakAtEnd;
+ if (breakAfterLastRow) {
+ MOZ_ASSERT(bEndRow < bSize, "bogus aFragmentainer.mCanBreakAtEnd");
+ nscoord availableSize = childAvailableSize;
+ if (isBDBClone) {
+ availableSize -= bpBEnd;
+ }
+ // Pretend we have at least 1px available size, otherwise we'll never make
+ // progress in consuming our bSize.
+ availableSize = std::max(availableSize,
+ aState.mFragBStart + AppUnitsPerCSSPixel());
+ // Fill the fragmentainer, but not more than our desired block-size and
+ // at least to the size of the last row (even if that overflows).
+ nscoord newBSize = std::min(bSize, availableSize);
+ newBSize = std::max(newBSize, bEndRow);
+ // If it's just the border+padding that is overflowing and we have
+ // box-decoration-break:clone then we are technically COMPLETE. There's
+ // no point in creating another zero-bsize fragment in this case.
+ if (newBSize < bSize || !isBDBClone) {
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ }
+ bSize = newBSize;
+ } else if (bSize <= bEndRow && startRow + 1 < endRow) {
+ if (endRow == numRows) {
+ // We have more than one row in this fragment, so we can break before
+ // the last row instead.
+ --endRow;
+ bEndRow = aState.mRows.GridLineEdge(endRow, GridLineSide::eBeforeGridGap);
+ bSize = bEndRow;
+ if (aFragmentainer.mIsAutoBSize) {
+ bSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
+ }
+ }
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ } else if (endRow < numRows) {
+ bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
+ } // else - no break opportunities.
+ } else {
+ // Even though our block-size fits we need to honor forced breaks, or if
+ // a row doesn't fit in an auto-sized container (unless it's constrained
+ // by a max-bsize which make us overflow-incomplete).
+ if (endRow < numRows && (isForcedBreak ||
+ (aFragmentainer.mIsAutoBSize && bEndRow == bSize))) {
+ bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
+ }
+ }
+
+ // If we can't fit all rows then we're at least overflow-incomplete.
+ if (endRow < numRows) {
+ childAvailableSize = bEndRow;
+ if (NS_FRAME_IS_COMPLETE(aStatus)) {
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(aStatus);
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+ } else {
+ // Children always have the full size of the rows in this fragment.
+ childAvailableSize = std::max(childAvailableSize, bEndRow);
+ }
+
+ return ReflowRowsInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
+ aFragmentainer, aContainerSize, sortedItems,
+ startRow, endRow, bSize, childAvailableSize);
+}
+
+nscoord
+nsGridContainerFrame::ReflowRowsInFragmentainer(
+ GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus,
+ Fragmentainer& aFragmentainer,
+ const nsSize& aContainerSize,
+ const nsTArray<const GridItemInfo*>& aSortedItems,
+ uint32_t aStartRow,
+ uint32_t aEndRow,
+ nscoord aBSize,
+ nscoord aAvailableSize)
+{
+ FrameHashtable pushedItems;
+ FrameHashtable incompleteItems;
+ FrameHashtable overflowIncompleteItems;
+ bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone;
+ bool didGrowRow = false;
+ // As we walk across rows, we track whether the current row is at the top
+ // of its grid-fragment, to help decide whether we can break before it. When
+ // this function starts, our row is at the top of the current fragment if:
+ // - we're starting with a nonzero row (i.e. we're a continuation)
+ // OR:
+ // - we're starting with the first row, & we're not allowed to break before
+ // it (which makes it effectively at the top of its grid-fragment).
+ bool isRowTopOfPage = aStartRow != 0 || !aFragmentainer.mCanBreakAtStart;
+ const bool isStartRowTopOfPage = isRowTopOfPage;
+ // Save our full available size for later.
+ const nscoord gridAvailableSize = aFragmentainer.mToFragmentainerEnd;
+ // Propagate the constrained size to our children.
+ aFragmentainer.mToFragmentainerEnd = aAvailableSize;
+ // Reflow the items in row order up to |aEndRow| and push items after that.
+ uint32_t row = 0;
+ // |i| is intentionally signed, so we can set it to -1 to restart the loop.
+ for (int32_t i = 0, len = aSortedItems.Length(); i < len; ++i) {
+ const GridItemInfo* const info = aSortedItems[i];
+ nsIFrame* child = info->mFrame;
+ row = info->mArea.mRows.mStart;
+ MOZ_ASSERT(child->GetPrevInFlow() ? row < aStartRow : row >= aStartRow,
+ "unexpected child start row");
+ if (row >= aEndRow) {
+ pushedItems.PutEntry(child);
+ continue;
+ }
+
+ bool rowCanGrow = false;
+ nscoord maxRowSize = 0;
+ if (row >= aStartRow) {
+ if (row > aStartRow) {
+ isRowTopOfPage = false;
+ }
+ // Can we grow this row? Only consider span=1 items per spec...
+ rowCanGrow = !didGrowRow && info->mArea.mRows.Extent() == 1;
+ if (rowCanGrow) {
+ auto& sz = aState.mRows.mSizes[row];
+ // and only min-/max-content rows or flex rows in an auto-sized container
+ rowCanGrow = (sz.mState & TrackSize::eMinOrMaxContentMinSizing) ||
+ ((sz.mState & TrackSize::eFlexMaxSizing) &&
+ aFragmentainer.mIsAutoBSize);
+ if (rowCanGrow) {
+ if (isBDBClone) {
+ maxRowSize = gridAvailableSize -
+ aState.mBorderPadding.BEnd(aState.mWM);
+ } else {
+ maxRowSize = gridAvailableSize;
+ }
+ maxRowSize -= sz.mPosition;
+ // ...and only if there is space for it to grow.
+ rowCanGrow = maxRowSize > sz.mBase;
+ }
+ }
+ }
+
+ // aFragmentainer.mIsTopOfPage is propagated to the child reflow state.
+ // When it's false the child can request BREAK_BEFORE. We intentionally
+ // set it to false when the row is growable (as determined in CSS Grid
+ // Fragmentation) and there is a non-zero space between it and the
+ // fragmentainer end (that can be used to grow it). If the child reports
+ // a forced break in this case, we grow this row to fill the fragment and
+ // restart the loop. We also restart the loop with |aEndRow = row|
+ // (but without growing any row) for a BREAK_BEFORE child if it spans
+ // beyond the last row in this fragment. This is to avoid fragmenting it.
+ // We only restart the loop once.
+ aFragmentainer.mIsTopOfPage = isRowTopOfPage && !rowCanGrow;
+ nsReflowStatus childStatus;
+ // Pass along how much to stretch this fragment, in case it's needed.
+ nscoord bSize =
+ aState.mRows.GridLineEdge(std::min(aEndRow, info->mArea.mRows.mEnd),
+ GridLineSide::eBeforeGridGap) -
+ aState.mRows.GridLineEdge(std::max(aStartRow, row),
+ GridLineSide::eAfterGridGap);
+ ReflowInFlowChild(child, info, aContainerSize, Some(bSize), &aFragmentainer,
+ aState, aContentArea, aDesiredSize, childStatus);
+ MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(childStatus) ||
+ !NS_FRAME_IS_FULLY_COMPLETE(childStatus) ||
+ !child->GetNextInFlow(),
+ "fully-complete reflow should destroy any NIFs");
+
+ if (NS_INLINE_IS_BREAK_BEFORE(childStatus)) {
+ MOZ_ASSERT(!child->GetPrevInFlow(),
+ "continuations should never report BREAK_BEFORE status");
+ MOZ_ASSERT(!aFragmentainer.mIsTopOfPage,
+ "got NS_INLINE_IS_BREAK_BEFORE at top of page");
+ if (!didGrowRow) {
+ if (rowCanGrow) {
+ // Grow this row and restart with the next row as |aEndRow|.
+ aState.mRows.ResizeRow(row, maxRowSize);
+ if (aState.mSharedGridData) {
+ aState.mSharedGridData->mRows.ResizeRow(row, maxRowSize);
+ }
+ didGrowRow = true;
+ aEndRow = row + 1; // growing this row makes the next one not fit
+ i = -1; // i == 0 after the next loop increment
+ isRowTopOfPage = isStartRowTopOfPage;
+ overflowIncompleteItems.Clear();
+ incompleteItems.Clear();
+ nscoord bEndRow =
+ aState.mRows.GridLineEdge(aEndRow, GridLineSide::eBeforeGridGap);
+ aFragmentainer.mToFragmentainerEnd = bEndRow;
+ if (aFragmentainer.mIsAutoBSize) {
+ aBSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
+ } else if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
+ aBSize = NS_CSS_MINMAX(aState.mReflowInput->ComputedBSize(),
+ aState.mReflowInput->ComputedMinBSize(),
+ aState.mReflowInput->ComputedMaxBSize());
+ aBSize = std::min(bEndRow, aBSize);
+ }
+ continue;
+ }
+
+ if (!isRowTopOfPage) {
+ // We can break before this row - restart with it as the new end row.
+ aEndRow = row;
+ aBSize = aState.mRows.GridLineEdge(aEndRow, GridLineSide::eBeforeGridGap);
+ i = -1; // i == 0 after the next loop increment
+ isRowTopOfPage = isStartRowTopOfPage;
+ overflowIncompleteItems.Clear();
+ incompleteItems.Clear();
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ continue;
+ }
+ NS_ERROR("got BREAK_BEFORE at top-of-page");
+ childStatus = NS_FRAME_COMPLETE;
+ } else {
+ NS_ERROR("got BREAK_BEFORE again after growing the row?");
+ NS_FRAME_SET_INCOMPLETE(childStatus);
+ }
+ } else if (NS_INLINE_IS_BREAK_AFTER(childStatus)) {
+ MOZ_ASSERT_UNREACHABLE("unexpected child reflow status");
+ }
+
+ if (NS_FRAME_IS_NOT_COMPLETE(childStatus)) {
+ incompleteItems.PutEntry(child);
+ } else if (!NS_FRAME_IS_FULLY_COMPLETE(childStatus)) {
+ overflowIncompleteItems.PutEntry(child);
+ }
+ }
+
+ // Record a break before |aEndRow|.
+ aState.mNextFragmentStartRow = aEndRow;
+ if (aEndRow < aState.mRows.mSizes.Length()) {
+ aState.mRows.BreakBeforeRow(aEndRow);
+ if (aState.mSharedGridData) {
+ aState.mSharedGridData->mRows.BreakBeforeRow(aEndRow);
+ }
+ }
+
+ if (!pushedItems.IsEmpty() ||
+ !incompleteItems.IsEmpty() ||
+ !overflowIncompleteItems.IsEmpty()) {
+ if (NS_FRAME_IS_COMPLETE(aStatus)) {
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(aStatus);
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+ // Iterate the children in normal document order and append them (or a NIF)
+ // to one of the following frame lists according to their status.
+ nsFrameList pushedList;
+ nsFrameList incompleteList;
+ nsFrameList overflowIncompleteList;
+ auto* pc = PresContext();
+ auto* fc = pc->PresShell()->FrameConstructor();
+ for (nsIFrame* child = GetChildList(kPrincipalList).FirstChild(); child; ) {
+ MOZ_ASSERT((pushedItems.Contains(child) ? 1 : 0) +
+ (incompleteItems.Contains(child) ? 1 : 0) +
+ (overflowIncompleteItems.Contains(child) ? 1 : 0) <= 1,
+ "child should only be in one of these sets");
+ // Save the next-sibling so we can continue the loop if |child| is moved.
+ nsIFrame* next = child->GetNextSibling();
+ if (pushedItems.Contains(child)) {
+ MOZ_ASSERT(child->GetParent() == this);
+ StealFrame(child);
+ pushedList.AppendFrame(nullptr, child);
+ } else if (incompleteItems.Contains(child)) {
+ nsIFrame* childNIF = child->GetNextInFlow();
+ if (!childNIF) {
+ childNIF = fc->CreateContinuingFrame(pc, child, this);
+ incompleteList.AppendFrame(nullptr, childNIF);
+ } else {
+ auto parent = static_cast<nsGridContainerFrame*>(childNIF->GetParent());
+ MOZ_ASSERT(parent != this || !mFrames.ContainsFrame(childNIF),
+ "child's NIF shouldn't be in the same principal list");
+ // If child's existing NIF is an overflow container, convert it to an
+ // actual NIF, since now |child| has non-overflow stuff to give it.
+ // Or, if it's further away then our next-in-flow, then pull it up.
+ if ((childNIF->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
+ (parent != this && parent != GetNextInFlow())) {
+ parent->StealFrame(childNIF);
+ childNIF->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ if (parent == this) {
+ incompleteList.AppendFrame(nullptr, childNIF);
+ } else {
+ // If childNIF already lives on the next grid fragment, then we
+ // don't need to reparent it, since we know it's destined to end
+ // up there anyway. Just move it to its parent's overflow list.
+ if (parent == GetNextInFlow()) {
+ nsFrameList toMove(childNIF, childNIF);
+ parent->MergeSortedOverflow(toMove);
+ } else {
+ ReparentFrame(childNIF, parent, this);
+ incompleteList.AppendFrame(nullptr, childNIF);
+ }
+ }
+ }
+ }
+ } else if (overflowIncompleteItems.Contains(child)) {
+ nsIFrame* childNIF = child->GetNextInFlow();
+ if (!childNIF) {
+ childNIF = fc->CreateContinuingFrame(pc, child, this);
+ childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ overflowIncompleteList.AppendFrame(nullptr, childNIF);
+ } else {
+ DebugOnly<nsGridContainerFrame*> lastParent = this;
+ auto nif = static_cast<nsGridContainerFrame*>(GetNextInFlow());
+ // If child has any non-overflow-container NIFs, convert them to
+ // overflow containers, since that's all |child| needs now.
+ while (childNIF &&
+ !childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ auto parent = static_cast<nsGridContainerFrame*>(childNIF->GetParent());
+ parent->StealFrame(childNIF);
+ childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ if (parent == this) {
+ overflowIncompleteList.AppendFrame(nullptr, childNIF);
+ } else {
+ if (!nif || parent == nif) {
+ nsFrameList toMove(childNIF, childNIF);
+ parent->MergeSortedExcessOverflowContainers(toMove);
+ } else {
+ ReparentFrame(childNIF, parent, nif);
+ nsFrameList toMove(childNIF, childNIF);
+ nif->MergeSortedExcessOverflowContainers(toMove);
+ }
+ // We only need to reparent the first childNIF (or not at all if
+ // its parent is our NIF).
+ nif = nullptr;
+ }
+ lastParent = parent;
+ childNIF = childNIF->GetNextInFlow();
+ }
+ }
+ }
+ child = next;
+ }
+
+ // Merge the results into our respective overflow child lists.
+ if (!pushedList.IsEmpty()) {
+ MergeSortedOverflow(pushedList);
+ AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
+ // NOTE since we messed with our child list here, we intentionally
+ // make aState.mIter invalid to avoid any use of it after this point.
+ aState.mIter.Invalidate();
+ }
+ if (!incompleteList.IsEmpty()) {
+ MergeSortedOverflow(incompleteList);
+ // NOTE since we messed with our child list here, we intentionally
+ // make aState.mIter invalid to avoid any use of it after this point.
+ aState.mIter.Invalidate();
+ }
+ if (!overflowIncompleteList.IsEmpty()) {
+ MergeSortedExcessOverflowContainers(overflowIncompleteList);
+ }
+ }
+ return aBSize;
+}
+
+nscoord
+nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus)
+{
+ MOZ_ASSERT(aState.mReflowInput);
+
+ aStatus = NS_FRAME_COMPLETE;
+ nsOverflowAreas ocBounds;
+ nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
+ if (GetPrevInFlow()) {
+ ReflowOverflowContainerChildren(PresContext(), *aState.mReflowInput,
+ ocBounds, 0, ocStatus,
+ MergeSortedFrameListsFor);
+ }
+
+ WritingMode wm = aState.mReflowInput->GetWritingMode();
+ const nsSize containerSize =
+ (aContentArea.Size(wm) + aState.mBorderPadding.Size(wm)).GetPhysicalSize(wm);
+
+ nscoord bSize = aContentArea.BSize(wm);
+ Maybe<Fragmentainer> fragmentainer = GetNearestFragmentainer(aState);
+ if (MOZ_UNLIKELY(fragmentainer.isSome())) {
+ aState.mInFragmentainer = true;
+ bSize = ReflowInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
+ *fragmentainer, containerSize);
+ } else {
+ aState.mIter.Reset(GridItemCSSOrderIterator::eIncludeAll);
+ for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
+ nsIFrame* child = *aState.mIter;
+ const GridItemInfo* info = nullptr;
+ if (child->GetType() != nsGkAtoms::placeholderFrame) {
+ info = &aState.mGridItems[aState.mIter.GridItemIndex()];
+ }
+ ReflowInFlowChild(*aState.mIter, info, containerSize, Nothing(), nullptr,
+ aState, aContentArea, aDesiredSize, aStatus);
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(aStatus), "child should be complete "
+ "in unconstrained reflow");
+ }
+ }
+
+ // Merge overflow container bounds and status.
+ aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
+ NS_MergeReflowStatusInto(&aStatus, ocStatus);
+
+ if (IsAbsoluteContainer()) {
+ nsFrameList children(GetChildList(GetAbsoluteListID()));
+ if (!children.IsEmpty()) {
+ // 'gridOrigin' is the origin of the grid (the start of the first track),
+ // with respect to the grid container's padding-box (CB).
+ LogicalMargin pad(aState.mReflowInput->ComputedLogicalPadding());
+ const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
+ const LogicalRect gridCB(wm, 0, 0,
+ aContentArea.ISize(wm) + pad.IStartEnd(wm),
+ bSize + pad.BStartEnd(wm));
+ const nsSize gridCBPhysicalSize = gridCB.Size(wm).GetPhysicalSize(wm);
+ size_t i = 0;
+ for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
+ nsIFrame* child = e.get();
+ MOZ_ASSERT(i < aState.mAbsPosItems.Length());
+ MOZ_ASSERT(aState.mAbsPosItems[i].mFrame == child);
+ GridArea& area = aState.mAbsPosItems[i].mArea;
+ LogicalRect itemCB =
+ aState.ContainingBlockForAbsPos(area, gridOrigin, gridCB);
+ // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
+ nsRect* cb = child->Properties().Get(GridItemContainingBlockRect());
+ if (!cb) {
+ cb = new nsRect;
+ child->Properties().Set(GridItemContainingBlockRect(), cb);
+ }
+ *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize);
+ }
+ // We pass a dummy rect as CB because each child has its own CB rect.
+ // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to
+ // use those instead.
+ nsRect dummyRect;
+ AbsPosReflowFlags flags =
+ AbsPosReflowFlags::eCBWidthAndHeightChanged; // XXX could be optimized
+ flags |= AbsPosReflowFlags::eConstrainHeight;
+ flags |= AbsPosReflowFlags::eIsGridContainerCB;
+ GetAbsoluteContainingBlock()->Reflow(this, PresContext(),
+ *aState.mReflowInput,
+ aStatus, dummyRect, flags,
+ &aDesiredSize.mOverflowAreas);
+ }
+ }
+ return bSize;
+}
+
+void
+nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
+ return;
+ }
+
+ // First we gather child frames we should include in our reflow,
+ // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow
+ // children (that might now fit). It's important to note that these children
+ // can be in arbitrary order vis-a-vis the current children in our lists.
+ // E.g. grid items in the document order: A, B, C may be placed in the rows
+ // 3, 2, 1. Assume each row goes in a separate grid container fragment,
+ // and we reflow the second fragment. Now if C (in fragment 1) overflows,
+ // we can't just prepend it to our mFrames like we usually do because that
+ // would violate the document order invariant that other code depends on.
+ // Similarly if we pull up child A (from fragment 3) we can't just append
+ // that for the same reason. Instead, we must sort these children into
+ // our child lists. (The sorting is trivial given that both lists are
+ // already fully sorted individually - it's just a merge.)
+ //
+ // The invariants that we maintain are that each grid container child list
+ // is sorted in the normal document order at all times, but that children
+ // in different grid container continuations may be in arbitrary order.
+
+ auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
+ // Merge overflow frames from our prev-in-flow into our principal child list.
+ if (prevInFlow) {
+ AutoFrameListPtr overflow(aPresContext,
+ prevInFlow->StealOverflowFrames());
+ if (overflow) {
+ ReparentFrames(*overflow, prevInFlow, this);
+ ::MergeSortedFrameLists(mFrames, *overflow, GetContent());
+
+ // Move trailing next-in-flows into our overflow list.
+ nsFrameList continuations;
+ for (nsIFrame* f = mFrames.FirstChild(); f; ) {
+ nsIFrame* next = f->GetNextSibling();
+ nsIFrame* pif = f->GetPrevInFlow();
+ if (pif && pif->GetParent() == this) {
+ mFrames.RemoveFrame(f);
+ continuations.AppendFrame(nullptr, f);
+ }
+ f = next;
+ }
+ MergeSortedOverflow(continuations);
+
+ // Move trailing OC next-in-flows into our excess overflow containers list.
+ nsFrameList* overflowContainers =
+ GetPropTableFrames(OverflowContainersProperty());
+ if (overflowContainers) {
+ nsFrameList moveToEOC;
+ for (nsIFrame* f = overflowContainers->FirstChild(); f; ) {
+ nsIFrame* next = f->GetNextSibling();
+ nsIFrame* pif = f->GetPrevInFlow();
+ if (pif && pif->GetParent() == this) {
+ overflowContainers->RemoveFrame(f);
+ moveToEOC.AppendFrame(nullptr, f);
+ }
+ f = next;
+ }
+ if (overflowContainers->IsEmpty()) {
+ Properties().Delete(OverflowContainersProperty());
+ }
+ MergeSortedExcessOverflowContainers(moveToEOC);
+ }
+ }
+ }
+
+ // Merge our own overflow frames into our principal child list,
+ // except those that are a next-in-flow for one of our items.
+ DebugOnly<bool> foundOwnPushedChild = false;
+ {
+ nsFrameList* ourOverflow = GetOverflowFrames();
+ if (ourOverflow) {
+ nsFrameList items;
+ for (nsIFrame* f = ourOverflow->FirstChild(); f; ) {
+ nsIFrame* next = f->GetNextSibling();
+ nsIFrame* pif = f->GetPrevInFlow();
+ if (!pif || pif->GetParent() != this) {
+ MOZ_ASSERT(f->GetParent() == this);
+ ourOverflow->RemoveFrame(f);
+ items.AppendFrame(nullptr, f);
+ if (!pif) {
+ foundOwnPushedChild = true;
+ }
+ }
+ f = next;
+ }
+ ::MergeSortedFrameLists(mFrames, items, GetContent());
+ if (ourOverflow->IsEmpty()) {
+ DestroyOverflowList();
+ }
+ }
+ }
+
+ // Pull up any first-in-flow children we might have pushed.
+ if (HasAnyStateBits(NS_STATE_GRID_DID_PUSH_ITEMS)) {
+ RemoveStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
+ nsFrameList items;
+ auto nif = static_cast<nsGridContainerFrame*>(GetNextInFlow());
+ auto firstNIF = nif;
+ DebugOnly<bool> nifNeedPushedItem = false;
+ while (nif) {
+ nsFrameList nifItems;
+ for (nsIFrame* nifChild = nif->GetChildList(kPrincipalList).FirstChild();
+ nifChild; ) {
+ nsIFrame* next = nifChild->GetNextSibling();
+ if (!nifChild->GetPrevInFlow()) {
+ nif->StealFrame(nifChild);
+ ReparentFrame(nifChild, nif, this);
+ nifItems.AppendFrame(nullptr, nifChild);
+ nifNeedPushedItem = false;
+ }
+ nifChild = next;
+ }
+ ::MergeSortedFrameLists(items, nifItems, GetContent());
+
+ if (!nif->HasAnyStateBits(NS_STATE_GRID_DID_PUSH_ITEMS)) {
+ MOZ_ASSERT(!nifNeedPushedItem || mDidPushItemsBitMayLie,
+ "NS_STATE_GRID_DID_PUSH_ITEMS lied");
+ break;
+ }
+ nifNeedPushedItem = true;
+
+ for (nsIFrame* nifChild = nif->GetChildList(kOverflowList).FirstChild();
+ nifChild; ) {
+ nsIFrame* next = nifChild->GetNextSibling();
+ if (!nifChild->GetPrevInFlow()) {
+ nif->StealFrame(nifChild);
+ ReparentFrame(nifChild, nif, this);
+ nifItems.AppendFrame(nullptr, nifChild);
+ nifNeedPushedItem = false;
+ }
+ nifChild = next;
+ }
+ ::MergeSortedFrameLists(items, nifItems, GetContent());
+
+ nif->RemoveStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
+ nif = static_cast<nsGridContainerFrame*>(nif->GetNextInFlow());
+ MOZ_ASSERT(nif || !nifNeedPushedItem || mDidPushItemsBitMayLie,
+ "NS_STATE_GRID_DID_PUSH_ITEMS lied");
+ }
+
+ if (!items.IsEmpty()) {
+ // Pull up the first next-in-flow of the pulled up items too, unless its
+ // parent is our nif (to avoid leaving a hole there).
+ nsFrameList childNIFs;
+ nsFrameList childOCNIFs;
+ for (auto child : items) {
+ auto childNIF = child->GetNextInFlow();
+ if (childNIF && childNIF->GetParent() != firstNIF) {
+ auto parent = childNIF->GetParent();
+ parent->StealFrame(childNIF);
+ ReparentFrame(childNIF, parent, firstNIF);
+ if ((childNIF->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ childOCNIFs.AppendFrame(nullptr, childNIF);
+ } else {
+ childNIFs.AppendFrame(nullptr, childNIF);
+ }
+ }
+ }
+ // Merge items' NIFs into our NIF's respective overflow child lists.
+ firstNIF->MergeSortedOverflow(childNIFs);
+ firstNIF->MergeSortedExcessOverflowContainers(childOCNIFs);
+ }
+
+ MOZ_ASSERT(foundOwnPushedChild || !items.IsEmpty() || mDidPushItemsBitMayLie,
+ "NS_STATE_GRID_DID_PUSH_ITEMS lied");
+ ::MergeSortedFrameLists(mFrames, items, GetContent());
+ }
+
+ RenumberList();
+
+#ifdef DEBUG
+ mDidPushItemsBitMayLie = false;
+ SanityCheckGridItemsBeforeReflow();
+#endif // DEBUG
+
+ mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+
+ const nsStylePosition* stylePos = aReflowInput.mStylePosition;
+ if (!prevInFlow) {
+ InitImplicitNamedAreas(stylePos);
+ }
+ GridReflowInput gridReflowInput(this, aReflowInput);
+ if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
+ AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
+ } else {
+ RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
+ }
+ if (gridReflowInput.mIter.AtEnd()) {
+ // We have no grid items, our parent should synthesize a baseline if needed.
+ AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
+ } else {
+ RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
+ }
+ const nscoord computedBSize = aReflowInput.ComputedBSize();
+ const nscoord computedISize = aReflowInput.ComputedISize();
+ const WritingMode& wm = gridReflowInput.mWM;
+ LogicalSize computedSize(wm, computedISize, computedBSize);
+
+ nscoord consumedBSize = 0;
+ nscoord bSize;
+ if (!prevInFlow) {
+ Grid grid;
+ grid.PlaceGridItems(gridReflowInput, aReflowInput.ComputedMinSize(),
+ computedSize, aReflowInput.ComputedMaxSize());
+
+ gridReflowInput.CalculateTrackSizes(grid, computedSize,
+ SizingConstraint::eNoConstraint);
+ bSize = computedSize.BSize(wm);
+ } else {
+ consumedBSize = GetConsumedBSize();
+ gridReflowInput.InitializeForContinuation(this, consumedBSize);
+ const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
+ bSize = gridReflowInput.mRows.GridLineEdge(numRows,
+ GridLineSide::eAfterGridGap);
+ }
+ if (computedBSize == NS_AUTOHEIGHT) {
+ bSize = NS_CSS_MINMAX(bSize,
+ aReflowInput.ComputedMinBSize(),
+ aReflowInput.ComputedMaxBSize());
+ } else {
+ bSize = computedBSize;
+ }
+ bSize = std::max(bSize - consumedBSize, 0);
+ auto& bp = gridReflowInput.mBorderPadding;
+ LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm),
+ computedISize, bSize);
+
+ if (!prevInFlow) {
+ // Apply 'align/justify-content' to the grid.
+ // CalculateTrackSizes did the columns.
+ gridReflowInput.mRows.AlignJustifyContent(stylePos, wm, contentArea.Size(wm));
+ }
+
+ bSize = ReflowChildren(gridReflowInput, contentArea, aDesiredSize, aStatus);
+ bSize = std::max(bSize - consumedBSize, 0);
+
+ // Skip our block-end border if we're INCOMPLETE.
+ if (!NS_FRAME_IS_COMPLETE(aStatus) &&
+ !gridReflowInput.mSkipSides.BEnd() &&
+ StyleBorder()->mBoxDecorationBreak !=
+ StyleBoxDecorationBreak::Clone) {
+ bp.BEnd(wm) = nscoord(0);
+ }
+
+ LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
+ bSize + bp.BStartEnd(wm));
+ aDesiredSize.SetSize(wm, desiredSize);
+ nsRect frameRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
+ aDesiredSize.mOverflowAreas.UnionAllWith(frameRect);
+
+ // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
+ if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
+ if (!NS_FRAME_IS_COMPLETE(aStatus)) {
+ NS_FRAME_SET_OVERFLOW_INCOMPLETE(aStatus);
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ }
+ bSize = 0;
+ desiredSize.BSize(wm) = bSize + bp.BStartEnd(wm);
+ aDesiredSize.SetSize(wm, desiredSize);
+ }
+
+ if (!gridReflowInput.mInFragmentainer) {
+ MOZ_ASSERT(gridReflowInput.mIter.IsValid());
+ auto sz = frameRect.Size();
+ CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
+ &gridReflowInput.mGridItems, gridReflowInput.mCols,
+ 0, gridReflowInput.mCols.mSizes.Length(),
+ wm, sz, bp.IStart(wm),
+ bp.IEnd(wm), desiredSize.ISize(wm));
+ CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
+ &gridReflowInput.mGridItems, gridReflowInput.mRows,
+ 0, gridReflowInput.mRows.mSizes.Length(),
+ wm, sz, bp.BStart(wm),
+ bp.BEnd(wm), desiredSize.BSize(wm));
+ } else {
+ // Only compute 'first baseline' if this fragment contains the first track.
+ // XXXmats maybe remove this condition? bug 1306499
+ BaselineSet baselines = BaselineSet::eNone;
+ if (gridReflowInput.mStartRow == 0 &&
+ gridReflowInput.mStartRow != gridReflowInput.mNextFragmentStartRow) {
+ baselines = BaselineSet::eFirst;
+ }
+ // Only compute 'last baseline' if this fragment contains the last track.
+ // XXXmats maybe remove this condition? bug 1306499
+ uint32_t len = gridReflowInput.mRows.mSizes.Length();
+ if (gridReflowInput.mStartRow != len &&
+ gridReflowInput.mNextFragmentStartRow == len) {
+ baselines = BaselineSet(baselines | BaselineSet::eLast);
+ }
+ Maybe<GridItemCSSOrderIterator> iter;
+ Maybe<nsTArray<GridItemInfo>> gridItems;
+ if (baselines != BaselineSet::eNone) {
+ // We need to create a new iterator and GridItemInfo array because we
+ // might have pushed some children at this point.
+ // Even if the gridReflowInput iterator is invalid we can reuse its
+ // state about order to optimize initialization of the new iterator.
+ // An ordered child list can't become unordered by pushing frames.
+ // An unordered list can become ordered in a number of cases, but we
+ // ignore that here and guess that the child list is still unordered.
+ // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
+ using Filter = GridItemCSSOrderIterator::ChildFilter;
+ using Order = GridItemCSSOrderIterator::OrderState;
+ bool ordered = gridReflowInput.mIter.ItemsAreAlreadyInOrder();
+ auto orderState = ordered ? Order::eKnownOrdered : Order::eKnownUnordered;
+ iter.emplace(this, kPrincipalList, Filter::eSkipPlaceholders, orderState);
+ gridItems.emplace();
+ for (; !iter->AtEnd(); iter->Next()) {
+ auto child = **iter;
+ for (const auto& info : gridReflowInput.mGridItems) {
+ if (info.mFrame == child) {
+ gridItems->AppendElement(info);
+ }
+ }
+ }
+ }
+ auto sz = frameRect.Size();
+ CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
+ gridReflowInput.mCols, 0,
+ gridReflowInput.mCols.mSizes.Length(), wm, sz,
+ bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
+ CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
+ gridReflowInput.mRows, gridReflowInput.mStartRow,
+ gridReflowInput.mNextFragmentStartRow, wm, sz,
+ bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
+ }
+
+ if (HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
+ // This state bit will never be cleared, since reflow can be called
+ // multiple times in fragmented grids, and it's challenging to scope
+ // the bit to only that sequence of calls. This is relatively harmless
+ // since this bit is only set by accessing a ChromeOnly property, and
+ // therefore can't unduly slow down normal web browsing.
+
+ // Now that we know column and row sizes and positions, set
+ // the ComputedGridTrackInfo and related properties
+
+ uint32_t colTrackCount = gridReflowInput.mCols.mSizes.Length();
+ nsTArray<nscoord> colTrackPositions(colTrackCount);
+ nsTArray<nscoord> colTrackSizes(colTrackCount);
+ nsTArray<uint32_t> colTrackStates(colTrackCount);
+ nsTArray<bool> colRemovedRepeatTracks(
+ gridReflowInput.mColFunctions.mRemovedRepeatTracks);
+ uint32_t col = 0;
+ for (const TrackSize& sz : gridReflowInput.mCols.mSizes) {
+ colTrackPositions.AppendElement(sz.mPosition);
+ colTrackSizes.AppendElement(sz.mBase);
+ bool isRepeat = ((col >= gridReflowInput.mColFunctions.mRepeatAutoStart) &&
+ (col < gridReflowInput.mColFunctions.mRepeatAutoEnd));
+ colTrackStates.AppendElement(
+ isRepeat ?
+ (uint32_t)mozilla::dom::GridTrackState::Repeat :
+ (uint32_t)mozilla::dom::GridTrackState::Static
+ );
+
+ col++;
+ }
+ ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
+ gridReflowInput.mColFunctions.mExplicitGridOffset,
+ gridReflowInput.mColFunctions.NumExplicitTracks(),
+ 0,
+ col,
+ Move(colTrackPositions),
+ Move(colTrackSizes),
+ Move(colTrackStates),
+ Move(colRemovedRepeatTracks),
+ gridReflowInput.mColFunctions.mRepeatAutoStart);
+ Properties().Set(GridColTrackInfo(), colInfo);
+
+ uint32_t rowTrackCount = gridReflowInput.mRows.mSizes.Length();
+ nsTArray<nscoord> rowTrackPositions(rowTrackCount);
+ nsTArray<nscoord> rowTrackSizes(rowTrackCount);
+ nsTArray<uint32_t> rowTrackStates(rowTrackCount);
+ nsTArray<bool> rowRemovedRepeatTracks(
+ gridReflowInput.mRowFunctions.mRemovedRepeatTracks);
+ uint32_t row = 0;
+ for (const TrackSize& sz : gridReflowInput.mRows.mSizes) {
+ rowTrackPositions.AppendElement(sz.mPosition);
+ rowTrackSizes.AppendElement(sz.mBase);
+ bool isRepeat = ((row >= gridReflowInput.mRowFunctions.mRepeatAutoStart) &&
+ (row < gridReflowInput.mRowFunctions.mRepeatAutoEnd));
+ rowTrackStates.AppendElement(
+ isRepeat ?
+ (uint32_t)mozilla::dom::GridTrackState::Repeat :
+ (uint32_t)mozilla::dom::GridTrackState::Static
+ );
+
+ row++;
+ }
+ // Row info has to accomodate fragmentation of the grid, which may happen in
+ // later calls to Reflow. For now, presume that no more fragmentation will
+ // occur.
+ ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
+ gridReflowInput.mRowFunctions.mExplicitGridOffset,
+ gridReflowInput.mRowFunctions.NumExplicitTracks(),
+ gridReflowInput.mStartRow,
+ row,
+ Move(rowTrackPositions),
+ Move(rowTrackSizes),
+ Move(rowTrackStates),
+ Move(rowRemovedRepeatTracks),
+ gridReflowInput.mRowFunctions.mRepeatAutoStart);
+ Properties().Set(GridRowTrackInfo(), rowInfo);
+
+ if (prevInFlow) {
+ // This frame is fragmenting rows from a previous frame, so patch up
+ // the prior GridRowTrackInfo with a new end row.
+
+ // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
+
+ ComputedGridTrackInfo* priorRowInfo =
+ prevInFlow->Properties().Get(GridRowTrackInfo());
+
+ // Adjust track positions based on the first track in this fragment.
+ if (priorRowInfo->mPositions.Length() >
+ priorRowInfo->mStartFragmentTrack) {
+ nscoord delta =
+ priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
+ for (nscoord& pos : priorRowInfo->mPositions) {
+ pos -= delta;
+ }
+ }
+
+ ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
+ priorRowInfo->mNumLeadingImplicitTracks,
+ priorRowInfo->mNumExplicitTracks,
+ priorRowInfo->mStartFragmentTrack,
+ gridReflowInput.mStartRow,
+ Move(priorRowInfo->mPositions),
+ Move(priorRowInfo->mSizes),
+ Move(priorRowInfo->mStates),
+ Move(priorRowInfo->mRemovedRepeatTracks),
+ priorRowInfo->mRepeatFirstTrack);
+ prevInFlow->Properties().Set(GridRowTrackInfo(), revisedPriorRowInfo);
+ }
+
+ // Generate the line info properties. We need to provide the number of
+ // repeat tracks produced in the reflow. Only explicit names are assigned
+ // to lines here; the mozilla::dom::GridLines class will later extract
+ // implicit names from grid areas and assign them to the appropriate lines.
+
+ // Generate column lines first.
+ uint32_t capacity = gridReflowInput.mCols.mSizes.Length();
+ const nsStyleGridTemplate& gridColTemplate =
+ gridReflowInput.mGridStyle->mGridTemplateColumns;
+ nsTArray<nsTArray<nsString>> columnLineNames(capacity);
+ for (col = 0; col <= gridReflowInput.mCols.mSizes.Length(); col++) {
+ // Offset col by the explicit grid offset, to get the original names.
+ nsTArray<nsString> explicitNames =
+ gridReflowInput.mCols.GetExplicitLineNamesAtIndex(
+ gridColTemplate,
+ gridReflowInput.mColFunctions,
+ col - gridReflowInput.mColFunctions.mExplicitGridOffset);
+
+ columnLineNames.AppendElement(explicitNames);
+ }
+ ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
+ Move(columnLineNames),
+ gridColTemplate.mRepeatAutoLineNameListBefore,
+ gridColTemplate.mRepeatAutoLineNameListAfter);
+ Properties().Set(GridColumnLineInfo(), columnLineInfo);
+
+ // Generate row lines next.
+ capacity = gridReflowInput.mRows.mSizes.Length();
+ const nsStyleGridTemplate& gridRowTemplate =
+ gridReflowInput.mGridStyle->mGridTemplateRows;
+ nsTArray<nsTArray<nsString>> rowLineNames(capacity);
+ for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
+ // Offset row by the explicit grid offset, to get the original names.
+ nsTArray<nsString> explicitNames =
+ gridReflowInput.mRows.GetExplicitLineNamesAtIndex(
+ gridRowTemplate,
+ gridReflowInput.mRowFunctions,
+ row - gridReflowInput.mRowFunctions.mExplicitGridOffset);
+
+ rowLineNames.AppendElement(explicitNames);
+ }
+ ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
+ Move(rowLineNames),
+ gridRowTemplate.mRepeatAutoLineNameListBefore,
+ gridRowTemplate.mRepeatAutoLineNameListAfter);
+ Properties().Set(GridRowLineInfo(), rowLineInfo);
+
+ // Generate area info for explicit areas. Implicit areas are handled
+ // elsewhere.
+ if (gridReflowInput.mGridStyle->mGridTemplateAreas) {
+ nsTArray<css::GridNamedArea>* areas = new nsTArray<css::GridNamedArea>(
+ gridReflowInput.mGridStyle->mGridTemplateAreas->mNamedAreas);
+ Properties().Set(ExplicitNamedAreasProperty(), areas);
+ } else {
+ Properties().Delete(ExplicitNamedAreasProperty());
+ }
+ }
+
+ if (!prevInFlow) {
+ SharedGridData* sharedGridData = Properties().Get(SharedGridData::Prop());
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
+ if (!sharedGridData) {
+ sharedGridData = new SharedGridData;
+ Properties().Set(SharedGridData::Prop(), sharedGridData);
+ }
+ sharedGridData->mCols.mSizes.Clear();
+ sharedGridData->mCols.mSizes.SwapElements(gridReflowInput.mCols.mSizes);
+ sharedGridData->mCols.mContentBoxSize = gridReflowInput.mCols.mContentBoxSize;
+ sharedGridData->mCols.mBaselineSubtreeAlign[0] =
+ gridReflowInput.mCols.mBaselineSubtreeAlign[0];
+ sharedGridData->mCols.mBaselineSubtreeAlign[1] =
+ gridReflowInput.mCols.mBaselineSubtreeAlign[1];
+ sharedGridData->mRows.mSizes.Clear();
+ sharedGridData->mRows.mSizes.SwapElements(gridReflowInput.mRows.mSizes);
+ // Save the original row grid sizes and gaps so we can restore them later
+ // in GridReflowInput::Initialize for the continuations.
+ auto& origRowData = sharedGridData->mOriginalRowData;
+ origRowData.ClearAndRetainStorage();
+ origRowData.SetCapacity(sharedGridData->mRows.mSizes.Length());
+ nscoord prevTrackEnd = 0;
+ for (auto& sz : sharedGridData->mRows.mSizes) {
+ SharedGridData::RowData data = {sz.mBase, sz.mPosition - prevTrackEnd};
+ origRowData.AppendElement(data);
+ prevTrackEnd = sz.mPosition + sz.mBase;
+ }
+ sharedGridData->mRows.mContentBoxSize = gridReflowInput.mRows.mContentBoxSize;
+ sharedGridData->mRows.mBaselineSubtreeAlign[0] =
+ gridReflowInput.mRows.mBaselineSubtreeAlign[0];
+ sharedGridData->mRows.mBaselineSubtreeAlign[1] =
+ gridReflowInput.mRows.mBaselineSubtreeAlign[1];
+ sharedGridData->mGridItems.Clear();
+ sharedGridData->mGridItems.SwapElements(gridReflowInput.mGridItems);
+ sharedGridData->mAbsPosItems.Clear();
+ sharedGridData->mAbsPosItems.SwapElements(gridReflowInput.mAbsPosItems);
+
+ sharedGridData->mGenerateComputedGridInfo =
+ HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
+ } else if (sharedGridData && !GetNextInFlow()) {
+ Properties().Delete(SharedGridData::Prop());
+ }
+ }
+
+ FinishAndStoreOverflow(&aDesiredSize);
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nscoord
+nsGridContainerFrame::IntrinsicISize(nsRenderingContext* aRenderingContext,
+ IntrinsicISizeType aType)
+{
+ RenumberList();
+
+ // Calculate the sum of column sizes under intrinsic sizing.
+ // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
+ GridReflowInput state(this, *aRenderingContext);
+ InitImplicitNamedAreas(state.mGridStyle); // XXX optimize
+
+ auto GetDefiniteSizes = [] (const nsStyleCoord& aMinCoord,
+ const nsStyleCoord& aSizeCoord,
+ const nsStyleCoord& aMaxCoord,
+ nscoord* aMin,
+ nscoord* aSize,
+ nscoord* aMax) {
+ if (aMinCoord.ConvertsToLength()) {
+ *aMin = aMinCoord.ToLength();
+ }
+ if (aMaxCoord.ConvertsToLength()) {
+ *aMax = std::max(*aMin, aMaxCoord.ToLength());
+ }
+ if (aSizeCoord.ConvertsToLength()) {
+ *aSize = Clamp(aSizeCoord.ToLength(), *aMin, *aMax);
+ }
+ };
+ // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
+ // https://drafts.csswg.org/css-grid/#auto-repeat
+ // They're only used for auto-repeat so we skip computing them otherwise.
+ LogicalSize min(state.mWM, 0, 0);
+ LogicalSize sz(state.mWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+ LogicalSize max(state.mWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+ if (state.mColFunctions.mHasRepeatAuto) {
+ GetDefiniteSizes(state.mGridStyle->MinISize(state.mWM),
+ state.mGridStyle->ISize(state.mWM),
+ state.mGridStyle->MaxISize(state.mWM),
+ &min.ISize(state.mWM),
+ &sz.ISize(state.mWM),
+ &max.ISize(state.mWM));
+ }
+ if (state.mRowFunctions.mHasRepeatAuto &&
+ !(state.mGridStyle->mGridAutoFlow & NS_STYLE_GRID_AUTO_FLOW_ROW)) {
+ // Only 'grid-auto-flow:column' can create new implicit columns, so that's
+ // the only case where our block-size can affect the number of columns.
+ GetDefiniteSizes(state.mGridStyle->MinBSize(state.mWM),
+ state.mGridStyle->BSize(state.mWM),
+ state.mGridStyle->MaxBSize(state.mWM),
+ &min.BSize(state.mWM),
+ &sz.BSize(state.mWM),
+ &max.BSize(state.mWM));
+ }
+
+ Grid grid;
+ grid.PlaceGridItems(state, min, sz, max); // XXX optimize
+ if (grid.mGridColEnd == 0) {
+ return 0;
+ }
+ state.mCols.Initialize(state.mColFunctions, state.mGridStyle->mGridColumnGap,
+ grid.mGridColEnd, NS_UNCONSTRAINEDSIZE);
+ auto constraint = aType == nsLayoutUtils::MIN_ISIZE ?
+ SizingConstraint::eMinContent : SizingConstraint::eMaxContent;
+ state.mCols.CalculateSizes(state, state.mGridItems, state.mColFunctions,
+ NS_UNCONSTRAINEDSIZE, &GridArea::mCols,
+ constraint);
+ return state.mCols.BackComputedIntrinsicSize(state.mColFunctions,
+ state.mGridStyle->mGridColumnGap);
+}
+
+nscoord
+nsGridContainerFrame::GetMinISize(nsRenderingContext* aRC)
+{
+ DISPLAY_MIN_WIDTH(this, mCachedMinISize);
+ if (mCachedMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
+ mCachedMinISize = IntrinsicISize(aRC, nsLayoutUtils::MIN_ISIZE);
+ }
+ return mCachedMinISize;
+}
+
+nscoord
+nsGridContainerFrame::GetPrefISize(nsRenderingContext* aRC)
+{
+ DISPLAY_PREF_WIDTH(this, mCachedPrefISize);
+ if (mCachedPrefISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
+ mCachedPrefISize = IntrinsicISize(aRC, nsLayoutUtils::PREF_ISIZE);
+ }
+ return mCachedPrefISize;
+}
+
+void
+nsGridContainerFrame::MarkIntrinsicISizesDirty()
+{
+ mCachedMinISize = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mCachedPrefISize = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ nsContainerFrame::MarkIntrinsicISizesDirty();
+}
+
+nsIAtom*
+nsGridContainerFrame::GetType() const
+{
+ return nsGkAtoms::gridContainerFrame;
+}
+
+void
+nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+ if (GetPrevInFlow()) {
+ DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
+ }
+
+ // Our children are all grid-level boxes, which behave the same as
+ // inline-blocks in painting, so their borders/backgrounds all go on
+ // the BlockBorderBackgrounds list.
+ typedef GridItemCSSOrderIterator::OrderState OrderState;
+ OrderState order = HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
+ ? OrderState::eKnownOrdered
+ : OrderState::eKnownUnordered;
+ GridItemCSSOrderIterator iter(this, kPrincipalList,
+ GridItemCSSOrderIterator::eIncludeAll, order);
+ for (; !iter.AtEnd(); iter.Next()) {
+ nsIFrame* child = *iter;
+ BuildDisplayListForChild(aBuilder, child, aDirtyRect, aLists,
+ ::GetDisplayFlagsForGridItem(child));
+ }
+}
+
+bool
+nsGridContainerFrame::DrainSelfOverflowList()
+{
+ // Unlike nsContainerFrame::DrainSelfOverflowList we need to merge these lists
+ // so that the resulting mFrames is in document content order.
+ // NOTE: nsContainerFrame::AppendFrames/InsertFrames calls this method.
+ AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
+ if (overflowFrames) {
+ ::MergeSortedFrameLists(mFrames, *overflowFrames, GetContent());
+ return true;
+ }
+ return false;
+}
+
+void
+nsGridContainerFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList)
+{
+ NoteNewChildren(aListID, aFrameList);
+ nsContainerFrame::AppendFrames(aListID, aFrameList);
+}
+
+void
+nsGridContainerFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ NoteNewChildren(aListID, aFrameList);
+ nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
+}
+
+void
+nsGridContainerFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame)
+{
+#ifdef DEBUG
+ ChildListIDs supportedLists =
+ kAbsoluteList | kFixedList | kPrincipalList | kNoReflowPrincipalList;
+ MOZ_ASSERT(supportedLists.Contains(aListID), "unexpected child list");
+
+ // Note that kPrincipalList doesn't mean aOldFrame must be on that list.
+ // It can also be on kOverflowList, in which case it might be a pushed
+ // item, and if it's the only pushed item our DID_PUSH_ITEMS bit will lie.
+ if (aListID == kPrincipalList && !aOldFrame->GetPrevInFlow()) {
+ // Since the bit may lie, set the mDidPushItemsBitMayLie value to true for
+ // ourself and for all our contiguous previous-in-flow nsGridContainerFrames.
+ nsGridContainerFrame* frameThatMayLie = this;
+ do {
+ frameThatMayLie->mDidPushItemsBitMayLie = true;
+ frameThatMayLie = static_cast<nsGridContainerFrame*>(
+ frameThatMayLie->GetPrevInFlow());
+ } while (frameThatMayLie);
+ }
+#endif
+
+ nsContainerFrame::RemoveFrame(aListID, aOldFrame);
+}
+
+uint16_t
+nsGridContainerFrame::CSSAlignmentForAbsPosChild(const ReflowInput& aChildRI,
+ LogicalAxis aLogicalAxis) const
+{
+ MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
+ "This method should only be called for abspos children");
+
+ uint16_t alignment = (aLogicalAxis == eLogicalAxisInline) ?
+ aChildRI.mStylePosition->UsedJustifySelf(StyleContext()) :
+ aChildRI.mStylePosition->UsedAlignSelf(StyleContext());
+
+ // XXX strip off <overflow-position> bits until we implement it
+ // (bug 1311892)
+ alignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
+ if (alignment == NS_STYLE_ALIGN_NORMAL) {
+ // "the 'normal' keyword behaves as 'start' on replaced
+ // absolutely-positioned boxes, and behaves as 'stretch' on all other
+ // absolutely-positioned boxes."
+ // https://drafts.csswg.org/css-align/#align-abspos
+ // https://drafts.csswg.org/css-align/#justify-abspos
+ alignment = aChildRI.mFrame->IsFrameOfType(nsIFrame::eReplaced) ?
+ NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_STRETCH;
+ } else if (alignment == NS_STYLE_ALIGN_FLEX_START) {
+ alignment = NS_STYLE_ALIGN_START;
+ } else if (alignment == NS_STYLE_ALIGN_FLEX_END) {
+ alignment = NS_STYLE_ALIGN_END;
+ } else if (alignment == NS_STYLE_ALIGN_LEFT ||
+ alignment == NS_STYLE_ALIGN_RIGHT) {
+ if (aLogicalAxis == eLogicalAxisInline) {
+ const bool isLeft = (alignment == NS_STYLE_ALIGN_LEFT);
+ WritingMode wm = GetWritingMode();
+ alignment = (isLeft == wm.IsBidiLTR()) ? NS_STYLE_ALIGN_START
+ : NS_STYLE_ALIGN_END;
+ } else {
+ alignment = NS_STYLE_ALIGN_START;
+ }
+ } else if (alignment == NS_STYLE_ALIGN_BASELINE) {
+ alignment = NS_STYLE_ALIGN_START;
+ } else if (alignment == NS_STYLE_ALIGN_LAST_BASELINE) {
+ alignment = NS_STYLE_ALIGN_END;
+ }
+
+ return alignment;
+}
+
+nscoord
+nsGridContainerFrame::SynthesizeBaseline(
+ const FindItemInGridOrderResult& aGridOrderItem,
+ LogicalAxis aAxis,
+ BaselineSharingGroup aGroup,
+ const nsSize& aCBPhysicalSize,
+ nscoord aCBSize,
+ WritingMode aCBWM)
+{
+ if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
+ // No item in this fragment - synthesize a baseline from our border-box.
+ return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aCBSize);
+ }
+ auto GetBBaseline = [] (BaselineSharingGroup aGroup, WritingMode aWM,
+ const nsIFrame* aFrame, nscoord* aBaseline) {
+ return aGroup == BaselineSharingGroup::eFirst ?
+ nsLayoutUtils::GetFirstLineBaseline(aWM, aFrame, aBaseline) :
+ nsLayoutUtils::GetLastLineBaseline(aWM, aFrame, aBaseline);
+ };
+ nsIFrame* child = aGridOrderItem.mItem->mFrame;
+ nsGridContainerFrame* grid = do_QueryFrame(child);
+ auto childWM = child->GetWritingMode();
+ bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
+ nscoord baseline;
+ nscoord start;
+ nscoord size;
+ if (aAxis == eLogicalAxisBlock) {
+ start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
+ size = child->BSize(aCBWM);
+ if (grid && aGridOrderItem.mIsInEdgeTrack) {
+ isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) :
+ grid->GetBBaseline(aGroup, &baseline);
+ } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
+ baseline = child->BaselineBOffset(childWM, aGroup, AlignmentContext::eGrid);
+ } else {
+ baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
+ }
+ } else {
+ start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
+ size = child->ISize(aCBWM);
+ if (grid && aGridOrderItem.mIsInEdgeTrack) {
+ isOrthogonal ? grid->GetBBaseline(aGroup, &baseline) :
+ grid->GetIBaseline(aGroup, &baseline);
+ } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
+ GetBBaseline(aGroup, childWM, child, &baseline)) {
+ if (aGroup == BaselineSharingGroup::eLast) {
+ baseline = size - baseline; // convert to distance from border-box end
+ }
+ } else {
+ baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
+ }
+ }
+ return aGroup == BaselineSharingGroup::eFirst ? start + baseline :
+ aCBSize - start - size + baseline;
+}
+
+void
+nsGridContainerFrame::CalculateBaselines(
+ BaselineSet aBaselineSet,
+ GridItemCSSOrderIterator* aIter,
+ const nsTArray<GridItemInfo>* aGridItems,
+ const Tracks& aTracks,
+ uint32_t aFragmentStartTrack,
+ uint32_t aFirstExcludedTrack,
+ WritingMode aWM,
+ const nsSize& aCBPhysicalSize,
+ nscoord aCBBorderPaddingStart,
+ nscoord aCBBorderPaddingEnd,
+ nscoord aCBSize)
+{
+ const auto axis = aTracks.mAxis;
+ auto firstBaseline = aTracks.mBaseline[BaselineSharingGroup::eFirst];
+ if (!(aBaselineSet & BaselineSet::eFirst)) {
+ mBaseline[axis][BaselineSharingGroup::eFirst] =
+ ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::eFirst, aWM,
+ aCBSize);
+ } else if (firstBaseline == NS_INTRINSIC_WIDTH_UNKNOWN) {
+ FindItemInGridOrderResult gridOrderFirstItem =
+ FindFirstItemInGridOrder(*aIter, *aGridItems,
+ axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
+ axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
+ aFragmentStartTrack);
+ mBaseline[axis][BaselineSharingGroup::eFirst] =
+ SynthesizeBaseline(gridOrderFirstItem,
+ axis,
+ BaselineSharingGroup::eFirst,
+ aCBPhysicalSize,
+ aCBSize,
+ aWM);
+ } else {
+ // We have a 'first baseline' group in the start track in this fragment.
+ // Convert it from track to grid container border-box coordinates.
+ MOZ_ASSERT(!aGridItems->IsEmpty());
+ nscoord gapBeforeStartTrack = aFragmentStartTrack == 0 ?
+ aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::eAfterGridGap) :
+ nscoord(0); // no content gap at start of fragment
+ mBaseline[axis][BaselineSharingGroup::eFirst] =
+ aCBBorderPaddingStart + gapBeforeStartTrack + firstBaseline;
+ }
+
+ auto lastBaseline = aTracks.mBaseline[BaselineSharingGroup::eLast];
+ if (!(aBaselineSet & BaselineSet::eLast)) {
+ mBaseline[axis][BaselineSharingGroup::eLast] =
+ ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::eLast, aWM,
+ aCBSize);
+ } else if (lastBaseline == NS_INTRINSIC_WIDTH_UNKNOWN) {
+ // For finding items for the 'last baseline' we need to create a reverse
+ // iterator ('aIter' is the forward iterator from the GridReflowInput).
+ using Iter = ReverseGridItemCSSOrderIterator;
+ auto orderState = aIter->ItemsAreAlreadyInOrder() ?
+ Iter::OrderState::eKnownOrdered : Iter::OrderState::eKnownUnordered;
+ Iter iter(this, kPrincipalList, Iter::ChildFilter::eSkipPlaceholders,
+ orderState);
+ iter.SetGridItemCount(aGridItems->Length());
+ FindItemInGridOrderResult gridOrderLastItem =
+ FindLastItemInGridOrder(iter, *aGridItems,
+ axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
+ axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
+ aFragmentStartTrack, aFirstExcludedTrack);
+ mBaseline[axis][BaselineSharingGroup::eLast] =
+ SynthesizeBaseline(gridOrderLastItem,
+ axis,
+ BaselineSharingGroup::eLast,
+ aCBPhysicalSize,
+ aCBSize,
+ aWM);
+ } else {
+ // We have a 'last baseline' group in the end track in this fragment.
+ // Convert it from track to grid container border-box coordinates.
+ MOZ_ASSERT(!aGridItems->IsEmpty());
+ auto borderBoxStartToEndOfEndTrack = aCBBorderPaddingStart +
+ aTracks.GridLineEdge(aFirstExcludedTrack, GridLineSide::eBeforeGridGap) -
+ aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::eBeforeGridGap);
+ mBaseline[axis][BaselineSharingGroup::eLast] =
+ (aCBSize - borderBoxStartToEndOfEndTrack) + lastBaseline;
+ }
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsGridContainerFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("GridContainer"), aResult);
+}
+#endif
+
+void
+nsGridContainerFrame::NoteNewChildren(ChildListID aListID,
+ const nsFrameList& aFrameList)
+{
+#ifdef DEBUG
+ ChildListIDs supportedLists =
+ kAbsoluteList | kFixedList | kPrincipalList | kNoReflowPrincipalList;
+ MOZ_ASSERT(supportedLists.Contains(aListID), "unexpected child list");
+#endif
+
+ nsIPresShell* shell = PresContext()->PresShell();
+ for (auto pif = GetPrevInFlow(); pif; pif = pif->GetPrevInFlow()) {
+ if (aListID == kPrincipalList) {
+ pif->AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
+ }
+ shell->FrameNeedsReflow(pif, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
+ }
+}
+
+void
+nsGridContainerFrame::MergeSortedOverflow(nsFrameList& aList)
+{
+ if (aList.IsEmpty()) {
+ return;
+ }
+ MOZ_ASSERT(!aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
+ "this is the wrong list to put this child frame");
+ MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
+ nsFrameList* overflow = GetOverflowFrames();
+ if (overflow) {
+ ::MergeSortedFrameLists(*overflow, aList, GetContent());
+ } else {
+ SetOverflowFrames(aList);
+ }
+}
+
+void
+nsGridContainerFrame::MergeSortedExcessOverflowContainers(nsFrameList& aList)
+{
+ if (aList.IsEmpty()) {
+ return;
+ }
+ MOZ_ASSERT(aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
+ "this is the wrong list to put this child frame");
+ MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
+ nsFrameList* eoc = GetPropTableFrames(ExcessOverflowContainersProperty());
+ if (eoc) {
+ ::MergeSortedFrameLists(*eoc, aList, GetContent());
+ } else {
+ SetPropTableFrames(new (PresContext()->PresShell()) nsFrameList(aList),
+ ExcessOverflowContainersProperty());
+ }
+}
+
+/* static */ nsGridContainerFrame::FindItemInGridOrderResult
+nsGridContainerFrame::FindFirstItemInGridOrder(
+ GridItemCSSOrderIterator& aIter,
+ const nsTArray<GridItemInfo>& aGridItems,
+ LineRange GridArea::* aMajor,
+ LineRange GridArea::* aMinor,
+ uint32_t aFragmentStartTrack)
+{
+ FindItemInGridOrderResult result = { nullptr, false };
+ uint32_t minMajor = kTranslatedMaxLine + 1;
+ uint32_t minMinor = kTranslatedMaxLine + 1;
+ aIter.Reset();
+ for (; !aIter.AtEnd(); aIter.Next()) {
+ const GridItemInfo& item = aGridItems[aIter.GridItemIndex()];
+ if ((item.mArea.*aMajor).mEnd <= aFragmentStartTrack) {
+ continue; // item doesn't span any track in this fragment
+ }
+ uint32_t major = (item.mArea.*aMajor).mStart;
+ uint32_t minor = (item.mArea.*aMinor).mStart;
+ if (major < minMajor || (major == minMajor && minor < minMinor)) {
+ minMajor = major;
+ minMinor = minor;
+ result.mItem = &item;
+ result.mIsInEdgeTrack = major == 0U;
+ }
+ }
+ return result;
+}
+
+/* static */ nsGridContainerFrame::FindItemInGridOrderResult
+nsGridContainerFrame::FindLastItemInGridOrder(
+ ReverseGridItemCSSOrderIterator& aIter,
+ const nsTArray<GridItemInfo>& aGridItems,
+ LineRange GridArea::* aMajor,
+ LineRange GridArea::* aMinor,
+ uint32_t aFragmentStartTrack,
+ uint32_t aFirstExcludedTrack)
+{
+ FindItemInGridOrderResult result = { nullptr, false };
+ int32_t maxMajor = -1;
+ int32_t maxMinor = -1;
+ aIter.Reset();
+ int32_t lastMajorTrack = int32_t(aFirstExcludedTrack) - 1;
+ for (; !aIter.AtEnd(); aIter.Next()) {
+ const GridItemInfo& item = aGridItems[aIter.GridItemIndex()];
+ // Subtract 1 from the end line to get the item's last track index.
+ int32_t major = (item.mArea.*aMajor).mEnd - 1;
+ // Currently, this method is only called with aFirstExcludedTrack ==
+ // the first track in the next fragment, so we take the opportunity
+ // to assert this item really belongs to this fragment.
+ MOZ_ASSERT((item.mArea.*aMajor).mStart < aFirstExcludedTrack,
+ "found an item that belongs to some later fragment");
+ if (major < int32_t(aFragmentStartTrack)) {
+ continue; // item doesn't span any track in this fragment
+ }
+ int32_t minor = (item.mArea.*aMinor).mEnd - 1;
+ MOZ_ASSERT(minor >= 0 && major >= 0, "grid item must have span >= 1");
+ if (major > maxMajor || (major == maxMajor && minor > maxMinor)) {
+ maxMajor = major;
+ maxMinor = minor;
+ result.mItem = &item;
+ result.mIsInEdgeTrack = major == lastMajorTrack;
+ }
+ }
+ return result;
+}
+
+#ifdef DEBUG
+void
+nsGridContainerFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ ChildListIDs supportedLists = kAbsoluteList | kFixedList | kPrincipalList;
+ MOZ_ASSERT(supportedLists.Contains(aListID), "unexpected child list");
+
+ return nsContainerFrame::SetInitialChildList(aListID, aChildList);
+}
+
+void
+nsGridContainerFrame::SanityCheckGridItemsBeforeReflow() const
+{
+ ChildListIDs absLists = kAbsoluteList | kFixedList |
+ kOverflowContainersList | kExcessOverflowContainersList;
+ ChildListIDs itemLists = kPrincipalList | kOverflowList;
+ for (const nsIFrame* f = this; f; f = f->GetNextInFlow()) {
+ MOZ_ASSERT(!f->HasAnyStateBits(NS_STATE_GRID_DID_PUSH_ITEMS),
+ "At start of reflow, we should've pulled items back from all "
+ "NIFs and cleared NS_STATE_GRID_DID_PUSH_ITEMS in the process");
+ for (nsIFrame::ChildListIterator childLists(f);
+ !childLists.IsDone(); childLists.Next()) {
+ if (!itemLists.Contains(childLists.CurrentID())) {
+ MOZ_ASSERT(absLists.Contains(childLists.CurrentID()),
+ "unexpected non-empty child list");
+ continue;
+ }
+ for (auto child : childLists.CurrentList()) {
+ MOZ_ASSERT(f == this || child->GetPrevInFlow(),
+ "all pushed items must be pulled up before reflow");
+ }
+ }
+ }
+ // If we have a prev-in-flow, each of its children's next-in-flow
+ // should be one of our children or be null.
+ const auto pif = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
+ if (pif) {
+ const nsFrameList* oc =
+ GetPropTableFrames(OverflowContainersProperty());
+ const nsFrameList* eoc =
+ GetPropTableFrames(ExcessOverflowContainersProperty());
+ const nsFrameList* pifEOC =
+ pif->GetPropTableFrames(ExcessOverflowContainersProperty());
+ for (const nsIFrame* child : pif->GetChildList(kPrincipalList)) {
+ const nsIFrame* childNIF = child->GetNextInFlow();
+ MOZ_ASSERT(!childNIF || mFrames.ContainsFrame(childNIF) ||
+ (pifEOC && pifEOC->ContainsFrame(childNIF)) ||
+ (oc && oc->ContainsFrame(childNIF)) ||
+ (eoc && eoc->ContainsFrame(childNIF)));
+ }
+ }
+}
+
+void
+nsGridContainerFrame::TrackSize::Dump() const
+{
+ printf("mPosition=%d mBase=%d mLimit=%d", mPosition, mBase, mLimit);
+
+ printf(" min:");
+ if (mState & eAutoMinSizing) {
+ printf("auto ");
+ } else if (mState & eMinContentMinSizing) {
+ printf("min-content ");
+ } else if (mState & eMaxContentMinSizing) {
+ printf("max-content ");
+ }
+
+ printf(" max:");
+ if (mState & eAutoMaxSizing) {
+ printf("auto ");
+ } else if (mState & eMinContentMaxSizing) {
+ printf("min-content ");
+ } else if (mState & eMaxContentMaxSizing) {
+ printf("max-content ");
+ } else if (mState & eFlexMaxSizing) {
+ printf("flex ");
+ }
+
+ if (mState & eFrozen) {
+ printf("frozen ");
+ }
+ if (mState & eBreakBefore) {
+ printf("break-before ");
+ }
+}
+
+#endif // DEBUG
+
+nsGridContainerFrame*
+nsGridContainerFrame::GetGridFrameWithComputedInfo(nsIFrame* aFrame)
+{
+ // Prepare a lambda function that we may need to call multiple times.
+ auto GetGridContainerFrame = [](nsIFrame *aFrame) {
+ // Return the aFrame's content insertion frame, iff it is
+ // a grid container.
+ nsGridContainerFrame* gridFrame = nullptr;
+
+ if (aFrame) {
+ nsIFrame* contentFrame = aFrame->GetContentInsertionFrame();
+ if (contentFrame &&
+ (contentFrame->GetType() == nsGkAtoms::gridContainerFrame)) {
+ gridFrame = static_cast<nsGridContainerFrame*>(contentFrame);
+ }
+ }
+ return gridFrame;
+ };
+
+ nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
+ if (gridFrame) {
+ // if any of our properties are missing, generate them
+ bool reflowNeeded = (!gridFrame->Properties().Has(GridColTrackInfo()) ||
+ !gridFrame->Properties().Has(GridRowTrackInfo()) ||
+ !gridFrame->Properties().Has(GridColumnLineInfo()) ||
+ !gridFrame->Properties().Has(GridRowLineInfo()));
+
+ if (reflowNeeded) {
+ // Trigger a reflow that generates additional grid property data.
+ nsIPresShell* shell = gridFrame->PresContext()->PresShell();
+ gridFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
+ shell->FrameNeedsReflow(gridFrame,
+ nsIPresShell::eResize,
+ NS_FRAME_IS_DIRTY);
+ shell->FlushPendingNotifications(Flush_Layout);
+
+ // Since the reflow may have side effects, get the grid frame again.
+ gridFrame = GetGridContainerFrame(aFrame);
+
+ // Assert the grid properties are present
+ MOZ_ASSERT(!gridFrame ||
+ gridFrame->Properties().Has(GridColTrackInfo()));
+ MOZ_ASSERT(!gridFrame ||
+ gridFrame->Properties().Has(GridRowTrackInfo()));
+ MOZ_ASSERT(!gridFrame ||
+ gridFrame->Properties().Has(GridColumnLineInfo()));
+ MOZ_ASSERT(!gridFrame ||
+ gridFrame->Properties().Has(GridRowLineInfo()));
+ }
+ }
+
+ return gridFrame;
+}
diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h
new file mode 100644
index 000000000..e610dfa0b
--- /dev/null
+++ b/layout/generic/nsGridContainerFrame.h
@@ -0,0 +1,458 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: grid | inline-grid" */
+
+#ifndef nsGridContainerFrame_h___
+#define nsGridContainerFrame_h___
+
+#include "mozilla/Maybe.h"
+#include "mozilla/TypeTraits.h"
+#include "nsContainerFrame.h"
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
+/**
+ * Factory function.
+ * @return a newly allocated nsGridContainerFrame (infallible)
+ */
+nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+namespace mozilla {
+/**
+ * The number of implicit / explicit tracks and their sizes.
+ */
+struct ComputedGridTrackInfo
+{
+ ComputedGridTrackInfo(uint32_t aNumLeadingImplicitTracks,
+ uint32_t aNumExplicitTracks,
+ uint32_t aStartFragmentTrack,
+ uint32_t aEndFragmentTrack,
+ nsTArray<nscoord>&& aPositions,
+ nsTArray<nscoord>&& aSizes,
+ nsTArray<uint32_t>&& aStates,
+ nsTArray<bool>&& aRemovedRepeatTracks,
+ uint32_t aRepeatFirstTrack)
+ : mNumLeadingImplicitTracks(aNumLeadingImplicitTracks)
+ , mNumExplicitTracks(aNumExplicitTracks)
+ , mStartFragmentTrack(aStartFragmentTrack)
+ , mEndFragmentTrack(aEndFragmentTrack)
+ , mPositions(aPositions)
+ , mSizes(aSizes)
+ , mStates(aStates)
+ , mRemovedRepeatTracks(aRemovedRepeatTracks)
+ , mRepeatFirstTrack(aRepeatFirstTrack)
+ {}
+ uint32_t mNumLeadingImplicitTracks;
+ uint32_t mNumExplicitTracks;
+ uint32_t mStartFragmentTrack;
+ uint32_t mEndFragmentTrack;
+ nsTArray<nscoord> mPositions;
+ nsTArray<nscoord> mSizes;
+ nsTArray<uint32_t> mStates;
+ nsTArray<bool> mRemovedRepeatTracks;
+ uint32_t mRepeatFirstTrack;
+};
+
+struct ComputedGridLineInfo
+{
+ explicit ComputedGridLineInfo(nsTArray<nsTArray<nsString>>&& aNames,
+ const nsTArray<nsString>& aNamesBefore,
+ const nsTArray<nsString>& aNamesAfter)
+ : mNames(aNames)
+ , mNamesBefore(aNamesBefore)
+ , mNamesAfter(aNamesAfter)
+ {}
+ nsTArray<nsTArray<nsString>> mNames;
+ nsTArray<nsString> mNamesBefore;
+ nsTArray<nsString> mNamesAfter;
+};
+} // namespace mozilla
+
+class nsGridContainerFrame final : public nsContainerFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsGridContainerFrame)
+ NS_DECL_QUERYFRAME
+ typedef mozilla::ComputedGridTrackInfo ComputedGridTrackInfo;
+ typedef mozilla::ComputedGridLineInfo ComputedGridLineInfo;
+
+ // nsIFrame overrides
+ void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
+ nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
+ void MarkIntrinsicISizesDirty() override;
+ nsIAtom* GetType() const override;
+ bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~nsIFrame::eCanContainOverflowContainers);
+ }
+
+ void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override
+ {
+ if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
+ // Return a baseline synthesized from our margin-box.
+ return nsContainerFrame::GetLogicalBaseline(aWM);
+ }
+ nscoord b;
+ GetBBaseline(BaselineSharingGroup::eFirst, &b);
+ return b;
+ }
+
+ bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+ nscoord* aBaseline) const override
+ {
+ return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
+ }
+
+ bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ nscoord* aBaseline) const override
+ {
+ if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
+ return false;
+ }
+ return GetBBaseline(aBaselineGroup, aBaseline);
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ // nsContainerFrame overrides
+ bool DrainSelfOverflowList() override;
+ void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
+ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
+ uint16_t CSSAlignmentForAbsPosChild(
+ const ReflowInput& aChildRI,
+ mozilla::LogicalAxis aLogicalAxis) const override;
+
+#ifdef DEBUG
+ void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+#endif
+
+ /**
+ * Return the containing block for aChild which MUST be an abs.pos. child
+ * of a grid container. This is just a helper method for
+ * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere.
+ */
+ static const nsRect& GridItemCB(nsIFrame* aChild);
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridItemContainingBlockRect, nsRect)
+
+ /**
+ * These properties are created by a call to
+ * nsGridContainerFrame::GetGridFrameWithComputedInfo, typically from
+ * Element::GetGridFragments.
+ */
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColTrackInfo, ComputedGridTrackInfo)
+ const ComputedGridTrackInfo* GetComputedTemplateColumns()
+ {
+ const ComputedGridTrackInfo* info = Properties().Get(GridColTrackInfo());
+ MOZ_ASSERT(info, "Property generation wasn't requested.");
+ return info;
+ }
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowTrackInfo, ComputedGridTrackInfo)
+ const ComputedGridTrackInfo* GetComputedTemplateRows()
+ {
+ const ComputedGridTrackInfo* info = Properties().Get(GridRowTrackInfo());
+ MOZ_ASSERT(info, "Property generation wasn't requested.");
+ return info;
+ }
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColumnLineInfo, ComputedGridLineInfo)
+ const ComputedGridLineInfo* GetComputedTemplateColumnLines()
+ {
+ const ComputedGridLineInfo* info = Properties().Get(GridColumnLineInfo());
+ MOZ_ASSERT(info, "Property generation wasn't requested.");
+ return info;
+ }
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo)
+ const ComputedGridLineInfo* GetComputedTemplateRowLines()
+ {
+ const ComputedGridLineInfo* info = Properties().Get(GridRowLineInfo());
+ MOZ_ASSERT(info, "Property generation wasn't requested.");
+ return info;
+ }
+
+ typedef nsBaseHashtable<nsStringHashKey,
+ mozilla::css::GridNamedArea,
+ mozilla::css::GridNamedArea> ImplicitNamedAreas;
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
+ ImplicitNamedAreas)
+ ImplicitNamedAreas* GetImplicitNamedAreas() const {
+ return Properties().Get(ImplicitNamedAreasProperty());
+ }
+
+ typedef nsTArray<mozilla::css::GridNamedArea> ExplicitNamedAreas;
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,
+ ExplicitNamedAreas)
+ ExplicitNamedAreas* GetExplicitNamedAreas() const {
+ return Properties().Get(ExplicitNamedAreasProperty());
+ }
+
+ /**
+ * Return a containing grid frame, and ensure it has computed grid info
+ * @return nullptr if aFrame has no grid container, or frame was destroyed
+ * @note this might destroy layout/style data since it may flush layout
+ */
+ static nsGridContainerFrame* GetGridFrameWithComputedInfo(nsIFrame* aFrame);
+
+ struct TrackSize;
+ struct GridItemInfo;
+ struct GridReflowInput;
+ template<typename Iterator> class GridItemCSSOrderIteratorT;
+ typedef GridItemCSSOrderIteratorT<nsFrameList::iterator>
+ GridItemCSSOrderIterator;
+ typedef GridItemCSSOrderIteratorT<nsFrameList::reverse_iterator>
+ ReverseGridItemCSSOrderIterator;
+ struct FindItemInGridOrderResult
+ {
+ // The first(last) item in (reverse) grid order.
+ const GridItemInfo* mItem;
+ // Does the above item span the first(last) track?
+ bool mIsInEdgeTrack;
+ };
+protected:
+ static const uint32_t kAutoLine;
+ // The maximum line number, in the zero-based translated grid.
+ static const uint32_t kTranslatedMaxLine;
+ typedef mozilla::LogicalPoint LogicalPoint;
+ typedef mozilla::LogicalRect LogicalRect;
+ typedef mozilla::LogicalSize LogicalSize;
+ typedef mozilla::WritingMode WritingMode;
+ typedef mozilla::css::GridNamedArea GridNamedArea;
+ typedef mozilla::layout::AutoFrameListPtr AutoFrameListPtr;
+ typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
+ struct Grid;
+ struct GridArea;
+ class LineNameMap;
+ struct LineRange;
+ struct SharedGridData;
+ struct TrackSizingFunctions;
+ struct Tracks;
+ struct TranslatedLineRange;
+ friend nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ explicit nsGridContainerFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+ , mCachedMinISize(NS_INTRINSIC_WIDTH_UNKNOWN)
+ , mCachedPrefISize(NS_INTRINSIC_WIDTH_UNKNOWN)
+ {
+ mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+ }
+
+ /**
+ * XXX temporary - move the ImplicitNamedAreas stuff to the style system.
+ * The implicit area names that come from x-start .. x-end lines in
+ * grid-template-columns / grid-template-rows are stored in this frame
+ * property when needed, as a ImplicitNamedAreas* value.
+ */
+ void InitImplicitNamedAreas(const nsStylePosition* aStyle);
+ void AddImplicitNamedAreas(const nsTArray<nsTArray<nsString>>& aLineNameLists);
+
+ /**
+ * Reflow and place our children.
+ * @return the consumed size of all of this grid container's continuations
+ * so far including this frame
+ */
+ nscoord ReflowChildren(GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus);
+
+ /**
+ * Helper for GetMinISize / GetPrefISize.
+ */
+ nscoord IntrinsicISize(nsRenderingContext* aRenderingContext,
+ IntrinsicISizeType aConstraint);
+
+ // Helper for AppendFrames / InsertFrames.
+ void NoteNewChildren(ChildListID aListID, const nsFrameList& aFrameList);
+
+ // Helper to move child frames into the kOverflowList.
+ void MergeSortedOverflow(nsFrameList& aList);
+ // Helper to move child frames into the kExcessOverflowContainersList:.
+ void MergeSortedExcessOverflowContainers(nsFrameList& aList);
+
+ bool GetBBaseline(BaselineSharingGroup aBaselineGroup, nscoord* aResult) const
+ {
+ *aResult = mBaseline[mozilla::eLogicalAxisBlock][aBaselineGroup];
+ return true;
+ }
+ bool GetIBaseline(BaselineSharingGroup aBaselineGroup, nscoord* aResult) const
+ {
+ *aResult = mBaseline[mozilla::eLogicalAxisInline][aBaselineGroup];
+ return true;
+ }
+
+ /**
+ * Calculate this grid container's baselines.
+ * @param aBaselineSet which baseline(s) to derive from a baseline-group or
+ * items; a baseline not included is synthesized from the border-box instead.
+ * @param aFragmentStartTrack is the first track in this fragment in the same
+ * axis as aMajor. Pass zero if that's not the axis we're fragmenting in.
+ * @param aFirstExcludedTrack should be the first track in the next fragment
+ * or one beyond the final track in the last fragment, in aMajor's axis.
+ * Pass the number of tracks if that's not the axis we're fragmenting in.
+ */
+ enum BaselineSet : uint32_t {
+ eNone = 0x0,
+ eFirst = 0x1,
+ eLast = 0x2,
+ eBoth = eFirst | eLast,
+ };
+ void CalculateBaselines(BaselineSet aBaselineSet,
+ GridItemCSSOrderIterator* aIter,
+ const nsTArray<GridItemInfo>* aGridItems,
+ const Tracks& aTracks,
+ uint32_t aFragmentStartTrack,
+ uint32_t aFirstExcludedTrack,
+ WritingMode aWM,
+ const nsSize& aCBPhysicalSize,
+ nscoord aCBBorderPaddingStart,
+ nscoord aCBBorderPaddingStartEnd,
+ nscoord aCBSize);
+
+ /**
+ * Synthesize a Grid container baseline for aGroup.
+ */
+ nscoord SynthesizeBaseline(const FindItemInGridOrderResult& aItem,
+ mozilla::LogicalAxis aAxis,
+ BaselineSharingGroup aGroup,
+ const nsSize& aCBPhysicalSize,
+ nscoord aCBSize,
+ WritingMode aCBWM);
+ /**
+ * Find the first item in Grid Order in this fragment.
+ * https://drafts.csswg.org/css-grid/#grid-order
+ * @param aFragmentStartTrack is the first track in this fragment in the same
+ * axis as aMajor. Pass zero if that's not the axis we're fragmenting in.
+ */
+ static FindItemInGridOrderResult
+ FindFirstItemInGridOrder(GridItemCSSOrderIterator& aIter,
+ const nsTArray<GridItemInfo>& aGridItems,
+ LineRange GridArea::* aMajor,
+ LineRange GridArea::* aMinor,
+ uint32_t aFragmentStartTrack);
+ /**
+ * Find the last item in Grid Order in this fragment.
+ * @param aFragmentStartTrack is the first track in this fragment in the same
+ * axis as aMajor. Pass zero if that's not the axis we're fragmenting in.
+ * @param aFirstExcludedTrack should be the first track in the next fragment
+ * or one beyond the final track in the last fragment, in aMajor's axis.
+ * Pass the number of tracks if that's not the axis we're fragmenting in.
+ */
+ static FindItemInGridOrderResult
+ FindLastItemInGridOrder(ReverseGridItemCSSOrderIterator& aIter,
+ const nsTArray<GridItemInfo>& aGridItems,
+ LineRange GridArea::* aMajor,
+ LineRange GridArea::* aMinor,
+ uint32_t aFragmentStartTrack,
+ uint32_t aFirstExcludedTrack);
+
+#ifdef DEBUG
+ void SanityCheckGridItemsBeforeReflow() const;
+#endif // DEBUG
+
+private:
+ // Helpers for ReflowChildren
+ struct Fragmentainer {
+ /**
+ * The distance from the first grid container fragment's block-axis content
+ * edge to the fragmentainer end.
+ */
+ nscoord mToFragmentainerEnd;
+ /**
+ * True if the current fragment is at the start of the fragmentainer.
+ */
+ bool mIsTopOfPage;
+ /**
+ * Is there a Class C break opportunity at the start content edge?
+ */
+ bool mCanBreakAtStart;
+ /**
+ * Is there a Class C break opportunity at the end content edge?
+ */
+ bool mCanBreakAtEnd;
+ /**
+ * Is the grid container's block-size unconstrained?
+ */
+ bool mIsAutoBSize;
+ };
+
+ mozilla::Maybe<nsGridContainerFrame::Fragmentainer>
+ GetNearestFragmentainer(const GridReflowInput& aState) const;
+
+ // @return the consumed size of all continuations so far including this frame
+ nscoord ReflowInFragmentainer(GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus,
+ Fragmentainer& aFragmentainer,
+ const nsSize& aContainerSize);
+
+ // Helper for ReflowInFragmentainer
+ // @return the consumed size of all continuations so far including this frame
+ nscoord ReflowRowsInFragmentainer(GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus,
+ Fragmentainer& aFragmentainer,
+ const nsSize& aContainerSize,
+ const nsTArray<const GridItemInfo*>& aItems,
+ uint32_t aStartRow,
+ uint32_t aEndRow,
+ nscoord aBSize,
+ nscoord aAvailableSize);
+
+ // Helper for ReflowChildren / ReflowInFragmentainer
+ void ReflowInFlowChild(nsIFrame* aChild,
+ const GridItemInfo* aGridItemInfo,
+ nsSize aContainerSize,
+ mozilla::Maybe<nscoord> aStretchBSize,
+ const Fragmentainer* aFragmentainer,
+ const GridReflowInput& aState,
+ const LogicalRect& aContentArea,
+ ReflowOutput& aDesiredSize,
+ nsReflowStatus& aStatus);
+
+ /**
+ * Cached values to optimize GetMinISize/GetPrefISize.
+ */
+ nscoord mCachedMinISize;
+ nscoord mCachedPrefISize;
+
+ // Our baselines, one per BaselineSharingGroup per axis.
+ nscoord mBaseline[2/*LogicalAxis*/][2/*BaselineSharingGroup*/];
+
+#ifdef DEBUG
+ // If true, NS_STATE_GRID_DID_PUSH_ITEMS may be set even though all pushed
+ // frames may have been removed. This is used to suppress an assertion
+ // in case RemoveFrame removed all associated child frames.
+ bool mDidPushItemsBitMayLie;
+#endif
+};
+
+#endif /* nsGridContainerFrame_h___ */
diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp
new file mode 100644
index 000000000..bad3a710f
--- /dev/null
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -0,0 +1,437 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for the HTML <canvas> element */
+
+#include "nsHTMLCanvasFrame.h"
+
+#include "nsGkAtoms.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "nsDisplayList.h"
+#include "nsLayoutUtils.h"
+#include "nsStyleUtil.h"
+#include "ImageLayers.h"
+#include "Layers.h"
+#include "ActiveLayerTracker.h"
+
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+
+/* Helper for our nsIFrame::GetIntrinsicSize() impl. Takes the result of
+ * "GetCanvasSize()" as a parameter, which may help avoid redundant
+ * indirect calls to GetCanvasSize().
+ *
+ * @param aCanvasSizeInPx The canvas's size in CSS pixels, as returned
+ * by GetCanvasSize().
+ * @return The canvas's intrinsic size, as an IntrinsicSize object.
+ */
+static IntrinsicSize
+IntrinsicSizeFromCanvasSize(const nsIntSize& aCanvasSizeInPx)
+{
+ IntrinsicSize intrinsicSize;
+ intrinsicSize.width.SetCoordValue(
+ nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.width));
+ intrinsicSize.height.SetCoordValue(
+ nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.height));
+
+ return intrinsicSize;
+}
+
+/* Helper for our nsIFrame::GetIntrinsicRatio() impl. Takes the result of
+ * "GetCanvasSize()" as a parameter, which may help avoid redundant
+ * indirect calls to GetCanvasSize().
+ *
+ * @param aCanvasSizeInPx The canvas's size in CSS pixels, as returned
+ * by GetCanvasSize().
+ * @return The canvas's intrinsic ratio, as a nsSize.
+ */
+static nsSize
+IntrinsicRatioFromCanvasSize(const nsIntSize& aCanvasSizeInPx)
+{
+ return nsSize(nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.width),
+ nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.height));
+}
+
+class nsDisplayCanvas : public nsDisplayItem {
+public:
+ nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ {
+ MOZ_COUNT_CTOR(nsDisplayCanvas);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayCanvas() {
+ MOZ_COUNT_DTOR(nsDisplayCanvas);
+ }
+#endif
+
+ NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS)
+
+ virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override {
+ *aSnap = false;
+ nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
+ HTMLCanvasElement* canvas =
+ HTMLCanvasElement::FromContent(f->GetContent());
+ nsRegion result;
+ if (canvas->GetIsOpaque()) {
+ // OK, the entire region painted by the canvas is opaque. But what is
+ // that region? It's the canvas's "dest rect" (controlled by the
+ // object-fit/object-position CSS properties), clipped to the container's
+ // content box (which is what GetBounds() returns). So, we grab those
+ // rects and intersect them.
+ nsRect constraintRect = GetBounds(aBuilder, aSnap);
+
+ // Need intrinsic size & ratio, for ComputeObjectDestRect:
+ nsIntSize canvasSize = f->GetCanvasSize();
+ IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSize);
+ nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSize);
+
+ const nsRect destRect =
+ nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+ intrinsicSize, intrinsicRatio,
+ f->StylePosition());
+ return nsRegion(destRect.Intersect(constraintRect));
+ }
+ return result;
+ }
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override {
+ *aSnap = true;
+ nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
+ return f->GetInnerArea() + ToReferenceFrame();
+ }
+
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) override
+ {
+ return static_cast<nsHTMLCanvasFrame*>(mFrame)->
+ BuildLayer(aBuilder, aManager, this, aContainerParameters);
+ }
+ virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) override
+ {
+ if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager))
+ return LAYER_INACTIVE;
+
+ // If compositing is cheap, just do that
+ if (aManager->IsCompositingCheap() ||
+ ActiveLayerTracker::IsContentActive(mFrame))
+ return mozilla::LAYER_ACTIVE;
+
+ return LAYER_INACTIVE;
+ }
+};
+
+
+nsIFrame*
+NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsHTMLCanvasFrame(aContext);
+}
+
+NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame)
+ NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame)
+
+void
+nsHTMLCanvasFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ // We can fill in the canvas before the canvas frame is created, in
+ // which case we never get around to marking the content as active. Therefore,
+ // we mark it active here when we create the frame.
+ ActiveLayerTracker::NotifyContentChange(this);
+}
+
+nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
+{
+}
+
+nsIntSize
+nsHTMLCanvasFrame::GetCanvasSize()
+{
+ nsIntSize size(0,0);
+ HTMLCanvasElement *canvas =
+ HTMLCanvasElement::FromContentOrNull(GetContent());
+ if (canvas) {
+ size = canvas->GetSize();
+ MOZ_ASSERT(size.width >= 0 && size.height >= 0,
+ "we should've required <canvas> width/height attrs to be "
+ "unsigned (non-negative) values");
+ } else {
+ NS_NOTREACHED("couldn't get canvas size");
+ }
+
+ return size;
+}
+
+/* virtual */ nscoord
+nsHTMLCanvasFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ // XXX The caller doesn't account for constraints of the height,
+ // min-height, and max-height properties.
+ bool vertical = GetWritingMode().IsVertical();
+ nscoord result = nsPresContext::CSSPixelsToAppUnits(
+ vertical ? GetCanvasSize().height : GetCanvasSize().width);
+ DISPLAY_MIN_WIDTH(this, result);
+ return result;
+}
+
+/* virtual */ nscoord
+nsHTMLCanvasFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ // XXX The caller doesn't account for constraints of the height,
+ // min-height, and max-height properties.
+ bool vertical = GetWritingMode().IsVertical();
+ nscoord result = nsPresContext::CSSPixelsToAppUnits(
+ vertical ? GetCanvasSize().height : GetCanvasSize().width);
+ DISPLAY_PREF_WIDTH(this, result);
+ return result;
+}
+
+/* virtual */ IntrinsicSize
+nsHTMLCanvasFrame::GetIntrinsicSize()
+{
+ return IntrinsicSizeFromCanvasSize(GetCanvasSize());
+}
+
+/* virtual */ nsSize
+nsHTMLCanvasFrame::GetIntrinsicRatio()
+{
+ return IntrinsicRatioFromCanvasSize(GetCanvasSize());
+}
+
+/* virtual */
+LogicalSize
+nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ nsIntSize size = GetCanvasSize();
+
+ IntrinsicSize intrinsicSize;
+ intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width));
+ intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height));
+
+ nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used
+
+ return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
+ intrinsicSize, intrinsicRatio,
+ aCBSize, aMargin, aBorder, aPadding,
+ aFlags);
+}
+
+void
+nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
+ aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalSize finalSize(wm,
+ aReflowInput.ComputedISize(),
+ aReflowInput.ComputedBSize());
+
+ // stash this away so we can compute our inner area later
+ mBorderPadding = aReflowInput.ComputedLogicalBorderPadding();
+
+ finalSize.ISize(wm) += mBorderPadding.IStartEnd(wm);
+ finalSize.BSize(wm) += mBorderPadding.BStartEnd(wm);
+
+ if (GetPrevInFlow()) {
+ nscoord y = GetContinuationOffset(&finalSize.ISize(wm));
+ finalSize.BSize(wm) -= y + mBorderPadding.BStart(wm);
+ finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
+ }
+
+ aMetrics.SetSize(wm, finalSize);
+ aMetrics.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aMetrics);
+
+ // Reflow the single anon block child.
+ nsReflowStatus childStatus;
+ nsIFrame* childFrame = mFrames.FirstChild();
+ WritingMode childWM = childFrame->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(childWM);
+ availSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
+ NS_ASSERTION(!childFrame->GetNextSibling(), "HTML canvas should have 1 kid");
+ ReflowOutput childDesiredSize(aReflowInput.GetWritingMode(), aMetrics.mFlags);
+ ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
+ availSize);
+ ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
+ 0, 0, 0, childStatus, nullptr);
+ FinishReflowChild(childFrame, aPresContext, childDesiredSize,
+ &childReflowInput, 0, 0, 0);
+
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
+ aMetrics.ISize(wm), aMetrics.BSize(wm)));
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+// FIXME taken from nsImageFrame, but then had splittable frame stuff
+// removed. That needs to be fixed.
+// XXXdholbert As in nsImageFrame, this function's clients should probably
+// just be calling GetContentRectRelativeToSelf().
+nsRect
+nsHTMLCanvasFrame::GetInnerArea() const
+{
+ nsMargin bp = mBorderPadding.GetPhysicalMargin(GetWritingMode());
+ nsRect r;
+ r.x = bp.left;
+ r.y = bp.top;
+ r.width = mRect.width - bp.left - bp.right;
+ r.height = mRect.height - bp.top - bp.bottom;
+ return r;
+}
+
+already_AddRefed<Layer>
+nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ const ContainerLayerParameters& aContainerParameters)
+{
+ nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
+ HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent());
+ nsIntSize canvasSizeInPx = GetCanvasSize();
+
+ nsPresContext* presContext = PresContext();
+ element->HandlePrintCallback(presContext->Type());
+
+ if (canvasSizeInPx.width <= 0 || canvasSizeInPx.height <= 0 || area.IsEmpty())
+ return nullptr;
+
+ CanvasLayer* oldLayer = static_cast<CanvasLayer*>
+ (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
+ RefPtr<Layer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
+ if (!layer)
+ return nullptr;
+
+ IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx);
+ nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx);
+
+ nsRect dest =
+ nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, intrinsicRatio,
+ StylePosition());
+
+ gfxRect destGFXRect = presContext->AppUnitsToGfxUnits(dest);
+
+ // Transform the canvas into the right place
+ gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
+ Matrix transform = Matrix::Translation(p.x, p.y);
+ transform.PreScale(destGFXRect.Width() / canvasSizeInPx.width,
+ destGFXRect.Height() / canvasSizeInPx.height);
+ layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
+ if (layer->GetType() == layers::Layer::TYPE_CANVAS) {
+ RefPtr<CanvasLayer> canvasLayer = static_cast<CanvasLayer*>(layer.get());
+ canvasLayer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
+ } else if (layer->GetType() == layers::Layer::TYPE_IMAGE) {
+ RefPtr<ImageLayer> imageLayer = static_cast<ImageLayer*>(layer.get());
+ imageLayer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
+ }
+
+ return layer.forget();
+}
+
+void
+nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ uint32_t clipFlags =
+ nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
+ 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
+
+ DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
+ clip(aBuilder, this, clipFlags);
+
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayCanvas(aBuilder, this));
+
+ DisplaySelectionOverlay(aBuilder, aLists.Content(),
+ nsISelectionDisplay::DISPLAY_IMAGES);
+}
+
+nsIAtom*
+nsHTMLCanvasFrame::GetType() const
+{
+ return nsGkAtoms::HTMLCanvasFrame;
+}
+
+// get the offset into the content area of the image where aImg starts if it is a continuation.
+// from nsImageFrame
+nscoord
+nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const
+{
+ nscoord offset = 0;
+ if (aWidth) {
+ *aWidth = 0;
+ }
+
+ if (GetPrevInFlow()) {
+ for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
+ nsRect rect = prevInFlow->GetRect();
+ if (aWidth) {
+ *aWidth = rect.width;
+ }
+ offset += rect.height;
+ }
+ offset -= mBorderPadding.GetPhysicalMargin(GetWritingMode()).top;
+ offset = std::max(0, offset);
+ }
+ return offset;
+}
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsHTMLCanvasFrame::AccessibleType()
+{
+ return a11y::eHTMLCanvasType;
+}
+#endif
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult);
+}
+#endif
+
diff --git a/layout/generic/nsHTMLCanvasFrame.h b/layout/generic/nsHTMLCanvasFrame.h
new file mode 100644
index 000000000..e4235deae
--- /dev/null
+++ b/layout/generic/nsHTMLCanvasFrame.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for the HTML <canvas> element */
+
+#ifndef nsHTMLCanvasFrame_h___
+#define nsHTMLCanvasFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+#include "FrameLayerBuilder.h"
+
+namespace mozilla {
+namespace layers {
+class Layer;
+class LayerManager;
+} // namespace layers
+} // namespace mozilla
+
+class nsPresContext;
+class nsDisplayItem;
+class nsAString;
+
+nsIFrame* NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+class nsHTMLCanvasFrame : public nsContainerFrame
+{
+public:
+ typedef mozilla::layers::Layer Layer;
+ typedef mozilla::layers::LayerManager LayerManager;
+ typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
+
+ NS_DECL_QUERYFRAME_TARGET(nsHTMLCanvasFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsHTMLCanvasFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+ , mBorderPadding(GetWritingMode()) {}
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ const ContainerLayerParameters& aContainerParameters);
+
+ /* get the size of the canvas's image */
+ nsIntSize GetCanvasSize();
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual mozilla::IntrinsicSize GetIntrinsicSize() override;
+ virtual nsSize GetIntrinsicRatio() override;
+
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ nsRect GetInnerArea() const;
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsSplittableFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ // Inserted child content gets its frames parented by our child block
+ virtual nsContainerFrame* GetContentInsertionFrame() override {
+ return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
+ }
+
+protected:
+ virtual ~nsHTMLCanvasFrame();
+
+ nscoord GetContinuationOffset(nscoord* aWidth = 0) const;
+
+ mozilla::LogicalMargin mBorderPadding;
+};
+
+#endif /* nsHTMLCanvasFrame_h___ */
diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h
new file mode 100644
index 000000000..89a7a6edd
--- /dev/null
+++ b/layout/generic/nsHTMLParts.h
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* factory functions for rendering object classes */
+
+#ifndef nsHTMLParts_h___
+#define nsHTMLParts_h___
+
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nsIFrame.h"
+class nsIAtom;
+class nsNodeInfoManager;
+class nsIContent;
+class nsIDocument;
+class nsIFrame;
+class nsIHTMLContentSink;
+class nsIFragmentContentSink;
+class nsStyleContext;
+class nsIURI;
+class nsIPresShell;
+class nsIChannel;
+class nsTableColFrame;
+
+// These are all the block specific frame bits, they are copied from
+// the prev-in-flow to a newly created next-in-flow, except for the
+// NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
+#define NS_BLOCK_FLAGS_MASK (NS_BLOCK_MARGIN_ROOT | \
+ NS_BLOCK_FLOAT_MGR | \
+ NS_BLOCK_CLIP_PAGINATED_OVERFLOW | \
+ NS_BLOCK_HAS_FIRST_LETTER_STYLE | \
+ NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET | \
+ NS_BLOCK_HAS_FIRST_LETTER_CHILD | \
+ NS_BLOCK_FRAME_HAS_INSIDE_BULLET)
+
+// This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
+// by default. They should only be set on the first-in-flow.
+// See nsBlockFrame::Init.
+#define NS_BLOCK_FLAGS_NON_INHERITED_MASK \
+ (NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET | \
+ NS_BLOCK_HAS_FIRST_LETTER_CHILD | \
+ NS_BLOCK_FRAME_HAS_INSIDE_BULLET)
+
+// Factory methods for creating html layout objects
+
+// Create a frame that supports "display: block" layout behavior
+class nsBlockFrame;
+nsBlockFrame*
+NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+// Special Generated Content Node. It contains text taken from an
+// attribute of its *grandparent* content node.
+nsresult
+NS_NewAttributeContent(nsNodeInfoManager *aNodeInfoManager,
+ int32_t aNameSpaceID, nsIAtom* aAttrName,
+ nsIContent** aResult);
+
+// Create a basic area frame but the GetFrameForPoint is overridden to always
+// return the option frame
+// By default, area frames will extend
+// their height to cover any children that "stick out".
+nsContainerFrame*
+NS_NewSelectsAreaFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags);
+
+// Create a block formatting context blockframe
+nsBlockFrame*
+NS_NewBlockFormattingContext(nsIPresShell* aPresShell, nsStyleContext* aStyleContext);
+
+nsIFrame*
+NS_NewBRFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+nsIFrame*
+NS_NewCommentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+// <frame> and <iframe>
+nsIFrame*
+NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+// <frameset>
+nsIFrame*
+NS_NewHTMLFramesetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+class ViewportFrame;
+ViewportFrame*
+NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsCanvasFrame;
+nsCanvasFrame*
+NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsInlineFrame;
+nsInlineFrame*
+NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+inline nsIFrame*
+NS_NewWBRFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) {
+ return NS_NewEmptyFrame(aPresShell, aContext);
+}
+
+nsContainerFrame*
+NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags);
+
+class nsSimplePageSequenceFrame;
+nsSimplePageSequenceFrame*
+NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsPageFrame;
+nsPageFrame*
+NS_NewPageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsPageContentFrame;
+nsPageContentFrame*
+NS_NewPageContentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewPageBreakFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsFirstLetterFrame;
+nsFirstLetterFrame*
+NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsFirstLineFrame;
+nsFirstLineFrame*
+NS_NewFirstLineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+// forms
+nsContainerFrame*
+NS_NewGfxButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewNativeButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewImageControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsContainerFrame*
+NS_NewHTMLButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewGfxCheckboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewNativeCheckboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsContainerFrame*
+NS_NewFieldSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewColorControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewLegendFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewNativeTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewGfxAutoTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewGfxRadioControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewNativeRadioControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewNativeSelectControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsContainerFrame*
+NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsContainerFrame*
+NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags);
+nsIFrame*
+NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewMeterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewRangeFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewNumberControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsBlockFrame*
+NS_NewDetailsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+// Table frame factories
+class nsTableWrapperFrame;
+nsTableWrapperFrame*
+NS_NewTableWrapperFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsTableFrame;
+nsTableFrame*
+NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsTableColFrame*
+NS_NewTableColFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsTableColGroupFrame;
+nsTableColGroupFrame*
+NS_NewTableColGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsTableRowFrame;
+nsTableRowFrame*
+NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsTableRowGroupFrame;
+nsTableRowGroupFrame*
+NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+class nsTableCellFrame;
+nsTableCellFrame*
+NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsTableFrame* aTableFrame);
+
+nsresult
+NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult,
+ nsIDocument* aDoc, nsIURI* aURL,
+ nsISupports* aContainer, // e.g. docshell
+ nsIChannel* aChannel);
+nsresult
+NS_NewHTMLFragmentContentSink(nsIFragmentContentSink** aInstancePtrResult);
+nsresult
+NS_NewHTMLFragmentContentSink2(nsIFragmentContentSink** aInstancePtrResult);
+
+#endif /* nsHTMLParts_h___ */
diff --git a/layout/generic/nsIAnonymousContentCreator.h b/layout/generic/nsIAnonymousContentCreator.h
new file mode 100644
index 000000000..e7d4399b6
--- /dev/null
+++ b/layout/generic/nsIAnonymousContentCreator.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * interface for rendering objects that manually create subtrees of
+ * anonymous content
+ */
+
+#ifndef nsIAnonymousContentCreator_h___
+#define nsIAnonymousContentCreator_h___
+
+#include "nsQueryFrame.h"
+#include "nsStyleContext.h"
+#include "nsTArrayForwardDeclare.h"
+
+class nsIContent;
+class nsIFrame;
+
+/**
+ * Any source for anonymous content can implement this interface to provide it.
+ * HTML frames like nsFileControlFrame currently use this.
+ *
+ * @see nsCSSFrameConstructor
+ */
+class nsIAnonymousContentCreator
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsIAnonymousContentCreator)
+
+ struct ContentInfo {
+ explicit ContentInfo(nsIContent* aContent) :
+ mContent(aContent)
+ {}
+
+ ContentInfo(nsIContent* aContent, nsStyleContext* aStyleContext) :
+ mContent(aContent), mStyleContext(aStyleContext)
+ {}
+
+ nsIContent* mContent;
+ RefPtr<nsStyleContext> mStyleContext;
+ nsTArray<ContentInfo> mChildren;
+ };
+
+ /**
+ * Creates "native" anonymous content and adds the created content to
+ * the aElements array. None of the returned elements can be nullptr.
+ *
+ * If the anonymous content creator sets the editable flag on some
+ * of the elements that it creates, the flag will be applied to the node
+ * upon being bound to the document.
+ *
+ * @note The returned elements are owned by this object. This object is
+ * responsible for calling UnbindFromTree on the elements it returned
+ * from CreateAnonymousContent when appropriate (i.e. before releasing
+ * them).
+ *
+ * @note Implementations of this method that add items to mChildren must not
+ * hook them up to any parent since frame construction takes care of
+ * that.
+ */
+ virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements)=0;
+
+ /**
+ * Appends "native" anonymous children created by CreateAnonymousContent()
+ * to the given content list depending on the filter.
+ *
+ * @see nsIContent::GetChildren for set of values used for filter.
+ */
+ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilter) = 0;
+
+ /**
+ * Implementations can override this method to create special frames for the
+ * anonymous content returned from CreateAnonymousContent.
+ * By default this method returns nullptr, which means the default frame
+ * is created.
+ */
+ virtual nsIFrame* CreateFrameFor(nsIContent* aContent) { return nullptr; }
+};
+
+#endif
+
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
new file mode 100644
index 000000000..50eb958e0
--- /dev/null
+++ b/layout/generic/nsIFrame.h
@@ -0,0 +1,4021 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* interface for all rendering objects */
+
+#ifndef nsIFrame_h___
+#define nsIFrame_h___
+
+#ifndef MOZILLA_INTERNAL_API
+#error This header/class should only be used within Mozilla code. It should not be used by extensions.
+#endif
+
+#define MAX_REFLOW_DEPTH 200
+
+/* nsIFrame is in the process of being deCOMtaminated, i.e., this file is eventually
+ going to be eliminated, and all callers will use nsFrame instead. At the moment
+ we're midway through this process, so you will see inlined functions and member
+ variables in this file. -dwh */
+
+#include <algorithm>
+#include <stdio.h>
+
+#include "CaretAssociationHint.h"
+#include "FramePropertyTable.h"
+#include "mozilla/layout/FrameChildList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/WritingModes.h"
+#include "nsDirection.h"
+#include "nsFrameList.h"
+#include "nsFrameState.h"
+#include "mozilla/ReflowOutput.h"
+#include "nsITheme.h"
+#include "nsLayoutUtils.h"
+#include "nsQueryFrame.h"
+#include "nsStringGlue.h"
+#include "nsStyleContext.h"
+#include "nsStyleStruct.h"
+#include "Visibility.h"
+
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/AccTypes.h"
+#endif
+
+/**
+ * New rules of reflow:
+ * 1. you get a WillReflow() followed by a Reflow() followed by a DidReflow() in order
+ * (no separate pass over the tree)
+ * 2. it's the parent frame's responsibility to size/position the child's view (not
+ * the child frame's responsibility as it is today) during reflow (and before
+ * sending the DidReflow() notification)
+ * 3. positioning of child frames (and their views) is done on the way down the tree,
+ * and sizing of child frames (and their views) on the way back up
+ * 4. if you move a frame (outside of the reflow process, or after reflowing it),
+ * then you must make sure that its view (or its child frame's views) are re-positioned
+ * as well. It's reasonable to not position the view until after all reflowing the
+ * entire line, for example, but the frame should still be positioned and sized (and
+ * the view sized) during the reflow (i.e., before sending the DidReflow() notification)
+ * 5. the view system handles moving of widgets, i.e., it's not our problem
+ */
+
+class nsIAtom;
+class nsPresContext;
+class nsIPresShell;
+class nsRenderingContext;
+class nsView;
+class nsIWidget;
+class nsISelectionController;
+class nsBoxLayoutState;
+class nsBoxLayout;
+class nsILineIterator;
+class nsDisplayListBuilder;
+class nsDisplayListSet;
+class nsDisplayList;
+class gfxSkipChars;
+class gfxSkipCharsIterator;
+class gfxContext;
+class nsLineList_iterator;
+class nsAbsoluteContainingBlock;
+class nsIContent;
+class nsContainerFrame;
+
+struct nsPeekOffsetStruct;
+struct nsPoint;
+struct nsRect;
+struct nsSize;
+struct nsMargin;
+struct CharacterDataChangeInfo;
+
+namespace mozilla {
+
+enum class CSSPseudoElementType : uint8_t;
+class EventStates;
+struct ReflowInput;
+class ReflowOutput;
+
+namespace layers {
+class Layer;
+} // namespace layers
+
+namespace gfx {
+class Matrix;
+} // namespace gfx
+} // namespace mozilla
+
+/**
+ * Indication of how the frame can be split. This is used when doing runaround
+ * of floats, and when pulling up child frames from a next-in-flow.
+ *
+ * The choices are splittable, not splittable at all, and splittable in
+ * a non-rectangular fashion. This last type only applies to block-level
+ * elements, and indicates whether splitting can be used when doing runaround.
+ * If you can split across page boundaries, but you expect each continuing
+ * frame to be the same width then return frSplittable and not
+ * frSplittableNonRectangular.
+ *
+ * @see #GetSplittableType()
+ */
+typedef uint32_t nsSplittableType;
+
+#define NS_FRAME_NOT_SPLITTABLE 0 // Note: not a bit!
+#define NS_FRAME_SPLITTABLE 0x1
+#define NS_FRAME_SPLITTABLE_NON_RECTANGULAR 0x3
+
+#define NS_FRAME_IS_SPLITTABLE(type)\
+ (0 != ((type) & NS_FRAME_SPLITTABLE))
+
+#define NS_FRAME_IS_NOT_SPLITTABLE(type)\
+ (0 == ((type) & NS_FRAME_SPLITTABLE))
+
+#define NS_INTRINSIC_WIDTH_UNKNOWN nscoord_MIN
+
+//----------------------------------------------------------------------
+
+#define NS_SUBTREE_DIRTY(_frame) \
+ (((_frame)->GetStateBits() & \
+ (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0)
+
+/**
+ * Constant used to indicate an unconstrained size.
+ *
+ * @see #Reflow()
+ */
+#define NS_UNCONSTRAINEDSIZE NS_MAXSIZE
+
+#define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE
+#define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE
+// +1 is to avoid clamped huge margin values being processed as auto margins
+#define NS_AUTOMARGIN (NS_UNCONSTRAINEDSIZE + 1)
+#define NS_AUTOOFFSET NS_UNCONSTRAINEDSIZE
+// NOTE: there are assumptions all over that these have the same value, namely NS_UNCONSTRAINEDSIZE
+// if any are changed to be a value other than NS_UNCONSTRAINEDSIZE
+// at least update AdjustComputedHeight/Width and test ad nauseum
+
+// 1 million CSS pixels less than our max app unit measure.
+// For reflowing with an "infinite" available inline space per [css-sizing].
+// (reflowing with an NS_UNCONSTRAINEDSIZE available inline size isn't allowed
+// and leads to assertions)
+#define INFINITE_ISIZE_COORD nscoord(NS_MAXSIZE - (1000000*60))
+
+//----------------------------------------------------------------------
+
+enum nsSelectionAmount {
+ eSelectCharacter = 0, // a single Unicode character;
+ // do not use this (prefer Cluster) unless you
+ // are really sure it's what you want
+ eSelectCluster = 1, // a grapheme cluster: this is usually the right
+ // choice for movement or selection by "character"
+ // as perceived by the user
+ eSelectWord = 2,
+ eSelectWordNoSpace = 3, // select a "word" without selecting the following
+ // space, no matter what the default platform
+ // behavior is
+ eSelectLine = 4, // previous drawn line in flow.
+ // NOTE that selection code depends on the ordering of the above values,
+ // allowing simple <= tests to check categories of caret movement.
+ // Don't rearrange without checking the usage in nsSelection.cpp!
+
+ eSelectBeginLine = 5,
+ eSelectEndLine = 6,
+ eSelectNoAmount = 7, // just bounce back current offset.
+ eSelectParagraph = 8 // select a "paragraph"
+};
+
+enum nsSpread {
+ eSpreadNone = 0,
+ eSpreadAcross = 1,
+ eSpreadDown = 2
+};
+
+// Carried out margin flags
+#define NS_CARRIED_TOP_MARGIN_IS_AUTO 0x1
+#define NS_CARRIED_BOTTOM_MARGIN_IS_AUTO 0x2
+
+//----------------------------------------------------------------------
+
+/**
+ * Reflow status returned by the reflow methods. There are three
+ * completion statuses, represented by two bit flags.
+ *
+ * NS_FRAME_COMPLETE means the frame is fully complete.
+ *
+ * NS_FRAME_NOT_COMPLETE bit flag means the frame does not map all its
+ * content, and that the parent frame should create a continuing frame.
+ * If this bit isn't set it means the frame does map all its content.
+ * This bit is mutually exclusive with NS_FRAME_OVERFLOW_INCOMPLETE.
+ *
+ * NS_FRAME_OVERFLOW_INCOMPLETE bit flag means that the frame has
+ * overflow that is not complete, but its own box is complete.
+ * (This happens when content overflows a fixed-height box.)
+ * The reflower should place and size the frame and continue its reflow,
+ * but needs to create an overflow container as a continuation for this
+ * frame. See nsContainerFrame.h for more information.
+ * This bit is mutually exclusive with NS_FRAME_NOT_COMPLETE.
+ *
+ * Please use the SET macro for handling
+ * NS_FRAME_NOT_COMPLETE and NS_FRAME_OVERFLOW_INCOMPLETE.
+ *
+ * NS_FRAME_REFLOW_NEXTINFLOW bit flag means that the next-in-flow is
+ * dirty, and also needs to be reflowed. This status only makes sense
+ * for a frame that is not complete, i.e. you wouldn't set both
+ * NS_FRAME_COMPLETE and NS_FRAME_REFLOW_NEXTINFLOW.
+ *
+ * The low 8 bits of the nsReflowStatus are reserved for future extensions;
+ * the remaining 24 bits are zero (and available for extensions; however
+ * API's that accept/return nsReflowStatus must not receive/return any
+ * extension bits).
+ *
+ * @see #Reflow()
+ */
+typedef uint32_t nsReflowStatus;
+
+#define NS_FRAME_COMPLETE 0 // Note: not a bit!
+#define NS_FRAME_NOT_COMPLETE 0x1
+#define NS_FRAME_REFLOW_NEXTINFLOW 0x2
+#define NS_FRAME_OVERFLOW_INCOMPLETE 0x4
+
+#define NS_FRAME_IS_COMPLETE(status) \
+ (0 == ((status) & NS_FRAME_NOT_COMPLETE))
+
+#define NS_FRAME_IS_NOT_COMPLETE(status) \
+ (0 != ((status) & NS_FRAME_NOT_COMPLETE))
+
+#define NS_FRAME_OVERFLOW_IS_INCOMPLETE(status) \
+ (0 != ((status) & NS_FRAME_OVERFLOW_INCOMPLETE))
+
+#define NS_FRAME_IS_FULLY_COMPLETE(status) \
+ (NS_FRAME_IS_COMPLETE(status) && !NS_FRAME_OVERFLOW_IS_INCOMPLETE(status))
+
+// These macros set or switch incomplete statuses without touching the
+// NS_FRAME_REFLOW_NEXTINFLOW bit.
+#define NS_FRAME_SET_INCOMPLETE(status) \
+ status = (status & ~NS_FRAME_OVERFLOW_INCOMPLETE) | NS_FRAME_NOT_COMPLETE
+
+#define NS_FRAME_SET_OVERFLOW_INCOMPLETE(status) \
+ status = (status & ~NS_FRAME_NOT_COMPLETE) | NS_FRAME_OVERFLOW_INCOMPLETE
+
+// This bit is set, when a break is requested. This bit is orthogonal
+// to the nsIFrame::nsReflowStatus completion bits.
+#define NS_INLINE_BREAK 0x0100
+
+// When a break is requested, this bit when set indicates that the
+// break should occur after the frame just reflowed; when the bit is
+// clear the break should occur before the frame just reflowed.
+#define NS_INLINE_BREAK_BEFORE 0x0000
+#define NS_INLINE_BREAK_AFTER 0x0200
+
+// The type of break requested can be found in these bits.
+#define NS_INLINE_BREAK_TYPE_MASK 0xF000
+
+// Set when a break was induced by completion of a first-letter
+#define NS_INLINE_BREAK_FIRST_LETTER_COMPLETE 0x10000
+
+//----------------------------------------
+// Macros that use those bits
+
+#define NS_INLINE_IS_BREAK(_status) \
+ (0 != ((_status) & NS_INLINE_BREAK))
+
+#define NS_INLINE_IS_BREAK_AFTER(_status) \
+ (0 != ((_status) & NS_INLINE_BREAK_AFTER))
+
+#define NS_INLINE_IS_BREAK_BEFORE(_status) \
+ (NS_INLINE_BREAK == ((_status) & (NS_INLINE_BREAK|NS_INLINE_BREAK_AFTER)))
+
+#define NS_INLINE_GET_BREAK_TYPE(_status) \
+ (static_cast<StyleClear>(((_status) >> 12) & 0xF))
+
+#define NS_INLINE_MAKE_BREAK_TYPE(_type) (static_cast<int>(_type) << 12)
+
+// Construct a line-break-before status. Note that there is no
+// completion status for a line-break before because we *know* that
+// the frame will be reflowed later and hence its current completion
+// status doesn't matter.
+#define NS_INLINE_LINE_BREAK_BEFORE() \
+ (NS_INLINE_BREAK | NS_INLINE_BREAK_BEFORE | \
+ NS_INLINE_MAKE_BREAK_TYPE(StyleClear::Line))
+
+// Take a completion status and add to it the desire to have a
+// line-break after. For this macro we do need the completion status
+// because the user of the status will need to know whether to
+// continue the frame or not.
+#define NS_INLINE_LINE_BREAK_AFTER(_completionStatus) \
+ ((_completionStatus) | NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER | \
+ NS_INLINE_MAKE_BREAK_TYPE(StyleClear::Line))
+
+// A frame is "truncated" if the part of the frame before the first
+// possible break point was unable to fit in the available vertical
+// space. Therefore, the entire frame should be moved to the next page.
+// A frame that begins at the top of the page must never be "truncated".
+// Doing so would likely cause an infinite loop.
+#define NS_FRAME_TRUNCATED 0x0010
+#define NS_FRAME_IS_TRUNCATED(status) \
+ (0 != ((status) & NS_FRAME_TRUNCATED))
+#define NS_FRAME_SET_TRUNCATION(status, aReflowInput, aMetrics) \
+ aReflowInput.SetTruncated(aMetrics, &status);
+
+// Merge the incompleteness, truncation and NS_FRAME_REFLOW_NEXTINFLOW
+// status from aSecondary into aPrimary.
+void NS_MergeReflowStatusInto(nsReflowStatus* aPrimary,
+ nsReflowStatus aSecondary);
+
+//----------------------------------------------------------------------
+
+/**
+ * DidReflow status values.
+ */
+enum class nsDidReflowStatus : uint32_t {
+ NOT_FINISHED,
+ FINISHED
+};
+
+/**
+ * When there is no scrollable overflow rect, the visual overflow rect
+ * may be stored as four 1-byte deltas each strictly LESS THAN 0xff, for
+ * the four edges of the rectangle, or the four bytes may be read as a
+ * single 32-bit "overflow-rect type" value including at least one 0xff
+ * byte as an indicator that the value does NOT represent four deltas.
+ * If all four deltas are zero, this means that no overflow rect has
+ * actually been set (this is the initial state of newly-created frames).
+ */
+#define NS_FRAME_OVERFLOW_DELTA_MAX 0xfe // max delta we can store
+
+#define NS_FRAME_OVERFLOW_NONE 0x00000000 // there are no overflow rects;
+ // code relies on this being
+ // the all-zero value
+
+#define NS_FRAME_OVERFLOW_LARGE 0x000000ff // overflow is stored as a
+ // separate rect property
+
+/**
+ * nsBidiLevel is the type of the level values in our Unicode Bidi
+ * implementation.
+ * It holds an embedding level and indicates the visual direction
+ * by its bit 0 (even/odd value).<p>
+ *
+ * <li><code>aParaLevel</code> can be set to the
+ * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code>
+ * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul>
+ *
+ * @see nsBidi::SetPara
+ *
+ * <p>The related constants are not real, valid level values.
+ * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify
+ * a default for the paragraph level for
+ * when the <code>SetPara</code> function
+ * shall determine it but there is no
+ * strongly typed character in the input.<p>
+ *
+ * Note that the value for <code>NSBIDI_DEFAULT_LTR</code> is even
+ * and the one for <code>NSBIDI_DEFAULT_RTL</code> is odd,
+ * just like with normal LTR and RTL level values -
+ * these special values are designed that way. Also, the implementation
+ * assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
+ *
+ * @see NSBIDI_DEFAULT_LTR
+ * @see NSBIDI_DEFAULT_RTL
+ * @see NSBIDI_LEVEL_OVERRIDE
+ * @see NSBIDI_MAX_EXPLICIT_LEVEL
+ */
+typedef uint8_t nsBidiLevel;
+
+/** Paragraph level setting.
+ * If there is no strong character, then set the paragraph level to 0 (left-to-right).
+ */
+#define NSBIDI_DEFAULT_LTR 0xfe
+
+/** Paragraph level setting.
+ * If there is no strong character, then set the paragraph level to 1 (right-to-left).
+ */
+#define NSBIDI_DEFAULT_RTL 0xff
+
+/**
+ * Maximum explicit embedding level.
+ * (The maximum resolved level can be up to <code>NSBIDI_MAX_EXPLICIT_LEVEL+1</code>).
+ *
+ */
+#define NSBIDI_MAX_EXPLICIT_LEVEL 125
+
+/** Bit flag for level input.
+ * Overrides directional properties.
+ */
+#define NSBIDI_LEVEL_OVERRIDE 0x80
+
+/**
+ * <code>nsBidiDirection</code> values indicate the text direction.
+ */
+enum nsBidiDirection {
+ /** All left-to-right text This is a 0 value. */
+ NSBIDI_LTR,
+ /** All right-to-left text This is a 1 value. */
+ NSBIDI_RTL,
+ /** Mixed-directional text. */
+ NSBIDI_MIXED
+};
+
+namespace mozilla {
+
+// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
+enum BaselineSharingGroup
+{
+ // NOTE Used as an array index so must be 0 and 1.
+ eFirst = 0,
+ eLast = 1,
+};
+
+// Loosely: https://drafts.csswg.org/css-align-3/#shared-alignment-context
+enum class AlignmentContext
+{
+ eInline,
+ eTable,
+ eFlexbox,
+ eGrid,
+};
+
+/*
+ * For replaced elements only. Gets the intrinsic dimensions of this element.
+ * The dimensions may only be one of the following two types:
+ *
+ * eStyleUnit_Coord - a length in app units
+ * eStyleUnit_None - the element has no intrinsic size in this dimension
+ */
+struct IntrinsicSize {
+ nsStyleCoord width, height;
+
+ IntrinsicSize()
+ : width(eStyleUnit_None), height(eStyleUnit_None)
+ {}
+ IntrinsicSize(const IntrinsicSize& rhs)
+ : width(rhs.width), height(rhs.height)
+ {}
+ IntrinsicSize& operator=(const IntrinsicSize& rhs) {
+ width = rhs.width; height = rhs.height; return *this;
+ }
+ bool operator==(const IntrinsicSize& rhs) {
+ return width == rhs.width && height == rhs.height;
+ }
+ bool operator!=(const IntrinsicSize& rhs) {
+ return !(*this == rhs);
+ }
+};
+
+// Pseudo bidi embedding level indicating nonexistence.
+static const nsBidiLevel kBidiLevelNone = 0xff;
+
+struct FrameBidiData
+{
+ nsBidiLevel baseLevel;
+ nsBidiLevel embeddingLevel;
+ // The embedding level of virtual bidi formatting character before
+ // this frame if any. kBidiLevelNone is used to indicate nonexistence
+ // or unnecessity of such virtual character.
+ nsBidiLevel precedingControl;
+};
+
+} // namespace mozilla
+
+/// Generic destructor for frame properties. Calls delete.
+template<typename T>
+static void DeleteValue(T* aPropertyValue)
+{
+ delete aPropertyValue;
+}
+
+/// Generic destructor for frame properties. Calls Release().
+template<typename T>
+static void ReleaseValue(T* aPropertyValue)
+{
+ aPropertyValue->Release();
+}
+
+//----------------------------------------------------------------------
+
+/**
+ * A frame in the layout model. This interface is supported by all frame
+ * objects.
+ *
+ * Frames can have multiple child lists: the default child list
+ * (referred to as the <i>principal</i> child list, and additional named
+ * child lists. There is an ordering of frames within a child list, but
+ * there is no order defined between frames in different child lists of
+ * the same parent frame.
+ *
+ * Frames are NOT reference counted. Use the Destroy() member function
+ * to destroy a frame. The lifetime of the frame hierarchy is bounded by the
+ * lifetime of the presentation shell which owns the frames.
+ *
+ * nsIFrame is a private Gecko interface. If you are not Gecko then you
+ * should not use it. If you're not in layout, then you won't be able to
+ * link to many of the functions defined here. Too bad.
+ *
+ * If you're not in layout but you must call functions in here, at least
+ * restrict yourself to calling virtual methods, which won't hurt you as badly.
+ */
+class nsIFrame : public nsQueryFrame
+{
+public:
+ using AlignmentContext = mozilla::AlignmentContext;
+ using BaselineSharingGroup = mozilla::BaselineSharingGroup;
+ template <typename T> using Maybe = mozilla::Maybe<T>;
+ using Nothing = mozilla::Nothing;
+ using OnNonvisible = mozilla::OnNonvisible;
+ template<typename T=void>
+ using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
+ using ReflowInput = mozilla::ReflowInput;
+ using ReflowOutput = mozilla::ReflowOutput;
+ using Visibility = mozilla::Visibility;
+
+ typedef mozilla::FrameProperties FrameProperties;
+ typedef mozilla::layers::Layer Layer;
+ typedef mozilla::layout::FrameChildList ChildList;
+ typedef mozilla::layout::FrameChildListID ChildListID;
+ typedef mozilla::layout::FrameChildListIDs ChildListIDs;
+ typedef mozilla::layout::FrameChildListIterator ChildListIterator;
+ typedef mozilla::layout::FrameChildListArrayIterator ChildListArrayIterator;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::Matrix Matrix;
+ typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+ typedef mozilla::Sides Sides;
+ typedef mozilla::LogicalSides LogicalSides;
+
+ NS_DECL_QUERYFRAME_TARGET(nsIFrame)
+
+ nsPresContext* PresContext() const {
+ return StyleContext()->PresContext();
+ }
+
+ /**
+ * Called to initialize the frame. This is called immediately after creating
+ * the frame.
+ *
+ * If the frame is a continuing frame, then aPrevInFlow indicates the previous
+ * frame (the frame that was split).
+ *
+ * If you want a view associated with your frame, you should create the view
+ * after Init() has returned.
+ *
+ * @param aContent the content object associated with the frame
+ * @param aParent the parent frame
+ * @param aPrevInFlow the prev-in-flow frame
+ */
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) = 0;
+
+ /**
+ * Destroys this frame and each of its child frames (recursively calls
+ * Destroy() for each child). If this frame is a first-continuation, this
+ * also removes the frame from the primary frame map and clears undisplayed
+ * content for its content node.
+ * If the frame is a placeholder, it also ensures the out-of-flow frame's
+ * removal and destruction.
+ */
+ void Destroy() { DestroyFrom(this); }
+
+ /** Flags for PeekOffsetCharacter, PeekOffsetNoAmount, PeekOffsetWord return values.
+ */
+ enum FrameSearchResult {
+ // Peek found a appropriate offset within frame.
+ FOUND = 0x00,
+ // try next frame for offset.
+ CONTINUE = 0x1,
+ // offset not found because the frame was empty of text.
+ CONTINUE_EMPTY = 0x2 | CONTINUE,
+ // offset not found because the frame didn't contain any text that could be selected.
+ CONTINUE_UNSELECTABLE = 0x4 | CONTINUE,
+ };
+
+protected:
+ /**
+ * Return true if the frame is part of a Selection.
+ * Helper method to implement the public IsSelected() API.
+ */
+ virtual bool IsFrameSelected() const;
+
+ /**
+ * Implements Destroy(). Do not call this directly except from within a
+ * DestroyFrom() implementation.
+ *
+ * @note This will always be called, so it is not necessary to override
+ * Destroy() in subclasses of nsFrame, just DestroyFrom().
+ *
+ * @param aDestructRoot is the root of the subtree being destroyed
+ */
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0;
+ friend class nsFrameList; // needed to pass aDestructRoot through to children
+ friend class nsLineBox; // needed to pass aDestructRoot through to children
+ friend class nsContainerFrame; // needed to pass aDestructRoot through to children
+ friend class nsFrame; // need to assign mParent
+public:
+
+ /**
+ * Get the content object associated with this frame. Does not add a reference.
+ */
+ nsIContent* GetContent() const { return mContent; }
+
+ /**
+ * Get the frame that should be the parent for the frames of child elements
+ * May return nullptr during reflow
+ */
+ virtual nsContainerFrame* GetContentInsertionFrame() { return nullptr; }
+
+ /**
+ * Move any frames on our overflow list to the end of our principal list.
+ * @return true if there were any overflow frames
+ */
+ virtual bool DrainSelfOverflowList() { return false; }
+
+ /**
+ * Get the frame that should be scrolled if the content associated
+ * with this frame is targeted for scrolling. For frames implementing
+ * nsIScrollableFrame this will return the frame itself. For frames
+ * like nsTextControlFrame that contain a scrollframe, will return
+ * that scrollframe.
+ */
+ virtual nsIScrollableFrame* GetScrollTargetFrame() { return nullptr; }
+
+ /**
+ * Get the offsets of the frame. most will be 0,0
+ *
+ */
+ virtual nsresult GetOffsets(int32_t &start, int32_t &end) const = 0;
+
+ /**
+ * Reset the offsets when splitting frames during Bidi reordering
+ *
+ */
+ virtual void AdjustOffsetsForBidi(int32_t aStart, int32_t aEnd) {}
+
+ /**
+ * Get the style context associated with this frame.
+ */
+ nsStyleContext* StyleContext() const { return mStyleContext; }
+ void SetStyleContext(nsStyleContext* aContext)
+ {
+ if (aContext != mStyleContext) {
+ nsStyleContext* oldStyleContext = mStyleContext;
+ mStyleContext = aContext;
+ aContext->AddRef();
+#ifdef DEBUG
+ aContext->FrameAddRef();
+#endif
+ DidSetStyleContext(oldStyleContext);
+#ifdef DEBUG
+ oldStyleContext->FrameRelease();
+#endif
+ oldStyleContext->Release();
+ }
+ }
+
+ /**
+ * SetStyleContextWithoutNotification is for changes to the style
+ * context that should suppress style change processing, in other
+ * words, those that aren't really changes. This generally means only
+ * changes that happen during frame construction.
+ */
+ void SetStyleContextWithoutNotification(nsStyleContext* aContext)
+ {
+ if (aContext != mStyleContext) {
+#ifdef DEBUG
+ mStyleContext->FrameRelease();
+#endif
+ mStyleContext->Release();
+ mStyleContext = aContext;
+ aContext->AddRef();
+#ifdef DEBUG
+ aContext->FrameAddRef();
+#endif
+ }
+ }
+
+ // Style post processing hook
+ // Attention: the old style context is the one we're forgetting,
+ // and hence possibly completely bogus for GetStyle* purposes.
+ // Use PeekStyleData instead.
+ virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) = 0;
+
+ /**
+ * Define typesafe getter functions for each style struct by
+ * preprocessing the list of style structs. These functions are the
+ * preferred way to get style data. The macro creates functions like:
+ * const nsStyleBorder* StyleBorder();
+ * const nsStyleColor* StyleColor();
+ *
+ * Callers outside of libxul should use nsIDOMWindow::GetComputedStyle()
+ * instead of these accessors.
+ */
+ #define STYLE_STRUCT(name_, checkdata_cb_) \
+ const nsStyle##name_ * Style##name_ () const { \
+ NS_ASSERTION(mStyleContext, "No style context found!"); \
+ return mStyleContext->Style##name_ (); \
+ }
+ #include "nsStyleStructList.h"
+ #undef STYLE_STRUCT
+
+ /** Also forward GetVisitedDependentColor to the style context */
+ nscolor GetVisitedDependentColor(nsCSSPropertyID aProperty)
+ { return mStyleContext->GetVisitedDependentColor(aProperty); }
+
+ /**
+ * These methods are to access any additional style contexts that
+ * the frame may be holding. These are contexts that are children
+ * of the frame's primary context and are NOT used as style contexts
+ * for any child frames. These contexts also MUST NOT have any child
+ * contexts whatsoever. If you need to insert style contexts into the
+ * style tree, then you should create pseudo element frames to own them
+ * The indicies must be consecutive and implementations MUST return an
+ * NS_ERROR_INVALID_ARG if asked for an index that is out of range.
+ */
+ virtual nsStyleContext* GetAdditionalStyleContext(int32_t aIndex) const = 0;
+
+ virtual void SetAdditionalStyleContext(int32_t aIndex,
+ nsStyleContext* aStyleContext) = 0;
+
+ /**
+ * Accessor functions for geometric parent.
+ */
+ nsContainerFrame* GetParent() const { return mParent; }
+ /**
+ * Set this frame's parent to aParent.
+ * If the frame may have moved into or out of a scrollframe's
+ * frame subtree, StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary
+ * must also be called.
+ */
+ void SetParent(nsContainerFrame* aParent);
+
+ /**
+ * The frame's writing-mode, used for logical layout computations.
+ */
+ virtual mozilla::WritingMode GetWritingMode() const {
+ return mozilla::WritingMode(StyleContext());
+ }
+
+ /**
+ * Get the writing mode of this frame, but if it is styled with
+ * unicode-bidi: plaintext, reset the direction to the resolved paragraph
+ * level of the given subframe (typically the first frame on the line),
+ * not this frame's writing mode, because the container frame could be split
+ * by hard line breaks into multiple paragraphs with different base direction.
+ */
+ mozilla::WritingMode GetWritingMode(nsIFrame* aSubFrame) const;
+
+ /**
+ * Bounding rect of the frame. The values are in app units, and the origin is
+ * relative to the upper-left of the geometric parent. The size includes the
+ * content area, borders, and padding.
+ *
+ * Note: moving or sizing the frame does not affect the view's size or
+ * position.
+ */
+ nsRect GetRect() const { return mRect; }
+ nsPoint GetPosition() const { return mRect.TopLeft(); }
+ nsSize GetSize() const { return mRect.Size(); }
+ nsRect GetRectRelativeToSelf() const {
+ return nsRect(nsPoint(0, 0), mRect.Size());
+ }
+ /**
+ * Dimensions and position in logical coordinates in the frame's writing mode
+ * or another writing mode
+ */
+ mozilla::LogicalRect GetLogicalRect(const nsSize& aContainerSize) const {
+ return GetLogicalRect(GetWritingMode(), aContainerSize);
+ }
+ mozilla::LogicalPoint GetLogicalPosition(const nsSize& aContainerSize) const {
+ return GetLogicalPosition(GetWritingMode(), aContainerSize);
+ }
+ mozilla::LogicalSize GetLogicalSize() const {
+ return GetLogicalSize(GetWritingMode());
+ }
+ mozilla::LogicalRect GetLogicalRect(mozilla::WritingMode aWritingMode,
+ const nsSize& aContainerSize) const {
+ return mozilla::LogicalRect(aWritingMode, GetRect(), aContainerSize);
+ }
+ mozilla::LogicalPoint GetLogicalPosition(mozilla::WritingMode aWritingMode,
+ const nsSize& aContainerSize) const {
+ return GetLogicalRect(aWritingMode, aContainerSize).Origin(aWritingMode);
+ }
+ mozilla::LogicalSize GetLogicalSize(mozilla::WritingMode aWritingMode) const {
+ return mozilla::LogicalSize(aWritingMode, GetSize());
+ }
+ nscoord IStart(const nsSize& aContainerSize) const {
+ return IStart(GetWritingMode(), aContainerSize);
+ }
+ nscoord IStart(mozilla::WritingMode aWritingMode,
+ const nsSize& aContainerSize) const {
+ return GetLogicalPosition(aWritingMode, aContainerSize).I(aWritingMode);
+ }
+ nscoord BStart(const nsSize& aContainerSize) const {
+ return BStart(GetWritingMode(), aContainerSize);
+ }
+ nscoord BStart(mozilla::WritingMode aWritingMode,
+ const nsSize& aContainerSize) const {
+ return GetLogicalPosition(aWritingMode, aContainerSize).B(aWritingMode);
+ }
+ nscoord ISize() const { return ISize(GetWritingMode()); }
+ nscoord ISize(mozilla::WritingMode aWritingMode) const {
+ return GetLogicalSize(aWritingMode).ISize(aWritingMode);
+ }
+ nscoord BSize() const { return BSize(GetWritingMode()); }
+ nscoord BSize(mozilla::WritingMode aWritingMode) const {
+ return GetLogicalSize(aWritingMode).BSize(aWritingMode);
+ }
+ nscoord ContentBSize() const { return ContentBSize(GetWritingMode()); }
+ nscoord ContentBSize(mozilla::WritingMode aWritingMode) const {
+ auto bp = GetLogicalUsedBorderAndPadding(aWritingMode);
+ bp.ApplySkipSides(GetLogicalSkipSides());
+ return std::max(0, BSize(aWritingMode) - bp.BStartEnd(aWritingMode));
+ }
+
+ /**
+ * When we change the size of the frame's border-box rect, we may need to
+ * reset the overflow rect if it was previously stored as deltas.
+ * (If it is currently a "large" overflow and could be re-packed as deltas,
+ * we don't bother as the cost of the allocation has already been paid.)
+ */
+ void SetRect(const nsRect& aRect) {
+ if (mOverflow.mType != NS_FRAME_OVERFLOW_LARGE &&
+ mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
+ nsOverflowAreas overflow = GetOverflowAreas();
+ mRect = aRect;
+ SetOverflowAreas(overflow);
+ } else {
+ mRect = aRect;
+ }
+ }
+ /**
+ * Set this frame's rect from a logical rect in its own writing direction
+ */
+ void SetRect(const mozilla::LogicalRect& aRect,
+ const nsSize& aContainerSize) {
+ SetRect(GetWritingMode(), aRect, aContainerSize);
+ }
+ /**
+ * Set this frame's rect from a logical rect in a different writing direction
+ * (GetPhysicalRect will assert if the writing mode doesn't match)
+ */
+ void SetRect(mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalRect& aRect,
+ const nsSize& aContainerSize) {
+ SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerSize));
+ }
+
+ /**
+ * Set this frame's size from a logical size in its own writing direction.
+ * This leaves the frame's logical position unchanged, which means its
+ * physical position may change (for right-to-left modes).
+ */
+ void SetSize(const mozilla::LogicalSize& aSize) {
+ SetSize(GetWritingMode(), aSize);
+ }
+ /*
+ * Set this frame's size from a logical size in a different writing direction.
+ * This leaves the frame's logical position in the given mode unchanged,
+ * which means its physical position may change (for right-to-left modes).
+ */
+ void SetSize(mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aSize)
+ {
+ if ((!aWritingMode.IsVertical() && !aWritingMode.IsBidiLTR()) ||
+ aWritingMode.IsVerticalRL()) {
+ nscoord oldWidth = mRect.width;
+ SetSize(aSize.GetPhysicalSize(aWritingMode));
+ mRect.x -= mRect.width - oldWidth;
+ } else {
+ SetSize(aSize.GetPhysicalSize(aWritingMode));
+ }
+ }
+
+ /**
+ * Set this frame's physical size. This leaves the frame's physical position
+ * (topLeft) unchanged.
+ */
+ void SetSize(const nsSize& aSize) {
+ SetRect(nsRect(mRect.TopLeft(), aSize));
+ }
+
+ void SetPosition(const nsPoint& aPt) { mRect.MoveTo(aPt); }
+ void SetPosition(mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalPoint& aPt,
+ const nsSize& aContainerSize) {
+ // We subtract mRect.Size() from the container size to account for
+ // the fact that logical origins in RTL coordinate systems are at
+ // the top right of the frame instead of the top left.
+ mRect.MoveTo(aPt.GetPhysicalPoint(aWritingMode,
+ aContainerSize - mRect.Size()));
+ }
+
+ /**
+ * Move the frame, accounting for relative positioning. Use this when
+ * adjusting the frame's position by a known amount, to properly update its
+ * saved normal position (see GetNormalPosition below).
+ *
+ * This must be used only when moving a frame *after*
+ * ReflowInput::ApplyRelativePositioning is called. When moving
+ * a frame during the reflow process prior to calling
+ * ReflowInput::ApplyRelativePositioning, the position should
+ * simply be adjusted directly (e.g., using SetPosition()).
+ */
+ void MovePositionBy(const nsPoint& aTranslation);
+
+ /**
+ * As above, using a logical-point delta in a given writing mode.
+ */
+ void MovePositionBy(mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalPoint& aTranslation)
+ {
+ // The LogicalPoint represents a vector rather than a point within a
+ // rectangular coordinate space, so we use a null containerSize when
+ // converting logical to physical.
+ const nsSize nullContainerSize;
+ MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode,
+ nullContainerSize));
+ }
+
+ /**
+ * Return frame's rect without relative positioning
+ */
+ nsRect GetNormalRect() const;
+
+ /**
+ * Return frame's position without relative positioning
+ */
+ nsPoint GetNormalPosition() const;
+ mozilla::LogicalPoint
+ GetLogicalNormalPosition(mozilla::WritingMode aWritingMode,
+ const nsSize& aContainerSize) const
+ {
+ // Subtract the size of this frame from the container size to get
+ // the correct position in rtl frames where the origin is on the
+ // right instead of the left
+ return mozilla::LogicalPoint(aWritingMode,
+ GetNormalPosition(),
+ aContainerSize - mRect.Size());
+ }
+
+ virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild)
+ { return aChild->GetPosition(); }
+
+ nsPoint GetPositionIgnoringScrolling();
+
+ typedef AutoTArray<nsIContent*, 2> ContentArray;
+ static void DestroyContentArray(ContentArray* aArray);
+
+#define NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type, dtor) \
+ static const mozilla::FramePropertyDescriptor<type>* prop() { \
+ /* Use of constexpr caused startup crashes with MSVC2015u1 PGO. */ \
+ static const auto descriptor = \
+ mozilla::FramePropertyDescriptor<type>::NewWithDestructor<dtor>(); \
+ return &descriptor; \
+ }
+
+// Don't use this unless you really know what you're doing!
+#define NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(prop, type, dtor) \
+ static const mozilla::FramePropertyDescriptor<type>* prop() { \
+ /* Use of constexpr caused startup crashes with MSVC2015u1 PGO. */ \
+ static const auto descriptor = mozilla:: \
+ FramePropertyDescriptor<type>::NewWithDestructorWithFrame<dtor>(); \
+ return &descriptor; \
+ }
+
+#define NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(prop, type) \
+ static const mozilla::FramePropertyDescriptor<type>* prop() { \
+ /* Use of constexpr caused startup crashes with MSVC2015u1 PGO. */ \
+ static const auto descriptor = \
+ mozilla::FramePropertyDescriptor<type>::NewWithoutDestructor(); \
+ return &descriptor; \
+ }
+
+#define NS_DECLARE_FRAME_PROPERTY_DELETABLE(prop, type) \
+ NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type, DeleteValue)
+
+#define NS_DECLARE_FRAME_PROPERTY_RELEASABLE(prop, type) \
+ NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type, ReleaseValue)
+
+#define NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(prop, type) \
+ static void AssertOnDestroyingProperty##prop(type*) { \
+ MOZ_ASSERT_UNREACHABLE("Frame property " #prop " should never " \
+ "be destroyed by the FramePropertyTable"); \
+ } \
+ NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type, \
+ AssertOnDestroyingProperty##prop)
+
+#define NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(prop, type) \
+ NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(prop, mozilla::SmallValueHolder<type>)
+
+ NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(IBSplitSibling, nsIFrame)
+ NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(IBSplitPrevSibling, nsIFrame)
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(NormalPositionProperty, nsPoint)
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(ComputedOffsetProperty, nsMargin)
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutlineInnerRectProperty, nsRect)
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(PreEffectsBBoxProperty, nsRect)
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(PreTransformOverflowAreasProperty,
+ nsOverflowAreas)
+
+ // The initial overflow area passed to FinishAndStoreOverflow. This is only set
+ // on frames that Preserve3D() or HasPerspective() or IsTransformed(), and
+ // when at least one of the overflow areas differs from the frame bound rect.
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(InitialOverflowProperty, nsOverflowAreas)
+
+#ifdef DEBUG
+ // InitialOverflowPropertyDebug is added to the frame to indicate that either
+ // the InitialOverflowProperty has been stored or the InitialOverflowProperty
+ // has been suppressed due to being set to the default value (frame bounds)
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugInitialOverflowPropertyApplied, bool)
+#endif
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(UsedMarginProperty, nsMargin)
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(UsedPaddingProperty, nsMargin)
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(UsedBorderProperty, nsMargin)
+
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(LineBaselineOffset, nscoord)
+
+ // Temporary override for a flex item's main-size property (either width
+ // or height), imposed by its flex container.
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FlexItemMainSizeOverride, nscoord)
+
+ NS_DECLARE_FRAME_PROPERTY_RELEASABLE(CachedBackgroundImageDT, DrawTarget)
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(InvalidationRect, nsRect)
+
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(RefusedAsyncAnimationProperty, bool)
+
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FragStretchBSizeProperty, nscoord)
+
+ // The block-axis margin-box size associated with eBClampMarginBoxMinSize.
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BClampMarginBoxMinSizeProperty, nscoord)
+
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(IBaselinePadProperty, nscoord)
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BBaselinePadProperty, nscoord)
+
+ NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(GenConProperty, ContentArray,
+ DestroyContentArray)
+
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
+
+ mozilla::FrameBidiData GetBidiData()
+ {
+ return Properties().Get(BidiDataProperty());
+ }
+
+ nsBidiLevel GetBaseLevel()
+ {
+ return GetBidiData().baseLevel;
+ }
+
+ nsBidiLevel GetEmbeddingLevel()
+ {
+ return GetBidiData().embeddingLevel;
+ }
+
+ nsTArray<nsIContent*>* GetGenConPseudos() {
+ return Properties().Get(GenConProperty());
+ }
+
+ /**
+ * Return the distance between the border edge of the frame and the
+ * margin edge of the frame. Like GetRect(), returns the dimensions
+ * as of the most recent reflow.
+ *
+ * This doesn't include any margin collapsing that may have occurred.
+ *
+ * It also treats 'auto' margins as zero, and treats any margins that
+ * should have been turned into 'auto' because of overconstraint as
+ * having their original values.
+ */
+ virtual nsMargin GetUsedMargin() const;
+ virtual mozilla::LogicalMargin
+ GetLogicalUsedMargin(mozilla::WritingMode aWritingMode) const {
+ return mozilla::LogicalMargin(aWritingMode, GetUsedMargin());
+ }
+
+ /**
+ * Return the distance between the border edge of the frame (which is
+ * its rect) and the padding edge of the frame. Like GetRect(), returns
+ * the dimensions as of the most recent reflow.
+ *
+ * Note that this differs from StyleBorder()->GetComputedBorder() in
+ * that this describes a region of the frame's box, and
+ * StyleBorder()->GetComputedBorder() describes a border. They differ
+ * for tables (particularly border-collapse tables) and themed
+ * elements.
+ */
+ virtual nsMargin GetUsedBorder() const;
+ virtual mozilla::LogicalMargin
+ GetLogicalUsedBorder(mozilla::WritingMode aWritingMode) const {
+ return mozilla::LogicalMargin(aWritingMode, GetUsedBorder());
+ }
+
+ /**
+ * Return the distance between the padding edge of the frame and the
+ * content edge of the frame. Like GetRect(), returns the dimensions
+ * as of the most recent reflow.
+ */
+ virtual nsMargin GetUsedPadding() const;
+ virtual mozilla::LogicalMargin
+ GetLogicalUsedPadding(mozilla::WritingMode aWritingMode) const {
+ return mozilla::LogicalMargin(aWritingMode, GetUsedPadding());
+ }
+
+ nsMargin GetUsedBorderAndPadding() const {
+ return GetUsedBorder() + GetUsedPadding();
+ }
+ mozilla::LogicalMargin
+ GetLogicalUsedBorderAndPadding(mozilla::WritingMode aWritingMode) const {
+ return mozilla::LogicalMargin(aWritingMode, GetUsedBorderAndPadding());
+ }
+
+ /**
+ * Like the frame's rect (see |GetRect|), which is the border rect,
+ * other rectangles of the frame, in app units, relative to the parent.
+ */
+ nsRect GetPaddingRect() const;
+ nsRect GetPaddingRectRelativeToSelf() const;
+ nsRect GetContentRect() const;
+ nsRect GetContentRectRelativeToSelf() const;
+ nsRect GetMarginRectRelativeToSelf() const;
+
+ /**
+ * The area to paint box-shadows around. The default is the border rect.
+ * (nsFieldSetFrame overrides this).
+ */
+ virtual nsRect VisualBorderRectRelativeToSelf() const {
+ return nsRect(0, 0, mRect.width, mRect.height);
+ }
+
+ /**
+ * Get the size, in app units, of the border radii. It returns FALSE iff all
+ * returned radii == 0 (so no border radii), TRUE otherwise.
+ * For the aRadii indexes, use the NS_CORNER_* constants in nsStyleConsts.h
+ * If a side is skipped via aSkipSides, its corners are forced to 0.
+ *
+ * All corner radii are then adjusted so they do not require more
+ * space than aBorderArea, according to the algorithm in css3-background.
+ *
+ * aFrameSize is used as the basis for percentage widths and heights.
+ * aBorderArea is used for the adjustment of radii that might be too
+ * large.
+ * FIXME: In the long run, we can probably get away with only one of
+ * these, especially if we change the way we handle outline-radius (by
+ * removing it and inflating the border radius)
+ *
+ * Return whether any radii are nonzero.
+ */
+ static bool ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
+ const nsSize& aFrameSize,
+ const nsSize& aBorderArea,
+ Sides aSkipSides,
+ nscoord aRadii[8]);
+
+ /*
+ * Given a set of border radii for one box (e.g., border box), convert
+ * it to the equivalent set of radii for another box (e.g., in to
+ * padding box, out to outline box) by reducing radii or increasing
+ * nonzero radii as appropriate.
+ *
+ * Indices into aRadii are the NS_CORNER_* constants in nsStyleConsts.h
+ *
+ * Note that InsetBorderRadii is lossy, since it can turn nonzero
+ * radii into zero, and OutsetBorderRadii does not inflate zero radii.
+ * Therefore, callers should always inset or outset directly from the
+ * original value coming from style.
+ */
+ static void InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets);
+ static void OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets);
+
+ /**
+ * Fill in border radii for this frame. Return whether any are nonzero.
+ * Indices into aRadii are the NS_CORNER_* constants in nsStyleConsts.h
+ * aSkipSides is a union of SIDE_BIT_LEFT/RIGHT/TOP/BOTTOM bits that says
+ * which side(s) to skip.
+ */
+ virtual bool GetBorderRadii(const nsSize& aFrameSize,
+ const nsSize& aBorderArea,
+ Sides aSkipSides,
+ nscoord aRadii[8]) const;
+ bool GetBorderRadii(nscoord aRadii[8]) const;
+
+ bool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const;
+ bool GetContentBoxBorderRadii(nscoord aRadii[8]) const;
+
+ /**
+ * XXX: this method will likely be replaced by GetVerticalAlignBaseline
+ * Get the position of the frame's baseline, relative to the top of
+ * the frame (its top border edge). Only valid when Reflow is not
+ * needed.
+ * @note You should only call this on frames with a WM that's parallel to aWM.
+ * @param aWM the writing-mode of the alignment context, with the ltr/rtl
+ * direction tweak done by nsIFrame::GetWritingMode(nsIFrame*) in inline
+ * contexts (see that method).
+ */
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const = 0;
+
+ /**
+ * Synthesize a first(last) inline-axis baseline from our margin-box.
+ * An alphabetical baseline is at the start(end) edge and a central baseline
+ * is at the center of our block-axis margin-box (aWM tells which to use).
+ * https://drafts.csswg.org/css-align-3/#synthesize-baselines
+ * @note You should only call this on frames with a WM that's parallel to aWM.
+ * @param aWM the writing-mode of the alignment context
+ * @return an offset from our border-box block-axis start(end) edge for
+ * a first(last) baseline respectively
+ * (implemented in nsIFrameInlines.h)
+ */
+ inline nscoord SynthesizeBaselineBOffsetFromMarginBox(
+ mozilla::WritingMode aWM,
+ BaselineSharingGroup aGroup) const;
+
+ /**
+ * Synthesize a first(last) inline-axis baseline from our border-box.
+ * An alphabetical baseline is at the start(end) edge and a central baseline
+ * is at the center of our block-axis border-box (aWM tells which to use).
+ * https://drafts.csswg.org/css-align-3/#synthesize-baselines
+ * @note The returned value is only valid when reflow is not needed.
+ * @note You should only call this on frames with a WM that's parallel to aWM.
+ * @param aWM the writing-mode of the alignment context
+ * @return an offset from our border-box block-axis start(end) edge for
+ * a first(last) baseline respectively
+ * (implemented in nsIFrameInlines.h)
+ */
+ inline nscoord SynthesizeBaselineBOffsetFromBorderBox(
+ mozilla::WritingMode aWM,
+ BaselineSharingGroup aGroup) const;
+
+ /**
+ * Return the position of the frame's inline-axis baseline, or synthesize one
+ * for the given alignment context. The returned baseline is the distance from
+ * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
+ * @note The returned value is only valid when reflow is not needed.
+ * @note You should only call this on frames with a WM that's parallel to aWM.
+ * @param aWM the writing-mode of the alignment context
+ * @param aBaselineOffset out-param, only valid if the method returns true
+ * (implemented in nsIFrameInlines.h)
+ */
+ inline nscoord BaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ AlignmentContext aAlignmentContext) const;
+
+ /**
+ * XXX: this method is taking over the role that GetLogicalBaseline has.
+ * Return true if the frame has a CSS2 'vertical-align' baseline.
+ * If it has, then the returned baseline is the distance from the block-
+ * axis border-box start edge.
+ * @note This method should only be used in AlignmentContext::eInline contexts.
+ * @note The returned value is only valid when reflow is not needed.
+ * @note You should only call this on frames with a WM that's parallel to aWM.
+ * @param aWM the writing-mode of the alignment context
+ * @param aBaseline the baseline offset, only valid if the method returns true
+ */
+ virtual bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+ nscoord* aBaseline) const {
+ return false;
+ }
+
+ /**
+ * Return true if the frame has a first(last) inline-axis natural baseline per
+ * CSS Box Alignment. If so, then the returned baseline is the distance from
+ * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
+ * https://drafts.csswg.org/css-align-3/#natural-baseline
+ * @note The returned value is only valid when reflow is not needed.
+ * @note You should only call this on frames with a WM that's parallel to aWM.
+ * @param aWM the writing-mode of the alignment context
+ * @param aBaseline the baseline offset, only valid if the method returns true
+ */
+ virtual bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ nscoord* aBaseline) const {
+ return false;
+ }
+
+ /**
+ * Get the position of the baseline on which the caret needs to be placed,
+ * relative to the top of the frame. This is mostly needed for frames
+ * which return a baseline from GetBaseline which is not useful for
+ * caret positioning.
+ */
+ virtual nscoord GetCaretBaseline() const {
+ return GetLogicalBaseline(GetWritingMode());
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // The public visibility API.
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /// @return true if we're tracking visibility for this frame.
+ bool TrackingVisibility() const
+ {
+ return bool(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
+ }
+
+ /// @return the visibility state of this frame. See the Visibility enum
+ /// for the possible return values and their meanings.
+ Visibility GetVisibility() const;
+
+ /// Update the visibility state of this frame synchronously.
+ /// XXX(seth): Avoid using this method; we should be relying on the refresh
+ /// driver for visibility updates. This method, which replaces
+ /// nsLayoutUtils::UpdateApproximateFrameVisibility(), exists purely as a
+ /// temporary measure to avoid changing behavior during the transition from
+ /// the old image visibility code.
+ void UpdateVisibilitySynchronously();
+
+ // A frame property which stores the visibility state of this frame. Right
+ // now that consists of an approximate visibility counter represented as a
+ // uint32_t. When the visibility of this frame is not being tracked, this
+ // property is absent.
+ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(VisibilityStateProperty, uint32_t);
+
+protected:
+
+ /**
+ * Subclasses can call this method to enable visibility tracking for this frame.
+ *
+ * If visibility tracking was previously disabled, this will schedule an
+ * update an asynchronous update of visibility.
+ */
+ void EnableVisibilityTracking();
+
+ /**
+ * Subclasses can call this method to disable visibility tracking for this frame.
+ *
+ * Note that if visibility tracking was previously enabled, disabling visibility
+ * tracking will cause a synchronous call to OnVisibilityChange().
+ */
+ void DisableVisibilityTracking();
+
+ /**
+ * Called when a frame transitions between visibility states (for example,
+ * from nonvisible to visible, or from visible to nonvisible).
+ *
+ * @param aNewVisibility The new visibility state.
+ * @param aNonvisibleAction A requested action if the frame has become
+ * nonvisible. If Nothing(), no action is
+ * requested. If DISCARD_IMAGES is specified, the
+ * frame is requested to ask any images it's
+ * associated with to discard their surfaces if
+ * possible.
+ *
+ * Subclasses which override this method should call their parent class's
+ * implementation.
+ */
+ virtual void OnVisibilityChange(Visibility aNewVisibility,
+ Maybe<OnNonvisible> aNonvisibleAction = Nothing());
+
+public:
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Internal implementation for the approximate frame visibility API.
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * We track the approximate visibility of frames using a counter; if it's
+ * non-zero, then the frame is considered visible. Using a counter allows us
+ * to account for situations where the frame may be visible in more than one
+ * place (for example, via -moz-element), and it simplifies the
+ * implementation of our approximate visibility tracking algorithms.
+ *
+ * @param aNonvisibleAction A requested action if the frame has become
+ * nonvisible. If Nothing(), no action is
+ * requested. If DISCARD_IMAGES is specified, the
+ * frame is requested to ask any images it's
+ * associated with to discard their surfaces if
+ * possible.
+ */
+ void DecApproximateVisibleCount(Maybe<OnNonvisible> aNonvisibleAction = Nothing());
+ void IncApproximateVisibleCount();
+
+
+ /**
+ * Get the specified child list.
+ *
+ * @param aListID identifies the requested child list.
+ * @return the child list. If the requested list is unsupported by this
+ * frame type, an empty list will be returned.
+ */
+ virtual const nsFrameList& GetChildList(ChildListID aListID) const = 0;
+ const nsFrameList& PrincipalChildList() const { return GetChildList(kPrincipalList); }
+ virtual void GetChildLists(nsTArray<ChildList>* aLists) const = 0;
+
+ /**
+ * Gets the child lists for this frame, including
+ * ones belong to a child document.
+ */
+ void GetCrossDocChildLists(nsTArray<ChildList>* aLists);
+
+ // The individual concrete child lists.
+ static const ChildListID kPrincipalList = mozilla::layout::kPrincipalList;
+ static const ChildListID kAbsoluteList = mozilla::layout::kAbsoluteList;
+ static const ChildListID kBulletList = mozilla::layout::kBulletList;
+ static const ChildListID kCaptionList = mozilla::layout::kCaptionList;
+ static const ChildListID kColGroupList = mozilla::layout::kColGroupList;
+ static const ChildListID kExcessOverflowContainersList = mozilla::layout::kExcessOverflowContainersList;
+ static const ChildListID kFixedList = mozilla::layout::kFixedList;
+ static const ChildListID kFloatList = mozilla::layout::kFloatList;
+ static const ChildListID kOverflowContainersList = mozilla::layout::kOverflowContainersList;
+ static const ChildListID kOverflowList = mozilla::layout::kOverflowList;
+ static const ChildListID kOverflowOutOfFlowList = mozilla::layout::kOverflowOutOfFlowList;
+ static const ChildListID kPopupList = mozilla::layout::kPopupList;
+ static const ChildListID kPushedFloatsList = mozilla::layout::kPushedFloatsList;
+ static const ChildListID kSelectPopupList = mozilla::layout::kSelectPopupList;
+ static const ChildListID kBackdropList = mozilla::layout::kBackdropList;
+ // A special alias for kPrincipalList that do not request reflow.
+ static const ChildListID kNoReflowPrincipalList = mozilla::layout::kNoReflowPrincipalList;
+
+ /**
+ * Child frames are linked together in a doubly-linked list
+ */
+ nsIFrame* GetNextSibling() const { return mNextSibling; }
+ void SetNextSibling(nsIFrame* aNextSibling) {
+ NS_ASSERTION(this != aNextSibling, "Creating a circular frame list, this is very bad.");
+ if (mNextSibling && mNextSibling->GetPrevSibling() == this) {
+ mNextSibling->mPrevSibling = nullptr;
+ }
+ mNextSibling = aNextSibling;
+ if (mNextSibling) {
+ mNextSibling->mPrevSibling = this;
+ }
+ }
+
+ nsIFrame* GetPrevSibling() const { return mPrevSibling; }
+
+ /**
+ * Builds the display lists for the content represented by this frame
+ * and its descendants. The background+borders of this element must
+ * be added first, before any other content.
+ *
+ * This should only be called by methods in nsFrame. Instead of calling this
+ * directly, call either BuildDisplayListForStackingContext or
+ * BuildDisplayListForChild.
+ *
+ * See nsDisplayList.h for more information about display lists.
+ *
+ * @param aDirtyRect content outside this rectangle can be ignored; the
+ * rectangle is in frame coordinates
+ */
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) {}
+ /**
+ * Displays the caret onto the given display list builder. The caret is
+ * painted on top of the rest of the display list items.
+ *
+ * @param aDirtyRect is the dirty rectangle that we're repainting.
+ */
+ void DisplayCaret(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ nsDisplayList* aList);
+
+ /**
+ * Get the preferred caret color at the offset.
+ *
+ * @param aOffset is offset of the content.
+ */
+ virtual nscolor GetCaretColorAt(int32_t aOffset);
+
+
+ bool IsThemed(nsITheme::Transparency* aTransparencyState = nullptr) const {
+ return IsThemed(StyleDisplay(), aTransparencyState);
+ }
+ bool IsThemed(const nsStyleDisplay* aDisp,
+ nsITheme::Transparency* aTransparencyState = nullptr) const {
+ nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
+ if (!aDisp->mAppearance)
+ return false;
+ nsPresContext* pc = PresContext();
+ nsITheme *theme = pc->GetTheme();
+ if(!theme ||
+ !theme->ThemeSupportsWidget(pc, mutable_this, aDisp->mAppearance))
+ return false;
+ if (aTransparencyState) {
+ *aTransparencyState =
+ theme->GetWidgetTransparency(mutable_this, aDisp->mAppearance);
+ }
+ return true;
+ }
+
+ /**
+ * Builds a display list for the content represented by this frame,
+ * treating this frame as the root of a stacking context.
+ * @param aDirtyRect content outside this rectangle can be ignored; the
+ * rectangle is in frame coordinates
+ */
+ void BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ nsDisplayList* aList);
+
+ enum {
+ DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT = 0x01,
+ DISPLAY_CHILD_FORCE_STACKING_CONTEXT = 0x02,
+ DISPLAY_CHILD_INLINE = 0x04
+ };
+ /**
+ * Adjusts aDirtyRect for the child's offset, checks that the dirty rect
+ * actually intersects the child (or its descendants), calls BuildDisplayList
+ * on the child if necessary, and puts things in the right lists if the child
+ * is positioned.
+ *
+ * @param aFlags combination of DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT,
+ * DISPLAY_CHILD_FORCE_STACKING_CONTEXT and DISPLAY_CHILD_INLINE
+ */
+ void BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aChild,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists,
+ uint32_t aFlags = 0);
+
+ /**
+ * Does this frame need a view?
+ */
+ virtual bool NeedsView() { return false; }
+
+ bool RefusedAsyncAnimation() const
+ {
+ return Properties().Get(RefusedAsyncAnimationProperty());
+ }
+
+ /**
+ * Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
+ * or if its parent is an SVG frame that has children-only transforms (e.g.
+ * an SVG viewBox attribute) or if its transform-style is preserve-3d or
+ * the frame has transform animations.
+ */
+ bool IsTransformed() const;
+
+ /**
+ * Returns true if the frame is translucent or the frame has opacity
+ * animations for the purposes of creating a stacking context.
+ */
+ bool HasOpacity() const
+ {
+ return HasOpacityInternal(1.0f);
+ }
+ /**
+ * Returns true if the frame is translucent for display purposes.
+ */
+ bool HasVisualOpacity() const
+ {
+ // Treat an opacity value of 0.99 and above as opaque. This is an
+ // optimization aimed at Web content which use opacity:0.99 as a hint for
+ // creating a stacking context only.
+ return HasOpacityInternal(0.99f);
+ }
+
+ /**
+ * Return true if this frame might be using a transform getter.
+ */
+ virtual bool HasTransformGetter() const { return false; }
+
+ /**
+ * Returns true if this frame is an SVG frame that has SVG transforms applied
+ * to it, or if its parent frame is an SVG frame that has children-only
+ * transforms (e.g. an SVG viewBox attribute).
+ * If aOwnTransforms is non-null and the frame has its own SVG transforms,
+ * aOwnTransforms will be set to these transforms. If aFromParentTransforms
+ * is non-null and the frame has an SVG parent with children-only transforms,
+ * then aFromParentTransforms will be set to these transforms.
+ */
+ virtual bool IsSVGTransformed(Matrix *aOwnTransforms = nullptr,
+ Matrix *aFromParentTransforms = nullptr) const;
+
+ /**
+ * Returns whether this frame will attempt to extend the 3d transforms of its
+ * children. This requires transform-style: preserve-3d, as well as no clipping
+ * or svg effects.
+ */
+ bool Extend3DContext() const;
+
+ /**
+ * Returns whether this frame has a parent that Extend3DContext() and has
+ * its own transform (or hidden backface) to be combined with the parent's
+ * transform.
+ */
+ bool Combines3DTransformWithAncestors() const;
+
+ /**
+ * Returns whether this frame has a hidden backface and has a parent that
+ * Extend3DContext(). This is useful because in some cases the hidden
+ * backface can safely be ignored if it could not be visible anyway.
+ */
+ bool In3DContextAndBackfaceIsHidden() const;
+
+ bool IsPreserve3DLeaf() const {
+ return Combines3DTransformWithAncestors() && !Extend3DContext();
+ }
+
+ bool HasPerspective() const;
+
+ bool ChildrenHavePerspective() const;
+
+ /**
+ * Includes the overflow area of all descendants that participate in the current
+ * 3d context into aOverflowAreas.
+ */
+ void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas);
+
+ void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame);
+
+ /**
+ * Returns the number of ancestors between this and the root of our frame tree
+ */
+ uint32_t GetDepthInFrameTree() const;
+
+ /**
+ * Event handling of GUI events.
+ *
+ * @param aEvent event structure describing the type of event and rge widget
+ * where the event originated
+ * The |point| member of this is in the coordinate system of the
+ * view returned by GetOffsetFromView.
+ * @param aEventStatus a return value indicating whether the event was handled
+ * and whether default processing should be done
+ *
+ * XXX From a frame's perspective it's unclear what the effect of the event status
+ * is. Does it cause the event to continue propagating through the frame hierarchy
+ * or is it just returned to the widgets?
+ *
+ * @see WidgetGUIEvent
+ * @see nsEventStatus
+ */
+ virtual nsresult HandleEvent(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) = 0;
+
+ virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
+ nsIContent** aContent) = 0;
+
+ // This structure keeps track of the content node and offsets associated with
+ // a point; there is a primary and a secondary offset associated with any
+ // point. The primary and secondary offsets differ when the point is over a
+ // non-text object. The primary offset is the expected position of the
+ // cursor calculated from a point; the secondary offset, when it is different,
+ // indicates that the point is in the boundaries of some selectable object.
+ // Note that the primary offset can be after the secondary offset; for places
+ // that need the beginning and end of the object, the StartOffset and
+ // EndOffset helpers can be used.
+ struct MOZ_STACK_CLASS ContentOffsets
+ {
+ ContentOffsets() : offset(0)
+ , secondaryOffset(0)
+ , associate(mozilla::CARET_ASSOCIATE_BEFORE) {}
+ bool IsNull() { return !content; }
+ // Helpers for places that need the ends of the offsets and expect them in
+ // numerical order, as opposed to wanting the primary and secondary offsets
+ int32_t StartOffset() { return std::min(offset, secondaryOffset); }
+ int32_t EndOffset() { return std::max(offset, secondaryOffset); }
+
+ nsCOMPtr<nsIContent> content;
+ int32_t offset;
+ int32_t secondaryOffset;
+ // This value indicates whether the associated content is before or after
+ // the offset; the most visible use is to allow the caret to know which line
+ // to display on.
+ mozilla::CaretAssociationHint associate;
+ };
+ enum {
+ IGNORE_SELECTION_STYLE = 0x01,
+ // Treat visibility:hidden frames as non-selectable
+ SKIP_HIDDEN = 0x02
+ };
+ /**
+ * This function calculates the content offsets for selection relative to
+ * a point. Note that this should generally only be callled on the event
+ * frame associated with an event because this function does not account
+ * for frame lists other than the primary one.
+ * @param aPoint point relative to this frame
+ */
+ ContentOffsets GetContentOffsetsFromPoint(nsPoint aPoint,
+ uint32_t aFlags = 0);
+
+ virtual ContentOffsets GetContentOffsetsFromPointExternal(nsPoint aPoint,
+ uint32_t aFlags = 0)
+ { return GetContentOffsetsFromPoint(aPoint, aFlags); }
+
+ /**
+ * Ensure that aImage gets notifed when the underlying image request loads
+ * or animates.
+ */
+ void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext);
+
+ /**
+ * This structure holds information about a cursor. mContainer represents a
+ * loaded image that should be preferred. If it is not possible to use it, or
+ * if it is null, mCursor should be used.
+ */
+ struct MOZ_STACK_CLASS Cursor {
+ nsCOMPtr<imgIContainer> mContainer;
+ int32_t mCursor;
+ bool mHaveHotspot;
+ bool mLoading;
+ float mHotspotX, mHotspotY;
+ };
+ /**
+ * Get the cursor for a given frame.
+ */
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ Cursor& aCursor) = 0;
+
+ /**
+ * Get a point (in the frame's coordinate space) given an offset into
+ * the content. This point should be on the baseline of text with
+ * the correct horizontal offset
+ */
+ virtual nsresult GetPointFromOffset(int32_t inOffset,
+ nsPoint* outPoint) = 0;
+
+ /**
+ * Get a list of character rects in a given range.
+ * This is similar version of GetPointFromOffset.
+ */
+ virtual nsresult GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aRects) = 0;
+
+ /**
+ * Get the child frame of this frame which contains the given
+ * content offset. outChildFrame may be this frame, or nullptr on return.
+ * outContentOffset returns the content offset relative to the start
+ * of the returned node. You can also pass a hint which tells the method
+ * to stick to the end of the first found frame or the beginning of the
+ * next in case the offset falls on a boundary.
+ */
+ virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
+ bool inHint,//false stick left
+ int32_t* outFrameContentOffset,
+ nsIFrame** outChildFrame) = 0;
+
+ /**
+ * Get the current frame-state value for this frame. aResult is
+ * filled in with the state bits.
+ */
+ nsFrameState GetStateBits() const { return mState; }
+
+ /**
+ * Update the current frame-state value for this frame.
+ */
+ void AddStateBits(nsFrameState aBits) { mState |= aBits; }
+ void RemoveStateBits(nsFrameState aBits) { mState &= ~aBits; }
+
+ /**
+ * Checks if the current frame-state includes all of the listed bits
+ */
+ bool HasAllStateBits(nsFrameState aBits) const
+ {
+ return (mState & aBits) == aBits;
+ }
+
+ /**
+ * Checks if the current frame-state includes any of the listed bits
+ */
+ bool HasAnyStateBits(nsFrameState aBits) const
+ {
+ return mState & aBits;
+ }
+
+ /**
+ * This call is invoked on the primary frame for a character data content
+ * node, when it is changed in the content tree.
+ */
+ virtual nsresult CharacterDataChanged(CharacterDataChangeInfo* aInfo) = 0;
+
+ /**
+ * This call is invoked when the value of a content objects's attribute
+ * is changed.
+ * The first frame that maps that content is asked to deal
+ * with the change by doing whatever is appropriate.
+ *
+ * @param aNameSpaceID the namespace of the attribute
+ * @param aAttribute the atom name of the attribute
+ * @param aModType Whether or not the attribute was added, changed, or removed.
+ * The constants are defined in nsIDOMMutationEvent.h.
+ */
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) = 0;
+
+ /**
+ * When the content states of a content object change, this method is invoked
+ * on the primary frame of that content object.
+ *
+ * @param aStates the changed states
+ */
+ virtual void ContentStatesChanged(mozilla::EventStates aStates);
+
+ /**
+ * Return how your frame can be split.
+ */
+ virtual nsSplittableType GetSplittableType() const = 0;
+
+ /**
+ * Continuation member functions
+ */
+ virtual nsIFrame* GetPrevContinuation() const = 0;
+ virtual void SetPrevContinuation(nsIFrame*) = 0;
+ virtual nsIFrame* GetNextContinuation() const = 0;
+ virtual void SetNextContinuation(nsIFrame*) = 0;
+ virtual nsIFrame* FirstContinuation() const {
+ return const_cast<nsIFrame*>(this);
+ }
+ virtual nsIFrame* LastContinuation() const {
+ return const_cast<nsIFrame*>(this);
+ }
+
+ /**
+ * GetTailContinuation gets the last non-overflow-container continuation
+ * in the continuation chain, i.e. where the next sibling element
+ * should attach).
+ */
+ nsIFrame* GetTailContinuation();
+
+ /**
+ * Flow member functions
+ */
+ virtual nsIFrame* GetPrevInFlowVirtual() const = 0;
+ nsIFrame* GetPrevInFlow() const { return GetPrevInFlowVirtual(); }
+ virtual void SetPrevInFlow(nsIFrame*) = 0;
+
+ virtual nsIFrame* GetNextInFlowVirtual() const = 0;
+ nsIFrame* GetNextInFlow() const { return GetNextInFlowVirtual(); }
+ virtual void SetNextInFlow(nsIFrame*) = 0;
+
+ /**
+ * Return the first frame in our current flow.
+ */
+ virtual nsIFrame* FirstInFlow() const {
+ return const_cast<nsIFrame*>(this);
+ }
+
+ /**
+ * Return the last frame in our current flow.
+ */
+ virtual nsIFrame* LastInFlow() const {
+ return const_cast<nsIFrame*>(this);
+ }
+
+ /**
+ * Note: "width" in the names and comments on the following methods
+ * means inline-size, which could be height in vertical layout
+ */
+
+ /**
+ * Mark any stored intrinsic width information as dirty (requiring
+ * re-calculation). Note that this should generally not be called
+ * directly; nsPresShell::FrameNeedsReflow will call it instead.
+ */
+ virtual void MarkIntrinsicISizesDirty() = 0;
+
+ /**
+ * Get the min-content intrinsic inline size of the frame. This must be
+ * less than or equal to the max-content intrinsic inline size.
+ *
+ * This is *not* affected by the CSS 'min-width', 'width', and
+ * 'max-width' properties on this frame, but it is affected by the
+ * values of those properties on this frame's descendants. (It may be
+ * called during computation of the values of those properties, so it
+ * cannot depend on any values in the nsStylePosition for this frame.)
+ *
+ * The value returned should **NOT** include the space required for
+ * padding and border.
+ *
+ * Note that many frames will cache the result of this function call
+ * unless MarkIntrinsicISizesDirty is called.
+ *
+ * It is not acceptable for a frame to mark itself dirty when this
+ * method is called.
+ *
+ * This method must not return a negative value.
+ */
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) = 0;
+
+ /**
+ * Get the max-content intrinsic inline size of the frame. This must be
+ * greater than or equal to the min-content intrinsic inline size.
+ *
+ * Otherwise, all the comments for |GetMinISize| above apply.
+ */
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) = 0;
+
+ /**
+ * |InlineIntrinsicISize| represents the intrinsic width information
+ * in inline layout. Code that determines the intrinsic width of a
+ * region of inline layout accumulates the result into this structure.
+ * This pattern is needed because we need to maintain state
+ * information about whitespace (for both collapsing and trimming).
+ */
+ struct InlineIntrinsicISizeData {
+ InlineIntrinsicISizeData()
+ : mLine(nullptr)
+ , mLineContainer(nullptr)
+ , mPrevLines(0)
+ , mCurrentLine(0)
+ , mTrailingWhitespace(0)
+ , mSkipWhitespace(true)
+ {}
+
+ // The line. This may be null if the inlines are not associated with
+ // a block or if we just don't know the line.
+ const nsLineList_iterator* mLine;
+
+ // The line container. Private, to ensure we always use SetLineContainer
+ // to update it (so that we have a chance to store the mLineContainerWM).
+ //
+ // Note that nsContainerFrame::DoInlineIntrinsicISize will clear the
+ // |mLine| and |mLineContainer| fields when following a next-in-flow link,
+ // so we must not assume these can always be dereferenced.
+ private:
+ nsIFrame* mLineContainer;
+
+ // Setter and getter for the lineContainer field:
+ public:
+ void SetLineContainer(nsIFrame* aLineContainer)
+ {
+ mLineContainer = aLineContainer;
+ if (mLineContainer) {
+ mLineContainerWM = mLineContainer->GetWritingMode();
+ }
+ }
+ nsIFrame* LineContainer() const { return mLineContainer; }
+
+ // The maximum intrinsic width for all previous lines.
+ nscoord mPrevLines;
+
+ // The maximum intrinsic width for the current line. At a line
+ // break (mandatory for preferred width; allowed for minimum width),
+ // the caller should call |Break()|.
+ nscoord mCurrentLine;
+
+ // This contains the width of the trimmable whitespace at the end of
+ // |mCurrentLine|; it is zero if there is no such whitespace.
+ nscoord mTrailingWhitespace;
+
+ // True if initial collapsable whitespace should be skipped. This
+ // should be true at the beginning of a block, after hard breaks
+ // and when the last text ended with whitespace.
+ bool mSkipWhitespace;
+
+ // Writing mode of the line container (stored here so that we don't
+ // lose track of it if the mLineContainer field is reset).
+ mozilla::WritingMode mLineContainerWM;
+
+ // Floats encountered in the lines.
+ class FloatInfo {
+ public:
+ FloatInfo(const nsIFrame* aFrame, nscoord aWidth)
+ : mFrame(aFrame), mWidth(aWidth)
+ { }
+ const nsIFrame* Frame() const { return mFrame; }
+ nscoord Width() const { return mWidth; }
+
+ private:
+ const nsIFrame* mFrame;
+ nscoord mWidth;
+ };
+
+ nsTArray<FloatInfo> mFloats;
+ };
+
+ struct InlineMinISizeData : public InlineIntrinsicISizeData {
+ InlineMinISizeData()
+ : mAtStartOfLine(true)
+ {}
+
+ // The default implementation for nsIFrame::AddInlineMinISize.
+ void DefaultAddInlineMinISize(nsIFrame* aFrame,
+ nscoord aISize,
+ bool aAllowBreak = true);
+
+ // We need to distinguish forced and optional breaks for cases where the
+ // current line total is negative. When it is, we need to ignore
+ // optional breaks to prevent min-width from ending up bigger than
+ // pref-width.
+ void ForceBreak();
+
+ // If the break here is actually taken, aHyphenWidth must be added to the
+ // width of the current line.
+ void OptionallyBreak(nscoord aHyphenWidth = 0);
+
+ // Whether we're currently at the start of the line. If we are, we
+ // can't break (for example, between the text-indent and the first
+ // word).
+ bool mAtStartOfLine;
+ };
+
+ struct InlinePrefISizeData : public InlineIntrinsicISizeData {
+ void ForceBreak();
+
+ // The default implementation for nsIFrame::AddInlinePrefISize.
+ void DefaultAddInlinePrefISize(nscoord aISize);
+ };
+
+ /**
+ * Add the intrinsic minimum width of a frame in a way suitable for
+ * use in inline layout to an |InlineIntrinsicISizeData| object that
+ * represents the intrinsic width information of all the previous
+ * frames in the inline layout region.
+ *
+ * All *allowed* breakpoints within the frame determine what counts as
+ * a line for the |InlineIntrinsicISizeData|. This means that
+ * |aData->mTrailingWhitespace| will always be zero (unlike for
+ * AddInlinePrefISize).
+ *
+ * All the comments for |GetMinISize| apply, except that this function
+ * is responsible for adding padding, border, and margin and for
+ * considering the effects of 'width', 'min-width', and 'max-width'.
+ *
+ * This may be called on any frame. Frames that do not participate in
+ * line breaking can inherit the default implementation on nsFrame,
+ * which calls |GetMinISize|.
+ */
+ virtual void
+ AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) = 0;
+
+ /**
+ * Add the intrinsic preferred width of a frame in a way suitable for
+ * use in inline layout to an |InlineIntrinsicISizeData| object that
+ * represents the intrinsic width information of all the previous
+ * frames in the inline layout region.
+ *
+ * All the comments for |AddInlineMinISize| and |GetPrefISize| apply,
+ * except that this fills in an |InlineIntrinsicISizeData| structure
+ * based on using all *mandatory* breakpoints within the frame.
+ */
+ virtual void
+ AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) = 0;
+
+ /**
+ * Return the horizontal components of padding, border, and margin
+ * that contribute to the intrinsic width that applies to the parent.
+ */
+ struct IntrinsicISizeOffsetData {
+ nscoord hPadding, hBorder, hMargin;
+ float hPctPadding, hPctMargin;
+
+ IntrinsicISizeOffsetData()
+ : hPadding(0), hBorder(0), hMargin(0)
+ , hPctPadding(0.0f), hPctMargin(0.0f)
+ {}
+ };
+ virtual IntrinsicISizeOffsetData IntrinsicISizeOffsets() = 0;
+
+ /**
+ * Return the bsize components of padding, border, and margin
+ * that contribute to the intrinsic width that applies to the parent.
+ */
+ IntrinsicISizeOffsetData IntrinsicBSizeOffsets();
+
+ virtual mozilla::IntrinsicSize GetIntrinsicSize() = 0;
+
+ /**
+ * Get the intrinsic ratio of this element, or nsSize(0,0) if it has
+ * no intrinsic ratio. The intrinsic ratio is the ratio of the
+ * height/width of a box with an intrinsic size or the intrinsic
+ * aspect ratio of a scalable vector image without an intrinsic size.
+ *
+ * Either one of the sides may be zero, indicating a zero or infinite
+ * ratio.
+ */
+ virtual nsSize GetIntrinsicRatio() = 0;
+
+ /**
+ * Bit-flags to pass to ComputeSize in |aFlags| parameter.
+ */
+ enum ComputeSizeFlags {
+ eDefault = 0,
+ /**
+ * Set if the frame is in a context where non-replaced blocks should
+ * shrink-wrap (e.g., it's floating, absolutely positioned, or
+ * inline-block).
+ */
+ eShrinkWrap = 1 << 0,
+ /**
+ * Set if we'd like to compute our 'auto' bsize, regardless of our actual
+ * corresponding computed value. (e.g. to get an intrinsic height for flex
+ * items with "min-height: auto" to use during flexbox layout.)
+ */
+ eUseAutoBSize = 1 << 1,
+ /**
+ * Indicates that we should clamp the margin-box min-size to the given CB
+ * size. This is used for implementing the grid area clamping here:
+ * https://drafts.csswg.org/css-grid/#min-size-auto
+ */
+ eIClampMarginBoxMinSize = 1 << 2, // clamp in our inline axis
+ eBClampMarginBoxMinSize = 1 << 3, // clamp in our block axis
+ };
+
+ /**
+ * Compute the size that a frame will occupy. Called while
+ * constructing the ReflowInput to be used to Reflow the frame,
+ * in order to fill its mComputedWidth and mComputedHeight member
+ * variables.
+ *
+ * The |height| member of the return value may be
+ * NS_UNCONSTRAINEDSIZE, but the |width| member must not be.
+ *
+ * Note that the reason that border and padding need to be passed
+ * separately is so that the 'box-sizing' property can be handled.
+ * Thus aMargin includes absolute positioning offsets as well.
+ *
+ * @param aWritingMode The writing mode to use for the returned size
+ * (need not match this frame's writing mode).
+ * This is also the writing mode of the passed-in
+ * LogicalSize parameters.
+ * @param aCBSize The size of the element's containing block. (Well,
+ * the |height| component isn't really.)
+ * @param aAvailableWidth The available width for 'auto' widths.
+ * This is usually the same as aCBSize.width,
+ * but differs in cases such as block
+ * formatting context roots next to floats, or
+ * in some cases of float reflow in quirks
+ * mode.
+ * @param aMargin The sum of the vertical / horizontal margins
+ * ***AND*** absolute positioning offsets (top, right,
+ * bottom, left) of the frame, including actual values
+ * resulting from percentages and from the
+ * "hypothetical box" for absolute positioning, but
+ * not including actual values resulting from 'auto'
+ * margins or ignored 'auto' values in absolute
+ * positioning.
+ * @param aBorder The sum of the vertical / horizontal border widths
+ * of the frame.
+ * @param aPadding The sum of the vertical / horizontal margins of
+ * the frame, including actual values resulting from
+ * percentages.
+ * @param aFlags Flags to further customize behavior (definitions above).
+ */
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) = 0;
+
+ /**
+ * Compute a tight bounding rectangle for the frame. This is a rectangle
+ * that encloses the pixels that are actually drawn. We're allowed to be
+ * conservative and currently we don't try very hard. The rectangle is
+ * in appunits and relative to the origin of this frame.
+ *
+ * This probably only needs to include frame bounds, glyph bounds, and
+ * text decorations, but today it sometimes includes other things that
+ * contribute to visual overflow.
+ *
+ * @param aDrawTarget a draw target that can be used if we need
+ * to do measurement
+ */
+ virtual nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const;
+
+ /**
+ * This function is similar to GetPrefISize and ComputeTightBounds: it
+ * computes the left and right coordinates of a preferred tight bounding
+ * rectangle for the frame. This is a rectangle that would enclose the pixels
+ * that are drawn if we lay out the element without taking any optional line
+ * breaks. The rectangle is in appunits and relative to the origin of this
+ * frame. Currently, this function is only implemented for nsBlockFrame and
+ * nsTextFrame and is used to determine intrinsic widths of MathML token
+ * elements.
+
+ * @param aContext a rendering context that can be used if we need
+ * to do measurement
+ * @param aX computed left coordinate of the tight bounding rectangle
+ * @param aXMost computed intrinsic width of the tight bounding rectangle
+ *
+ */
+ virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+ nscoord* aX,
+ nscoord* aXMost);
+
+ /**
+ * The frame is given an available size and asked for its desired
+ * size. This is the frame's opportunity to reflow its children.
+ *
+ * If the frame has the NS_FRAME_IS_DIRTY bit set then it is
+ * responsible for completely reflowing itself and all of its
+ * descendants.
+ *
+ * Otherwise, if the frame has the NS_FRAME_HAS_DIRTY_CHILDREN bit
+ * set, then it is responsible for reflowing at least those
+ * children that have NS_FRAME_HAS_DIRTY_CHILDREN or NS_FRAME_IS_DIRTY
+ * set.
+ *
+ * If a difference in available size from the previous reflow causes
+ * the frame's size to change, it should reflow descendants as needed.
+ *
+ * @param aReflowOutput <i>out</i> parameter where you should return the
+ * desired size and ascent/descent info. You should include any
+ * space you want for border/padding in the desired size you return.
+ *
+ * It's okay to return a desired size that exceeds the avail
+ * size if that's the smallest you can be, i.e. it's your
+ * minimum size.
+ *
+ * For an incremental reflow you are responsible for invalidating
+ * any area within your frame that needs repainting (including
+ * borders). If your new desired size is different than your current
+ * size, then your parent frame is responsible for making sure that
+ * the difference between the two rects is repainted
+ *
+ * @param aReflowInput information about your reflow including the reason
+ * for the reflow and the available space in which to lay out. Each
+ * dimension of the available space can either be constrained or
+ * unconstrained (a value of NS_UNCONSTRAINEDSIZE).
+ *
+ * Note that the available space can be negative. In this case you
+ * still must return an accurate desired size. If you're a container
+ * you must <b>always</b> reflow at least one frame regardless of the
+ * available space
+ *
+ * @param aStatus a return value indicating whether the frame is complete
+ * and whether the next-in-flow is dirty and needs to be reflowed
+ */
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aReflowOutput,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) = 0;
+
+ /**
+ * Post-reflow hook. After a frame is reflowed this method will be called
+ * informing the frame that this reflow process is complete, and telling the
+ * frame the status returned by the Reflow member function.
+ *
+ * This call may be invoked many times, while NS_FRAME_IN_REFLOW is set, before
+ * it is finally called once with a NS_FRAME_REFLOW_COMPLETE value. When called
+ * with a NS_FRAME_REFLOW_COMPLETE value the NS_FRAME_IN_REFLOW bit in the
+ * frame state will be cleared.
+ *
+ * XXX This doesn't make sense. If the frame is reflowed but not complete, then
+ * the status should be NS_FRAME_NOT_COMPLETE and not NS_FRAME_COMPLETE
+ * XXX Don't we want the semantics to dictate that we only call this once for
+ * a given reflow?
+ */
+ virtual void DidReflow(nsPresContext* aPresContext,
+ const ReflowInput* aReflowInput,
+ nsDidReflowStatus aStatus) = 0;
+
+ /**
+ * Updates the overflow areas of the frame. This can be called if an
+ * overflow area of the frame's children has changed without reflowing.
+ * @return true if either of the overflow areas for this frame have changed.
+ */
+ bool UpdateOverflow();
+
+ /**
+ * Computes any overflow area created by the frame itself (outside of the
+ * frame bounds) and includes it into aOverflowAreas.
+ *
+ * Returns false if updating overflow isn't supported for this frame.
+ * If the frame requires a reflow instead, then it is responsible
+ * for scheduling one.
+ */
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) = 0;
+
+ /**
+ * Computes any overflow area created by children of this frame and
+ * includes it into aOverflowAreas.
+ */
+ virtual void UnionChildOverflow(nsOverflowAreas& aOverflowAreas) = 0;
+
+ /**
+ * Helper method used by block reflow to identify runs of text so
+ * that proper word-breaking can be done.
+ *
+ * @return
+ * true if we can continue a "text run" through the frame. A
+ * text run is text that should be treated contiguously for line
+ * and word breaking.
+ */
+ virtual bool CanContinueTextRun() const = 0;
+
+ /**
+ * Computes an approximation of the rendered text of the frame and its
+ * continuations. Returns nothing for non-text frames.
+ * The appended text will often not contain all the whitespace from source,
+ * depending on CSS white-space processing.
+ * if aEndOffset goes past end, use the text up to the string's end.
+ * Call this on the primary frame for a text node.
+ * aStartOffset and aEndOffset can be content offsets or offsets in the
+ * rendered text, depending on aOffsetType.
+ * Returns a string, as well as offsets identifying the start of the text
+ * within the rendered text for the whole node, and within the text content
+ * of the node.
+ */
+ struct RenderedText {
+ nsAutoString mString;
+ uint32_t mOffsetWithinNodeRenderedText;
+ int32_t mOffsetWithinNodeText;
+ RenderedText() : mOffsetWithinNodeRenderedText(0),
+ mOffsetWithinNodeText(0) {}
+ };
+ enum class TextOffsetType {
+ // Passed-in start and end offsets are within the content text.
+ OFFSETS_IN_CONTENT_TEXT,
+ // Passed-in start and end offsets are within the rendered text.
+ OFFSETS_IN_RENDERED_TEXT
+ };
+ enum class TrailingWhitespace {
+ TRIM_TRAILING_WHITESPACE,
+ // Spaces preceding a caret at the end of a line should not be trimmed
+ DONT_TRIM_TRAILING_WHITESPACE
+ };
+ virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0,
+ uint32_t aEndOffset = UINT32_MAX,
+ TextOffsetType aOffsetType =
+ TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+ TrailingWhitespace aTrimTrailingWhitespace =
+ TrailingWhitespace::TRIM_TRAILING_WHITESPACE)
+ { return RenderedText(); }
+
+ /**
+ * Returns true if the frame contains any non-collapsed characters.
+ * This method is only available for text frames, and it will return false
+ * for all other frame types.
+ */
+ virtual bool HasAnyNoncollapsedCharacters()
+ { return false; }
+
+ /**
+ * Accessor functions to get/set the associated view object
+ *
+ * GetView returns non-null if and only if |HasView| returns true.
+ */
+ bool HasView() const { return !!(mState & NS_FRAME_HAS_VIEW); }
+ nsView* GetView() const;
+ nsresult SetView(nsView* aView);
+
+ /**
+ * Find the closest view (on |this| or an ancestor).
+ * If aOffset is non-null, it will be set to the offset of |this|
+ * from the returned view.
+ */
+ nsView* GetClosestView(nsPoint* aOffset = nullptr) const;
+
+ /**
+ * Find the closest ancestor (excluding |this| !) that has a view
+ */
+ nsIFrame* GetAncestorWithView() const;
+
+ /**
+ * Get the offset between the coordinate systems of |this| and aOther.
+ * Adding the return value to a point in the coordinate system of |this|
+ * will transform the point to the coordinate system of aOther.
+ *
+ * aOther must be non-null.
+ *
+ * This function is fastest when aOther is an ancestor of |this|.
+ *
+ * This function _DOES NOT_ work across document boundaries.
+ * Use this function only when |this| and aOther are in the same document.
+ *
+ * NOTE: this actually returns the offset from aOther to |this|, but
+ * that offset is added to transform _coordinates_ from |this| to
+ * aOther.
+ */
+ nsPoint GetOffsetTo(const nsIFrame* aOther) const;
+
+ /**
+ * Get the offset between the coordinate systems of |this| and aOther
+ * expressed in appunits per dev pixel of |this|' document. Adding the return
+ * value to a point that is relative to the origin of |this| will make the
+ * point relative to the origin of aOther but in the appunits per dev pixel
+ * ratio of |this|.
+ *
+ * aOther must be non-null.
+ *
+ * This function is fastest when aOther is an ancestor of |this|.
+ *
+ * This function works across document boundaries.
+ *
+ * Because this function may cross document boundaries that have different
+ * app units per dev pixel ratios it needs to be used very carefully.
+ *
+ * NOTE: this actually returns the offset from aOther to |this|, but
+ * that offset is added to transform _coordinates_ from |this| to
+ * aOther.
+ */
+ nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther) const;
+
+ /**
+ * Like GetOffsetToCrossDoc, but the caller can specify which appunits
+ * to return the result in.
+ */
+ nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const;
+
+ /**
+ * Get the screen rect of the frame in pixels.
+ * @return the pixel rect of the frame in screen coordinates.
+ */
+ nsIntRect GetScreenRect() const;
+
+ /**
+ * Get the screen rect of the frame in app units.
+ * @return the app unit rect of the frame in screen coordinates.
+ */
+ nsRect GetScreenRectInAppUnits() const;
+
+ /**
+ * Returns the offset from this frame to the closest geometric parent that
+ * has a view. Also returns the containing view or null in case of error
+ */
+ void GetOffsetFromView(nsPoint& aOffset, nsView** aView) const;
+
+ /**
+ * Returns the nearest widget containing this frame. If this frame has a
+ * view and the view has a widget, then this frame's widget is
+ * returned, otherwise this frame's geometric parent is checked
+ * recursively upwards.
+ */
+ nsIWidget* GetNearestWidget() const;
+
+ /**
+ * Same as GetNearestWidget() above but uses an outparam to return the offset
+ * of this frame to the returned widget expressed in appunits of |this| (the
+ * widget might be in a different document with a different zoom).
+ */
+ nsIWidget* GetNearestWidget(nsPoint& aOffset) const;
+
+ /**
+ * Get the "type" of the frame. May return nullptr.
+ *
+ * @see nsGkAtoms
+ */
+ virtual nsIAtom* GetType() const = 0;
+
+ /**
+ * Returns a transformation matrix that converts points in this frame's
+ * coordinate space to points in some ancestor frame's coordinate space.
+ * The frame decides which ancestor it will use as a reference point.
+ * If this frame has no ancestor, aOutAncestor will be set to null.
+ *
+ * @param aStopAtAncestor don't look further than aStopAtAncestor. If null,
+ * all ancestors (including across documents) will be traversed.
+ * @param aOutAncestor [out] The ancestor frame the frame has chosen. If
+ * this frame has no ancestor, *aOutAncestor will be set to null. If
+ * this frame is not a root frame, then *aOutAncestor will be in the same
+ * document as this frame. If this frame IsTransformed(), then *aOutAncestor
+ * will be the parent frame (if not preserve-3d) or the nearest non-transformed
+ * ancestor (if preserve-3d).
+ * @return A Matrix4x4 that converts points in this frame's coordinate space
+ * into points in aOutAncestor's coordinate space.
+ */
+ Matrix4x4 GetTransformMatrix(const nsIFrame* aStopAtAncestor,
+ nsIFrame **aOutAncestor);
+
+ /**
+ * Bit-flags to pass to IsFrameOfType()
+ */
+ enum {
+ eMathML = 1 << 0,
+ eSVG = 1 << 1,
+ eSVGForeignObject = 1 << 2,
+ eSVGContainer = 1 << 3,
+ eSVGGeometry = 1 << 4,
+ eSVGPaintServer = 1 << 5,
+ eBidiInlineContainer = 1 << 6,
+ // the frame is for a replaced element, such as an image
+ eReplaced = 1 << 7,
+ // Frame that contains a block but looks like a replaced element
+ // from the outside
+ eReplacedContainsBlock = 1 << 8,
+ // A frame that participates in inline reflow, i.e., one that
+ // requires ReflowInput::mLineLayout.
+ eLineParticipant = 1 << 9,
+ eXULBox = 1 << 10,
+ eCanContainOverflowContainers = 1 << 11,
+ eBlockFrame = 1 << 12,
+ eTablePart = 1 << 13,
+ // If this bit is set, the frame doesn't allow ignorable whitespace as
+ // children. For example, the whitespace between <table>\n<tr>\n<td>
+ // will be excluded during the construction of children.
+ eExcludesIgnorableWhitespace = 1 << 14,
+ eSupportsCSSTransforms = 1 << 15,
+
+ // A replaced element that has replaced-element sizing
+ // characteristics (i.e., like images or iframes), as opposed to
+ // inline-block sizing characteristics (like form controls).
+ eReplacedSizing = 1 << 16,
+
+ // These are to allow nsFrame::Init to assert that IsFrameOfType
+ // implementations all call the base class method. They are only
+ // meaningful in DEBUG builds.
+ eDEBUGAllFrames = 1 << 30,
+ eDEBUGNoFrames = 1 << 31
+ };
+
+ /**
+ * API for doing a quick check if a frame is of a given
+ * type. Returns true if the frame matches ALL flags passed in.
+ *
+ * Implementations should always override with inline virtual
+ * functions that call the base class's IsFrameOfType method.
+ */
+ virtual bool IsFrameOfType(uint32_t aFlags) const
+ {
+#ifdef DEBUG
+ return !(aFlags & ~(nsIFrame::eDEBUGAllFrames | nsIFrame::eSupportsCSSTransforms));
+#else
+ return !(aFlags & ~nsIFrame::eSupportsCSSTransforms);
+#endif
+ }
+
+ /**
+ * Returns true if the frame is a block wrapper.
+ */
+ bool IsBlockWrapper() const;
+
+ /**
+ * Get this frame's CSS containing block.
+ *
+ * The algorithm is defined in
+ * http://www.w3.org/TR/CSS2/visudet.html#containing-block-details.
+ *
+ * NOTE: This is guaranteed to return a non-null pointer when invoked on any
+ * frame other than the root frame.
+ *
+ * Requires SKIP_SCROLLED_FRAME to get behaviour matching the spec, otherwise
+ * it can return anonymous inner scrolled frames. Bug 1204044 is filed for
+ * investigating whether any of the callers actually require the default
+ * behaviour.
+ */
+ enum {
+ // If the containing block is an anonymous scrolled frame, then skip over
+ // this and return the outer scroll frame.
+ SKIP_SCROLLED_FRAME = 0x01
+ };
+ nsIFrame* GetContainingBlock(uint32_t aFlags = 0) const;
+
+ /**
+ * Is this frame a containing block for floating elements?
+ * Note that very few frames are, so default to false.
+ */
+ virtual bool IsFloatContainingBlock() const { return false; }
+
+ /**
+ * Is this a leaf frame? Frames that want the frame constructor to be able
+ * to construct kids for them should return false, all others should return
+ * true. Note that returning true here does not mean that the frame _can't_
+ * have kids. It could still have kids created via
+ * nsIAnonymousContentCreator. Returning true indicates that "normal"
+ * (non-anonymous, XBL-bound, CSS generated content, etc) children should not
+ * be constructed.
+ */
+ virtual bool IsLeaf() const;
+
+ /**
+ * Marks all display items created by this frame as needing a repaint,
+ * and calls SchedulePaint() if requested and one is not already pending.
+ *
+ * This includes all display items created by this frame, including
+ * container types.
+ *
+ * @param aDisplayItemKey If specified, only issues an invalidate
+ * if this frame painted a display item of that type during the
+ * previous paint. SVG rendering observers are always notified.
+ */
+ virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0);
+
+ /**
+ * Same as InvalidateFrame(), but only mark a fixed rect as needing
+ * repainting.
+ *
+ * @param aRect The rect to invalidate, relative to the TopLeft of the
+ * frame's border box.
+ * @param aDisplayItemKey If specified, only issues an invalidate
+ * if this frame painted a display item of that type during the
+ * previous paint. SVG rendering observers are always notified.
+ */
+ virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0);
+
+ /**
+ * Calls InvalidateFrame() on all frames descendant frames (including
+ * this one).
+ *
+ * This function doesn't walk through placeholder frames to invalidate
+ * the out-of-flow frames.
+ *
+ * @param aDisplayItemKey If specified, only issues an invalidate
+ * if this frame painted a display item of that type during the
+ * previous paint. SVG rendering observers are always notified.
+ */
+ void InvalidateFrameSubtree(uint32_t aDisplayItemKey = 0);
+
+ /**
+ * Called when a frame is about to be removed and needs to be invalidated.
+ * Normally does nothing since DLBI handles removed frames.
+ */
+ virtual void InvalidateFrameForRemoval() {}
+
+ /**
+ * When HasUserData(frame->LayerIsPrerenderedDataKey()), then the
+ * entire overflow area of this frame has been rendered in its
+ * layer(s).
+ */
+ static void* LayerIsPrerenderedDataKey() {
+ return &sLayerIsPrerenderedDataKey;
+ }
+ static uint8_t sLayerIsPrerenderedDataKey;
+
+ /**
+ * Try to update this frame's transform without invalidating any
+ * content. Return true iff successful. If unsuccessful, the
+ * caller is responsible for scheduling an invalidating paint.
+ *
+ * If the result is true, aLayerResult will be filled in with the
+ * transform layer for the frame.
+ */
+ bool TryUpdateTransformOnly(Layer** aLayerResult);
+
+ /**
+ * Checks if a frame has had InvalidateFrame() called on it since the
+ * last paint.
+ *
+ * If true, then the invalid rect is returned in aRect, with an
+ * empty rect meaning all pixels drawn by this frame should be
+ * invalidated.
+ * If false, aRect is left unchanged.
+ */
+ bool IsInvalid(nsRect& aRect);
+
+ /**
+ * Check if any frame within the frame subtree (including this frame)
+ * returns true for IsInvalid().
+ */
+ bool HasInvalidFrameInSubtree()
+ {
+ return HasAnyStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT);
+ }
+
+ /**
+ * Removes the invalid state from the current frame and all
+ * descendant frames.
+ */
+ void ClearInvalidationStateBits();
+
+ /**
+ * Ensures that the refresh driver is running, and schedules a view
+ * manager flush on the next tick.
+ *
+ * The view manager flush will update the layer tree, repaint any
+ * invalid areas in the layer tree and schedule a layer tree
+ * composite operation to display the layer tree.
+ *
+ * In general it is not necessary for frames to call this when they change.
+ * For example, changes that result in a reflow will have this called for
+ * them by PresContext::DoReflow when the reflow begins. Style changes that
+ * do not trigger a reflow should have this called for them by
+ * DoApplyRenderingChangeToTree.
+ *
+ * @param aType PAINT_COMPOSITE_ONLY : No changes have been made
+ * that require a layer tree update, so only schedule a layer
+ * tree composite.
+ * PAINT_DELAYED_COMPRESS : Schedule a paint to be executed after a delay, and
+ * put FrameLayerBuilder in 'compressed' mode that avoids short cut optimizations.
+ */
+ enum PaintType {
+ PAINT_DEFAULT = 0,
+ PAINT_COMPOSITE_ONLY,
+ PAINT_DELAYED_COMPRESS
+ };
+ void SchedulePaint(PaintType aType = PAINT_DEFAULT);
+
+ /**
+ * Checks if the layer tree includes a dedicated layer for this
+ * frame/display item key pair, and invalidates at least aDamageRect
+ * area within that layer.
+ *
+ * If no layer is found, calls InvalidateFrame() instead.
+ *
+ * @param aDamageRect Area of the layer to invalidate.
+ * @param aFrameDamageRect If no layer is found, the area of the frame to
+ * invalidate. If null, the entire frame will be
+ * invalidated.
+ * @param aDisplayItemKey Display item type.
+ * @param aFlags UPDATE_IS_ASYNC : Will skip the invalidation
+ * if the found layer is being composited by a remote
+ * compositor.
+ * @return Layer, if found, nullptr otherwise.
+ */
+ enum {
+ UPDATE_IS_ASYNC = 1 << 0
+ };
+ Layer* InvalidateLayer(uint32_t aDisplayItemKey,
+ const nsIntRect* aDamageRect = nullptr,
+ const nsRect* aFrameDamageRect = nullptr,
+ uint32_t aFlags = 0);
+
+ /**
+ * Returns a rect that encompasses everything that might be painted by
+ * this frame. This includes this frame, all its descendant frames, this
+ * frame's outline, and descendant frames' outline, but does not include
+ * areas clipped out by the CSS "overflow" and "clip" properties.
+ *
+ * HasOverflowRects() (below) will return true when this overflow
+ * rect has been explicitly set, even if it matches mRect.
+ * XXX Note: because of a space optimization using the formula above,
+ * during reflow this function does not give accurate data if
+ * FinishAndStoreOverflow has been called but mRect hasn't yet been
+ * updated yet. FIXME: This actually isn't true, but it should be.
+ *
+ * The visual overflow rect should NEVER be used for things that
+ * affect layout. The scrollable overflow rect is permitted to affect
+ * layout.
+ *
+ * @return the rect relative to this frame's origin, but after
+ * CSS transforms have been applied (i.e. not really this frame's coordinate
+ * system, and may not contain the frame's border-box, e.g. if there
+ * is a CSS transform scaling it down)
+ */
+ nsRect GetVisualOverflowRect() const {
+ return GetOverflowRect(eVisualOverflow);
+ }
+
+ /**
+ * Returns a rect that encompasses the area of this frame that the
+ * user should be able to scroll to reach. This is similar to
+ * GetVisualOverflowRect, but does not include outline or shadows, and
+ * may in the future include more margins than visual overflow does.
+ * It does not include areas clipped out by the CSS "overflow" and
+ * "clip" properties.
+ *
+ * HasOverflowRects() (below) will return true when this overflow
+ * rect has been explicitly set, even if it matches mRect.
+ * XXX Note: because of a space optimization using the formula above,
+ * during reflow this function does not give accurate data if
+ * FinishAndStoreOverflow has been called but mRect hasn't yet been
+ * updated yet.
+ *
+ * @return the rect relative to this frame's origin, but after
+ * CSS transforms have been applied (i.e. not really this frame's coordinate
+ * system, and may not contain the frame's border-box, e.g. if there
+ * is a CSS transform scaling it down)
+ */
+ nsRect GetScrollableOverflowRect() const {
+ return GetOverflowRect(eScrollableOverflow);
+ }
+
+ nsRect GetOverflowRect(nsOverflowType aType) const;
+
+ nsOverflowAreas GetOverflowAreas() const;
+
+ /**
+ * Same as GetOverflowAreas, except in this frame's coordinate
+ * system (before transforms are applied).
+ *
+ * @return the overflow areas relative to this frame, before any CSS transforms have
+ * been applied, i.e. in this frame's coordinate system
+ */
+ nsOverflowAreas GetOverflowAreasRelativeToSelf() const;
+
+ /**
+ * Same as GetScrollableOverflowRect, except relative to the parent
+ * frame.
+ *
+ * @return the rect relative to the parent frame, in the parent frame's
+ * coordinate system
+ */
+ nsRect GetScrollableOverflowRectRelativeToParent() const;
+
+ /**
+ * Same as GetScrollableOverflowRect, except in this frame's coordinate
+ * system (before transforms are applied).
+ *
+ * @return the rect relative to this frame, before any CSS transforms have
+ * been applied, i.e. in this frame's coordinate system
+ */
+ nsRect GetScrollableOverflowRectRelativeToSelf() const;
+
+ /**
+ * Like GetVisualOverflowRect, except in this frame's
+ * coordinate system (before transforms are applied).
+ *
+ * @return the rect relative to this frame, before any CSS transforms have
+ * been applied, i.e. in this frame's coordinate system
+ */
+ nsRect GetVisualOverflowRectRelativeToSelf() const;
+
+ /**
+ * Same as GetVisualOverflowRect, except relative to the parent
+ * frame.
+ *
+ * @return the rect relative to the parent frame, in the parent frame's
+ * coordinate system
+ */
+ nsRect GetVisualOverflowRectRelativeToParent() const;
+
+ /**
+ * Returns this frame's visual overflow rect as it would be before taking
+ * account of SVG effects or transforms. The rect returned is relative to
+ * this frame.
+ */
+ nsRect GetPreEffectsVisualOverflowRect() const;
+
+ /**
+ * Store the overflow area in the frame's mOverflow.mVisualDeltas
+ * fields or as a frame property in the frame manager so that it can
+ * be retrieved later without reflowing the frame. Returns true if either of
+ * the overflow areas changed.
+ */
+ bool FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
+ nsSize aNewSize, nsSize* aOldSize = nullptr);
+
+ bool FinishAndStoreOverflow(ReflowOutput* aMetrics) {
+ return FinishAndStoreOverflow(aMetrics->mOverflowAreas,
+ nsSize(aMetrics->Width(), aMetrics->Height()));
+ }
+
+ /**
+ * Returns whether the frame has an overflow rect that is different from
+ * its border-box.
+ */
+ bool HasOverflowAreas() const {
+ return mOverflow.mType != NS_FRAME_OVERFLOW_NONE;
+ }
+
+ /**
+ * Removes any stored overflow rects (visual and scrollable) from the frame.
+ * Returns true if the overflow changed.
+ */
+ bool ClearOverflowRects();
+
+ /**
+ * Determine whether borders, padding, margins etc should NOT be applied
+ * on certain sides of the frame.
+ * @see mozilla::Sides in gfx/2d/BaseMargin.h
+ * @see mozilla::LogicalSides in layout/generic/WritingModes.h
+ *
+ * @note (See also bug 743402, comment 11) GetSkipSides() checks to see
+ * if this frame has a previous or next continuation to determine
+ * if a side should be skipped.
+ * Unfortunately, this only works after reflow has been completed. In
+ * lieu of this, during reflow, an ReflowInput parameter can be
+ * passed in, indicating that it should be used to determine if sides
+ * should be skipped during reflow.
+ */
+ Sides GetSkipSides(const ReflowInput* aReflowInput = nullptr) const;
+ virtual LogicalSides
+ GetLogicalSkipSides(const ReflowInput* aReflowInput = nullptr) const {
+ return LogicalSides();
+ }
+
+ /**
+ * @returns true if this frame is selected.
+ */
+ bool IsSelected() const;
+
+ /**
+ * called to discover where this frame, or a parent frame has user-select style
+ * applied, which affects that way that it is selected.
+ *
+ * @param aIsSelectable out param. Set to true if the frame can be selected
+ * (i.e. is not affected by user-select: none)
+ * @param aSelectStyle out param. Returns the type of selection style found
+ * (using values defined in nsStyleConsts.h).
+ */
+ virtual nsresult IsSelectable(bool* aIsSelectable,
+ mozilla::StyleUserSelect* aSelectStyle) const = 0;
+
+ /**
+ * Called to retrieve the SelectionController associated with the frame.
+ * @param aSelCon will contain the selection controller associated with
+ * the frame.
+ */
+ virtual nsresult GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) = 0;
+
+ /**
+ * Call to get nsFrameSelection for this frame.
+ */
+ already_AddRefed<nsFrameSelection> GetFrameSelection();
+
+ /**
+ * GetConstFrameSelection returns an object which methods are safe to use for
+ * example in nsIFrame code.
+ */
+ const nsFrameSelection* GetConstFrameSelection() const;
+
+ /**
+ * called to find the previous/next character, word, or line returns the actual
+ * nsIFrame and the frame offset. THIS DOES NOT CHANGE SELECTION STATE
+ * uses frame's begin selection state to start. if no selection on this frame will
+ * return NS_ERROR_FAILURE
+ * @param aPOS is defined in nsFrameSelection
+ */
+ virtual nsresult PeekOffset(nsPeekOffsetStruct *aPos);
+
+ /**
+ * called to find the previous/next non-anonymous selectable leaf frame.
+ * @param aDirection [in] the direction to move in (eDirPrevious or eDirNext)
+ * @param aVisual [in] whether bidi caret behavior is visual (true) or logical (false)
+ * @param aJumpLines [in] whether to allow jumping across line boundaries
+ * @param aScrollViewStop [in] whether to stop when reaching a scroll frame boundary
+ * @param aOutFrame [out] the previous/next selectable leaf frame
+ * @param aOutOffset [out] 0 indicates that we arrived at the beginning of the output frame;
+ * -1 indicates that we arrived at its end.
+ * @param aOutJumpedLine [out] whether this frame and the returned frame are on different lines
+ * @param aOutMovedOverNonSelectableText [out] whether we jumped over a non-selectable
+ * frame during the search
+ */
+ nsresult GetFrameFromDirection(nsDirection aDirection, bool aVisual,
+ bool aJumpLines, bool aScrollViewStop,
+ nsIFrame** aOutFrame, int32_t* aOutOffset,
+ bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText);
+
+ /**
+ * called to see if the children of the frame are visible from indexstart to index end.
+ * this does not change any state. returns true only if the indexes are valid and any of
+ * the children are visible. for textframes this index is the character index.
+ * if aStart = aEnd result will be false
+ * @param aStart start index of first child from 0-N (number of children)
+ * @param aEnd end index of last child from 0-N
+ * @param aRecurse should this frame talk to siblings to get to the contents other children?
+ * @param aFinished did this frame have the aEndIndex? or is there more work to do
+ * @param _retval return value true or false. false = range is not rendered.
+ */
+ virtual nsresult CheckVisibility(nsPresContext* aContext, int32_t aStartIndex, int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *_retval)=0;
+
+ /**
+ * Called to tell a frame that one of its child frames is dirty (i.e.,
+ * has the NS_FRAME_IS_DIRTY *or* NS_FRAME_HAS_DIRTY_CHILDREN bit
+ * set). This should always set the NS_FRAME_HAS_DIRTY_CHILDREN on
+ * the frame, and may do other work.
+ */
+ virtual void ChildIsDirty(nsIFrame* aChild) = 0;
+
+ /**
+ * Called to retrieve this frame's accessible.
+ * If this frame implements Accessibility return a valid accessible
+ * If not return NS_ERROR_NOT_IMPLEMENTED.
+ * Note: Accessible must be refcountable. Do not implement directly on your frame
+ * Use a mediatior of some kind.
+ */
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() = 0;
+#endif
+
+ /**
+ * Get the frame whose style context should be the parent of this
+ * frame's style context (i.e., provide the parent style context).
+ * This frame must either be an ancestor of this frame or a child. If
+ * this returns a child frame, then the child frame must be sure to
+ * return a grandparent or higher! Furthermore, if a child frame is
+ * returned it must have the same GetContent() as this frame.
+ *
+ * @param aProviderFrame (out) the frame associated with the returned value
+ * or nullptr if the style context is for display:contents content.
+ * @return The style context that should be the parent of this frame's
+ * style context. Null is permitted, and means that this frame's
+ * style context should be the root of the style context tree.
+ */
+ virtual nsStyleContext* GetParentStyleContext(nsIFrame** aProviderFrame) const = 0;
+
+ /**
+ * Determines whether a frame is visible for painting;
+ * taking into account whether it is painting a selection or printing.
+ */
+ bool IsVisibleForPainting(nsDisplayListBuilder* aBuilder);
+ /**
+ * Determines whether a frame is visible for painting or collapsed;
+ * taking into account whether it is painting a selection or printing,
+ */
+ bool IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder);
+ /**
+ * As above, but slower because we have to recompute some stuff that
+ * aBuilder already has.
+ */
+ bool IsVisibleForPainting();
+ /**
+ * Check whether this frame is visible in the current selection. Returns
+ * true if there is no current selection.
+ */
+ bool IsVisibleInSelection(nsDisplayListBuilder* aBuilder);
+
+ /**
+ * Overridable function to determine whether this frame should be considered
+ * "in" the given non-null aSelection for visibility purposes.
+ */
+ virtual bool IsVisibleInSelection(nsISelection* aSelection);
+
+ /**
+ * Determines whether this frame is a pseudo stacking context, looking
+ * only as style --- i.e., assuming that it's in-flow and not a replaced
+ * element and not an SVG element.
+ * XXX maybe check IsTransformed()?
+ */
+ bool IsPseudoStackingContextFromStyle();
+
+ virtual bool HonorPrintBackgroundSettings() { return true; }
+
+ /**
+ * Determine whether the frame is logically empty, which is roughly
+ * whether the layout would be the same whether or not the frame is
+ * present. Placeholder frames should return true. Block frames
+ * should be considered empty whenever margins collapse through them,
+ * even though those margins are relevant. Text frames containing
+ * only whitespace that does not contribute to the height of the line
+ * should return true.
+ */
+ virtual bool IsEmpty() = 0;
+ /**
+ * Return the same as IsEmpty(). This may only be called after the frame
+ * has been reflowed and before any further style or content changes.
+ */
+ virtual bool CachedIsEmpty();
+ /**
+ * Determine whether the frame is logically empty, assuming that all
+ * its children are empty.
+ */
+ virtual bool IsSelfEmpty() = 0;
+
+ /**
+ * IsGeneratedContentFrame returns whether a frame corresponds to
+ * generated content
+ *
+ * @return whether the frame correspods to generated content
+ */
+ bool IsGeneratedContentFrame() const {
+ return (mState & NS_FRAME_GENERATED_CONTENT) != 0;
+ }
+
+ /**
+ * IsPseudoFrame returns whether a frame is a pseudo frame (eg an
+ * anonymous table-row frame created for a CSS table-cell without an
+ * enclosing table-row.
+ *
+ * @param aParentContent the content node corresponding to the parent frame
+ * @return whether the frame is a pseudo frame
+ */
+ bool IsPseudoFrame(const nsIContent* aParentContent) {
+ return mContent == aParentContent;
+ }
+
+ FrameProperties Properties() const {
+ return FrameProperties(PresContext()->PropertyTable(), this);
+ }
+
+ /**
+ * Return true if and only if this frame obeys visibility:hidden.
+ * if it does not, then nsContainerFrame will hide its view even though
+ * this means children can't be made visible again.
+ */
+ virtual bool SupportsVisibilityHidden() { return true; }
+
+ /**
+ * Returns the clip rect set via the 'clip' property, if the 'clip' property
+ * applies to this frame; otherwise returns Nothing(). The 'clip' property
+ * applies to HTML frames if they are absolutely positioned. The 'clip'
+ * property applies to SVG frames regardless of the value of the 'position'
+ * property.
+ *
+ * The coordinates of the returned rectangle are relative to this frame's
+ * origin.
+ */
+ Maybe<nsRect> GetClipPropClipRect(const nsStyleDisplay* aDisp,
+ const nsStyleEffects* aEffects,
+ const nsSize& aSize) const;
+
+ /**
+ * Check if this frame is focusable and in the current tab order.
+ * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
+ * For example, only the selected radio button in a group is in the
+ * tab order, unless the radio group has no selection in which case
+ * all of the visible, non-disabled radio buttons in the group are
+ * in the tab order. On the other hand, all of the visible, non-disabled
+ * radio buttons are always focusable via clicking or script.
+ * Also, depending on the pref accessibility.tabfocus some widgets may be
+ * focusable but removed from the tab order. This is the default on
+ * Mac OS X, where fewer items are focusable.
+ * @param [in, optional] aTabIndex the computed tab index
+ * < 0 if not tabbable
+ * == 0 if in normal tab order
+ * > 0 can be tabbed to in the order specified by this value
+ * @param [in, optional] aWithMouse, is this focus query for mouse clicking
+ * @return whether the frame is focusable via mouse, kbd or script.
+ */
+ virtual bool IsFocusable(int32_t *aTabIndex = nullptr, bool aWithMouse = false);
+
+ // BOX LAYOUT METHODS
+ // These methods have been migrated from nsIBox and are in the process of
+ // being refactored. DO NOT USE OUTSIDE OF XUL.
+ bool IsXULBoxFrame() const
+ {
+ return IsFrameOfType(nsIFrame::eXULBox);
+ }
+
+ enum Halignment {
+ hAlign_Left,
+ hAlign_Right,
+ hAlign_Center
+ };
+
+ enum Valignment {
+ vAlign_Top,
+ vAlign_Middle,
+ vAlign_BaseLine,
+ vAlign_Bottom
+ };
+
+ /**
+ * This calculates the minimum size required for a box based on its state
+ * @param[in] aBoxLayoutState The desired state to calculate for
+ * @return The minimum size
+ */
+ virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) = 0;
+
+ /**
+ * This calculates the preferred size of a box based on its state
+ * @param[in] aBoxLayoutState The desired state to calculate for
+ * @return The preferred size
+ */
+ virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) = 0;
+
+ /**
+ * This calculates the maximum size for a box based on its state
+ * @param[in] aBoxLayoutState The desired state to calculate for
+ * @return The maximum size
+ */
+ virtual nsSize GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) = 0;
+
+ /**
+ * This returns the minimum size for the scroll area if this frame is
+ * being scrolled. Usually it's (0,0).
+ */
+ virtual nsSize GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) = 0;
+
+ // Implemented in nsBox, used in nsBoxFrame
+ uint32_t GetXULOrdinal();
+
+ virtual nscoord GetXULFlex() = 0;
+ virtual nscoord GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState) = 0;
+ virtual bool IsXULCollapsed() = 0;
+ // This does not alter the overflow area. If the caller is changing
+ // the box size, the caller is responsible for updating the overflow
+ // area. It's enough to just call XULLayout or SyncLayout on the
+ // box. You can pass true to aRemoveOverflowArea as a
+ // convenience.
+ virtual void SetXULBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
+ bool aRemoveOverflowAreas = false) = 0;
+ nsresult XULLayout(nsBoxLayoutState& aBoxLayoutState);
+ // Box methods. Note that these do NOT just get the CSS border, padding,
+ // etc. They also talk to nsITheme.
+ virtual nsresult GetXULBorderAndPadding(nsMargin& aBorderAndPadding);
+ virtual nsresult GetXULBorder(nsMargin& aBorder)=0;
+ virtual nsresult GetXULPadding(nsMargin& aBorderAndPadding)=0;
+ virtual nsresult GetXULMargin(nsMargin& aMargin)=0;
+ virtual void SetXULLayoutManager(nsBoxLayout* aLayout) { }
+ virtual nsBoxLayout* GetXULLayoutManager() { return nullptr; }
+ nsresult GetXULClientRect(nsRect& aContentRect);
+
+ virtual uint32_t GetXULLayoutFlags()
+ { return 0; }
+
+ // For nsSprocketLayout
+ virtual Valignment GetXULVAlign() const = 0;
+ virtual Halignment GetXULHAlign() const = 0;
+
+ bool IsXULHorizontal() const { return (mState & NS_STATE_IS_HORIZONTAL) != 0; }
+ bool IsXULNormalDirection() const { return (mState & NS_STATE_IS_DIRECTION_NORMAL) != 0; }
+
+ nsresult XULRedraw(nsBoxLayoutState& aState);
+ virtual nsresult XULRelayoutChildAtOrdinal(nsIFrame* aChild)=0;
+
+#ifdef DEBUG_LAYOUT
+ virtual nsresult SetXULDebug(nsBoxLayoutState& aState, bool aDebug)=0;
+ virtual nsresult GetXULDebug(bool& aDebug)=0;
+
+ virtual nsresult XULDumpBox(FILE* out)=0;
+#endif
+
+ static bool AddXULPrefSize(nsIFrame* aBox, nsSize& aSize, bool& aWidth, bool& aHeightSet);
+ static bool AddXULMinSize(nsBoxLayoutState& aState, nsIFrame* aBox,
+ nsSize& aSize, bool& aWidth, bool& aHeightSet);
+ static bool AddXULMaxSize(nsIFrame* aBox, nsSize& aSize, bool& aWidth, bool& aHeightSet);
+ static bool AddXULFlex(nsIFrame* aBox, nscoord& aFlex);
+
+ // END OF BOX LAYOUT METHODS
+ // The above methods have been migrated from nsIBox and are in the process of
+ // being refactored. DO NOT USE OUTSIDE OF XUL.
+
+ /**
+ * @return true if this text frame ends with a newline character. It
+ * should return false if this is not a text frame.
+ */
+ virtual bool HasSignificantTerminalNewline() const;
+
+ struct CaretPosition {
+ CaretPosition();
+ ~CaretPosition();
+
+ nsCOMPtr<nsIContent> mResultContent;
+ int32_t mContentOffset;
+ };
+
+ /**
+ * gets the first or last possible caret position within the frame
+ *
+ * @param [in] aStart
+ * true for getting the first possible caret position
+ * false for getting the last possible caret position
+ * @return The caret position in a CaretPosition.
+ * the returned value is a 'best effort' in case errors
+ * are encountered rummaging through the frame.
+ */
+ CaretPosition GetExtremeCaretPosition(bool aStart);
+
+ /**
+ * Get a line iterator for this frame, if supported.
+ *
+ * @return nullptr if no line iterator is supported.
+ * @note dispose the line iterator using nsILineIterator::DisposeLineIterator
+ */
+ virtual nsILineIterator* GetLineIterator() = 0;
+
+ /**
+ * If this frame is a next-in-flow, and its prev-in-flow has something on its
+ * overflow list, pull those frames into the child list of this one.
+ */
+ virtual void PullOverflowsFromPrevInFlow() {}
+
+ /**
+ * Clear the list of child PresShells generated during the last paint
+ * so that we can begin generating a new one.
+ */
+ void ClearPresShellsFromLastPaint() {
+ PaintedPresShellList()->Clear();
+ }
+
+ /**
+ * Flag a child PresShell as painted so that it will get its paint count
+ * incremented during empty transactions.
+ */
+ void AddPaintedPresShell(nsIPresShell* shell) {
+ PaintedPresShellList()->AppendElement(do_GetWeakReference(shell));
+ }
+
+ /**
+ * Increment the paint count of all child PresShells that were painted during
+ * the last repaint.
+ */
+ void UpdatePaintCountForPaintedPresShells() {
+ for (nsWeakPtr& item : *PaintedPresShellList()) {
+ nsCOMPtr<nsIPresShell> shell = do_QueryReferent(item);
+ if (shell) {
+ shell->IncrementPaintCount();
+ }
+ }
+ }
+
+ /**
+ * @return true if we painted @aShell during the last repaint.
+ */
+ bool DidPaintPresShell(nsIPresShell* aShell)
+ {
+ for (nsWeakPtr& item : *PaintedPresShellList()) {
+ nsCOMPtr<nsIPresShell> shell = do_QueryReferent(item);
+ if (shell == aShell) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Accessors for the absolute containing block.
+ */
+ bool IsAbsoluteContainer() const { return !!(mState & NS_FRAME_HAS_ABSPOS_CHILDREN); }
+ bool HasAbsolutelyPositionedChildren() const;
+ nsAbsoluteContainingBlock* GetAbsoluteContainingBlock() const;
+ void MarkAsAbsoluteContainingBlock();
+ void MarkAsNotAbsoluteContainingBlock();
+ // Child frame types override this function to select their own child list name
+ virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const { return kAbsoluteList; }
+
+ // Checks if we (or any of our descendents) have NS_FRAME_PAINTED_THEBES set, and
+ // clears this bit if so.
+ bool CheckAndClearPaintedState();
+
+ // CSS visibility just doesn't cut it because it doesn't inherit through
+ // documents. Also if this frame is in a hidden card of a deck then it isn't
+ // visible either and that isn't expressed using CSS visibility. Also if it
+ // is in a hidden view (there are a few cases left and they are hopefully
+ // going away soon).
+ // If the VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY flag is passed then we
+ // ignore the chrome/content boundary, otherwise we stop looking when we
+ // reach it.
+ enum {
+ VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY = 0x01
+ };
+ bool IsVisibleConsideringAncestors(uint32_t aFlags = 0) const;
+
+ struct FrameWithDistance
+ {
+ nsIFrame* mFrame;
+ nscoord mXDistance;
+ nscoord mYDistance;
+ };
+
+ /**
+ * Finds a frame that is closer to a specified point than a current
+ * distance. Distance is measured as for text selection -- a closer x
+ * distance beats a closer y distance.
+ *
+ * Normally, this function will only check the distance between this
+ * frame's rectangle and the specified point. SVGTextFrame overrides
+ * this so that it can manage all of its descendant frames and take
+ * into account any SVG text layout.
+ *
+ * If aPoint is closer to this frame's rectangle than aCurrentBestFrame
+ * indicates, then aCurrentBestFrame is updated with the distance between
+ * aPoint and this frame's rectangle, and with a pointer to this frame.
+ * If aPoint is not closer, then aCurrentBestFrame is left unchanged.
+ *
+ * @param aPoint The point to check for its distance to this frame.
+ * @param aCurrentBestFrame Pointer to a struct that will be updated with
+ * a pointer to this frame and its distance to aPoint, if this frame
+ * is indeed closer than the current distance in aCurrentBestFrame.
+ */
+ virtual void FindCloserFrameForSelection(nsPoint aPoint,
+ FrameWithDistance* aCurrentBestFrame);
+
+ /**
+ * Is this a flex item? (i.e. a non-abs-pos child of a flex container)
+ */
+ inline bool IsFlexItem() const;
+ /**
+ * Is this a flex or grid item? (i.e. a non-abs-pos child of a flex/grid container)
+ */
+ inline bool IsFlexOrGridItem() const;
+ inline bool IsFlexOrGridContainer() const;
+
+ /**
+ * @return true if this frame is used as a table caption.
+ */
+ inline bool IsTableCaption() const;
+
+ inline bool IsBlockInside() const;
+ inline bool IsBlockOutside() const;
+ inline bool IsInlineOutside() const;
+ inline mozilla::StyleDisplay GetDisplay() const;
+ inline bool IsFloating() const;
+ inline bool IsAbsPosContainingBlock() const;
+ inline bool IsFixedPosContainingBlock() const;
+ inline bool IsRelativelyPositioned() const;
+ inline bool IsAbsolutelyPositioned() const;
+
+ /**
+ * Returns the vertical-align value to be used for layout, if it is one
+ * of the enumerated values. If this is an SVG text frame, it returns a value
+ * that corresponds to the value of dominant-baseline. If the
+ * vertical-align property has length or percentage value, this returns
+ * eInvalidVerticalAlign.
+ */
+ uint8_t VerticalAlignEnum() const;
+ enum { eInvalidVerticalAlign = 0xFF };
+
+ bool IsSVGText() const { return mState & NS_FRAME_IS_SVG_TEXT; }
+
+ void CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, nsDisplayList* aList);
+
+ /**
+ * Adds the NS_FRAME_IN_POPUP state bit to aFrame, and
+ * all descendant frames (including cross-doc ones).
+ */
+ static void AddInPopupStateBitToDescendants(nsIFrame* aFrame);
+ /**
+ * Removes the NS_FRAME_IN_POPUP state bit from aFrame and
+ * all descendant frames (including cross-doc ones), unless
+ * the frame is a popup itself.
+ */
+ static void RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame);
+
+ /**
+ * Sorts the given nsFrameList, so that for every two adjacent frames in the
+ * list, the former is less than or equal to the latter, according to the
+ * templated IsLessThanOrEqual method.
+ *
+ * Note: this method uses a stable merge-sort algorithm.
+ */
+ template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+ static void SortFrameList(nsFrameList& aFrameList);
+
+ /**
+ * Returns true if the given frame list is already sorted, according to the
+ * templated IsLessThanOrEqual function.
+ */
+ template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+ static bool IsFrameListSorted(nsFrameList& aFrameList);
+
+ /**
+ * Return true if aFrame is in an {ib} split and is NOT one of the
+ * continuations of the first inline in it.
+ */
+ bool FrameIsNonFirstInIBSplit() const {
+ return (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
+ FirstContinuation()->Properties().Get(nsIFrame::IBSplitPrevSibling());
+ }
+
+ /**
+ * Return true if aFrame is in an {ib} split and is NOT one of the
+ * continuations of the last inline in it.
+ */
+ bool FrameIsNonLastInIBSplit() const {
+ return (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
+ FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling());
+ }
+
+ /**
+ * Return whether this is a frame whose width is used when computing
+ * the font size inflation of its descendants.
+ */
+ bool IsContainerForFontSizeInflation() const {
+ return GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER;
+ }
+
+ /**
+ * Return whether this frame keeps track of overflow areas. (Frames for
+ * non-display SVG elements -- e.g. <clipPath> -- do not maintain overflow
+ * areas, because they're never painted.)
+ */
+ bool FrameMaintainsOverflow() const {
+ return !HasAllStateBits(NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY);
+ }
+
+ /**
+ * Returns the content node within the anonymous content that this frame
+ * generated and which corresponds to the specified pseudo-element type,
+ * or nullptr if there is no such anonymous content.
+ */
+ virtual mozilla::dom::Element*
+ GetPseudoElement(mozilla::CSSPseudoElementType aType);
+
+ bool BackfaceIsHidden() const {
+ return StyleDisplay()->BackfaceIsHidden();
+ }
+
+ /**
+ * Returns true if the frame is scrolled out of view.
+ */
+ bool IsScrolledOutOfView();
+
+ /**
+ * If this returns true, the frame it's called on should get the
+ * NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly
+ * if it's already in reflow, or via calling FrameNeedsReflow() to schedule a
+ * reflow.
+ */
+ virtual bool RenumberFrameAndDescendants(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting) { return false; }
+
+ /**
+ * Helper function - computes the content-box inline size for aCoord.
+ */
+ nscoord ComputeISizeValue(nsRenderingContext* aRenderingContext,
+ nscoord aContainingBlockISize,
+ nscoord aContentEdgeToBoxSizing,
+ nscoord aBoxSizingToMarginEdge,
+ const nsStyleCoord& aCoord,
+ ComputeSizeFlags aFlags = eDefault);
+protected:
+ // Members
+ nsRect mRect;
+ nsIContent* mContent;
+ nsStyleContext* mStyleContext;
+private:
+ nsContainerFrame* mParent;
+ nsIFrame* mNextSibling; // doubly-linked list of frames
+ nsIFrame* mPrevSibling; // Do not touch outside SetNextSibling!
+
+ void MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect);
+
+ static void DestroyPaintedPresShellList(nsTArray<nsWeakPtr>* list) {
+ list->Clear();
+ delete list;
+ }
+
+ // Stores weak references to all the PresShells that were painted during
+ // the last paint event so that we can increment their paint count during
+ // empty transactions
+ NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(PaintedPresShellsProperty,
+ nsTArray<nsWeakPtr>,
+ DestroyPaintedPresShellList)
+
+ nsTArray<nsWeakPtr>* PaintedPresShellList() {
+ nsTArray<nsWeakPtr>* list = Properties().Get(PaintedPresShellsProperty());
+
+ if (!list) {
+ list = new nsTArray<nsWeakPtr>();
+ Properties().Set(PaintedPresShellsProperty(), list);
+ }
+
+ return list;
+ }
+
+protected:
+ void MarkInReflow() {
+#ifdef DEBUG_dbaron_off
+ // bug 81268
+ NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
+#endif
+ mState |= NS_FRAME_IN_REFLOW;
+ }
+
+ nsFrameState mState;
+
+ // When there is an overflow area only slightly larger than mRect,
+ // we store a set of four 1-byte deltas from the edges of mRect
+ // rather than allocating a whole separate rectangle property.
+ // Note that these are unsigned values, all measured "outwards"
+ // from the edges of mRect, so /mLeft/ and /mTop/ are reversed from
+ // our normal coordinate system.
+ // If mOverflow.mType == NS_FRAME_OVERFLOW_LARGE, then the
+ // delta values are not meaningful and the overflow area is stored
+ // as a separate rect property.
+ struct VisualDeltas {
+ uint8_t mLeft;
+ uint8_t mTop;
+ uint8_t mRight;
+ uint8_t mBottom;
+ bool operator==(const VisualDeltas& aOther) const
+ {
+ return mLeft == aOther.mLeft && mTop == aOther.mTop &&
+ mRight == aOther.mRight && mBottom == aOther.mBottom;
+ }
+ bool operator!=(const VisualDeltas& aOther) const
+ {
+ return !(*this == aOther);
+ }
+ };
+ union {
+ uint32_t mType;
+ VisualDeltas mVisualDeltas;
+ } mOverflow;
+
+ // Helpers
+ /**
+ * Can we stop inside this frame when we're skipping non-rendered whitespace?
+ * @param aForward [in] Are we moving forward (or backward) in content order.
+ * @param aOffset [in/out] At what offset into the frame to start looking.
+ * on output - what offset was reached (whether or not we found a place to stop).
+ * @return STOP: An appropriate offset was found within this frame,
+ * and is given by aOffset.
+ * CONTINUE: Not found within this frame, need to try the next frame.
+ * see enum FrameSearchResult for more details.
+ */
+ virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) = 0;
+
+ /**
+ * Search the frame for the next character
+ * @param aForward [in] Are we moving forward (or backward) in content order.
+ * @param aOffset [in/out] At what offset into the frame to start looking.
+ * on output - what offset was reached (whether or not we found a place to stop).
+ * @param aRespectClusters [in] Whether to restrict result to valid cursor locations
+ * (between grapheme clusters) - default TRUE maintains "normal" behavior,
+ * FALSE is used for selection by "code unit" (instead of "character")
+ * @return STOP: An appropriate offset was found within this frame,
+ * and is given by aOffset.
+ * CONTINUE: Not found within this frame, need to try the next frame.
+ * see enum FrameSearchResult for more details.
+ */
+ virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) = 0;
+
+ /**
+ * Search the frame for the next word boundary
+ * @param aForward [in] Are we moving forward (or backward) in content order.
+ * @param aWordSelectEatSpace [in] true: look for non-whitespace following
+ * whitespace (in the direction of movement).
+ * false: look for whitespace following non-whitespace (in the
+ * direction of movement).
+ * @param aIsKeyboardSelect [in] Was the action initiated by a keyboard operation?
+ * If true, punctuation immediately following a word is considered part
+ * of that word. Otherwise, a sequence of punctuation is always considered
+ * as a word on its own.
+ * @param aOffset [in/out] At what offset into the frame to start looking.
+ * on output - what offset was reached (whether or not we found a place to stop).
+ * @param aState [in/out] the state that is carried from frame to frame
+ * @return true: An appropriate offset was found within this frame,
+ * and is given by aOffset.
+ * false: Not found within this frame, need to try the next frame.
+ */
+ struct PeekWordState {
+ // true when we're still at the start of the search, i.e., we can't return
+ // this point as a valid offset!
+ bool mAtStart;
+ // true when we've encountered at least one character of the pre-boundary type
+ // (whitespace if aWordSelectEatSpace is true, non-whitespace otherwise)
+ bool mSawBeforeType;
+ // true when the last character encountered was punctuation
+ bool mLastCharWasPunctuation;
+ // true when the last character encountered was whitespace
+ bool mLastCharWasWhitespace;
+ // true when we've seen non-punctuation since the last whitespace
+ bool mSeenNonPunctuationSinceWhitespace;
+ // text that's *before* the current frame when aForward is true, *after*
+ // the current frame when aForward is false. Only includes the text
+ // on the current line.
+ nsAutoString mContext;
+
+ PeekWordState() : mAtStart(true), mSawBeforeType(false),
+ mLastCharWasPunctuation(false), mLastCharWasWhitespace(false),
+ mSeenNonPunctuationSinceWhitespace(false) {}
+ void SetSawBeforeType() { mSawBeforeType = true; }
+ void Update(bool aAfterPunctuation, bool aAfterWhitespace) {
+ mLastCharWasPunctuation = aAfterPunctuation;
+ mLastCharWasWhitespace = aAfterWhitespace;
+ if (aAfterWhitespace) {
+ mSeenNonPunctuationSinceWhitespace = false;
+ } else if (!aAfterPunctuation) {
+ mSeenNonPunctuationSinceWhitespace = true;
+ }
+ mAtStart = false;
+ }
+ };
+ virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
+ int32_t* aOffset, PeekWordState* aState) = 0;
+
+ /**
+ * Search for the first paragraph boundary before or after the given position
+ * @param aPos See description in nsFrameSelection.h. The following fields are
+ * used by this method:
+ * Input: mDirection
+ * Output: mResultContent, mContentOffset
+ */
+ nsresult PeekOffsetParagraph(nsPeekOffsetStruct *aPos);
+
+private:
+ nsOverflowAreas* GetOverflowAreasProperty();
+ nsRect GetVisualOverflowFromDeltas() const {
+ MOZ_ASSERT(mOverflow.mType != NS_FRAME_OVERFLOW_LARGE,
+ "should not be called when overflow is in a property");
+ // Calculate the rect using deltas from the frame's border rect.
+ // Note that the mOverflow.mDeltas fields are unsigned, but we will often
+ // need to return negative values for the left and top, so take care
+ // to cast away the unsigned-ness.
+ return nsRect(-(int32_t)mOverflow.mVisualDeltas.mLeft,
+ -(int32_t)mOverflow.mVisualDeltas.mTop,
+ mRect.width + mOverflow.mVisualDeltas.mRight +
+ mOverflow.mVisualDeltas.mLeft,
+ mRect.height + mOverflow.mVisualDeltas.mBottom +
+ mOverflow.mVisualDeltas.mTop);
+ }
+ /**
+ * Returns true if any overflow changed.
+ */
+ bool SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
+
+ // Helper-functions for SortFrameList():
+ template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+ static nsIFrame* SortedMerge(nsIFrame *aLeft, nsIFrame *aRight);
+
+ template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+ static nsIFrame* MergeSort(nsIFrame *aSource);
+
+ bool HasOpacityInternal(float aThreshold) const;
+
+#ifdef DEBUG_FRAME_DUMP
+public:
+ static void IndentBy(FILE* out, int32_t aIndent) {
+ while (--aIndent >= 0) fputs(" ", out);
+ }
+ void ListTag(FILE* out) const {
+ ListTag(out, this);
+ }
+ static void ListTag(FILE* out, const nsIFrame* aFrame) {
+ nsAutoCString t;
+ ListTag(t, aFrame);
+ fputs(t.get(), out);
+ }
+ static void ListTag(FILE* out, const nsFrameList& aFrameList) {
+ for (nsIFrame* frame : aFrameList) {
+ ListTag(out, frame);
+ }
+ }
+ void ListTag(nsACString& aTo) const;
+ nsAutoCString ListTag() const {
+ nsAutoCString tag;
+ ListTag(tag);
+ return tag;
+ }
+ static void ListTag(nsACString& aTo, const nsIFrame* aFrame);
+ void ListGeneric(nsACString& aTo, const char* aPrefix = "", uint32_t aFlags = 0) const;
+ enum {
+ TRAVERSE_SUBDOCUMENT_FRAMES = 0x01
+ };
+ virtual void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const;
+ /**
+ * lists the frames beginning from the root frame
+ * - calls root frame's List(...)
+ */
+ static void RootFrameList(nsPresContext* aPresContext,
+ FILE* out = stderr, const char* aPrefix = "");
+ virtual void DumpFrameTree() const;
+ void DumpFrameTreeLimited() const;
+
+ virtual nsresult GetFrameName(nsAString& aResult) const = 0;
+#endif
+
+#ifdef DEBUG
+public:
+ virtual nsFrameState GetDebugStateBits() const = 0;
+ virtual nsresult DumpRegressionData(nsPresContext* aPresContext,
+ FILE* out, int32_t aIndent) = 0;
+#endif
+};
+
+//----------------------------------------------------------------------
+
+/**
+ * nsWeakFrame can be used to keep a reference to a nsIFrame in a safe way.
+ * Whenever an nsIFrame object is deleted, the nsWeakFrames pointing
+ * to it will be cleared.
+ *
+ * Create nsWeakFrame object when it is sure that nsIFrame object
+ * is alive and after some operations which may destroy the nsIFrame
+ * (for example any DOM modifications) use IsAlive() or GetFrame() methods to
+ * check whether it is safe to continue to use the nsIFrame object.
+ *
+ * @note The usage of this class should be kept to a minimum.
+ */
+
+class nsWeakFrame {
+public:
+ nsWeakFrame() : mPrev(nullptr), mFrame(nullptr) { }
+
+ nsWeakFrame(const nsWeakFrame& aOther) : mPrev(nullptr), mFrame(nullptr)
+ {
+ Init(aOther.GetFrame());
+ }
+
+ MOZ_IMPLICIT nsWeakFrame(nsIFrame* aFrame) : mPrev(nullptr), mFrame(nullptr)
+ {
+ Init(aFrame);
+ }
+
+ nsWeakFrame& operator=(nsWeakFrame& aOther) {
+ Init(aOther.GetFrame());
+ return *this;
+ }
+
+ nsWeakFrame& operator=(nsIFrame* aFrame) {
+ Init(aFrame);
+ return *this;
+ }
+
+ nsIFrame* operator->()
+ {
+ return mFrame;
+ }
+
+ operator nsIFrame*()
+ {
+ return mFrame;
+ }
+
+ void Clear(nsIPresShell* aShell) {
+ if (aShell) {
+ aShell->RemoveWeakFrame(this);
+ }
+ mFrame = nullptr;
+ mPrev = nullptr;
+ }
+
+ bool IsAlive() { return !!mFrame; }
+
+ nsIFrame* GetFrame() const { return mFrame; }
+
+ nsWeakFrame* GetPreviousWeakFrame() { return mPrev; }
+
+ void SetPreviousWeakFrame(nsWeakFrame* aPrev) { mPrev = aPrev; }
+
+ ~nsWeakFrame()
+ {
+ Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
+ }
+private:
+ void Init(nsIFrame* aFrame);
+
+ nsWeakFrame* mPrev;
+ nsIFrame* mFrame;
+};
+
+inline bool
+nsFrameList::ContinueRemoveFrame(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(!aFrame->GetPrevSibling() || !aFrame->GetNextSibling(),
+ "Forgot to call StartRemoveFrame?");
+ if (aFrame == mLastChild) {
+ MOZ_ASSERT(!aFrame->GetNextSibling(), "broken frame list");
+ nsIFrame* prevSibling = aFrame->GetPrevSibling();
+ if (!prevSibling) {
+ MOZ_ASSERT(aFrame == mFirstChild, "broken frame list");
+ mFirstChild = mLastChild = nullptr;
+ return true;
+ }
+ MOZ_ASSERT(prevSibling->GetNextSibling() == aFrame, "Broken frame linkage");
+ prevSibling->SetNextSibling(nullptr);
+ mLastChild = prevSibling;
+ return true;
+ }
+ if (aFrame == mFirstChild) {
+ MOZ_ASSERT(!aFrame->GetPrevSibling(), "broken frame list");
+ mFirstChild = aFrame->GetNextSibling();
+ aFrame->SetNextSibling(nullptr);
+ MOZ_ASSERT(mFirstChild, "broken frame list");
+ return true;
+ }
+ return false;
+}
+
+inline bool
+nsFrameList::StartRemoveFrame(nsIFrame* aFrame)
+{
+ if (aFrame->GetPrevSibling() && aFrame->GetNextSibling()) {
+ UnhookFrameFromSiblings(aFrame);
+ return true;
+ }
+ return ContinueRemoveFrame(aFrame);
+}
+
+inline void
+nsFrameList::Enumerator::Next()
+{
+ NS_ASSERTION(!AtEnd(), "Should have checked AtEnd()!");
+ mFrame = mFrame->GetNextSibling();
+}
+
+inline
+nsFrameList::FrameLinkEnumerator::
+FrameLinkEnumerator(const nsFrameList& aList, nsIFrame* aPrevFrame)
+ : Enumerator(aList)
+{
+ mPrev = aPrevFrame;
+ mFrame = aPrevFrame ? aPrevFrame->GetNextSibling() : aList.FirstChild();
+}
+
+inline void
+nsFrameList::FrameLinkEnumerator::Next()
+{
+ mPrev = mFrame;
+ Enumerator::Next();
+}
+
+// Operators of nsFrameList::Iterator
+// ---------------------------------------------------
+
+inline nsFrameList::Iterator&
+nsFrameList::Iterator::operator++()
+{
+ mCurrent = mCurrent->GetNextSibling();
+ return *this;
+}
+
+inline nsFrameList::Iterator&
+nsFrameList::Iterator::operator--()
+{
+ if (!mCurrent) {
+ mCurrent = mList.LastChild();
+ } else {
+ mCurrent = mCurrent->GetPrevSibling();
+ }
+ return *this;
+}
+
+// Helper-functions for nsIFrame::SortFrameList()
+// ---------------------------------------------------
+
+template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+/* static */ nsIFrame*
+nsIFrame::SortedMerge(nsIFrame *aLeft, nsIFrame *aRight)
+{
+ NS_PRECONDITION(aLeft && aRight, "SortedMerge must have non-empty lists");
+
+ nsIFrame *result;
+ // Unroll first iteration to avoid null-check 'result' inside the loop.
+ if (IsLessThanOrEqual(aLeft, aRight)) {
+ result = aLeft;
+ aLeft = aLeft->GetNextSibling();
+ if (!aLeft) {
+ result->SetNextSibling(aRight);
+ return result;
+ }
+ }
+ else {
+ result = aRight;
+ aRight = aRight->GetNextSibling();
+ if (!aRight) {
+ result->SetNextSibling(aLeft);
+ return result;
+ }
+ }
+
+ nsIFrame *last = result;
+ for (;;) {
+ if (IsLessThanOrEqual(aLeft, aRight)) {
+ last->SetNextSibling(aLeft);
+ last = aLeft;
+ aLeft = aLeft->GetNextSibling();
+ if (!aLeft) {
+ last->SetNextSibling(aRight);
+ return result;
+ }
+ }
+ else {
+ last->SetNextSibling(aRight);
+ last = aRight;
+ aRight = aRight->GetNextSibling();
+ if (!aRight) {
+ last->SetNextSibling(aLeft);
+ return result;
+ }
+ }
+ }
+}
+
+template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+/* static */ nsIFrame*
+nsIFrame::MergeSort(nsIFrame *aSource)
+{
+ NS_PRECONDITION(aSource, "MergeSort null arg");
+
+ nsIFrame *sorted[32] = { nullptr };
+ nsIFrame **fill = &sorted[0];
+ nsIFrame **left;
+ nsIFrame *rest = aSource;
+
+ do {
+ nsIFrame *current = rest;
+ rest = rest->GetNextSibling();
+ current->SetNextSibling(nullptr);
+
+ // Merge it with sorted[0] if present; then merge the result with sorted[1] etc.
+ // sorted[0] is a list of length 1 (or nullptr).
+ // sorted[1] is a list of length 2 (or nullptr).
+ // sorted[2] is a list of length 4 (or nullptr). etc.
+ for (left = &sorted[0]; left != fill && *left; ++left) {
+ current = SortedMerge<IsLessThanOrEqual>(*left, current);
+ *left = nullptr;
+ }
+
+ // Fill the empty slot that we couldn't merge with the last result.
+ *left = current;
+
+ if (left == fill)
+ ++fill;
+ } while (rest);
+
+ // Collect and merge the results.
+ nsIFrame *result = nullptr;
+ for (left = &sorted[0]; left != fill; ++left) {
+ if (*left) {
+ result = result ? SortedMerge<IsLessThanOrEqual>(*left, result) : *left;
+ }
+ }
+ return result;
+}
+
+template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+/* static */ void
+nsIFrame::SortFrameList(nsFrameList& aFrameList)
+{
+ nsIFrame* head = MergeSort<IsLessThanOrEqual>(aFrameList.FirstChild());
+ aFrameList = nsFrameList(head, nsLayoutUtils::GetLastSibling(head));
+ MOZ_ASSERT(IsFrameListSorted<IsLessThanOrEqual>(aFrameList),
+ "After we sort a frame list, it should be in sorted order...");
+}
+
+template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
+/* static */ bool
+nsIFrame::IsFrameListSorted(nsFrameList& aFrameList)
+{
+ if (aFrameList.IsEmpty()) {
+ // empty lists are trivially sorted.
+ return true;
+ }
+
+ // We'll walk through the list with two iterators, one trailing behind the
+ // other. The list is sorted IFF trailingIter <= iter, across the whole list.
+ nsFrameList::Enumerator trailingIter(aFrameList);
+ nsFrameList::Enumerator iter(aFrameList);
+ iter.Next(); // Skip |iter| past first frame. (List is nonempty, so we can.)
+
+ // Now, advance the iterators in parallel, comparing each adjacent pair.
+ while (!iter.AtEnd()) {
+ MOZ_ASSERT(!trailingIter.AtEnd(), "trailing iter shouldn't finish first");
+ if (!IsLessThanOrEqual(trailingIter.get(), iter.get())) {
+ return false;
+ }
+ trailingIter.Next();
+ iter.Next();
+ }
+
+ // We made it to the end without returning early, so the list is sorted.
+ return true;
+}
+
+#endif /* nsIFrame_h___ */
diff --git a/layout/generic/nsIFrameInlines.h b/layout/generic/nsIFrameInlines.h
new file mode 100644
index 000000000..eb9a7202a
--- /dev/null
+++ b/layout/generic/nsIFrameInlines.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIFrameInlines_h___
+#define nsIFrameInlines_h___
+
+#include "nsContainerFrame.h"
+#include "nsStyleStructInlines.h"
+#include "nsCSSAnonBoxes.h"
+
+bool
+nsIFrame::IsFlexItem() const
+{
+ return GetParent() &&
+ GetParent()->GetType() == nsGkAtoms::flexContainerFrame &&
+ !(GetStateBits() & NS_FRAME_OUT_OF_FLOW);
+}
+
+bool
+nsIFrame::IsFlexOrGridContainer() const
+{
+ nsIAtom* t = GetType();
+ return t == nsGkAtoms::flexContainerFrame ||
+ t == nsGkAtoms::gridContainerFrame;
+}
+
+bool
+nsIFrame::IsFlexOrGridItem() const
+{
+ return !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
+ GetParent() &&
+ GetParent()->IsFlexOrGridContainer();
+}
+
+bool
+nsIFrame::IsTableCaption() const
+{
+ return StyleDisplay()->mDisplay == mozilla::StyleDisplay::TableCaption &&
+ GetParent()->StyleContext()->GetPseudo() == nsCSSAnonBoxes::tableWrapper;
+}
+
+bool
+nsIFrame::IsFloating() const
+{
+ return StyleDisplay()->IsFloating(this);
+}
+
+bool
+nsIFrame::IsAbsPosContainingBlock() const
+{
+ return StyleDisplay()->IsAbsPosContainingBlock(this);
+}
+
+bool
+nsIFrame::IsFixedPosContainingBlock() const
+{
+ return StyleDisplay()->IsFixedPosContainingBlock(this);
+}
+
+bool
+nsIFrame::IsRelativelyPositioned() const
+{
+ return StyleDisplay()->IsRelativelyPositioned(this);
+}
+
+bool
+nsIFrame::IsAbsolutelyPositioned() const
+{
+ return StyleDisplay()->IsAbsolutelyPositioned(this);
+}
+
+bool
+nsIFrame::IsBlockInside() const
+{
+ return StyleDisplay()->IsBlockInside(this);
+}
+
+bool
+nsIFrame::IsBlockOutside() const
+{
+ return StyleDisplay()->IsBlockOutside(this);
+}
+
+bool
+nsIFrame::IsInlineOutside() const
+{
+ return StyleDisplay()->IsInlineOutside(this);
+}
+
+mozilla::StyleDisplay
+nsIFrame::GetDisplay() const
+{
+ return StyleDisplay()->GetDisplay(this);
+}
+
+nscoord
+nsIFrame::SynthesizeBaselineBOffsetFromMarginBox(
+ mozilla::WritingMode aWM,
+ BaselineSharingGroup aGroup) const
+{
+ MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+ auto margin = GetLogicalUsedMargin(aWM);
+ if (aGroup == BaselineSharingGroup::eFirst) {
+ if (aWM.IsAlphabeticalBaseline()) {
+ // First baseline for inverted-line content is the block-start margin edge,
+ // as the frame is in effect "flipped" for alignment purposes.
+ return MOZ_UNLIKELY(aWM.IsLineInverted()) ? -margin.BStart(aWM)
+ : BSize(aWM) + margin.BEnd(aWM);
+ }
+ nscoord marginBoxCenter = (BSize(aWM) + margin.BStartEnd(aWM)) / 2;
+ return marginBoxCenter - margin.BStart(aWM);
+ }
+ MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+ if (aWM.IsAlphabeticalBaseline()) {
+ // Last baseline for inverted-line content is the block-start margin edge,
+ // as the frame is in effect "flipped" for alignment purposes.
+ return MOZ_UNLIKELY(aWM.IsLineInverted()) ? BSize(aWM) + margin.BStart(aWM)
+ : -margin.BEnd(aWM);
+ }
+ // Round up for central baseline offset, to be consistent with eFirst.
+ nscoord marginBoxSize = BSize(aWM) + margin.BStartEnd(aWM);
+ nscoord marginBoxCenter = (marginBoxSize / 2) + (marginBoxSize % 2);
+ return marginBoxCenter - margin.BEnd(aWM);
+}
+
+nscoord
+nsIFrame::SynthesizeBaselineBOffsetFromBorderBox(
+ mozilla::WritingMode aWM,
+ BaselineSharingGroup aGroup) const
+{
+ MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+ nscoord borderBoxSize = BSize(aWM);
+ if (aGroup == BaselineSharingGroup::eFirst) {
+ return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? borderBoxSize
+ : borderBoxSize / 2;
+ }
+ MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+ // Round up for central baseline offset, to be consistent with eFirst.
+ auto borderBoxCenter = (borderBoxSize / 2) + (borderBoxSize % 2);
+ return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? 0 : borderBoxCenter;
+}
+
+nscoord
+nsIFrame::BaselineBOffset(mozilla::WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ AlignmentContext aAlignmentContext) const
+{
+ MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+ nscoord baseline;
+ if (GetNaturalBaselineBOffset(aWM, aBaselineGroup, &baseline)) {
+ return baseline;
+ }
+ if (aAlignmentContext == AlignmentContext::eInline) {
+ return SynthesizeBaselineBOffsetFromMarginBox(aWM, aBaselineGroup);
+ }
+ // XXX AlignmentContext::eTable should use content box?
+ return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup);
+}
+
+#endif
diff --git a/layout/generic/nsIFrameUtil.h b/layout/generic/nsIFrameUtil.h
new file mode 100644
index 000000000..57eee670e
--- /dev/null
+++ b/layout/generic/nsIFrameUtil.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* utilities for regression tests based on frame tree comparison */
+
+#ifndef nsIFrameUtil_h___
+#define nsIFrameUtil_h___
+
+#include <stdio.h>
+#include "nsISupports.h"
+
+/* a6cf90d4-15b3-11d2-932e-00805f8add32 */
+#define NS_IFRAME_UTIL_IID \
+ { 0xa6cf90d6, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
+
+/**
+ * Frame utility interface
+ */
+class nsIFrameUtil : public nsISupports {
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFRAME_UTIL_IID)
+ /**
+ * Compare two regression data dumps. The return status will be NS_OK
+ * if the trees compare favoribly, otherwise the return will indicate
+ * NS_ERROR_FAILURE. Other return status's will indicate some other
+ * type of failure. The files, aFile1 and aFile2 are closed before
+ * returning.
+ * aRegressionOutput will vary output, 0 is full output, 1 is brief
+ */
+ NS_IMETHOD CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput) = 0;
+
+ /**
+ * Display the regression dump data stored in aInputFile1 to
+ * aOutputFile . The file is closed before returning. If the
+ * regression data is in error somehow then NS_ERROR_FAILURE will be
+ * returned.
+ */
+ NS_IMETHOD DumpRegressionData(FILE* aInputFile, FILE* aOutputFile) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIFrameUtil, NS_IFRAME_UTIL_IID)
+
+#endif /* nsIFrameUtil_h___ */
diff --git a/layout/generic/nsILineIterator.h b/layout/generic/nsILineIterator.h
new file mode 100644
index 000000000..7d20691f4
--- /dev/null
+++ b/layout/generic/nsILineIterator.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsILineIterator_h___
+#define nsILineIterator_h___
+
+#include "nscore.h"
+#include "nsPoint.h"
+#include "mozilla/Attributes.h"
+
+class nsIFrame;
+struct nsRect;
+
+/**
+ * Line iterator API.
+ *
+ * Lines are numbered from 0 to N, where 0 is the top line and N is
+ * the bottom line.
+ *
+ * Obtain this interface from frames via nsIFrame::GetLineIterator.
+ * When you are finished using the iterator, call DisposeLineIterator()
+ * to destroy the iterator if appropriate.
+ */
+class nsILineIterator
+{
+protected:
+ ~nsILineIterator() { }
+
+public:
+ virtual void DisposeLineIterator() = 0;
+
+ /**
+ * The number of lines in the block
+ */
+ virtual int32_t GetNumLines() = 0;
+
+ /**
+ * The prevailing direction of lines.
+ *
+ * @return true if the CSS direction property for the block is
+ * "rtl", otherwise false
+ *
+ *XXX after bug 924851 change this to return a UBiDiDirection
+ */
+ virtual bool GetDirection() = 0;
+
+ // Return structural information about a line. aFirstFrameOnLine is
+ // the first frame on the line and aNumFramesOnLine is the number of
+ // frames that are on the line. If the line-number is invalid then
+ // aFirstFrameOnLine will be nullptr and aNumFramesOnLine will be
+ // zero.
+ //
+ // For valid line numbers, aLineBounds is set to the bounding box of
+ // the line (which is based on the in-flow position of the frames on
+ // the line; if a frame was moved because of relative positioning
+ // then its coordinates may be outside the line bounds).
+ NS_IMETHOD GetLine(int32_t aLineNumber,
+ nsIFrame** aFirstFrameOnLine,
+ int32_t* aNumFramesOnLine,
+ nsRect& aLineBounds) = 0;
+
+ /**
+ * Given a frame that's a child of the block, find which line its on
+ * and return that line index, as long as it's at least as big as
+ * aStartLine. Returns -1 if the frame cannot be found on lines
+ * starting with aStartLine.
+ */
+ virtual int32_t FindLineContaining(nsIFrame* aFrame,
+ int32_t aStartLine = 0) = 0;
+
+ // Given a line number and a coordinate, find the frame on the line
+ // that is nearest to aPos along the inline axis. (The block-axis coord
+ // of aPos is irrelevant.)
+ // The aPosIsBeforeFirstFrame and aPosIsAfterLastFrame flags are updated
+ // appropriately.
+ NS_IMETHOD FindFrameAt(int32_t aLineNumber,
+ nsPoint aPos,
+ nsIFrame** aFrameFound,
+ bool* aPosIsBeforeFirstFrame,
+ bool* aPosIsAfterLastFrame) = 0;
+
+ // Give the line iterator implementor a chance todo something more complicated than
+ // nsIFrame::GetNextSibling()
+ NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) = 0;
+
+ // Check whether visual and logical order of frames within a line are identical.
+ // If not, return the first and last visual frames
+ NS_IMETHOD CheckLineOrder(int32_t aLine,
+ bool *aIsReordered,
+ nsIFrame **aFirstVisual,
+ nsIFrame **aLastVisual) = 0;
+};
+
+class nsAutoLineIterator
+{
+public:
+ nsAutoLineIterator() : mRawPtr(nullptr) { }
+ MOZ_IMPLICIT nsAutoLineIterator(nsILineIterator *i) : mRawPtr(i) { }
+
+ ~nsAutoLineIterator() {
+ if (mRawPtr)
+ mRawPtr->DisposeLineIterator();
+ }
+
+ operator nsILineIterator*() { return mRawPtr; }
+ nsILineIterator* operator->() { return mRawPtr; }
+
+ nsILineIterator* operator=(nsILineIterator* i) {
+ if (i == mRawPtr)
+ return i;
+
+ if (mRawPtr)
+ mRawPtr->DisposeLineIterator();
+
+ mRawPtr = i;
+ return i;
+ }
+
+private:
+ nsILineIterator* mRawPtr;
+};
+
+#endif /* nsILineIterator_h___ */
diff --git a/layout/generic/nsIObjectFrame.h b/layout/generic/nsIObjectFrame.h
new file mode 100644
index 000000000..801ca03b7
--- /dev/null
+++ b/layout/generic/nsIObjectFrame.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * interface for rendering objects for replaced elements implemented by
+ * a plugin
+ */
+
+#ifndef nsIObjectFrame_h___
+#define nsIObjectFrame_h___
+
+#include "nsQueryFrame.h"
+
+class nsNPAPIPluginInstance;
+class nsIWidget;
+
+class nsIObjectFrame : public nsQueryFrame
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsIObjectFrame)
+
+ NS_IMETHOD GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) = 0;
+
+ /**
+ * Get the native widget for the plugin, if any.
+ */
+ virtual nsIWidget* GetWidget() = 0;
+
+ /**
+ * Update plugin active state. Frame should update if it is on an active tab
+ * or not and forward that information to the plugin to make it possible to
+ * throttle down plugin instance in non active case.
+ */
+ virtual void SetIsDocumentActive(bool aIsActive) = 0;
+};
+
+#endif /* nsIObjectFrame_h___ */
diff --git a/layout/generic/nsIPageSequenceFrame.h b/layout/generic/nsIPageSequenceFrame.h
new file mode 100644
index 000000000..2359288fd
--- /dev/null
+++ b/layout/generic/nsIPageSequenceFrame.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsIPageSequenceFrame_h___
+#define nsIPageSequenceFrame_h___
+
+#include "nsQueryFrame.h"
+#include "nsCoord.h"
+
+class nsPresContext;
+class nsIPrintSettings;
+class nsITimerCallback;
+
+/**
+ * Interface for accessing special capabilities of the page sequence frame.
+ *
+ * Today all that exists are member functions for printing.
+ */
+class nsIPageSequenceFrame : public nsQueryFrame
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsIPageSequenceFrame)
+
+ /**
+ * Print the set of pages.
+ *
+ * @param aPrintOptions options for printing
+ * @param aStatusCallback interface that the client provides to receive
+ * progress notifications. Can be nullptr
+ * @return NS_OK if successful
+ * NS_ERROR_ABORT if the client cancels printing using the callback
+ * interface
+ * NS_ERROR_INVALID_ARG if printing a range of pages (not all pages)
+ * and the start page is greater than the total number of pages
+ * NS_ERROR_FAILURE if there is an error
+ */
+ NS_IMETHOD StartPrint(nsPresContext* aPresContext,
+ nsIPrintSettings* aPrintOptions,
+ const nsAString& aDocTitle,
+ const nsAString& aDocURL) = 0;
+
+ NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) = 0;
+ NS_IMETHOD PrintNextPage() = 0;
+ NS_IMETHOD ResetPrintCanvasList() = 0;
+ NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum) = 0;
+ NS_IMETHOD GetNumPages(int32_t* aNumPages) = 0;
+ NS_IMETHOD IsDoingPrintRange(bool* aDoing) = 0;
+ NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage) = 0;
+
+ NS_IMETHOD DoPageEnd() = 0;
+ NS_IMETHOD SetSelectionHeight(nscoord aYOffset, nscoord aHeight) = 0;
+
+ NS_IMETHOD SetTotalNumPages(int32_t aTotal) = 0;
+
+ // For Shrink To Fit
+ NS_IMETHOD GetSTFPercent(float& aSTFPercent) = 0;
+};
+
+#endif /* nsIPageSequenceFrame_h___ */
+
+
diff --git a/layout/generic/nsIScrollPositionListener.h b/layout/generic/nsIScrollPositionListener.h
new file mode 100644
index 000000000..1fda15776
--- /dev/null
+++ b/layout/generic/nsIScrollPositionListener.h
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIScrollPositionListener_h___
+#define nsIScrollPositionListener_h___
+
+#include "nsCoord.h"
+
+/**
+ * Provides a way to learn about scroll position changes of nsIScrollableFrame's.
+ */
+class nsIScrollPositionListener {
+public:
+
+ virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) = 0;
+ virtual void ScrollPositionDidChange(nscoord aX, nscoord aY) = 0;
+};
+
+#endif /* nsIScrollPositionListener_h___ */
+
diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h
new file mode 100644
index 000000000..b7e3caf46
--- /dev/null
+++ b/layout/generic/nsIScrollableFrame.h
@@ -0,0 +1,480 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * interface that provides scroll APIs implemented by scrollable frames
+ */
+
+#ifndef nsIScrollFrame_h___
+#define nsIScrollFrame_h___
+
+#include "nsCoord.h"
+#include "DisplayItemClip.h"
+#include "ScrollbarStyles.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/Point.h"
+#include "nsIScrollbarMediator.h"
+#include "Units.h"
+#include "FrameMetrics.h"
+
+#define NS_DEFAULT_VERTICAL_SCROLL_DISTANCE 3
+#define NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE 5
+
+class nsBoxLayoutState;
+class nsIScrollPositionListener;
+class nsIFrame;
+class nsPresContext;
+class nsIContent;
+class nsRenderingContext;
+class nsIAtom;
+class nsDisplayListBuilder;
+
+namespace mozilla {
+struct ContainerLayerParameters;
+namespace layers {
+class Layer;
+} // namespace layers
+} // namespace mozilla
+
+/**
+ * Interface for frames that are scrollable. This interface exposes
+ * APIs for examining scroll state, observing changes to scroll state,
+ * and triggering scrolling.
+ */
+class nsIScrollableFrame : public nsIScrollbarMediator {
+public:
+ typedef mozilla::CSSIntPoint CSSIntPoint;
+ typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
+ typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
+
+ NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame)
+
+ /**
+ * Get the frame for the content that we are scrolling within
+ * this scrollable frame.
+ */
+ virtual nsIFrame* GetScrolledFrame() const = 0;
+
+ /**
+ * Get the styles (NS_STYLE_OVERFLOW_SCROLL, NS_STYLE_OVERFLOW_HIDDEN,
+ * or NS_STYLE_OVERFLOW_AUTO) governing the horizontal and vertical
+ * scrollbars for this frame.
+ */
+ virtual mozilla::ScrollbarStyles GetScrollbarStyles() const = 0;
+
+ enum { HORIZONTAL = 0x01, VERTICAL = 0x02 };
+ /**
+ * Return the scrollbars which are visible. It's OK to call this during reflow
+ * of the scrolled contents, in which case it will reflect the current
+ * assumptions about scrollbar visibility.
+ */
+ virtual uint32_t GetScrollbarVisibility() const = 0;
+ /**
+ * Returns the directions in which scrolling is perceived to be allowed.
+ * A direction is perceived to be allowed if there is a visible scrollbar
+ * for that direction or if the scroll range is at least one device pixel.
+ */
+ uint32_t GetPerceivedScrollingDirections() const;
+ /**
+ * Return the actual sizes of all possible scrollbars. Returns 0 for scrollbar
+ * positions that don't have a scrollbar or where the scrollbar is not visible.
+ * Do not call this while this frame's descendants are being reflowed, it won't be
+ * accurate.
+ */
+ virtual nsMargin GetActualScrollbarSizes() const = 0;
+ /**
+ * Return the sizes of all scrollbars assuming that any scrollbars that could
+ * be visible due to overflowing content, are. This can be called during reflow
+ * of the scrolled contents.
+ */
+ virtual nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState) = 0;
+ /**
+ * Return the sizes of all scrollbars assuming that any scrollbars that could
+ * be visible due to overflowing content, are. This can be called during reflow
+ * of the scrolled contents.
+ */
+ virtual nsMargin GetDesiredScrollbarSizes(nsPresContext* aPresContext,
+ nsRenderingContext* aRC) = 0;
+ /**
+ * Return the width for non-disappearing scrollbars.
+ */
+ virtual nscoord
+ GetNondisappearingScrollbarWidth(nsPresContext* aPresContext,
+ nsRenderingContext* aRC,
+ mozilla::WritingMode aWM) = 0;
+ /**
+ * GetScrolledRect is designed to encapsulate deciding which
+ * directions of overflow should be reachable by scrolling and which
+ * should not. Callers should NOT depend on it having any particular
+ * behavior (although nsXULScrollFrame currently does).
+ *
+ * This should only be called when the scrolled frame has been
+ * reflowed with the scroll port size given in mScrollPort.
+ *
+ * Currently it allows scrolling down and to the right for
+ * nsHTMLScrollFrames with LTR directionality and for all
+ * nsXULScrollFrames, and allows scrolling down and to the left for
+ * nsHTMLScrollFrames with RTL directionality.
+ */
+ virtual nsRect GetScrolledRect() const = 0;
+ /**
+ * Get the area of the scrollport relative to the origin of this frame's
+ * border-box.
+ * This is the area of this frame minus border and scrollbars.
+ */
+ virtual nsRect GetScrollPortRect() const = 0;
+ /**
+ * Get the offset of the scrollport origin relative to the scrolled
+ * frame origin. Typically the position will be non-negative.
+ * This will always be a multiple of device pixels.
+ */
+ virtual nsPoint GetScrollPosition() const = 0;
+ /**
+ * As GetScrollPosition(), but uses the top-right as origin for RTL frames.
+ */
+ virtual nsPoint GetLogicalScrollPosition() const = 0;
+ /**
+ * Get the area that must contain the scroll position. Typically
+ * (but not always, e.g. for RTL content) x and y will be 0, and
+ * width or height will be nonzero if the content can be scrolled in
+ * that direction. Since scroll positions must be a multiple of
+ * device pixels, the range extrema will also be a multiple of
+ * device pixels.
+ */
+ virtual nsRect GetScrollRange() const = 0;
+ /**
+ * Get the size of the scroll port to use when clamping the scroll
+ * position.
+ */
+ virtual nsSize GetScrollPositionClampingScrollPortSize() const = 0;
+ /**
+ * Return how much we would try to scroll by in each direction if
+ * asked to scroll by one "line" vertically and horizontally.
+ */
+ virtual nsSize GetLineScrollAmount() const = 0;
+ /**
+ * Return how much we would try to scroll by in each direction if
+ * asked to scroll by one "page" vertically and horizontally.
+ */
+ virtual nsSize GetPageScrollAmount() const = 0;
+
+ /**
+ * When a scroll operation is requested, we ask for instant, smooth,
+ * smooth msd, or normal scrolling.
+ *
+ * SMOOTH scrolls have a symmetrical acceleration and deceleration curve
+ * modeled with a set of splines that guarantee that the destination will be
+ * reached over a fixed time interval. SMOOTH will only be smooth if smooth
+ * scrolling is actually enabled. This behavior is utilized by keyboard and
+ * mouse wheel scrolling events.
+ *
+ * SMOOTH_MSD implements a physically based model that approximates the
+ * behavior of a mass-spring-damper system. SMOOTH_MSD scrolls have a
+ * non-symmetrical acceleration and deceleration curve, can potentially
+ * overshoot the destination on intermediate frames, and complete over a
+ * variable time interval. SMOOTH_MSD will only be smooth if cssom-view
+ * smooth-scrolling is enabled.
+ *
+ * INSTANT is always synchronous, NORMAL can be asynchronous.
+ *
+ * If an INSTANT scroll request happens while a SMOOTH or async scroll is
+ * already in progress, the async scroll is interrupted and we instantly
+ * scroll to the destination.
+ *
+ * If an INSTANT or SMOOTH scroll request happens while a SMOOTH_MSD scroll
+ * is already in progress, the SMOOTH_MSD scroll is interrupted without
+ * first scrolling to the destination.
+ */
+ enum ScrollMode { INSTANT, SMOOTH, SMOOTH_MSD, NORMAL };
+ /**
+ * Some platforms (OSX) may generate additional scrolling events even
+ * after the user has stopped scrolling, simulating a momentum scrolling
+ * effect resulting from fling gestures.
+ * SYNTHESIZED_MOMENTUM_EVENT indicates that the scrolling is being requested
+ * by such a synthesized event and may be ignored if another scroll has
+ * been started since the last actual user input.
+ */
+ enum ScrollMomentum { NOT_MOMENTUM, SYNTHESIZED_MOMENTUM_EVENT };
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * Clamps aScrollPosition to GetScrollRange and sets the scroll position
+ * to that value.
+ * @param aRange If non-null, specifies area which contains aScrollPosition
+ * and can be used for choosing a performance-optimized scroll position.
+ * Any point within this area can be chosen.
+ * The choosen point will be as close as possible to aScrollPosition.
+ */
+ virtual void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
+ const nsRect* aRange = nullptr,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) = 0;
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * Scrolls to a particular position in integer CSS pixels.
+ * Keeps the exact current horizontal or vertical position if the current
+ * position, rounded to CSS pixels, matches aScrollPosition. If
+ * aScrollPosition.x/y is different from the current CSS pixel position,
+ * makes sure we only move in the direction given by the difference.
+ *
+ * When aMode is SMOOTH, INSTANT, or NORMAL, GetScrollPositionCSSPixels (the
+ * scroll position after rounding to CSS pixels) will be exactly
+ * aScrollPosition at the end of the scroll animation.
+ *
+ * When aMode is SMOOTH_MSD, intermediate animation frames may be outside the
+ * range and / or moving in any direction; GetScrollPositionCSSPixels will be
+ * exactly aScrollPosition at the end of the scroll animation unless the
+ * SMOOTH_MSD animation is interrupted.
+ */
+ virtual void ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
+ nsIScrollableFrame::ScrollMode aMode
+ = nsIScrollableFrame::INSTANT) = 0;
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * Scrolls to a particular position in float CSS pixels.
+ * This does not guarantee that GetScrollPositionCSSPixels equals
+ * aScrollPosition afterward. It tries to scroll as close to
+ * aScrollPosition as possible while scrolling by an integer
+ * number of layer pixels (so the operation is fast and looks clean).
+ */
+ virtual void ScrollToCSSPixelsApproximate(const mozilla::CSSPoint& aScrollPosition,
+ nsIAtom *aOrigin = nullptr) = 0;
+
+ /**
+ * Returns the scroll position in integer CSS pixels, rounded to the nearest
+ * pixel.
+ */
+ virtual CSSIntPoint GetScrollPositionCSSPixels() = 0;
+ /**
+ * When scrolling by a relative amount, we can choose various units.
+ */
+ enum ScrollUnit { DEVICE_PIXELS, LINES, PAGES, WHOLE };
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * Modifies the current scroll position by aDelta units given by aUnit,
+ * clamping it to GetScrollRange. If WHOLE is specified as the unit,
+ * content is scrolled all the way in the direction(s) given by aDelta.
+ * @param aOverflow if non-null, returns the amount that scrolling
+ * was clamped by in each direction (how far we moved the scroll position
+ * to bring it back into the legal range). This is never negative. The
+ * values are in device pixels.
+ */
+ virtual void ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit, ScrollMode aMode,
+ nsIntPoint* aOverflow = nullptr,
+ nsIAtom* aOrigin = nullptr,
+ ScrollMomentum aMomentum = NOT_MOMENTUM,
+ nsIScrollbarMediator::ScrollSnapMode aSnap
+ = nsIScrollbarMediator::DISABLE_SNAP) = 0;
+
+ /**
+ * Perform scroll snapping, possibly resulting in a smooth scroll to
+ * maintain the scroll snap position constraints. Velocity sampled from
+ * main thread scrolling is used to determine best matching snap point
+ * when called after a fling gesture on a trackpad or mouse wheel.
+ */
+ virtual void ScrollSnap() = 0;
+
+ /**
+ * @note This method might destroy the frame, pres shell and other objects.
+ * This tells the scroll frame to try scrolling to the scroll
+ * position that was restored from the history. This must be called
+ * at least once after state has been restored. It is called by the
+ * scrolled frame itself during reflow, but sometimes state can be
+ * restored after reflows are done...
+ * XXX should we take an aMode parameter here? Currently it's instant.
+ */
+ virtual void ScrollToRestoredPosition() = 0;
+
+ /**
+ * Add a scroll position listener. This listener must be removed
+ * before it is destroyed.
+ */
+ virtual void AddScrollPositionListener(nsIScrollPositionListener* aListener) = 0;
+ /**
+ * Remove a scroll position listener.
+ */
+ virtual void RemoveScrollPositionListener(nsIScrollPositionListener* aListener) = 0;
+
+ /**
+ * Internal method used by scrollbars to notify their scrolling
+ * container of changes.
+ */
+ virtual void CurPosAttributeChanged(nsIContent* aChild) = 0;
+
+ /**
+ * Allows the docshell to request that the scroll frame post an event
+ * after being restored from history.
+ */
+ NS_IMETHOD PostScrolledAreaEventForCurrentArea() = 0;
+
+ /**
+ * Returns true if this scrollframe is being "actively scrolled".
+ * This basically means that we should allocate resources in the
+ * expectation that scrolling is going to happen.
+ */
+ virtual bool IsScrollingActive(nsDisplayListBuilder* aBuilder) = 0;
+ /**
+ * Returns true if the scrollframe is currently processing an async
+ * or smooth scroll.
+ */
+ virtual bool IsProcessingAsyncScroll() = 0;
+ /**
+ * Call this when the layer(s) induced by active scrolling are being
+ * completely redrawn.
+ */
+ virtual void ResetScrollPositionForLayerPixelAlignment() = 0;
+ /**
+ * Was the current presentation state for this frame restored from history?
+ */
+ virtual bool DidHistoryRestore() const = 0;
+ /**
+ * Clear the flag so that DidHistoryRestore() returns false until the next
+ * RestoreState call.
+ * @see nsIStatefulFrame::RestoreState
+ */
+ virtual void ClearDidHistoryRestore() = 0;
+ /**
+ * Determine if the passed in rect is nearly visible according to the frame
+ * visibility heuristics for how close it is to the visible scrollport.
+ */
+ virtual bool IsRectNearlyVisible(const nsRect& aRect) = 0;
+ /**
+ * Expand the given rect taking into account which directions we can scroll
+ * and how far we want to expand for frame visibility purposes.
+ */
+ virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const = 0;
+ /**
+ * Returns the origin that triggered the last instant scroll. Will equal
+ * nsGkAtoms::apz when the compositor's replica frame metrics includes the
+ * latest instant scroll.
+ */
+ virtual nsIAtom* LastScrollOrigin() = 0;
+ /**
+ * Sets a flag on the scrollframe that indicates the current scroll origin
+ * has been sent over in a layers transaction, and subsequent changes to
+ * the scroll position by "weaker" origins are permitted to overwrite the
+ * the scroll origin. Scroll origins that nsLayoutUtils::CanScrollOriginClobberApz
+ * returns false for are considered "weaker" than scroll origins for which
+ * that function returns true.
+ */
+ virtual void AllowScrollOriginDowngrade() = 0;
+ /**
+ * Returns the origin that triggered the last smooth scroll.
+ * Will equal nsGkAtoms::apz when the compositor's replica frame
+ * metrics includes the latest smooth scroll. The compositor will always
+ * perform an instant scroll prior to instantiating any smooth scrolls
+ * if LastScrollOrigin and LastSmoothScrollOrigin indicate that
+ * an instant scroll and a smooth scroll have occurred since the last
+ * replication of the frame metrics.
+ *
+ * This is set to nullptr to when the compositor thread acknowledges that
+ * the smooth scroll has been started. If the smooth scroll has been stomped
+ * by an instant scroll before the smooth scroll could be started by the
+ * compositor, this is set to nullptr to clear the smooth scroll.
+ */
+ virtual nsIAtom* LastSmoothScrollOrigin() = 0;
+ /**
+ * Returns the current generation counter for the scroll. This counter
+ * increments every time the scroll position is set.
+ */
+ virtual uint32_t CurrentScrollGeneration() = 0;
+ /**
+ * LastScrollDestination returns the destination of the most recently
+ * requested smooth scroll animation.
+ */
+ virtual nsPoint LastScrollDestination() = 0;
+ /**
+ * Clears the "origin of last scroll" property stored in this frame, if
+ * the generation counter passed in matches the current scroll generation
+ * counter.
+ */
+ virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) = 0;
+ /**
+ * Determine whether it is desirable to be able to asynchronously scroll this
+ * scroll frame.
+ */
+ virtual bool WantAsyncScroll() const = 0;
+ /**
+ * aLayer's animated geometry root is this frame. If there needs to be a
+ * ScrollMetadata contributed by this frame, append it to aOutput.
+ */
+ virtual mozilla::Maybe<mozilla::layers::ScrollMetadata> ComputeScrollMetadata(
+ mozilla::layers::Layer* aLayer,
+ nsIFrame* aContainerReferenceFrame,
+ const ContainerLayerParameters& aParameters,
+ const mozilla::DisplayItemClip* aClip) const = 0;
+
+ /**
+ * If this scroll frame is ignoring viewporting clipping
+ */
+ virtual bool IsIgnoringViewportClipping() const = 0;
+
+ /**
+ * Mark the scrollbar frames for reflow.
+ */
+ virtual void MarkScrollbarsDirtyForReflow() const = 0;
+
+ virtual void SetTransformingByAPZ(bool aTransforming) = 0;
+ virtual bool IsTransformingByAPZ() const = 0;
+
+ /**
+ * Notify this scroll frame that it can be scrolled by APZ. In particular,
+ * this is called *after* the APZ code has created an APZC for this scroll
+ * frame and verified that it is not a scrollinfo layer. Therefore, setting an
+ * async transform on it is actually user visible.
+ */
+ virtual void SetScrollableByAPZ(bool aScrollable) = 0;
+
+ /**
+ * Notify this scroll frame that it can be zoomed by APZ.
+ */
+ virtual void SetZoomableByAPZ(bool aZoomable) = 0;
+
+ /**
+ * Whether or not this frame uses containerful scrolling.
+ */
+ virtual bool UsesContainerScrolling() const = 0;
+
+ /**
+ * Determine if we should build a scrollable layer for this scroll frame and
+ * return the result. It will also record this result on the scroll frame.
+ * Pass the dirty rect in aDirtyRect. On return it will be set to the
+ * displayport if there is one (ie the dirty rect that should be used).
+ * This function may create a display port where one did not exist before if
+ * aAllowCreateDisplayPort is true. It is only allowed to be false if there
+ * has been a call with it set to true before on the same paint.
+ */
+ virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+ nsRect* aDirtyRect,
+ bool aAllowCreateDisplayPort) = 0;
+
+ /**
+ * Notification that this scroll frame is getting its frame visibility updated.
+ */
+ virtual void NotifyApproximateFrameVisibilityUpdate() = 0;
+
+ /**
+ * Returns true if this scroll frame had a display port at the last frame
+ * visibility update and fills in aDisplayPort with that displayport. Returns
+ * false otherwise, and doesn't touch aDisplayPort.
+ */
+ virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) = 0;
+
+ /**
+ * This is called when a descendant scrollframe's has its displayport expired.
+ * This function will check to see if this scrollframe may safely expire its
+ * own displayport and schedule a timer to do that if it is safe.
+ */
+ virtual void TriggerDisplayPortExpiration() = 0;
+
+ /**
+ * Returns information required to determine where to snap to after a scroll.
+ */
+ virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
+
+ virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
+};
+
+#endif
diff --git a/layout/generic/nsIStatefulFrame.h b/layout/generic/nsIStatefulFrame.h
new file mode 100644
index 000000000..9205bb8c9
--- /dev/null
+++ b/layout/generic/nsIStatefulFrame.h
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * interface for rendering objects whose state is saved in
+ * session-history (back-forward navigation)
+ */
+
+#ifndef _nsIStatefulFrame_h
+#define _nsIStatefulFrame_h
+
+#include "nsContentUtils.h"
+#include "nsQueryFrame.h"
+
+class nsPresState;
+
+class nsIStatefulFrame
+{
+ public:
+ NS_DECL_QUERYFRAME_TARGET(nsIStatefulFrame)
+
+ // Save the state for this frame. If this method succeeds, the caller is
+ // responsible for deleting the resulting state when done with it.
+ NS_IMETHOD SaveState(nsPresState** aState) = 0;
+
+ // Restore the state for this frame from aState
+ NS_IMETHOD RestoreState(nsPresState* aState) = 0;
+
+ // Generate a key for this stateful frame
+ NS_IMETHOD GenerateStateKey(nsIContent* aContent,
+ nsIDocument* aDocument,
+ nsACString& aKey)
+ {
+ return nsContentUtils::GenerateStateKey(aContent, aDocument, aKey);
+ };
+};
+
+#endif /* _nsIStatefulFrame_h */
diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp
new file mode 100644
index 000000000..c64520f2e
--- /dev/null
+++ b/layout/generic/nsImageFrame.cpp
@@ -0,0 +1,2443 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for replaced elements with image data */
+
+#include "nsImageFrame.h"
+
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/Unused.h"
+
+#include "nsCOMPtr.h"
+#include "nsFontMetrics.h"
+#include "nsIImageLoadingContent.h"
+#include "nsString.h"
+#include "nsPrintfCString.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsIPresShell.h"
+#include "nsGkAtoms.h"
+#include "nsIDocument.h"
+#include "nsContentUtils.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsStyleContext.h"
+#include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
+#include "nsStyleUtil.h"
+#include "nsTransform2D.h"
+#include "nsImageMap.h"
+#include "nsIIOService.h"
+#include "nsILoadGroup.h"
+#include "nsISupportsPriority.h"
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+#include "nsCSSRendering.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsNameSpaceManager.h"
+#include <algorithm>
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+#include "nsIDOMNode.h"
+#include "nsLayoutUtils.h"
+#include "nsDisplayList.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "FrameLayerBuilder.h"
+#include "nsISelectionController.h"
+#include "nsISelection.h"
+
+#include "imgIContainer.h"
+#include "imgLoader.h"
+#include "imgRequestProxy.h"
+
+#include "nsCSSFrameConstructor.h"
+#include "nsIDOMRange.h"
+
+#include "nsError.h"
+#include "nsBidiUtils.h"
+#include "nsBidiPresUtils.h"
+
+#include "gfxRect.h"
+#include "ImageLayers.h"
+#include "ImageContainer.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "nsBlockFrame.h"
+#include "nsStyleStructInlines.h"
+
+#include "mozilla/Preferences.h"
+
+#include "mozilla/dom/Link.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::image;
+using namespace mozilla::layers;
+
+// sizes (pixels) for image icon, padding and border frame
+#define ICON_SIZE (16)
+#define ICON_PADDING (3)
+#define ALT_BORDER_WIDTH (1)
+
+//we must add hooks soon
+#define IMAGE_EDITOR_CHECK 1
+
+// Default alignment value (so we can tell an unset value from a set value)
+#define ALIGN_UNSET uint8_t(-1)
+
+// static icon information
+nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
+
+// cached IO service for loading icons
+nsIIOService* nsImageFrame::sIOService;
+
+// test if the width and height are fixed, looking at the style data
+// This is used by nsImageFrame::ShouldCreateImageFrameFor and should
+// not be used for layout decisions.
+static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition)
+{
+ // check the width and height values in the reflow state's style struct
+ // - if width and height are specified as either coord or percentage, then
+ // the size of the image frame is constrained
+ return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
+ aStylePosition->mHeight.IsCoordPercentCalcUnit();
+}
+
+// Decide whether we can optimize away reflows that result from the
+// image's intrinsic size changing.
+inline bool HaveFixedSize(const ReflowInput& aReflowInput)
+{
+ NS_ASSERTION(aReflowInput.mStylePosition, "crappy reflowInput - null stylePosition");
+ // Don't try to make this optimization when an image has percentages
+ // in its 'width' or 'height'. The percentages might be treated like
+ // auto (especially for intrinsic width calculations and for heights).
+ return aReflowInput.mStylePosition->mHeight.ConvertsToLength() &&
+ aReflowInput.mStylePosition->mWidth.ConvertsToLength();
+}
+
+nsIFrame*
+NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsImageFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
+
+
+nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
+ nsAtomicContainerFrame(aContext),
+ mComputedSize(0, 0),
+ mIntrinsicRatio(0, 0),
+ mDisplayingIcon(false),
+ mFirstFrameComplete(false),
+ mReflowCallbackPosted(false),
+ mForceSyncDecoding(false)
+{
+ EnableVisibilityTracking();
+
+ // We assume our size is not constrained and we haven't gotten an
+ // initial reflow yet, so don't touch those flags.
+ mIntrinsicSize.width.SetCoordValue(0);
+ mIntrinsicSize.height.SetCoordValue(0);
+}
+
+nsImageFrame::~nsImageFrame()
+{
+}
+
+NS_QUERYFRAME_HEAD(nsImageFrame)
+ NS_QUERYFRAME_ENTRY(nsImageFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsImageFrame::AccessibleType()
+{
+ // Don't use GetImageMap() to avoid reentrancy into accessibility.
+ if (HasImageMap()) {
+ return a11y::eHTMLImageMapType;
+ }
+
+ return a11y::eImageType;
+}
+#endif
+
+void
+nsImageFrame::DisconnectMap()
+{
+ if (mImageMap) {
+ mImageMap->Destroy();
+ mImageMap = nullptr;
+
+#ifdef ACCESSIBILITY
+ nsAccessibilityService* accService = GetAccService();
+ if (accService) {
+ accService->RecreateAccessible(PresContext()->PresShell(), mContent);
+ }
+#endif
+ }
+}
+
+void
+nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ if (mReflowCallbackPosted) {
+ PresContext()->PresShell()->CancelReflowCallback(this);
+ mReflowCallbackPosted = false;
+ }
+
+ // Tell our image map, if there is one, to clean up
+ // This causes the nsImageMap to unregister itself as
+ // a DOM listener.
+ DisconnectMap();
+
+ // set the frame to null so we don't send messages to a dead object.
+ if (mListener) {
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ if (imageLoader) {
+ // Notify our image loading content that we are going away so it can
+ // deregister with our refresh driver.
+ imageLoader->FrameDestroyed(this);
+
+ imageLoader->RemoveObserver(mListener);
+ }
+
+ reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
+ }
+
+ mListener = nullptr;
+
+ // If we were displaying an icon, take ourselves off the list
+ if (mDisplayingIcon)
+ gIconLoad->RemoveIconObserver(this);
+
+ nsAtomicContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+void
+nsImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+ nsAtomicContainerFrame::DidSetStyleContext(aOldStyleContext);
+
+ if (!mImage) {
+ // We'll pick this change up whenever we do get an image.
+ return;
+ }
+
+ nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
+
+ // We need to update our orientation either if we had no style context before
+ // because this is the first time it's been set, or if the image-orientation
+ // property changed from its previous value.
+ bool shouldUpdateOrientation =
+ !aOldStyleContext ||
+ aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation;
+
+ if (shouldUpdateOrientation) {
+ nsCOMPtr<imgIContainer> image(mImage->Unwrap());
+ mImage = nsLayoutUtils::OrientImage(image, newOrientation);
+
+ UpdateIntrinsicSize(mImage);
+ UpdateIntrinsicRatio(mImage);
+ }
+}
+
+void
+nsImageFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ mListener = new nsImageListener(this);
+
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
+ if (!imageLoader) {
+ NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
+ }
+
+ imageLoader->AddObserver(mListener);
+
+ nsPresContext *aPresContext = PresContext();
+
+ if (!gIconLoad)
+ LoadIcons(aPresContext);
+
+ // We have a PresContext now, so we need to notify the image content node
+ // that it can register images.
+ imageLoader->FrameCreated(this);
+
+ // Give image loads associated with an image frame a small priority boost!
+ nsCOMPtr<imgIRequest> currentRequest;
+ imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(currentRequest));
+ nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
+ if (p)
+ p->AdjustPriority(-1);
+}
+
+bool
+nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
+{
+ NS_PRECONDITION(aImage, "null image");
+ if (!aImage)
+ return false;
+
+ IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
+ mIntrinsicSize = IntrinsicSize();
+
+ // Set intrinsic size to match aImage's reported intrinsic width & height.
+ nsSize intrinsicSize;
+ if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
+ // If the image has no intrinsic width, intrinsicSize.width will be -1, and
+ // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
+ // Otherwise we use intrinsicSize.width. Height works the same way.
+ if (intrinsicSize.width != -1)
+ mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
+ if (intrinsicSize.height != -1)
+ mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
+ } else {
+ // Failure means that the image hasn't loaded enough to report a result. We
+ // treat this case as if the image's intrinsic size was 0x0.
+ mIntrinsicSize.width.SetCoordValue(0);
+ mIntrinsicSize.height.SetCoordValue(0);
+ }
+
+ return mIntrinsicSize != oldIntrinsicSize;
+}
+
+bool
+nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
+{
+ NS_PRECONDITION(aImage, "null image");
+
+ if (!aImage)
+ return false;
+
+ nsSize oldIntrinsicRatio = mIntrinsicRatio;
+
+ // Set intrinsic ratio to match aImage's reported intrinsic ratio.
+ if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
+ mIntrinsicRatio.SizeTo(0, 0);
+
+ return mIntrinsicRatio != oldIntrinsicRatio;
+}
+
+bool
+nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
+{
+ // First, figure out destRect (the rect we're rendering into).
+ // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
+ // because GetInnerArea() might be smaller if we're fragmented, whereas
+ // mComputedSize has our full content-box size (which we need for
+ // ComputeObjectDestRect to work correctly).
+ nsRect constraintRect(GetInnerArea().TopLeft(), mComputedSize);
+ constraintRect.y -= GetContinuationOffset();
+
+ nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+ mIntrinsicSize,
+ mIntrinsicRatio,
+ StylePosition());
+ // Set the translation components, based on destRect
+ // XXXbz does this introduce rounding errors because of the cast to
+ // float? Should we just manually add that stuff in every time
+ // instead?
+ aTransform.SetToTranslate(float(destRect.x),
+ float(destRect.y));
+
+ // Set the scale factors, based on destRect and intrinsic size.
+ if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
+ mIntrinsicSize.width.GetCoordValue() != 0 &&
+ mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
+ mIntrinsicSize.height.GetCoordValue() != 0 &&
+ mIntrinsicSize.width.GetCoordValue() != destRect.width &&
+ mIntrinsicSize.height.GetCoordValue() != destRect.height) {
+
+ aTransform.SetScale(float(destRect.width) /
+ float(mIntrinsicSize.width.GetCoordValue()),
+ float(destRect.height) /
+ float(mIntrinsicSize.height.GetCoordValue()));
+ return true;
+ }
+
+ return false;
+}
+
+// This function checks whether the given request is the current request for our
+// mContent.
+bool
+nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
+{
+ // Default to pending load in case of errors
+ nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
+ NS_ASSERTION(imageLoader, "No image loading content?");
+
+ int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
+ imageLoader->GetRequestType(aRequest, &requestType);
+
+ return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
+}
+
+nsRect
+nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
+{
+ // When scaling the image, row N of the source image may (depending on
+ // the scaling function) be used to draw any row in the destination image
+ // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
+ // floating-point scaling factor. The same holds true for columns.
+ // So, we start by computing that bound without the floor and ceiling.
+
+ nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
+ nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
+ nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
+ nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
+
+ nsTransform2D sourceToDest;
+ if (!GetSourceToDestTransform(sourceToDest)) {
+ // Failed to generate transform matrix. Return our whole inner area,
+ // to be on the safe side (since this method is used for generating
+ // invalidation rects).
+ return GetInnerArea();
+ }
+
+ sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
+
+ // Now, round the edges out to the pixel boundary.
+ nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
+ nscoord right = r.x + r.width;
+ nscoord bottom = r.y + r.height;
+
+ r.x -= (scale + (r.x % scale)) % scale;
+ r.y -= (scale + (r.y % scale)) % scale;
+ r.width = right + ((scale - (right % scale)) % scale) - r.x;
+ r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
+
+ return r;
+}
+
+// Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
+// that we'll construct image frames for them as needed if their display is
+// toggled from "none" (though we won't paint them, unless their visibility
+// is changed too).
+#define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
+ NS_EVENT_STATE_LOADING)
+
+// This is a macro so that we don't evaluate the boolean last arg
+// unless we have to; it can be expensive
+#define IMAGE_OK(_state, _loadingOK) \
+ (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
+ (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
+ (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
+
+/* static */
+bool
+nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
+ nsStyleContext* aStyleContext)
+{
+ EventStates state = aElement->State();
+ if (IMAGE_OK(state,
+ HaveSpecifiedSize(aStyleContext->StylePosition()))) {
+ // Image is fine; do the image frame thing
+ return true;
+ }
+
+ // Check if we want to use a placeholder box with an icon or just
+ // let the presShell make us into inline text. Decide as follows:
+ //
+ // - if our special "force icons" style is set, show an icon
+ // - else if our "do not show placeholders" pref is set, skip the icon
+ // - else:
+ // - if there is a src attribute, there is no alt attribute,
+ // and this is not an <object> (which could not possibly have
+ // such an attribute), show an icon.
+ // - if QuirksMode, and the IMG has a size show an icon.
+ // - otherwise, skip the icon
+ bool useSizedBox;
+
+ if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
+ useSizedBox = true;
+ }
+ else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
+ useSizedBox = false;
+ }
+ else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
+ !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
+ !aElement->IsHTMLElement(nsGkAtoms::object) &&
+ !aElement->IsHTMLElement(nsGkAtoms::input)) {
+ // Use a sized box if we have no alt text. This means no alt attribute
+ // and the node is not an object or an input (since those always have alt
+ // text).
+ useSizedBox = true;
+ }
+ else if (aStyleContext->PresContext()->CompatibilityMode() !=
+ eCompatibility_NavQuirks) {
+ useSizedBox = false;
+ }
+ else {
+ // check whether we have specified size
+ useSizedBox = HaveSpecifiedSize(aStyleContext->StylePosition());
+ }
+
+ return useSizedBox;
+}
+
+nsresult
+nsImageFrame::Notify(imgIRequest* aRequest,
+ int32_t aType,
+ const nsIntRect* aRect)
+{
+ if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
+ nsCOMPtr<imgIContainer> image;
+ aRequest->GetImage(getter_AddRefs(image));
+ return OnSizeAvailable(aRequest, image);
+ }
+
+ if (aType == imgINotificationObserver::FRAME_UPDATE) {
+ return OnFrameUpdate(aRequest, aRect);
+ }
+
+ if (aType == imgINotificationObserver::FRAME_COMPLETE) {
+ mFirstFrameComplete = true;
+ }
+
+ if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+ uint32_t imgStatus;
+ aRequest->GetImageStatus(&imgStatus);
+ nsresult status =
+ imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
+ return OnLoadComplete(aRequest, status);
+ }
+
+ return NS_OK;
+}
+
+static bool
+SizeIsAvailable(imgIRequest* aRequest)
+{
+ if (!aRequest)
+ return false;
+
+ uint32_t imageStatus = 0;
+ nsresult rv = aRequest->GetImageStatus(&imageStatus);
+
+ return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
+}
+
+nsresult
+nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
+{
+ if (!aImage) return NS_ERROR_INVALID_ARG;
+
+ /* Get requested animation policy from the pres context:
+ * normal = 0
+ * one frame = 1
+ * one loop = 2
+ */
+ nsPresContext *presContext = PresContext();
+ aImage->SetAnimationMode(presContext->ImageAnimationMode());
+
+ if (IsPendingLoad(aRequest)) {
+ // We don't care
+ return NS_OK;
+ }
+
+ bool intrinsicSizeChanged = false;
+ if (SizeIsAvailable(aRequest)) {
+ // This is valid and for the current request, so update our stored image
+ // container, orienting according to our style.
+ mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
+
+ intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
+ intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
+ } else {
+ // We no longer have a valid image, so release our stored image container.
+ mImage = mPrevImage = nullptr;
+
+ // Have to size to 0,0 so that GetDesiredSize recalculates the size.
+ mIntrinsicSize.width.SetCoordValue(0);
+ mIntrinsicSize.height.SetCoordValue(0);
+ mIntrinsicRatio.SizeTo(0, 0);
+ intrinsicSizeChanged = true;
+ }
+
+ if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
+ // Now we need to reflow if we have an unconstrained size and have
+ // already gotten the initial reflow
+ if (!(mState & IMAGE_SIZECONSTRAINED)) {
+ nsIPresShell *presShell = presContext->GetPresShell();
+ NS_ASSERTION(presShell, "No PresShell.");
+ if (presShell) {
+ presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+ }
+ } else {
+ // We've already gotten the initial reflow, and our size hasn't changed,
+ // so we're ready to request a decode.
+ MaybeDecodeForPredictedSize();
+ }
+
+ mPrevImage = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsImageFrame::OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect)
+{
+ NS_ENSURE_ARG_POINTER(aRect);
+
+ if (!(mState & IMAGE_GOTINITIALREFLOW)) {
+ // Don't bother to do anything; we have a reflow coming up!
+ return NS_OK;
+ }
+
+ if (mFirstFrameComplete && !StyleVisibility()->IsVisible()) {
+ return NS_OK;
+ }
+
+ if (IsPendingLoad(aRequest)) {
+ // We don't care
+ return NS_OK;
+ }
+
+ nsIntRect layerInvalidRect = mImage
+ ? mImage->GetImageSpaceInvalidationRect(*aRect)
+ : *aRect;
+
+ if (layerInvalidRect.IsEqualInterior(GetMaxSizedIntRect())) {
+ // Invalidate our entire area.
+ InvalidateSelf(nullptr, nullptr);
+ return NS_OK;
+ }
+
+ nsRect frameInvalidRect = SourceRectToDest(layerInvalidRect);
+ InvalidateSelf(&layerInvalidRect, &frameInvalidRect);
+ return NS_OK;
+}
+
+void
+nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
+ const nsRect* aFrameInvalidRect)
+{
+ InvalidateLayer(nsDisplayItem::TYPE_IMAGE,
+ aLayerInvalidRect,
+ aFrameInvalidRect);
+
+ if (!mFirstFrameComplete) {
+ InvalidateLayer(nsDisplayItem::TYPE_ALT_FEEDBACK,
+ aLayerInvalidRect,
+ aFrameInvalidRect);
+ }
+}
+
+nsresult
+nsImageFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
+{
+ // Check what request type we're dealing with
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ NS_ASSERTION(imageLoader, "Who's notifying us??");
+ int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
+ imageLoader->GetRequestType(aRequest, &loadType);
+ if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
+ loadType != nsIImageLoadingContent::PENDING_REQUEST) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NotifyNewCurrentRequest(aRequest, aStatus);
+ return NS_OK;
+}
+
+void
+nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
+ nsresult aStatus)
+{
+ nsCOMPtr<imgIContainer> image;
+ aRequest->GetImage(getter_AddRefs(image));
+ NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
+
+ // May have to switch sizes here!
+ bool intrinsicSizeChanged = true;
+ if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
+ // Update our stored image container, orienting according to our style.
+ mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
+
+ intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
+ intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
+ } else {
+ // We no longer have a valid image, so release our stored image container.
+ mImage = mPrevImage = nullptr;
+
+ // Have to size to 0,0 so that GetDesiredSize recalculates the size
+ mIntrinsicSize.width.SetCoordValue(0);
+ mIntrinsicSize.height.SetCoordValue(0);
+ mIntrinsicRatio.SizeTo(0, 0);
+ }
+
+ if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
+ if (intrinsicSizeChanged) {
+ if (!(mState & IMAGE_SIZECONSTRAINED)) {
+ nsIPresShell *presShell = PresContext()->GetPresShell();
+ if (presShell) {
+ presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+ }
+ } else {
+ // We've already gotten the initial reflow, and our size hasn't changed,
+ // so we're ready to request a decode.
+ MaybeDecodeForPredictedSize();
+ }
+
+ mPrevImage = nullptr;
+ }
+ // Update border+content to account for image change
+ InvalidateFrame();
+ }
+}
+
+void
+nsImageFrame::MaybeDecodeForPredictedSize()
+{
+ // Check that we're ready to decode.
+ if (!mImage) {
+ return; // Nothing to do yet.
+ }
+
+ if (mComputedSize.IsEmpty()) {
+ return; // We won't draw anything, so no point in decoding.
+ }
+
+ if (GetVisibility() != Visibility::APPROXIMATELY_VISIBLE) {
+ return; // We're not visible, so don't decode.
+ }
+
+ // OK, we're ready to decode. Compute the scale to the screen...
+ nsIPresShell* presShell = PresContext()->GetPresShell();
+ LayoutDeviceToScreenScale2D resolutionToScreen(
+ presShell->GetCumulativeResolution()
+ * nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(this));
+
+ // ...and this frame's content box...
+ const nsPoint offset =
+ GetOffsetToCrossDoc(nsLayoutUtils::GetReferenceFrame(this));
+ const nsRect frameContentBox = GetInnerArea() + offset;
+
+ // ...and our predicted dest rect...
+ const int32_t factor = PresContext()->AppUnitsPerDevPixel();
+ const LayoutDeviceRect destRect =
+ LayoutDeviceRect::FromAppUnits(PredictedDestRect(frameContentBox), factor);
+
+ // ...and use them to compute our predicted size in screen pixels.
+ const ScreenSize predictedScreenSize = destRect.Size() * resolutionToScreen;
+ const ScreenIntSize predictedScreenIntSize = RoundedToInt(predictedScreenSize);
+ if (predictedScreenIntSize.IsEmpty()) {
+ return;
+ }
+
+ // Determine the optimal image size to use.
+ uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING
+ | imgIContainer::FLAG_ASYNC_NOTIFY;
+ SamplingFilter samplingFilter =
+ nsLayoutUtils::GetSamplingFilterForFrame(this);
+ gfxSize gfxPredictedScreenSize = gfxSize(predictedScreenIntSize.width,
+ predictedScreenIntSize.height);
+ nsIntSize predictedImageSize =
+ mImage->OptimalImageSizeForDest(gfxPredictedScreenSize,
+ imgIContainer::FRAME_CURRENT,
+ samplingFilter, flags);
+
+ // Request a decode.
+ mImage->RequestDecodeForSize(predictedImageSize, flags);
+}
+
+nsRect
+nsImageFrame::PredictedDestRect(const nsRect& aFrameContentBox)
+{
+ // Note: To get the "dest rect", we have to provide the "constraint rect"
+ // (which is the content-box, with the effects of fragmentation undone).
+ nsRect constraintRect(aFrameContentBox.TopLeft(), mComputedSize);
+ constraintRect.y -= GetContinuationOffset();
+
+ return nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+ mIntrinsicSize,
+ mIntrinsicRatio,
+ StylePosition());
+}
+
+void
+nsImageFrame::EnsureIntrinsicSizeAndRatio()
+{
+ // If mIntrinsicSize.width and height are 0, then we need to update from the
+ // image container.
+ if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
+ mIntrinsicSize.width.GetCoordValue() == 0 &&
+ mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
+ mIntrinsicSize.height.GetCoordValue() == 0) {
+
+ if (mImage) {
+ UpdateIntrinsicSize(mImage);
+ UpdateIntrinsicRatio(mImage);
+ } else {
+ // image request is null or image size not known, probably an
+ // invalid image specified
+ if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
+ bool imageBroken = false;
+ // check for broken images. valid null images (eg. img src="") are
+ // not considered broken because they have no image requests
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ if (imageLoader) {
+ nsCOMPtr<imgIRequest> currentRequest;
+ imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(currentRequest));
+ uint32_t imageStatus;
+ imageBroken =
+ currentRequest &&
+ NS_SUCCEEDED(currentRequest->GetImageStatus(&imageStatus)) &&
+ (imageStatus & imgIRequest::STATUS_ERROR);
+ }
+ // invalid image specified. make the image big enough for the "broken" icon
+ if (imageBroken) {
+ nscoord edgeLengthToUse =
+ nsPresContext::CSSPixelsToAppUnits(
+ ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
+ mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
+ mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
+ mIntrinsicRatio.SizeTo(1, 1);
+ }
+ }
+ }
+ }
+}
+
+/* virtual */
+LogicalSize
+nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ EnsureIntrinsicSizeAndRatio();
+
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ NS_ASSERTION(imageLoader, "No content node??");
+ mozilla::IntrinsicSize intrinsicSize(mIntrinsicSize);
+
+ // XXX(seth): We may sometimes find ourselves in the situation where we have
+ // mImage, but imageLoader's current request does not have a size yet.
+ // This can happen when we load an image speculatively from cache, it fails
+ // to validate, and the new image load hasn't fired SIZE_AVAILABLE yet. In
+ // this situation we should always use mIntrinsicSize, because
+ // GetNaturalWidth/Height will return 0, so we check CurrentRequestHasSize()
+ // below. See bug 1019840. We will fix this in bug 1141395.
+
+ // Content may override our default dimensions. This is termed as overriding
+ // the intrinsic size by the spec, but all other consumers of mIntrinsic*
+ // values are being used to refer to the real/true size of the image data.
+ if (imageLoader && imageLoader->CurrentRequestHasSize() && mImage &&
+ intrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
+ intrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
+ uint32_t width;
+ uint32_t height;
+ if (NS_SUCCEEDED(imageLoader->GetNaturalWidth(&width)) &&
+ NS_SUCCEEDED(imageLoader->GetNaturalHeight(&height))) {
+ nscoord appWidth = nsPresContext::CSSPixelsToAppUnits((int32_t)width);
+ nscoord appHeight = nsPresContext::CSSPixelsToAppUnits((int32_t)height);
+ // If this image is rotated, we'll need to transpose the natural
+ // width/height.
+ bool coordFlip;
+ if (StyleVisibility()->mImageOrientation.IsFromImage()) {
+ coordFlip = mImage->GetOrientation().SwapsWidthAndHeight();
+ } else {
+ coordFlip = StyleVisibility()->mImageOrientation.SwapsWidthAndHeight();
+ }
+ intrinsicSize.width.SetCoordValue(coordFlip ? appHeight : appWidth);
+ intrinsicSize.height.SetCoordValue(coordFlip ? appWidth : appHeight);
+ }
+ }
+
+ return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
+ intrinsicSize, mIntrinsicRatio,
+ aCBSize, aMargin, aBorder, aPadding,
+ aFlags);
+}
+
+// XXXdholbert This function's clients should probably just be calling
+// GetContentRectRelativeToSelf() directly.
+nsRect
+nsImageFrame::GetInnerArea() const
+{
+ return GetContentRectRelativeToSelf();
+}
+
+Element*
+nsImageFrame::GetMapElement() const
+{
+ nsAutoString usemap;
+ if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap)) {
+ return mContent->OwnerDoc()->FindImageMap(usemap);
+ }
+ return nullptr;
+}
+
+// get the offset into the content area of the image where aImg starts if it is a continuation.
+nscoord
+nsImageFrame::GetContinuationOffset() const
+{
+ nscoord offset = 0;
+ for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
+ offset += f->GetContentRect().height;
+ }
+ NS_ASSERTION(offset >= 0, "bogus GetContentRect");
+ return offset;
+}
+
+/* virtual */ nscoord
+nsImageFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ // XXX The caller doesn't account for constraints of the height,
+ // min-height, and max-height properties.
+ DebugOnly<nscoord> result;
+ DISPLAY_MIN_WIDTH(this, result);
+ EnsureIntrinsicSizeAndRatio();
+ return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
+ mIntrinsicSize.width.GetCoordValue() : 0;
+}
+
+/* virtual */ nscoord
+nsImageFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ // XXX The caller doesn't account for constraints of the height,
+ // min-height, and max-height properties.
+ DebugOnly<nscoord> result;
+ DISPLAY_PREF_WIDTH(this, result);
+ EnsureIntrinsicSizeAndRatio();
+ // convert from normal twips to scaled twips (printing...)
+ return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
+ mIntrinsicSize.width.GetCoordValue() : 0;
+}
+
+/* virtual */ IntrinsicSize
+nsImageFrame::GetIntrinsicSize()
+{
+ return mIntrinsicSize;
+}
+
+/* virtual */ nsSize
+nsImageFrame::GetIntrinsicRatio()
+{
+ return mIntrinsicRatio;
+}
+
+void
+nsImageFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("enter nsImageFrame::Reflow: availSize=%d,%d",
+ aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ // see if we have a frozen size (i.e. a fixed width and height)
+ if (HaveFixedSize(aReflowInput)) {
+ mState |= IMAGE_SIZECONSTRAINED;
+ } else {
+ mState &= ~IMAGE_SIZECONSTRAINED;
+ }
+
+ // XXXldb These two bits are almost exact opposites (except in the
+ // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
+ if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+ mState |= IMAGE_GOTINITIALREFLOW;
+ }
+
+ mComputedSize =
+ nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
+
+ aMetrics.Width() = mComputedSize.width;
+ aMetrics.Height() = mComputedSize.height;
+
+ // add borders and padding
+ aMetrics.Width() += aReflowInput.ComputedPhysicalBorderPadding().LeftRight();
+ aMetrics.Height() += aReflowInput.ComputedPhysicalBorderPadding().TopBottom();
+
+ if (GetPrevInFlow()) {
+ aMetrics.Width() = GetPrevInFlow()->GetSize().width;
+ nscoord y = GetContinuationOffset();
+ aMetrics.Height() -= y + aReflowInput.ComputedPhysicalBorderPadding().top;
+ aMetrics.Height() = std::max(0, aMetrics.Height());
+ }
+
+
+ // we have to split images if we are:
+ // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
+ uint32_t loadStatus = imgIRequest::STATUS_NONE;
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ NS_ASSERTION(imageLoader, "No content node??");
+ if (imageLoader) {
+ nsCOMPtr<imgIRequest> currentRequest;
+ imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(currentRequest));
+ if (currentRequest) {
+ currentRequest->GetImageStatus(&loadStatus);
+ }
+ }
+ if (aPresContext->IsPaginated() &&
+ ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
+ NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableHeight() &&
+ aMetrics.Height() > aReflowInput.AvailableHeight()) {
+ // our desired height was greater than 0, so to avoid infinite
+ // splitting, use 1 pixel as the min
+ aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowInput.AvailableHeight());
+ aStatus = NS_FRAME_NOT_COMPLETE;
+ }
+
+ aMetrics.SetOverflowAreasToDesiredBounds();
+ EventStates contentState = mContent->AsElement()->State();
+ bool imageOK = IMAGE_OK(contentState, true);
+
+ // Determine if the size is available
+ bool haveSize = false;
+ if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
+ haveSize = true;
+ }
+
+ if (!imageOK || !haveSize) {
+ nsRect altFeedbackSize(0, 0,
+ nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
+ nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
+ // We include the altFeedbackSize in our visual overflow, but not in our
+ // scrollable overflow, since it doesn't really need to be scrolled to
+ // outside the image.
+ static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
+ nsRect& visualOverflow = aMetrics.VisualOverflow();
+ visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
+ } else {
+ // We've just reflowed and we should have an accurate size, so we're ready
+ // to request a decode.
+ MaybeDecodeForPredictedSize();
+ }
+ FinishAndStoreOverflow(&aMetrics);
+
+ if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
+ nsIPresShell* shell = PresContext()->PresShell();
+ mReflowCallbackPosted = true;
+ shell->PostReflowCallback(this);
+ }
+
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("exit nsImageFrame::Reflow: size=%d,%d",
+ aMetrics.Width(), aMetrics.Height()));
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+bool
+nsImageFrame::ReflowFinished()
+{
+ mReflowCallbackPosted = false;
+
+ // XXX(seth): We don't need this. The purpose of updating visibility
+ // synchronously is to ensure that animated images start animating
+ // immediately. In the short term, however,
+ // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
+ // animations start as soon as the image is painted for the first time, and in
+ // the long term we want to update visibility information from the display
+ // list whenever we paint, so we don't actually need to do this. However, to
+ // avoid behavior changes during the transition from the old image visibility
+ // code, we'll leave it in for now.
+ UpdateVisibilitySynchronously();
+
+ return false;
+}
+
+void
+nsImageFrame::ReflowCallbackCanceled()
+{
+ mReflowCallbackPosted = false;
+}
+
+// Computes the width of the specified string. aMaxWidth specifies the maximum
+// width available. Once this limit is reached no more characters are measured.
+// The number of characters that fit within the maximum width are returned in
+// aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
+// into the rendering context before this is called (for performance). MMP
+nscoord
+nsImageFrame::MeasureString(const char16_t* aString,
+ int32_t aLength,
+ nscoord aMaxWidth,
+ uint32_t& aMaxFit,
+ nsRenderingContext& aContext,
+ nsFontMetrics& aFontMetrics)
+{
+ nscoord totalWidth = 0;
+ aFontMetrics.SetTextRunRTL(false);
+ nscoord spaceWidth = aFontMetrics.SpaceWidth();
+
+ aMaxFit = 0;
+ while (aLength > 0) {
+ // Find the next place we can line break
+ uint32_t len = aLength;
+ bool trailingSpace = false;
+ for (int32_t i = 0; i < aLength; i++) {
+ if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
+ len = i; // don't include the space when measuring
+ trailingSpace = true;
+ break;
+ }
+ }
+
+ // Measure this chunk of text, and see if it fits
+ nscoord width =
+ nsLayoutUtils::AppUnitWidthOfStringBidi(aString, len, this, aFontMetrics,
+ aContext);
+ bool fits = (totalWidth + width) <= aMaxWidth;
+
+ // If it fits on the line, or it's the first word we've processed then
+ // include it
+ if (fits || (0 == totalWidth)) {
+ // New piece fits
+ totalWidth += width;
+
+ // If there's a trailing space then see if it fits as well
+ if (trailingSpace) {
+ if ((totalWidth + spaceWidth) <= aMaxWidth) {
+ totalWidth += spaceWidth;
+ } else {
+ // Space won't fit. Leave it at the end but don't include it in
+ // the width
+ fits = false;
+ }
+
+ len++;
+ }
+
+ aMaxFit += len;
+ aString += len;
+ aLength -= len;
+ }
+
+ if (!fits) {
+ break;
+ }
+ }
+ return totalWidth;
+}
+
+// Formats the alt-text to fit within the specified rectangle. Breaks lines
+// between words if a word would extend past the edge of the rectangle
+void
+nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
+ nsRenderingContext& aRenderingContext,
+ const nsString& aAltText,
+ const nsRect& aRect)
+{
+ // Set font and color
+ aRenderingContext.ThebesContext()->
+ SetColor(Color::FromABGR(StyleColor()->mColor));
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
+
+ // Format the text to display within the formatting rect
+
+ nscoord maxAscent = fm->MaxAscent();
+ nscoord maxDescent = fm->MaxDescent();
+ nscoord lineHeight = fm->MaxHeight(); // line-relative, so an x-coordinate
+ // length if writing mode is vertical
+
+ WritingMode wm = GetWritingMode();
+ bool isVertical = wm.IsVertical();
+
+ fm->SetVertical(isVertical);
+ fm->SetTextOrientation(StyleVisibility()->mTextOrientation);
+
+ // XXX It would be nice if there was a way to have the font metrics tell
+ // use where to break the text given a maximum width. At a minimum we need
+ // to be able to get the break character...
+ const char16_t* str = aAltText.get();
+ int32_t strLen = aAltText.Length();
+ nsPoint pt = wm.IsVerticalRL() ? aRect.TopRight() - nsPoint(lineHeight, 0)
+ : aRect.TopLeft();
+ nscoord iSize = isVertical ? aRect.height : aRect.width;
+
+ if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
+ aPresContext->SetBidiEnabled();
+ }
+
+ // Always show the first line, even if we have to clip it below
+ bool firstLine = true;
+ while (strLen > 0) {
+ if (!firstLine) {
+ // If we've run out of space, break out of the loop
+ if ((!isVertical && (pt.y + maxDescent) >= aRect.YMost()) ||
+ (wm.IsVerticalRL() && (pt.x + maxDescent < aRect.x)) ||
+ (wm.IsVerticalLR() && (pt.x + maxDescent >= aRect.XMost()))) {
+ break;
+ }
+ }
+
+ // Determine how much of the text to display on this line
+ uint32_t maxFit; // number of characters that fit
+ nscoord strWidth = MeasureString(str, strLen, iSize, maxFit,
+ aRenderingContext, *fm);
+
+ // Display the text
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (aPresContext->BidiEnabled()) {
+ nsBidiDirection dir;
+ nscoord x, y;
+
+ if (isVertical) {
+ x = pt.x + maxDescent;
+ if (wm.IsBidiLTR()) {
+ y = aRect.y;
+ dir = NSBIDI_LTR;
+ } else {
+ y = aRect.YMost() - strWidth;
+ dir = NSBIDI_RTL;
+ }
+ } else {
+ y = pt.y + maxAscent;
+ if (wm.IsBidiLTR()) {
+ x = aRect.x;
+ dir = NSBIDI_LTR;
+ } else {
+ x = aRect.XMost() - strWidth;
+ dir = NSBIDI_RTL;
+ }
+ }
+
+ rv = nsBidiPresUtils::RenderText(str, maxFit, dir,
+ aPresContext, aRenderingContext,
+ aRenderingContext.GetDrawTarget(),
+ *fm, x, y);
+ }
+ if (NS_FAILED(rv)) {
+ nsLayoutUtils::DrawUniDirString(str, maxFit,
+ isVertical
+ ? nsPoint(pt.x + maxDescent, pt.y)
+ : nsPoint(pt.x, pt.y + maxAscent),
+ *fm, aRenderingContext);
+ }
+
+ // Move to the next line
+ str += maxFit;
+ strLen -= maxFit;
+ if (wm.IsVerticalRL()) {
+ pt.x -= lineHeight;
+ } else if (wm.IsVerticalLR()) {
+ pt.x += lineHeight;
+ } else {
+ pt.y += lineHeight;
+ }
+
+ firstLine = false;
+ }
+}
+
+struct nsRecessedBorder : public nsStyleBorder {
+ nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
+ : nsStyleBorder(aPresContext)
+ {
+ NS_FOR_CSS_SIDES(side) {
+ mBorderColor[side] = StyleComplexColor::FromColor(NS_RGB(0, 0, 0));
+ mBorder.Side(side) = aBorderWidth;
+ // Note: use SetBorderStyle here because we want to affect
+ // mComputedBorder
+ SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
+ }
+ }
+};
+
+class nsDisplayAltFeedback : public nsDisplayItem {
+public:
+ nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame) {}
+
+ virtual nsDisplayItemGeometry*
+ AllocateGeometry(nsDisplayListBuilder* aBuilder) override
+ {
+ return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+ }
+
+ virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+ const nsDisplayItemGeometry* aGeometry,
+ nsRegion* aInvalidRegion) override
+ {
+ auto geometry =
+ static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+ if (aBuilder->ShouldSyncDecodeImages() &&
+ geometry->ShouldInvalidateToSyncDecodeImages()) {
+ bool snap;
+ aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+ }
+
+ nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
+ }
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override
+ {
+ *aSnap = false;
+ return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
+ }
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override
+ {
+ // Always sync decode, because these icons are UI, and since they're not
+ // discardable we'll pay the price of sync decoding at most once.
+ uint32_t flags = imgIContainer::FLAG_SYNC_DECODE;
+
+ nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
+ DrawResult result =
+ f->DisplayAltFeedback(*aCtx,
+ mVisibleRect,
+ ToReferenceFrame(),
+ flags);
+
+ nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
+ }
+
+ NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
+};
+
+DrawResult
+nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect,
+ nsPoint aPt,
+ uint32_t aFlags)
+{
+ // We should definitely have a gIconLoad here.
+ MOZ_ASSERT(gIconLoad, "How did we succeed in Init then?");
+
+ // Whether we draw the broken or loading icon.
+ bool isLoading = IMAGE_OK(GetContent()->AsElement()->State(), true);
+
+ // Calculate the inner area
+ nsRect inner = GetInnerArea() + aPt;
+
+ // Display a recessed one pixel border
+ nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
+
+ // if inner area is empty, then make it big enough for at least the icon
+ if (inner.IsEmpty()){
+ inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
+ 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
+ }
+
+ // Make sure we have enough room to actually render the border within
+ // our frame bounds
+ if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
+ return DrawResult::SUCCESS;
+ }
+
+ // Paint the border
+ if (!isLoading || gIconLoad->mPrefShowLoadingPlaceholder) {
+ nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
+
+ // Assert that we're not drawing a border-image here; if we were, we
+ // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns.
+ MOZ_ASSERT(recessedBorder.mBorderImageSource.GetType() == eStyleImageType_Null);
+
+ Unused <<
+ nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
+ this, inner, inner,
+ recessedBorder, mStyleContext,
+ PaintBorderFlags::SYNC_DECODE_IMAGES);
+ }
+
+ // Adjust the inner rect to account for the one pixel recessed border,
+ // and a six pixel padding on each edge
+ inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
+ nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
+ if (inner.IsEmpty()) {
+ return DrawResult::SUCCESS;
+ }
+
+ DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
+ gfxContext* gfx = aRenderingContext.ThebesContext();
+
+ // Clip so we don't render outside the inner rect
+ gfx->Save();
+ gfx->Clip(NSRectToSnappedRect(inner, PresContext()->AppUnitsPerDevPixel(),
+ *drawTarget));
+
+ DrawResult result = DrawResult::NOT_READY;
+
+ // Check if we should display image placeholders
+ if (!gIconLoad->mPrefShowPlaceholders ||
+ (isLoading && !gIconLoad->mPrefShowLoadingPlaceholder)) {
+ result = DrawResult::SUCCESS;
+ } else {
+ nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
+
+ imgIRequest* request = isLoading
+ ? nsImageFrame::gIconLoad->mLoadingImage
+ : nsImageFrame::gIconLoad->mBrokenImage;
+
+ // If we weren't previously displaying an icon, register ourselves
+ // as an observer for load and animation updates and flag that we're
+ // doing so now.
+ if (request && !mDisplayingIcon) {
+ gIconLoad->AddIconObserver(this);
+ mDisplayingIcon = true;
+ }
+
+ WritingMode wm = GetWritingMode();
+ bool flushRight =
+ (!wm.IsVertical() && !wm.IsBidiLTR()) || wm.IsVerticalRL();
+
+ // If the icon in question is loaded, draw it.
+ uint32_t imageStatus = 0;
+ if (request)
+ request->GetImageStatus(&imageStatus);
+ if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE) {
+ nsCOMPtr<imgIContainer> imgCon;
+ request->GetImage(getter_AddRefs(imgCon));
+ MOZ_ASSERT(imgCon, "Load complete, but no image container?");
+ nsRect dest(flushRight ? inner.XMost() - size : inner.x,
+ inner.y, size, size);
+ result = nsLayoutUtils::DrawSingleImage(*gfx, PresContext(), imgCon,
+ nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
+ nullptr, aFlags);
+ }
+
+ // If we could not draw the icon, just draw some graffiti in the mean time.
+ if (result == DrawResult::NOT_READY) {
+ ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
+
+ nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
+
+ // stroked rect:
+ nsRect rect(iconXPos, inner.y, size, size);
+ Rect devPxRect =
+ ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
+ drawTarget->StrokeRect(devPxRect, color);
+
+ // filled circle in bottom right quadrant of stroked rect:
+ nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
+ rect = nsRect(iconXPos + size/2, inner.y + size/2,
+ size/2 - twoPX, size/2 - twoPX);
+ devPxRect =
+ ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
+ RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
+ AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
+ RefPtr<Path> ellipse = builder->Finish();
+ drawTarget->Fill(ellipse, color);
+ }
+
+ // Reduce the inner rect by the width of the icon, and leave an
+ // additional ICON_PADDING pixels for padding
+ int32_t paddedIconSize =
+ nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
+ if (wm.IsVertical()) {
+ inner.y += paddedIconSize;
+ inner.height -= paddedIconSize;
+ } else {
+ if (!flushRight) {
+ inner.x += paddedIconSize;
+ }
+ inner.width -= paddedIconSize;
+ }
+ }
+
+ // If there's still room, display the alt-text
+ if (!inner.IsEmpty()) {
+ nsIContent* content = GetContent();
+ if (content) {
+ nsXPIDLString altText;
+ nsCSSFrameConstructor::GetAlternateTextFor(content,
+ content->NodeInfo()->NameAtom(),
+ altText);
+ DisplayAltText(PresContext(), aRenderingContext, altText, inner);
+ }
+ }
+
+ aRenderingContext.ThebesContext()->Restore();
+
+ return result;
+}
+
+#ifdef DEBUG
+static void PaintDebugImageMap(nsIFrame* aFrame, DrawTarget* aDrawTarget,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
+ nsRect inner = f->GetInnerArea() + aPt;
+ gfxPoint devPixelOffset =
+ nsLayoutUtils::PointToGfxPoint(inner.TopLeft(),
+ aFrame->PresContext()->AppUnitsPerDevPixel());
+ AutoRestoreTransform autoRestoreTransform(aDrawTarget);
+ aDrawTarget->SetTransform(
+ aDrawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
+ f->GetImageMap()->Draw(aFrame, *aDrawTarget,
+ ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f))));
+}
+#endif
+
+void
+nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ uint32_t flags = imgIContainer::FLAG_NONE;
+ if (aBuilder->ShouldSyncDecodeImages()) {
+ flags |= imgIContainer::FLAG_SYNC_DECODE;
+ }
+ if (aBuilder->IsPaintingToWindow()) {
+ flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
+ }
+
+ DrawResult result = static_cast<nsImageFrame*>(mFrame)->
+ PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
+
+ if (result == DrawResult::NOT_READY ||
+ result == DrawResult::INCOMPLETE ||
+ result == DrawResult::TEMPORARY_ERROR) {
+ // If the current image failed to paint because it's still loading or
+ // decoding, try painting the previous image.
+ if (mPrevImage) {
+ result = static_cast<nsImageFrame*>(mFrame)->
+ PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mPrevImage, flags);
+ }
+ }
+
+ nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
+}
+
+nsDisplayItemGeometry*
+nsDisplayImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+ return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+}
+
+void
+nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+ const nsDisplayItemGeometry* aGeometry,
+ nsRegion* aInvalidRegion)
+{
+ auto geometry =
+ static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+ if (aBuilder->ShouldSyncDecodeImages() &&
+ geometry->ShouldInvalidateToSyncDecodeImages()) {
+ bool snap;
+ aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+ }
+
+ nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
+}
+
+already_AddRefed<imgIContainer>
+nsDisplayImage::GetImage()
+{
+ nsCOMPtr<imgIContainer> image = mImage;
+ return image.forget();
+}
+
+nsRect
+nsDisplayImage::GetDestRect()
+{
+ bool snap = true;
+ const nsRect frameContentBox = GetBounds(&snap);
+
+ nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
+ return imageFrame->PredictedDestRect(frameContentBox);
+}
+
+LayerState
+nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters)
+{
+ bool animated = false;
+ if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
+ mImage->GetType() != imgIContainer::TYPE_RASTER ||
+ NS_FAILED(mImage->GetAnimated(&animated)) ||
+ !animated) {
+ if (!aManager->IsCompositingCheap() ||
+ !nsLayoutUtils::GPUImageScalingEnabled()) {
+ return LAYER_NONE;
+ }
+ }
+
+ if (!animated) {
+ int32_t imageWidth;
+ int32_t imageHeight;
+ mImage->GetWidth(&imageWidth);
+ mImage->GetHeight(&imageHeight);
+
+ NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
+
+ const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
+ const LayoutDeviceRect destRect =
+ LayoutDeviceRect::FromAppUnits(GetDestRect(), factor);
+ const LayerRect destLayerRect = destRect * aParameters.Scale();
+
+ // Calculate the scaling factor for the frame.
+ const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
+ destLayerRect.height / imageHeight);
+
+ // If we are not scaling at all, no point in separating this into a layer.
+ if (scale.width == 1.0f && scale.height == 1.0f) {
+ return LAYER_NONE;
+ }
+
+ // If the target size is pretty small, no point in using a layer.
+ if (destLayerRect.width * destLayerRect.height < 64 * 64) {
+ return LAYER_NONE;
+ }
+ }
+
+ uint32_t flags = aBuilder->ShouldSyncDecodeImages()
+ ? imgIContainer::FLAG_SYNC_DECODE
+ : imgIContainer::FLAG_NONE;
+
+ if (!mImage->IsImageContainerAvailable(aManager, flags)) {
+ return LAYER_NONE;
+ }
+
+ return LAYER_ACTIVE;
+}
+
+
+/* virtual */ nsRegion
+nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+ bool* aSnap)
+{
+ *aSnap = false;
+ if (mImage && mImage->WillDrawOpaqueNow()) {
+ const nsRect frameContentBox = GetBounds(aSnap);
+ return GetDestRect().Intersect(frameContentBox);
+ }
+ return nsRegion();
+}
+
+already_AddRefed<Layer>
+nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters)
+{
+ uint32_t flags = aBuilder->ShouldSyncDecodeImages()
+ ? imgIContainer::FLAG_SYNC_DECODE
+ : imgIContainer::FLAG_NONE;
+
+ RefPtr<ImageContainer> container =
+ mImage->GetImageContainer(aManager, flags);
+ if (!container) {
+ return nullptr;
+ }
+
+ RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
+ (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+ if (!layer) {
+ layer = aManager->CreateImageLayer();
+ if (!layer)
+ return nullptr;
+ }
+ layer->SetContainer(container);
+ ConfigureLayer(layer, aParameters);
+ return layer.forget();
+}
+
+DrawResult
+nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
+ const nsRect& aDirtyRect, imgIContainer* aImage,
+ uint32_t aFlags)
+{
+ DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
+
+ // Render the image into our content area (the area inside
+ // the borders and padding)
+ NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
+
+ // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
+ // because GetInnerArea() might be smaller if we're fragmented, whereas
+ // mComputedSize has our full content-box size (which we need for
+ // ComputeObjectDestRect to work correctly).
+ nsRect constraintRect(aPt + GetInnerArea().TopLeft(), mComputedSize);
+ constraintRect.y -= GetContinuationOffset();
+
+ nsPoint anchorPoint;
+ nsRect dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+ mIntrinsicSize,
+ mIntrinsicRatio,
+ StylePosition(),
+ &anchorPoint);
+
+ uint32_t flags = aFlags;
+ if (mForceSyncDecoding) {
+ flags |= imgIContainer::FLAG_SYNC_DECODE;
+ }
+
+ DrawResult result =
+ nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
+ PresContext(), aImage,
+ nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
+ nullptr, flags, &anchorPoint);
+
+ nsImageMap* map = GetImageMap();
+ if (map) {
+ gfxPoint devPixelOffset =
+ nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
+ PresContext()->AppUnitsPerDevPixel());
+ AutoRestoreTransform autoRestoreTransform(drawTarget);
+ drawTarget->SetTransform(
+ drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
+
+ // solid white stroke:
+ ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
+ map->Draw(this, *drawTarget, white);
+
+ // then dashed black stroke over the top:
+ ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
+ StrokeOptions strokeOptions;
+ nsLayoutUtils::InitDashPattern(strokeOptions, NS_STYLE_BORDER_STYLE_DOTTED);
+ map->Draw(this, *drawTarget, black, strokeOptions);
+ }
+
+ if (result == DrawResult::SUCCESS) {
+ mPrevImage = aImage;
+ } else if (result == DrawResult::BAD_IMAGE) {
+ mPrevImage = nullptr;
+ }
+
+ return result;
+}
+
+void
+nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ uint32_t clipFlags =
+ nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
+ 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
+
+ DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
+ clip(aBuilder, this, clipFlags);
+
+ if (mComputedSize.width != 0 && mComputedSize.height != 0) {
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ NS_ASSERTION(imageLoader, "Not an image loading content?");
+
+ nsCOMPtr<imgIRequest> currentRequest;
+ if (imageLoader) {
+ imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(currentRequest));
+ }
+
+ EventStates contentState = mContent->AsElement()->State();
+ bool imageOK = IMAGE_OK(contentState, true);
+
+ // XXX(seth): The SizeIsAvailable check here should not be necessary - the
+ // intention is that a non-null mImage means we have a size, but there is
+ // currently some code that violates this invariant.
+ if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
+ // No image yet, or image load failed. Draw the alt-text and an icon
+ // indicating the status
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayAltFeedback(aBuilder, this));
+
+ // This image is visible (we are being asked to paint it) but it's not
+ // decoded yet. And we are not going to ask the image to draw, so this
+ // may be the only chance to tell it that it should decode.
+ if (currentRequest) {
+ uint32_t status = 0;
+ currentRequest->GetImageStatus(&status);
+ if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
+ MaybeDecodeForPredictedSize();
+ }
+ }
+ } else {
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayImage(aBuilder, this, mImage, mPrevImage));
+
+ // If we were previously displaying an icon, we're not anymore
+ if (mDisplayingIcon) {
+ gIconLoad->RemoveIconObserver(this);
+ mDisplayingIcon = false;
+ }
+
+#ifdef DEBUG
+ if (GetShowFrameBorders() && GetImageMap()) {
+ aLists.Outlines()->AppendNewToTop(new (aBuilder)
+ nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
+ nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
+ }
+#endif
+ }
+ }
+
+ if (ShouldDisplaySelection()) {
+ DisplaySelectionOverlay(aBuilder, aLists.Content(),
+ nsISelectionDisplay::DISPLAY_IMAGES);
+ }
+}
+
+bool
+nsImageFrame::ShouldDisplaySelection()
+{
+ // XXX what on EARTH is this code for?
+ nsresult result;
+ nsPresContext* presContext = PresContext();
+ int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
+ if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
+ return false;//no need to check the blue border, we cannot be drawn selected
+//insert hook here for image selection drawing
+#if IMAGE_EDITOR_CHECK
+ //check to see if this frame is in an editor context
+ //isEditor check. this needs to be changed to have better way to check
+ if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
+ {
+ nsCOMPtr<nsISelectionController> selCon;
+ result = GetSelectionController(presContext, getter_AddRefs(selCon));
+ if (NS_SUCCEEDED(result) && selCon)
+ {
+ nsCOMPtr<nsISelection> selection;
+ result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
+ if (NS_SUCCEEDED(result) && selection)
+ {
+ int32_t rangeCount;
+ selection->GetRangeCount(&rangeCount);
+ if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
+ {
+ nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
+ if (parentContent)
+ {
+ int32_t thisOffset = parentContent->IndexOf(mContent);
+ nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
+ nsCOMPtr<nsIDOMNode> rangeNode;
+ int32_t rangeOffset;
+ nsCOMPtr<nsIDOMRange> range;
+ selection->GetRangeAt(0,getter_AddRefs(range));
+ if (range)
+ {
+ range->GetStartContainer(getter_AddRefs(rangeNode));
+ range->GetStartOffset(&rangeOffset);
+
+ if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
+ {
+ range->GetEndContainer(getter_AddRefs(rangeNode));
+ range->GetEndOffset(&rangeOffset);
+ if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
+ return false; //do not allow nsFrame do draw any further selection
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+ return true;
+}
+
+nsImageMap*
+nsImageFrame::GetImageMap()
+{
+ if (!mImageMap) {
+ nsIContent* map = GetMapElement();
+ if (map) {
+ mImageMap = new nsImageMap();
+ mImageMap->Init(this, map);
+ }
+ }
+
+ return mImageMap;
+}
+
+bool
+nsImageFrame::IsServerImageMap()
+{
+ return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
+}
+
+// Translate an point that is relative to our frame
+// into a localized pixel coordinate that is relative to the
+// content area of this frame (inside the border+padding).
+void
+nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
+ nsIntPoint& aResult)
+{
+ nscoord x = aPoint.x;
+ nscoord y = aPoint.y;
+
+ // Subtract out border and padding here so that the coordinates are
+ // now relative to the content area of this frame.
+ nsRect inner = GetInnerArea();
+ x -= inner.x;
+ y -= inner.y;
+
+ aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
+ aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
+}
+
+bool
+nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
+ nsIContent** aNode)
+{
+ bool status = false;
+ aTarget.Truncate();
+ *aHref = nullptr;
+ *aNode = nullptr;
+
+ // Walk up the content tree, looking for an nsIDOMAnchorElement
+ for (nsIContent* content = mContent->GetParent();
+ content; content = content->GetParent()) {
+ nsCOMPtr<dom::Link> link(do_QueryInterface(content));
+ if (link) {
+ nsCOMPtr<nsIURI> href = content->GetHrefURI();
+ if (href) {
+ href->Clone(aHref);
+ }
+ status = (*aHref != nullptr);
+
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
+ if (anchor) {
+ anchor->GetTarget(aTarget);
+ }
+ NS_ADDREF(*aNode = content);
+ break;
+ }
+ }
+ return status;
+}
+
+nsresult
+nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
+ nsIContent** aContent)
+{
+ NS_ENSURE_ARG_POINTER(aContent);
+
+ nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
+ if (f != this) {
+ return f->GetContentForEvent(aEvent, aContent);
+ }
+
+ // XXX We need to make this special check for area element's capturing the
+ // mouse due to bug 135040. Remove it once that's fixed.
+ nsIContent* capturingContent =
+ aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
+ nullptr;
+ if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
+ *aContent = capturingContent;
+ NS_IF_ADDREF(*aContent);
+ return NS_OK;
+ }
+
+ nsImageMap* map = GetImageMap();
+
+ if (nullptr != map) {
+ nsIntPoint p;
+ TranslateEventCoords(
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
+ nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
+ if (area) {
+ area.forget(aContent);
+ return NS_OK;
+ }
+ }
+
+ *aContent = GetContent();
+ NS_IF_ADDREF(*aContent);
+ return NS_OK;
+}
+
+// XXX what should clicks on transparent pixels do?
+nsresult
+nsImageFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+
+ if ((aEvent->mMessage == eMouseClick &&
+ aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
+ aEvent->mMessage == eMouseMove) {
+ nsImageMap* map = GetImageMap();
+ bool isServerMap = IsServerImageMap();
+ if ((nullptr != map) || isServerMap) {
+ nsIntPoint p;
+ TranslateEventCoords(
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
+ bool inside = false;
+ // Even though client-side image map triggering happens
+ // through content, we need to make sure we're not inside
+ // (in case we deal with a case of both client-side and
+ // sever-side on the same image - it happens!)
+ if (nullptr != map) {
+ inside = !!map->GetArea(p.x, p.y);
+ }
+
+ if (!inside && isServerMap) {
+
+ // Server side image maps use the href in a containing anchor
+ // element to provide the basis for the destination url.
+ nsCOMPtr<nsIURI> uri;
+ nsAutoString target;
+ nsCOMPtr<nsIContent> anchorNode;
+ if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
+ getter_AddRefs(anchorNode))) {
+ // XXX if the mouse is over/clicked in the border/padding area
+ // we should probably just pretend nothing happened. Nav4
+ // keeps the x,y coordinates positive as we do; IE doesn't
+ // bother. Both of them send the click through even when the
+ // mouse is over the border.
+ if (p.x < 0) p.x = 0;
+ if (p.y < 0) p.y = 0;
+
+ nsAutoCString spec;
+ nsresult rv = uri->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ spec += nsPrintfCString("?%d,%d", p.x, p.y);
+ rv = uri->SetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool clicked = false;
+ if (aEvent->mMessage == eMouseClick && !aEvent->DefaultPrevented()) {
+ *aEventStatus = nsEventStatus_eConsumeDoDefault;
+ clicked = true;
+ }
+ nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
+ clicked, true, true);
+ }
+ }
+ }
+ }
+
+ return nsAtomicContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
+}
+
+nsresult
+nsImageFrame::GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor)
+{
+ nsImageMap* map = GetImageMap();
+ if (nullptr != map) {
+ nsIntPoint p;
+ TranslateEventCoords(aPoint, p);
+ nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
+ if (area) {
+ // Use the cursor from the style of the *area* element.
+ // XXX Using the image as the parent style context isn't
+ // technically correct, but it's probably the right thing to do
+ // here, since it means that areas on which the cursor isn't
+ // specified will inherit the style from the image.
+ RefPtr<nsStyleContext> areaStyle =
+ PresContext()->PresShell()->StyleSet()->
+ ResolveStyleFor(area->AsElement(), StyleContext());
+ FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
+ aCursor);
+ if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
+ aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
+ }
+ return NS_OK;
+ }
+ }
+ return nsFrame::GetCursor(aPoint, aCursor);
+}
+
+nsresult
+nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ nsresult rv = nsAtomicContainerFrame::AttributeChanged(aNameSpaceID,
+ aAttribute, aModType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (nsGkAtoms::alt == aAttribute)
+ {
+ PresContext()->PresShell()->FrameNeedsReflow(this,
+ nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+ }
+
+ return NS_OK;
+}
+
+void
+nsImageFrame::OnVisibilityChange(Visibility aNewVisibility,
+ Maybe<OnNonvisible> aNonvisibleAction)
+{
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ if (!imageLoader) {
+ MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
+ nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
+ return;
+ }
+
+ imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
+
+ if (aNewVisibility == Visibility::APPROXIMATELY_VISIBLE) {
+ MaybeDecodeForPredictedSize();
+ }
+
+ nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
+}
+
+nsIAtom*
+nsImageFrame::GetType() const
+{
+ return nsGkAtoms::imageFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsImageFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
+}
+
+void
+nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+
+ // output the img src url
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+ if (imageLoader) {
+ nsCOMPtr<imgIRequest> currentRequest;
+ imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(currentRequest));
+ if (currentRequest) {
+ nsCOMPtr<nsIURI> uri;
+ currentRequest->GetURI(getter_AddRefs(uri));
+ nsAutoCString uristr;
+ uri->GetAsciiSpec(uristr);
+ str += nsPrintfCString(" [src=%s]", uristr.get());
+ }
+ }
+ fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+nsIFrame::LogicalSides
+nsImageFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
+{
+ if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone)) {
+ return LogicalSides();
+ }
+ LogicalSides skip;
+ if (nullptr != GetPrevInFlow()) {
+ skip |= eLogicalSideBitsBStart;
+ }
+ if (nullptr != GetNextInFlow()) {
+ skip |= eLogicalSideBitsBEnd;
+ }
+ return skip;
+}
+
+nsresult
+nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
+{
+ if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
+ mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
+ aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
+ mIntrinsicSize.height.GetCoordValue());
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsImageFrame::LoadIcon(const nsAString& aSpec,
+ nsPresContext *aPresContext,
+ imgRequestProxy** aRequest)
+{
+ nsresult rv = NS_OK;
+ NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
+
+ if (!sIOService) {
+ rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIURI> realURI;
+ SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
+
+ RefPtr<imgLoader> il =
+ nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
+
+ // For icon loads, we don't need to merge with the loadgroup flags
+ nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
+ nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
+
+ return il->LoadImage(realURI, /* icon URI */
+ nullptr, /* initial document URI; this is only
+ relevant for cookies, so does not
+ apply to icons. */
+ nullptr, /* referrer (not relevant for icons) */
+ mozilla::net::RP_Default,
+ nullptr, /* principal (not relevant for icons) */
+ loadGroup,
+ gIconLoad,
+ nullptr, /* No context */
+ nullptr, /* Not associated with any particular document */
+ loadFlags,
+ nullptr,
+ contentPolicyType,
+ EmptyString(),
+ aRequest);
+}
+
+void
+nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
+{
+ if (mContent) {
+ NS_ASSERTION(mContent->GetComposedDoc(),
+ "Frame still alive after content removed from document!");
+ aCharset = mContent->GetComposedDoc()->GetDocumentCharacterSet();
+ }
+}
+
+void
+nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
+ nsIURI **aURI)
+{
+ nsCOMPtr<nsIURI> baseURI;
+ if (mContent) {
+ baseURI = mContent->GetBaseURI();
+ }
+ nsAutoCString charset;
+ GetDocumentCharacterSet(charset);
+ NS_NewURI(aURI, aSpec,
+ charset.IsEmpty() ? nullptr : charset.get(),
+ baseURI, aIOService);
+}
+
+void
+nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
+{
+ if (!aPresContext)
+ return;
+
+ NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
+
+ nsIPresShell *shell = aPresContext->GetPresShell();
+
+ if (!shell)
+ return;
+
+ nsIDocument *doc = shell->GetDocument();
+ if (!doc)
+ return;
+
+ *aLoadGroup = doc->GetDocumentLoadGroup().take();
+}
+
+nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
+{
+ NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
+
+ NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
+ NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
+
+ gIconLoad = new IconLoad();
+ NS_ADDREF(gIconLoad);
+
+ nsresult rv;
+ // create a loader and load the images
+ rv = LoadIcon(loadingSrc,
+ aPresContext,
+ getter_AddRefs(gIconLoad->mLoadingImage));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = LoadIcon(brokenSrc,
+ aPresContext,
+ getter_AddRefs(gIconLoad->mBrokenImage));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
+ imgINotificationObserver)
+
+static const char* kIconLoadPrefs[] = {
+ "browser.display.force_inline_alttext",
+ "browser.display.show_image_placeholders",
+ "browser.display.show_loading_image_placeholder",
+ nullptr
+};
+
+nsImageFrame::IconLoad::IconLoad()
+{
+ // register observers
+ Preferences::AddStrongObservers(this, kIconLoadPrefs);
+ GetPrefs();
+}
+
+void
+nsImageFrame::IconLoad::Shutdown()
+{
+ Preferences::RemoveObservers(this, kIconLoadPrefs);
+ // in case the pref service releases us later
+ if (mLoadingImage) {
+ mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
+ mLoadingImage = nullptr;
+ }
+ if (mBrokenImage) {
+ mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
+ mBrokenImage = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
+ "wrong topic");
+#ifdef DEBUG
+ // assert |aData| is one of our prefs.
+ uint32_t i = 0;
+ for (; i < ArrayLength(kIconLoadPrefs); ++i) {
+ if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
+ break;
+ }
+ MOZ_ASSERT(i < ArrayLength(kIconLoadPrefs));
+#endif
+
+ GetPrefs();
+ return NS_OK;
+}
+
+void nsImageFrame::IconLoad::GetPrefs()
+{
+ mPrefForceInlineAltText =
+ Preferences::GetBool("browser.display.force_inline_alttext");
+
+ mPrefShowPlaceholders =
+ Preferences::GetBool("browser.display.show_image_placeholders", true);
+
+ mPrefShowLoadingPlaceholder =
+ Preferences::GetBool("browser.display.show_loading_image_placeholder", true);
+}
+
+NS_IMETHODIMP
+nsImageFrame::IconLoad::Notify(imgIRequest* aRequest,
+ int32_t aType,
+ const nsIntRect* aData)
+{
+ MOZ_ASSERT(aRequest);
+
+ if (aType != imgINotificationObserver::LOAD_COMPLETE &&
+ aType != imgINotificationObserver::FRAME_UPDATE) {
+ return NS_OK;
+ }
+
+ if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+ nsCOMPtr<imgIContainer> image;
+ aRequest->GetImage(getter_AddRefs(image));
+ if (!image) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Retrieve the image's intrinsic size.
+ int32_t width = 0;
+ int32_t height = 0;
+ image->GetWidth(&width);
+ image->GetHeight(&height);
+
+ // Request a decode at that size.
+ image->RequestDecodeForSize(IntSize(width, height),
+ imgIContainer::DECODE_FLAGS_DEFAULT);
+ }
+
+ nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
+ nsImageFrame *frame;
+ while (iter.HasMore()) {
+ frame = iter.GetNext();
+ frame->InvalidateFrame();
+ }
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
+
+nsImageListener::nsImageListener(nsImageFrame *aFrame) :
+ mFrame(aFrame)
+{
+}
+
+nsImageListener::~nsImageListener()
+{
+}
+
+NS_IMETHODIMP
+nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
+{
+ if (!mFrame)
+ return NS_ERROR_FAILURE;
+
+ return mFrame->Notify(aRequest, aType, aData);
+}
+
+static bool
+IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
+{
+ if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
+ return false;
+ // Check if the parent of the closest nsBlockFrame has auto width.
+ nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
+ if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
+ // Assume direct parent is a table cell frame.
+ nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
+ return grandAncestor &&
+ grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
+ }
+ return false;
+}
+
+/* virtual */ void
+nsImageFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlineMinISizeData* aData)
+{
+ nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ this, nsLayoutUtils::MIN_ISIZE);
+ bool canBreak = !IsInAutoWidthTableCellForQuirk(this);
+ aData->DefaultAddInlineMinISize(this, isize, canBreak);
+}
diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h
new file mode 100644
index 000000000..5bc59c042
--- /dev/null
+++ b/layout/generic/nsImageFrame.h
@@ -0,0 +1,469 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for replaced elements with image data */
+
+#ifndef nsImageFrame_h___
+#define nsImageFrame_h___
+
+#include "nsAtomicContainerFrame.h"
+#include "nsIIOService.h"
+#include "nsIObserver.h"
+
+#include "imgINotificationObserver.h"
+
+#include "nsDisplayList.h"
+#include "imgIContainer.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "nsIReflowCallback.h"
+#include "nsTObserverArray.h"
+
+class nsFontMetrics;
+class nsImageMap;
+class nsIURI;
+class nsILoadGroup;
+class nsDisplayImage;
+class nsPresContext;
+class nsImageFrame;
+class nsTransform2D;
+class nsImageLoadingContent;
+
+namespace mozilla {
+namespace layers {
+ class ImageContainer;
+ class ImageLayer;
+ class LayerManager;
+} // namespace layers
+} // namespace mozilla
+
+class nsImageListener : public imgINotificationObserver
+{
+protected:
+ virtual ~nsImageListener();
+
+public:
+ explicit nsImageListener(nsImageFrame *aFrame);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_IMGINOTIFICATIONOBSERVER
+
+ void SetFrame(nsImageFrame *frame) { mFrame = frame; }
+
+private:
+ nsImageFrame *mFrame;
+};
+
+class nsImageFrame : public nsAtomicContainerFrame
+ , public nsIReflowCallback {
+public:
+ template <typename T> using Maybe = mozilla::Maybe<T>;
+ using Nothing = mozilla::Nothing;
+ using Visibility = mozilla::Visibility;
+
+ typedef mozilla::image::DrawResult DrawResult;
+ typedef mozilla::layers::ImageContainer ImageContainer;
+ typedef mozilla::layers::ImageLayer ImageLayer;
+ typedef mozilla::layers::LayerManager LayerManager;
+
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsImageFrame(nsStyleContext* aContext);
+
+ NS_DECL_QUERYFRAME_TARGET(nsImageFrame)
+ NS_DECL_QUERYFRAME
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual mozilla::IntrinsicSize GetIntrinsicSize() override;
+ virtual nsSize GetIntrinsicRatio() override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
+ nsIContent** aContent) override;
+ virtual nsresult HandleEvent(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) override;
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor) override;
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+ void OnVisibilityChange(Visibility aNewVisibility,
+ Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsAtomicContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+ void List(FILE* out = stderr, const char* aPrefix = "",
+ uint32_t aFlags = 0) const override;
+#endif
+
+ nsSplittableType GetSplittableType() const override
+ {
+ return NS_FRAME_SPLITTABLE;
+ }
+
+ virtual LogicalSides GetLogicalSkipSides(const ReflowInput* aReflowInput = nullptr) const override;
+
+ nsresult GetIntrinsicImageSize(nsSize& aSize);
+
+ static void ReleaseGlobals() {
+ if (gIconLoad) {
+ gIconLoad->Shutdown();
+ NS_RELEASE(gIconLoad);
+ }
+ NS_IF_RELEASE(sIOService);
+ }
+
+ nsresult Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData);
+
+ /**
+ * Function to test whether aContent, which has aStyleContext as its style,
+ * should get an image frame. Note that this method is only used by the
+ * frame constructor; it's only here because it uses gIconLoad for now.
+ */
+ static bool ShouldCreateImageFrameFor(mozilla::dom::Element* aElement,
+ nsStyleContext* aStyleContext);
+
+ DrawResult DisplayAltFeedback(nsRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect,
+ nsPoint aPt,
+ uint32_t aFlags);
+
+ nsRect GetInnerArea() const;
+
+ /**
+ * Return a map element associated with this image.
+ */
+ mozilla::dom::Element* GetMapElement() const;
+
+ /**
+ * Return true if the image has associated image map.
+ */
+ bool HasImageMap() const { return mImageMap || GetMapElement(); }
+
+ nsImageMap* GetImageMap();
+ nsImageMap* GetExistingImageMap() const { return mImageMap; }
+
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+
+ void DisconnectMap();
+
+ // nsIReflowCallback
+ virtual bool ReflowFinished() override;
+ virtual void ReflowCallbackCanceled() override;
+
+protected:
+ virtual ~nsImageFrame();
+
+ void EnsureIntrinsicSizeAndRatio();
+
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ bool IsServerImageMap();
+
+ void TranslateEventCoords(const nsPoint& aPoint,
+ nsIntPoint& aResult);
+
+ bool GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
+ nsIContent** aNode);
+ /**
+ * Computes the width of the string that fits into the available space
+ *
+ * @param in aLength total length of the string in PRUnichars
+ * @param in aMaxWidth width not to be exceeded
+ * @param out aMaxFit length of the string that fits within aMaxWidth
+ * in PRUnichars
+ * @return width of the string that fits within aMaxWidth
+ */
+ nscoord MeasureString(const char16_t* aString,
+ int32_t aLength,
+ nscoord aMaxWidth,
+ uint32_t& aMaxFit,
+ nsRenderingContext& aContext,
+ nsFontMetrics& aFontMetrics);
+
+ void DisplayAltText(nsPresContext* aPresContext,
+ nsRenderingContext& aRenderingContext,
+ const nsString& aAltText,
+ const nsRect& aRect);
+
+ DrawResult PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
+ const nsRect& aDirtyRect, imgIContainer* aImage,
+ uint32_t aFlags);
+
+ /**
+ * If we're ready to decode - that is, if our current request's image is
+ * available and our decoding heuristics are satisfied - then trigger a decode
+ * for our image at the size we predict it will be drawn next time it's
+ * painted.
+ */
+ void MaybeDecodeForPredictedSize();
+
+protected:
+ friend class nsImageListener;
+ friend class nsImageLoadingContent;
+ friend class PresShell;
+
+ nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
+ nsresult OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect);
+ nsresult OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
+
+ /**
+ * Notification that aRequest will now be the current request.
+ */
+ void NotifyNewCurrentRequest(imgIRequest *aRequest, nsresult aStatus);
+
+ /// Always sync decode our image when painting if @aForce is true.
+ void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; }
+
+ /**
+ * Computes the predicted dest rect that we'll draw into, in app units, based
+ * upon the provided frame content box. (The content box is what
+ * nsDisplayImage::GetBounds() returns.)
+ * The result is not necessarily contained in the frame content box.
+ */
+ nsRect PredictedDestRect(const nsRect& aFrameContentBox);
+
+private:
+ // random helpers
+ inline void SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
+ nsIURI **aURI);
+
+ inline void GetLoadGroup(nsPresContext *aPresContext,
+ nsILoadGroup **aLoadGroup);
+ nscoord GetContinuationOffset() const;
+ void GetDocumentCharacterSet(nsACString& aCharset) const;
+ bool ShouldDisplaySelection();
+
+ /**
+ * Recalculate mIntrinsicSize from the image.
+ *
+ * @return whether aImage's size did _not_
+ * match our previous intrinsic size.
+ */
+ bool UpdateIntrinsicSize(imgIContainer* aImage);
+
+ /**
+ * Recalculate mIntrinsicRatio from the image.
+ *
+ * @return whether aImage's ratio did _not_
+ * match our previous intrinsic ratio.
+ */
+ bool UpdateIntrinsicRatio(imgIContainer* aImage);
+
+ /**
+ * This function calculates the transform for converting between
+ * source space & destination space. May fail if our image has a
+ * percent-valued or zero-valued height or width.
+ *
+ * @param aTransform The transform object to populate.
+ *
+ * @return whether we succeeded in creating the transform.
+ */
+ bool GetSourceToDestTransform(nsTransform2D& aTransform);
+
+ /**
+ * Helper function to check whether the request corresponds to a load we don't
+ * care about. Most of the decoder observer methods will bail early if this
+ * returns true.
+ */
+ bool IsPendingLoad(imgIRequest* aRequest) const;
+
+ /**
+ * Function to convert a dirty rect in the source image to a dirty
+ * rect for the image frame.
+ */
+ nsRect SourceRectToDest(const nsIntRect & aRect);
+
+ /**
+ * Triggers invalidation for both our image display item and, if appropriate,
+ * our alt-feedback display item.
+ *
+ * @param aLayerInvalidRect The area to invalidate in layer space. If null, the
+ * entire layer will be invalidated.
+ * @param aFrameInvalidRect The area to invalidate in frame space. If null, the
+ * entire frame will be invalidated.
+ */
+ void InvalidateSelf(const nsIntRect* aLayerInvalidRect,
+ const nsRect* aFrameInvalidRect);
+
+ RefPtr<nsImageMap> mImageMap;
+
+ nsCOMPtr<imgINotificationObserver> mListener;
+
+ nsCOMPtr<imgIContainer> mImage;
+ nsCOMPtr<imgIContainer> mPrevImage;
+ nsSize mComputedSize;
+ mozilla::IntrinsicSize mIntrinsicSize;
+ nsSize mIntrinsicRatio;
+
+ bool mDisplayingIcon;
+ bool mFirstFrameComplete;
+ bool mReflowCallbackPosted;
+ bool mForceSyncDecoding;
+
+ static nsIIOService* sIOService;
+
+ /* loading / broken image icon support */
+
+ // XXXbz this should be handled by the prescontext, I think; that
+ // way we would have a single iconload per mozilla session instead
+ // of one per document...
+
+ // LoadIcons: initiate the loading of the static icons used to show
+ // loading / broken images
+ nsresult LoadIcons(nsPresContext *aPresContext);
+ nsresult LoadIcon(const nsAString& aSpec, nsPresContext *aPresContext,
+ imgRequestProxy **aRequest);
+
+ class IconLoad final : public nsIObserver,
+ public imgINotificationObserver
+ {
+ // private class that wraps the data and logic needed for
+ // broken image and loading image icons
+ public:
+ IconLoad();
+
+ void Shutdown();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_IMGINOTIFICATIONOBSERVER
+
+ void AddIconObserver(nsImageFrame *frame) {
+ MOZ_ASSERT(!mIconObservers.Contains(frame),
+ "Observer shouldn't aleady be in array");
+ mIconObservers.AppendElement(frame);
+ }
+
+ void RemoveIconObserver(nsImageFrame *frame) {
+ mozilla::DebugOnly<bool> didRemove = mIconObservers.RemoveElement(frame);
+ MOZ_ASSERT(didRemove, "Observer not in array");
+ }
+
+ private:
+ ~IconLoad() {}
+
+ void GetPrefs();
+ nsTObserverArray<nsImageFrame*> mIconObservers;
+
+
+ public:
+ RefPtr<imgRequestProxy> mLoadingImage;
+ RefPtr<imgRequestProxy> mBrokenImage;
+ bool mPrefForceInlineAltText;
+ bool mPrefShowPlaceholders;
+ bool mPrefShowLoadingPlaceholder;
+ };
+
+public:
+ static IconLoad* gIconLoad; // singleton pattern: one LoadIcons instance is used
+
+ friend class nsDisplayImage;
+};
+
+/**
+ * Note that nsDisplayImage does not receive events. However, an image element
+ * is replaced content so its background will be z-adjacent to the
+ * image itself, and hence receive events just as if the image itself
+ * received events.
+ */
+class nsDisplayImage : public nsDisplayImageContainer {
+public:
+ typedef mozilla::layers::LayerManager LayerManager;
+
+ nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame,
+ imgIContainer* aImage, imgIContainer* aPrevImage)
+ : nsDisplayImageContainer(aBuilder, aFrame)
+ , mImage(aImage)
+ , mPrevImage(aPrevImage)
+ {
+ MOZ_COUNT_CTOR(nsDisplayImage);
+ }
+ virtual ~nsDisplayImage() {
+ MOZ_COUNT_DTOR(nsDisplayImage);
+ }
+
+ virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
+ virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+ const nsDisplayItemGeometry* aGeometry,
+ nsRegion* aInvalidRegion) override;
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+
+ virtual already_AddRefed<imgIContainer> GetImage() override;
+
+ /**
+ * @return The dest rect we'll use when drawing this image, in app units.
+ * Not necessarily contained in this item's bounds.
+ */
+ virtual nsRect GetDestRect() override;
+
+ virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) override;
+ nsRect GetBounds(bool* aSnap)
+ {
+ *aSnap = true;
+
+ nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
+ return imageFrame->GetInnerArea() + ToReferenceFrame();
+ }
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override
+ {
+ return GetBounds(aSnap);
+ }
+
+ virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override;
+
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) override;
+
+ NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE)
+private:
+ nsCOMPtr<imgIContainer> mImage;
+ nsCOMPtr<imgIContainer> mPrevImage;
+};
+
+#endif /* nsImageFrame_h___ */
diff --git a/layout/generic/nsImageMap.cpp b/layout/generic/nsImageMap.cpp
new file mode 100644
index 000000000..19c879756
--- /dev/null
+++ b/layout/generic/nsImageMap.cpp
@@ -0,0 +1,1020 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* code for HTML client-side image maps */
+
+#include "nsImageMap.h"
+
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/UniquePtr.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsPresContext.h"
+#include "nsNameSpaceManager.h"
+#include "nsGkAtoms.h"
+#include "nsImageFrame.h"
+#include "nsCoord.h"
+#include "nsIScriptError.h"
+#include "nsIStringBundle.h"
+#include "nsContentUtils.h"
+#include "ImageLayers.h"
+
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+class Area {
+public:
+ explicit Area(nsIContent* aArea);
+ virtual ~Area();
+
+ virtual void ParseCoords(const nsAString& aSpec);
+
+ virtual bool IsInside(nscoord x, nscoord y) const = 0;
+ virtual void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions) = 0;
+ virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) = 0;
+
+ void HasFocus(bool aHasFocus);
+
+ nsCOMPtr<nsIContent> mArea;
+ UniquePtr<nscoord[]> mCoords;
+ int32_t mNumCoords;
+ bool mHasFocus;
+};
+
+Area::Area(nsIContent* aArea)
+ : mArea(aArea)
+{
+ MOZ_COUNT_CTOR(Area);
+ NS_PRECONDITION(mArea, "How did that happen?");
+ mNumCoords = 0;
+ mHasFocus = false;
+}
+
+Area::~Area()
+{
+ MOZ_COUNT_DTOR(Area);
+}
+
+#include <stdlib.h>
+
+inline bool
+is_space(char c)
+{
+ return (c == ' ' ||
+ c == '\f' ||
+ c == '\n' ||
+ c == '\r' ||
+ c == '\t' ||
+ c == '\v');
+}
+
+static void logMessage(nsIContent* aContent,
+ const nsAString& aCoordsSpec,
+ int32_t aFlags,
+ const char* aMessageName) {
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ nsContentUtils::ReportToConsole(
+ aFlags, NS_LITERAL_CSTRING("Layout: ImageMap"), doc,
+ nsContentUtils::eLAYOUT_PROPERTIES,
+ aMessageName,
+ nullptr, /* params */
+ 0, /* params length */
+ nullptr,
+ PromiseFlatString(NS_LITERAL_STRING("coords=\"") +
+ aCoordsSpec +
+ NS_LITERAL_STRING("\""))); /* source line */
+}
+
+void Area::ParseCoords(const nsAString& aSpec)
+{
+ char* cp = ToNewCString(aSpec);
+ if (cp) {
+ char *tptr;
+ char *n_str;
+ int32_t i, cnt;
+
+ /*
+ * Nothing in an empty list
+ */
+ mNumCoords = 0;
+ mCoords = nullptr;
+ if (*cp == '\0')
+ {
+ free(cp);
+ return;
+ }
+
+ /*
+ * Skip beginning whitespace, all whitespace is empty list.
+ */
+ n_str = cp;
+ while (is_space(*n_str))
+ {
+ n_str++;
+ }
+ if (*n_str == '\0')
+ {
+ free(cp);
+ return;
+ }
+
+ /*
+ * Make a pass where any two numbers separated by just whitespace
+ * are given a comma separator. Count entries while passing.
+ */
+ cnt = 0;
+ while (*n_str != '\0')
+ {
+ bool has_comma;
+
+ /*
+ * Skip to a separator
+ */
+ tptr = n_str;
+ while (!is_space(*tptr) && *tptr != ',' && *tptr != '\0')
+ {
+ tptr++;
+ }
+ n_str = tptr;
+
+ /*
+ * If no more entries, break out here
+ */
+ if (*n_str == '\0')
+ {
+ break;
+ }
+
+ /*
+ * Skip to the end of the separator, noting if we have a
+ * comma.
+ */
+ has_comma = false;
+ while (is_space(*tptr) || *tptr == ',')
+ {
+ if (*tptr == ',')
+ {
+ if (!has_comma)
+ {
+ has_comma = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+ tptr++;
+ }
+ /*
+ * If this was trailing whitespace we skipped, we are done.
+ */
+ if ((*tptr == '\0') && !has_comma)
+ {
+ break;
+ }
+ /*
+ * Else if the separator is all whitespace, and this is not the
+ * end of the string, add a comma to the separator.
+ */
+ else if (!has_comma)
+ {
+ *n_str = ',';
+ }
+
+ /*
+ * count the entry skipped.
+ */
+ cnt++;
+
+ n_str = tptr;
+ }
+ /*
+ * count the last entry in the list.
+ */
+ cnt++;
+
+ /*
+ * Allocate space for the coordinate array.
+ */
+ UniquePtr<nscoord[]> value_list = MakeUnique<nscoord[]>(cnt);
+ if (!value_list)
+ {
+ free(cp);
+ return;
+ }
+
+ /*
+ * Second pass to copy integer values into list.
+ */
+ tptr = cp;
+ for (i=0; i<cnt; i++)
+ {
+ char *ptr;
+
+ ptr = strchr(tptr, ',');
+ if (ptr)
+ {
+ *ptr = '\0';
+ }
+ /*
+ * Strip whitespace in front of number because I don't
+ * trust atoi to do it on all platforms.
+ */
+ while (is_space(*tptr))
+ {
+ tptr++;
+ }
+ if (*tptr == '\0')
+ {
+ value_list[i] = 0;
+ }
+ else
+ {
+ value_list[i] = (nscoord) ::atoi(tptr);
+ }
+ if (ptr)
+ {
+ *ptr = ',';
+ tptr = ptr + 1;
+ }
+ }
+
+ mNumCoords = cnt;
+ mCoords = Move(value_list);
+
+ free(cp);
+ }
+}
+
+void Area::HasFocus(bool aHasFocus)
+{
+ mHasFocus = aHasFocus;
+}
+
+//----------------------------------------------------------------------
+
+class DefaultArea : public Area {
+public:
+ explicit DefaultArea(nsIContent* aArea);
+
+ virtual bool IsInside(nscoord x, nscoord y) const override;
+ virtual void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions) override;
+ virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
+};
+
+DefaultArea::DefaultArea(nsIContent* aArea)
+ : Area(aArea)
+{
+}
+
+bool DefaultArea::IsInside(nscoord x, nscoord y) const
+{
+ return true;
+}
+
+void DefaultArea::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (mHasFocus) {
+ nsRect r(nsPoint(0, 0), aFrame->GetSize());
+ const nscoord kOnePixel = nsPresContext::CSSPixelsToAppUnits(1);
+ r.width -= kOnePixel;
+ r.height -= kOnePixel;
+ Rect rect =
+ ToRect(nsLayoutUtils::RectToGfxRect(r, aFrame->PresContext()->AppUnitsPerDevPixel()));
+ StrokeSnappedEdgesOfRect(rect, aDrawTarget, aColor, aStrokeOptions);
+ }
+}
+
+void DefaultArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
+{
+ aRect = aFrame->GetRect();
+ aRect.MoveTo(0, 0);
+}
+
+//----------------------------------------------------------------------
+
+class RectArea : public Area {
+public:
+ explicit RectArea(nsIContent* aArea);
+
+ virtual void ParseCoords(const nsAString& aSpec) override;
+ virtual bool IsInside(nscoord x, nscoord y) const override;
+ virtual void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions) override;
+ virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
+};
+
+RectArea::RectArea(nsIContent* aArea)
+ : Area(aArea)
+{
+}
+
+void RectArea::ParseCoords(const nsAString& aSpec)
+{
+ Area::ParseCoords(aSpec);
+
+ bool saneRect = true;
+ int32_t flag = nsIScriptError::warningFlag;
+ if (mNumCoords >= 4) {
+ if (mCoords[0] > mCoords[2]) {
+ // x-coords in reversed order
+ nscoord x = mCoords[2];
+ mCoords[2] = mCoords[0];
+ mCoords[0] = x;
+ saneRect = false;
+ }
+
+ if (mCoords[1] > mCoords[3]) {
+ // y-coords in reversed order
+ nscoord y = mCoords[3];
+ mCoords[3] = mCoords[1];
+ mCoords[1] = y;
+ saneRect = false;
+ }
+
+ if (mNumCoords > 4) {
+ // Someone missed the concept of a rect here
+ saneRect = false;
+ }
+ } else {
+ saneRect = false;
+ flag = nsIScriptError::errorFlag;
+ }
+
+ if (!saneRect) {
+ logMessage(mArea, aSpec, flag, "ImageMapRectBoundsError");
+ }
+}
+
+bool RectArea::IsInside(nscoord x, nscoord y) const
+{
+ if (mNumCoords >= 4) { // Note: > is for nav compatibility
+ nscoord x1 = mCoords[0];
+ nscoord y1 = mCoords[1];
+ nscoord x2 = mCoords[2];
+ nscoord y2 = mCoords[3];
+ NS_ASSERTION(x1 <= x2 && y1 <= y2,
+ "Someone screwed up RectArea::ParseCoords");
+ if ((x >= x1) && (x <= x2) && (y >= y1) && (y <= y2)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void RectArea::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (mHasFocus) {
+ if (mNumCoords >= 4) {
+ nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
+ nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
+ nscoord x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
+ nscoord y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[3]);
+ NS_ASSERTION(x1 <= x2 && y1 <= y2,
+ "Someone screwed up RectArea::ParseCoords");
+ nsRect r(x1, y1, x2 - x1, y2 - y1);
+ Rect rect =
+ ToRect(nsLayoutUtils::RectToGfxRect(r, aFrame->PresContext()->AppUnitsPerDevPixel()));
+ StrokeSnappedEdgesOfRect(rect, aDrawTarget, aColor, aStrokeOptions);
+ }
+ }
+}
+
+void RectArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
+{
+ if (mNumCoords >= 4) {
+ nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
+ nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
+ nscoord x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
+ nscoord y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[3]);
+ NS_ASSERTION(x1 <= x2 && y1 <= y2,
+ "Someone screwed up RectArea::ParseCoords");
+
+ aRect.SetRect(x1, y1, x2, y2);
+ }
+}
+
+//----------------------------------------------------------------------
+
+class PolyArea : public Area {
+public:
+ explicit PolyArea(nsIContent* aArea);
+
+ virtual void ParseCoords(const nsAString& aSpec) override;
+ virtual bool IsInside(nscoord x, nscoord y) const override;
+ virtual void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions) override;
+ virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
+};
+
+PolyArea::PolyArea(nsIContent* aArea)
+ : Area(aArea)
+{
+}
+
+void PolyArea::ParseCoords(const nsAString& aSpec)
+{
+ Area::ParseCoords(aSpec);
+
+ if (mNumCoords >= 2) {
+ if (mNumCoords & 1U) {
+ logMessage(mArea,
+ aSpec,
+ nsIScriptError::warningFlag,
+ "ImageMapPolyOddNumberOfCoords");
+ }
+ } else {
+ logMessage(mArea,
+ aSpec,
+ nsIScriptError::errorFlag,
+ "ImageMapPolyWrongNumberOfCoords");
+ }
+}
+
+bool PolyArea::IsInside(nscoord x, nscoord y) const
+{
+ if (mNumCoords >= 6) {
+ int32_t intersects = 0;
+ nscoord wherex = x;
+ nscoord wherey = y;
+ int32_t totalv = mNumCoords / 2;
+ int32_t totalc = totalv * 2;
+ nscoord xval = mCoords[totalc - 2];
+ nscoord yval = mCoords[totalc - 1];
+ int32_t end = totalc;
+ int32_t pointer = 1;
+
+ if ((yval >= wherey) != (mCoords[pointer] >= wherey)) {
+ if ((xval >= wherex) == (mCoords[0] >= wherex)) {
+ intersects += (xval >= wherex) ? 1 : 0;
+ } else {
+ intersects += ((xval - (yval - wherey) *
+ (mCoords[0] - xval) /
+ (mCoords[pointer] - yval)) >= wherex) ? 1 : 0;
+ }
+ }
+
+ // XXX I wonder what this is doing; this is a translation of ptinpoly.c
+ while (pointer < end) {
+ yval = mCoords[pointer];
+ pointer += 2;
+ if (yval >= wherey) {
+ while((pointer < end) && (mCoords[pointer] >= wherey))
+ pointer+=2;
+ if (pointer >= end)
+ break;
+ if ((mCoords[pointer-3] >= wherex) ==
+ (mCoords[pointer-1] >= wherex)) {
+ intersects += (mCoords[pointer-3] >= wherex) ? 1 : 0;
+ } else {
+ intersects +=
+ ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
+ (mCoords[pointer-1] - mCoords[pointer-3]) /
+ (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
+ }
+ } else {
+ while((pointer < end) && (mCoords[pointer] < wherey))
+ pointer+=2;
+ if (pointer >= end)
+ break;
+ if ((mCoords[pointer-3] >= wherex) ==
+ (mCoords[pointer-1] >= wherex)) {
+ intersects += (mCoords[pointer-3] >= wherex) ? 1:0;
+ } else {
+ intersects +=
+ ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
+ (mCoords[pointer-1] - mCoords[pointer-3]) /
+ (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
+ }
+ }
+ }
+ if ((intersects & 1) != 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PolyArea::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (mHasFocus) {
+ if (mNumCoords >= 6) {
+ // Where possible, we want all horizontal and vertical lines to align on
+ // pixel rows or columns, and to start at pixel boundaries so that one
+ // pixel dashing neatly sits on pixels to give us neat lines. To achieve
+ // that we draw each line segment as a separate path, snapping it to
+ // device pixels if applicable.
+ nsPresContext* pc = aFrame->PresContext();
+ Point p1(pc->CSSPixelsToDevPixels(mCoords[0]),
+ pc->CSSPixelsToDevPixels(mCoords[1]));
+ Point p2, p1snapped, p2snapped;
+ for (int32_t i = 2; i < mNumCoords; i += 2) {
+ p2.x = pc->CSSPixelsToDevPixels(mCoords[i]);
+ p2.y = pc->CSSPixelsToDevPixels(mCoords[i+1]);
+ p1snapped = p1;
+ p2snapped = p2;
+ SnapLineToDevicePixelsForStroking(p1snapped, p2snapped, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1snapped, p2snapped, aColor, aStrokeOptions);
+ p1 = p2;
+ }
+ p2.x = pc->CSSPixelsToDevPixels(mCoords[0]);
+ p2.y = pc->CSSPixelsToDevPixels(mCoords[1]);
+ p1snapped = p1;
+ p2snapped = p2;
+ SnapLineToDevicePixelsForStroking(p1snapped, p2snapped, aDrawTarget,
+ aStrokeOptions.mLineWidth);
+ aDrawTarget.StrokeLine(p1snapped, p2snapped, aColor, aStrokeOptions);
+ }
+ }
+}
+
+void PolyArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
+{
+ if (mNumCoords >= 6) {
+ nscoord x1, x2, y1, y2, xtmp, ytmp;
+ x1 = x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
+ y1 = y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
+ for (int32_t i = 2; i < mNumCoords; i += 2) {
+ xtmp = nsPresContext::CSSPixelsToAppUnits(mCoords[i]);
+ ytmp = nsPresContext::CSSPixelsToAppUnits(mCoords[i+1]);
+ x1 = x1 < xtmp ? x1 : xtmp;
+ y1 = y1 < ytmp ? y1 : ytmp;
+ x2 = x2 > xtmp ? x2 : xtmp;
+ y2 = y2 > ytmp ? y2 : ytmp;
+ }
+
+ aRect.SetRect(x1, y1, x2, y2);
+ }
+}
+
+//----------------------------------------------------------------------
+
+class CircleArea : public Area {
+public:
+ explicit CircleArea(nsIContent* aArea);
+
+ virtual void ParseCoords(const nsAString& aSpec) override;
+ virtual bool IsInside(nscoord x, nscoord y) const override;
+ virtual void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions) override;
+ virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) override;
+};
+
+CircleArea::CircleArea(nsIContent* aArea)
+ : Area(aArea)
+{
+}
+
+void CircleArea::ParseCoords(const nsAString& aSpec)
+{
+ Area::ParseCoords(aSpec);
+
+ bool wrongNumberOfCoords = false;
+ int32_t flag = nsIScriptError::warningFlag;
+ if (mNumCoords >= 3) {
+ if (mCoords[2] < 0) {
+ logMessage(mArea,
+ aSpec,
+ nsIScriptError::errorFlag,
+ "ImageMapCircleNegativeRadius");
+ }
+
+ if (mNumCoords > 3) {
+ wrongNumberOfCoords = true;
+ }
+ } else {
+ wrongNumberOfCoords = true;
+ flag = nsIScriptError::errorFlag;
+ }
+
+ if (wrongNumberOfCoords) {
+ logMessage(mArea,
+ aSpec,
+ flag,
+ "ImageMapCircleWrongNumberOfCoords");
+ }
+}
+
+bool CircleArea::IsInside(nscoord x, nscoord y) const
+{
+ // Note: > is for nav compatibility
+ if (mNumCoords >= 3) {
+ nscoord x1 = mCoords[0];
+ nscoord y1 = mCoords[1];
+ nscoord radius = mCoords[2];
+ if (radius < 0) {
+ return false;
+ }
+ nscoord dx = x1 - x;
+ nscoord dy = y1 - y;
+ nscoord dist = (dx * dx) + (dy * dy);
+ if (dist <= (radius * radius)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CircleArea::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ if (mHasFocus) {
+ if (mNumCoords >= 3) {
+ Point center(aFrame->PresContext()->CSSPixelsToDevPixels(mCoords[0]),
+ aFrame->PresContext()->CSSPixelsToDevPixels(mCoords[1]));
+ Float diameter =
+ 2 * aFrame->PresContext()->CSSPixelsToDevPixels(mCoords[2]);
+ if (diameter <= 0) {
+ return;
+ }
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ AppendEllipseToPath(builder, center, Size(diameter, diameter));
+ RefPtr<Path> circle = builder->Finish();
+ aDrawTarget.Stroke(circle, aColor, aStrokeOptions);
+ }
+ }
+}
+
+void CircleArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
+{
+ if (mNumCoords >= 3) {
+ nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
+ nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
+ nscoord radius = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
+ if (radius < 0) {
+ return;
+ }
+
+ aRect.SetRect(x1 - radius, y1 - radius, x1 + radius, y1 + radius);
+ }
+}
+
+//----------------------------------------------------------------------
+
+
+nsImageMap::nsImageMap() :
+ mImageFrame(nullptr),
+ mContainsBlockContents(false)
+{
+}
+
+nsImageMap::~nsImageMap()
+{
+ NS_ASSERTION(mAreas.Length() == 0, "Destroy was not called");
+}
+
+NS_IMPL_ISUPPORTS(nsImageMap,
+ nsIMutationObserver,
+ nsIDOMEventListener)
+
+nsresult
+nsImageMap::GetBoundsForAreaContent(nsIContent *aContent,
+ nsRect& aBounds)
+{
+ NS_ENSURE_TRUE(aContent && mImageFrame, NS_ERROR_INVALID_ARG);
+
+ // Find the Area struct associated with this content node, and return bounds
+ uint32_t i, n = mAreas.Length();
+ for (i = 0; i < n; i++) {
+ Area* area = mAreas.ElementAt(i);
+ if (area->mArea == aContent) {
+ aBounds = nsRect();
+ area->GetRect(mImageFrame, aBounds);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+void
+nsImageMap::FreeAreas()
+{
+ uint32_t i, n = mAreas.Length();
+ for (i = 0; i < n; i++) {
+ Area* area = mAreas.ElementAt(i);
+ if (area->mArea->IsInUncomposedDoc()) {
+ NS_ASSERTION(area->mArea->GetPrimaryFrame() == mImageFrame,
+ "Unexpected primary frame");
+
+ area->mArea->SetPrimaryFrame(nullptr);
+ }
+
+ area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("focus"), this,
+ false);
+ area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("blur"), this,
+ false);
+ delete area;
+ }
+ mAreas.Clear();
+}
+
+nsresult
+nsImageMap::Init(nsImageFrame* aImageFrame, nsIContent* aMap)
+{
+ NS_PRECONDITION(aMap, "null ptr");
+ if (!aMap) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ mImageFrame = aImageFrame;
+
+ mMap = aMap;
+ mMap->AddMutationObserver(this);
+
+ // "Compile" the areas in the map into faster access versions
+ return UpdateAreas();
+}
+
+
+nsresult
+nsImageMap::SearchForAreas(nsIContent* aParent, bool& aFoundArea,
+ bool& aFoundAnchor)
+{
+ nsresult rv = NS_OK;
+ uint32_t i, n = aParent->GetChildCount();
+
+ // Look for <area> or <a> elements. We'll use whichever type we find first.
+ for (i = 0; i < n; i++) {
+ nsIContent *child = aParent->GetChildAt(i);
+
+ // If we haven't determined that the map element contains an
+ // <a> element yet, then look for <area>.
+ if (!aFoundAnchor && child->IsHTMLElement(nsGkAtoms::area)) {
+ aFoundArea = true;
+ rv = AddArea(child);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Continue to next child. This stops mContainsBlockContents from
+ // getting set. It also makes us ignore children of <area>s which
+ // is consistent with how we react to dynamic insertion of such
+ // children.
+ continue;
+ }
+
+ // If we haven't determined that the map element contains an
+ // <area> element yet, then look for <a>.
+ if (!aFoundArea && child->IsHTMLElement(nsGkAtoms::a)) {
+ aFoundAnchor = true;
+ rv = AddArea(child);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (child->IsElement()) {
+ mContainsBlockContents = true;
+ rv = SearchForAreas(child, aFoundArea, aFoundAnchor);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsImageMap::UpdateAreas()
+{
+ // Get rid of old area data
+ FreeAreas();
+
+ bool foundArea = false;
+ bool foundAnchor = false;
+ mContainsBlockContents = false;
+
+ nsresult rv = SearchForAreas(mMap, foundArea, foundAnchor);
+#ifdef ACCESSIBILITY
+ if (NS_SUCCEEDED(rv)) {
+ nsAccessibilityService* accService = GetAccService();
+ if (accService) {
+ accService->UpdateImageMap(mImageFrame);
+ }
+ }
+#endif
+ return rv;
+}
+
+nsresult
+nsImageMap::AddArea(nsIContent* aArea)
+{
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::rect, &nsGkAtoms::rectangle,
+ &nsGkAtoms::circle, &nsGkAtoms::circ,
+ &nsGkAtoms::_default,
+ &nsGkAtoms::poly, &nsGkAtoms::polygon,
+ nullptr};
+
+ Area* area;
+ switch (aArea->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::shape,
+ strings, eIgnoreCase)) {
+ case nsIContent::ATTR_VALUE_NO_MATCH:
+ case nsIContent::ATTR_MISSING:
+ case 0:
+ case 1:
+ area = new RectArea(aArea);
+ break;
+ case 2:
+ case 3:
+ area = new CircleArea(aArea);
+ break;
+ case 4:
+ area = new DefaultArea(aArea);
+ break;
+ case 5:
+ case 6:
+ area = new PolyArea(aArea);
+ break;
+ default:
+ area = nullptr;
+ NS_NOTREACHED("FindAttrValueIn returned an unexpected value.");
+ break;
+ }
+ if (!area)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ //Add focus listener to track area focus changes
+ aArea->AddSystemEventListener(NS_LITERAL_STRING("focus"), this, false,
+ false);
+ aArea->AddSystemEventListener(NS_LITERAL_STRING("blur"), this, false,
+ false);
+
+ // This is a nasty hack. It needs to go away: see bug 135040. Once this is
+ // removed, the code added to RestyleManager::RestyleElement,
+ // nsCSSFrameConstructor::ContentRemoved (both hacks there), and
+ // RestyleManager::ProcessRestyledFrames to work around this issue can
+ // be removed.
+ aArea->SetPrimaryFrame(mImageFrame);
+
+ nsAutoString coords;
+ aArea->GetAttr(kNameSpaceID_None, nsGkAtoms::coords, coords);
+ area->ParseCoords(coords);
+ mAreas.AppendElement(area);
+ return NS_OK;
+}
+
+nsIContent*
+nsImageMap::GetArea(nscoord aX, nscoord aY) const
+{
+ NS_ASSERTION(mMap, "Not initialized");
+ uint32_t i, n = mAreas.Length();
+ for (i = 0; i < n; i++) {
+ Area* area = mAreas.ElementAt(i);
+ if (area->IsInside(aX, aY)) {
+ return area->mArea;
+ }
+ }
+
+ return nullptr;
+}
+
+nsIContent*
+nsImageMap::GetAreaAt(uint32_t aIndex) const
+{
+ return mAreas.ElementAt(aIndex)->mArea;
+}
+
+void
+nsImageMap::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions)
+{
+ uint32_t i, n = mAreas.Length();
+ for (i = 0; i < n; i++) {
+ Area* area = mAreas.ElementAt(i);
+ area->Draw(aFrame, aDrawTarget, aColor, aStrokeOptions);
+ }
+}
+
+void
+nsImageMap::MaybeUpdateAreas(nsIContent *aContent)
+{
+ if (aContent == mMap || mContainsBlockContents) {
+ UpdateAreas();
+ }
+}
+
+void
+nsImageMap::AttributeChanged(nsIDocument* aDocument,
+ dom::Element* aElement,
+ int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue)
+{
+ // If the parent of the changing content node is our map then update
+ // the map. But only do this if the node is an HTML <area> or <a>
+ // and the attribute that's changing is "shape" or "coords" -- those
+ // are the only cases we care about.
+ if ((aElement->NodeInfo()->Equals(nsGkAtoms::area) ||
+ aElement->NodeInfo()->Equals(nsGkAtoms::a)) &&
+ aElement->IsHTMLElement() &&
+ aNameSpaceID == kNameSpaceID_None &&
+ (aAttribute == nsGkAtoms::shape ||
+ aAttribute == nsGkAtoms::coords)) {
+ MaybeUpdateAreas(aElement->GetParent());
+ } else if (aElement == mMap &&
+ aNameSpaceID == kNameSpaceID_None &&
+ (aAttribute == nsGkAtoms::name ||
+ aAttribute == nsGkAtoms::id) &&
+ mImageFrame) {
+ // ID or name has changed. Let ImageFrame recreate ImageMap.
+ mImageFrame->DisconnectMap();
+ }
+}
+
+void
+nsImageMap::ContentAppended(nsIDocument *aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t /* unused */)
+{
+ MaybeUpdateAreas(aContainer);
+}
+
+void
+nsImageMap::ContentInserted(nsIDocument *aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t /* unused */)
+{
+ MaybeUpdateAreas(aContainer);
+}
+
+void
+nsImageMap::ContentRemoved(nsIDocument *aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling)
+{
+ MaybeUpdateAreas(aContainer);
+}
+
+void
+nsImageMap::ParentChainChanged(nsIContent* aContent)
+{
+ NS_ASSERTION(aContent == mMap,
+ "Unexpected ParentChainChanged notification!");
+ if (mImageFrame) {
+ mImageFrame->DisconnectMap();
+ }
+}
+
+nsresult
+nsImageMap::HandleEvent(nsIDOMEvent* aEvent)
+{
+ nsAutoString eventType;
+ aEvent->GetType(eventType);
+ bool focus = eventType.EqualsLiteral("focus");
+ MOZ_ASSERT(focus == !eventType.EqualsLiteral("blur"),
+ "Unexpected event type");
+
+ //Set which one of our areas changed focus
+ nsCOMPtr<nsIContent> targetContent = do_QueryInterface(
+ aEvent->InternalDOMEvent()->GetTarget());
+ if (!targetContent) {
+ return NS_OK;
+ }
+ uint32_t i, n = mAreas.Length();
+ for (i = 0; i < n; i++) {
+ Area* area = mAreas.ElementAt(i);
+ if (area->mArea == targetContent) {
+ //Set or Remove internal focus
+ area->HasFocus(focus);
+ //Now invalidate the rect
+ if (mImageFrame) {
+ mImageFrame->InvalidateFrame();
+ }
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+void
+nsImageMap::Destroy(void)
+{
+ FreeAreas();
+ mImageFrame = nullptr;
+ mMap->RemoveMutationObserver(this);
+}
diff --git a/layout/generic/nsImageMap.h b/layout/generic/nsImageMap.h
new file mode 100644
index 000000000..50dd73797
--- /dev/null
+++ b/layout/generic/nsImageMap.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* code for HTML client-side image maps */
+
+#ifndef nsImageMap_h
+#define nsImageMap_h
+
+#include "mozilla/gfx/2D.h"
+#include "nsCOMPtr.h"
+#include "nsCoord.h"
+#include "nsTArray.h"
+#include "nsStubMutationObserver.h"
+#include "nsIDOMEventListener.h"
+
+class Area;
+class nsImageFrame;
+class nsIFrame;
+class nsIContent;
+struct nsRect;
+
+class nsImageMap final : public nsStubMutationObserver,
+ public nsIDOMEventListener
+{
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::ColorPattern ColorPattern;
+ typedef mozilla::gfx::StrokeOptions StrokeOptions;
+
+public:
+ nsImageMap();
+
+ nsresult Init(nsImageFrame* aImageFrame, nsIContent* aMap);
+
+ /**
+ * Return the first area element (in content order) for the given aX,aY pixel
+ * coordinate or nullptr if the coordinate is outside all areas.
+ */
+ nsIContent* GetArea(nscoord aX, nscoord aY) const;
+
+ /**
+ * Return area elements count associated with the image map.
+ */
+ uint32_t AreaCount() const { return mAreas.Length(); }
+
+ /**
+ * Return area element at the given index.
+ */
+ nsIContent* GetAreaAt(uint32_t aIndex) const;
+
+ void Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget,
+ const ColorPattern& aColor,
+ const StrokeOptions& aStrokeOptions = StrokeOptions());
+
+ /**
+ * Called just before the nsImageFrame releases us.
+ * Used to break the cycle caused by the DOM listener.
+ */
+ void Destroy();
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsIMutationObserver
+ NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+ NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
+
+ //nsIDOMEventListener
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ nsresult GetBoundsForAreaContent(nsIContent *aContent,
+ nsRect& aBounds);
+
+protected:
+ virtual ~nsImageMap();
+
+ void FreeAreas();
+
+ nsresult UpdateAreas();
+ nsresult SearchForAreas(nsIContent* aParent, bool& aFoundArea,
+ bool& aFoundAnchor);
+
+ nsresult AddArea(nsIContent* aArea);
+
+ void MaybeUpdateAreas(nsIContent *aContent);
+
+ nsImageFrame* mImageFrame; // the frame that owns us
+ nsCOMPtr<nsIContent> mMap;
+ AutoTArray<Area*, 8> mAreas; // almost always has some entries
+ bool mContainsBlockContents;
+};
+
+#endif /* nsImageMap_h */
diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp
new file mode 100644
index 000000000..54d93c85a
--- /dev/null
+++ b/layout/generic/nsInlineFrame.cpp
@@ -0,0 +1,1201 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS display:inline objects */
+
+#include "nsInlineFrame.h"
+#include "nsLineLayout.h"
+#include "nsBlockFrame.h"
+#include "nsPlaceholderFrame.h"
+#include "nsGkAtoms.h"
+#include "nsStyleContext.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsCSSAnonBoxes.h"
+#include "mozilla/RestyleManagerHandle.h"
+#include "mozilla/RestyleManagerHandleInlines.h"
+#include "nsDisplayList.h"
+#include "mozilla/Likely.h"
+#include "SVGTextFrame.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+
+#ifdef DEBUG
+#undef NOISY_PUSHING
+#endif
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+
+//////////////////////////////////////////////////////////////////////
+
+// Basic nsInlineFrame methods
+
+nsInlineFrame*
+NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsInlineFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsInlineFrame)
+
+NS_QUERYFRAME_HEAD(nsInlineFrame)
+ NS_QUERYFRAME_ENTRY(nsInlineFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsInlineFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Inline"), aResult);
+}
+#endif
+
+nsIAtom*
+nsInlineFrame::GetType() const
+{
+ return nsGkAtoms::inlineFrame;
+}
+
+void
+nsInlineFrame::InvalidateFrame(uint32_t aDisplayItemKey)
+{
+ if (IsSVGText()) {
+ nsIFrame* svgTextFrame =
+ nsLayoutUtils::GetClosestFrameOfType(GetParent(),
+ nsGkAtoms::svgTextFrame);
+ svgTextFrame->InvalidateFrame();
+ return;
+ }
+ nsContainerFrame::InvalidateFrame(aDisplayItemKey);
+}
+
+void
+nsInlineFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
+{
+ if (IsSVGText()) {
+ nsIFrame* svgTextFrame =
+ nsLayoutUtils::GetClosestFrameOfType(GetParent(),
+ nsGkAtoms::svgTextFrame);
+ svgTextFrame->InvalidateFrame();
+ return;
+ }
+ nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
+}
+
+static inline bool
+IsMarginZero(const nsStyleCoord &aCoord)
+{
+ return aCoord.GetUnit() == eStyleUnit_Auto ||
+ nsLayoutUtils::IsMarginZero(aCoord);
+}
+
+/* virtual */ bool
+nsInlineFrame::IsSelfEmpty()
+{
+#if 0
+ // I used to think inline frames worked this way, but it seems they
+ // don't. At least not in our codebase.
+ if (GetPresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
+ return false;
+ }
+#endif
+ const nsStyleMargin* margin = StyleMargin();
+ const nsStyleBorder* border = StyleBorder();
+ const nsStylePadding* padding = StylePadding();
+ // Block-start and -end ignored, since they shouldn't affect things, but this
+ // doesn't really match with nsLineLayout.cpp's setting of
+ // ZeroEffectiveSpanBox, anymore, so what should this really be?
+ WritingMode wm = GetWritingMode();
+ bool haveStart, haveEnd;
+ // Initially set up haveStart and haveEnd in terms of visual (LTR/TTB)
+ // coordinates; we'll exchange them later if bidi-RTL is in effect to
+ // get logical start and end flags.
+ if (wm.IsVertical()) {
+ haveStart =
+ border->GetComputedBorderWidth(NS_SIDE_TOP) != 0 ||
+ !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) ||
+ !IsMarginZero(margin->mMargin.GetTop());
+ haveEnd =
+ border->GetComputedBorderWidth(NS_SIDE_BOTTOM) != 0 ||
+ !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom()) ||
+ !IsMarginZero(margin->mMargin.GetBottom());
+ } else {
+ haveStart =
+ border->GetComputedBorderWidth(NS_SIDE_LEFT) != 0 ||
+ !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetLeft()) ||
+ !IsMarginZero(margin->mMargin.GetLeft());
+ haveEnd =
+ border->GetComputedBorderWidth(NS_SIDE_RIGHT) != 0 ||
+ !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetRight()) ||
+ !IsMarginZero(margin->mMargin.GetRight());
+ }
+ if (haveStart || haveEnd) {
+ // We skip this block and return false for box-decoration-break:clone since
+ // in that case all the continuations will have the border/padding/margin.
+ if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
+ StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice) {
+ // When direction=rtl, we need to consider logical rather than visual
+ // start and end, so swap the flags.
+ if (!wm.IsBidiLTR()) {
+ Swap(haveStart, haveEnd);
+ }
+ // For ib-split frames, ignore things we know we'll skip in GetSkipSides.
+ // XXXbz should we be doing this for non-ib-split frames too, in a more
+ // general way?
+
+ // Get the first continuation eagerly, as a performance optimization, to
+ // avoid having to get it twice..
+ nsIFrame* firstCont = FirstContinuation();
+ return
+ (!haveStart || firstCont->FrameIsNonFirstInIBSplit()) &&
+ (!haveEnd || firstCont->FrameIsNonLastInIBSplit());
+ }
+ return false;
+ }
+ return true;
+}
+
+bool
+nsInlineFrame::IsEmpty()
+{
+ if (!IsSelfEmpty()) {
+ return false;
+ }
+
+ for (nsIFrame* kid : mFrames) {
+ if (!kid->IsEmpty())
+ return false;
+ }
+
+ return true;
+}
+
+nsIFrame::FrameSearchResult
+nsInlineFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters)
+{
+ // Override the implementation in nsFrame, to skip empty inline frames
+ NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
+ int32_t startOffset = *aOffset;
+ if (startOffset < 0)
+ startOffset = 1;
+ if (aForward == (startOffset == 0)) {
+ // We're before the frame and moving forward, or after it and moving backwards:
+ // skip to the other side, but keep going.
+ *aOffset = 1 - startOffset;
+ }
+ return CONTINUE;
+}
+
+void
+nsInlineFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ nsFrameList* overflowFrames = GetOverflowFrames();
+ if (overflowFrames) {
+ // Fixup the parent pointers for any child frames on the OverflowList.
+ // nsIFrame::DestroyFrom depends on that to find the sticky scroll
+ // container (an ancestor).
+ nsIFrame* lineContainer = nsLayoutUtils::FindNearestBlockAncestor(this);
+ DrainSelfOverflowListInternal(eForDestroy, lineContainer);
+ }
+ nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+nsresult
+nsInlineFrame::StealFrame(nsIFrame* aChild)
+{
+ if (MaybeStealOverflowContainerFrame(aChild)) {
+ return NS_OK;
+ }
+
+ nsInlineFrame* parent = this;
+ bool removed = false;
+ do {
+ removed = parent->mFrames.StartRemoveFrame(aChild);
+ if (removed) {
+ break;
+ }
+
+ // We didn't find the child in our principal child list.
+ // Maybe it's on the overflow list?
+ nsFrameList* frameList = parent->GetOverflowFrames();
+ if (frameList) {
+ removed = frameList->ContinueRemoveFrame(aChild);
+ if (frameList->IsEmpty()) {
+ parent->DestroyOverflowList();
+ }
+ if (removed) {
+ break;
+ }
+ }
+
+ // Due to our "lazy reparenting" optimization 'aChild' might not actually
+ // be on any of our child lists, but instead in one of our next-in-flows.
+ parent = static_cast<nsInlineFrame*>(parent->GetNextInFlow());
+ } while (parent);
+
+ MOZ_ASSERT(removed, "nsInlineFrame::StealFrame: can't find aChild");
+ return removed ? NS_OK : NS_ERROR_UNEXPECTED;
+}
+
+void
+nsInlineFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
+
+ // The sole purpose of this is to trigger display of the selection
+ // window for Named Anchors, which don't have any children and
+ // normally don't have any size, but in Editor we use CSS to display
+ // an image to represent this "hidden" element.
+ if (!mFrames.FirstChild()) {
+ DisplaySelectionOverlay(aBuilder, aLists.Content());
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+// Reflow methods
+
+/* virtual */ void
+nsInlineFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData)
+{
+ DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::MIN_ISIZE);
+}
+
+/* virtual */ void
+nsInlineFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlinePrefISizeData *aData)
+{
+ DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::PREF_ISIZE);
+}
+
+/* virtual */
+LogicalSize
+nsInlineFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ // Inlines and text don't compute size before reflow.
+ return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+}
+
+nsRect
+nsInlineFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const
+{
+ // be conservative
+ if (StyleContext()->HasTextDecorationLines()) {
+ return GetVisualOverflowRect();
+ }
+ return ComputeSimpleTightBounds(aDrawTarget);
+}
+
+void
+nsInlineFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
+ nsIFrame* aFrame,
+ bool aReparentSiblings)
+{
+ // XXXbz this would be better if it took a nsFrameList or a frame
+ // list slice....
+ NS_ASSERTION(aOurLineContainer->GetNextContinuation() ||
+ aOurLineContainer->GetPrevContinuation(),
+ "Don't call this when we have no continuation, it's a waste");
+ if (!aFrame) {
+ NS_ASSERTION(aReparentSiblings, "Why did we get called?");
+ return;
+ }
+
+ nsBlockFrame* frameBlock = nsLayoutUtils::GetFloatContainingBlock(aFrame);
+ if (!frameBlock || frameBlock == aOurLineContainer) {
+ return;
+ }
+
+ nsBlockFrame* ourBlock = nsLayoutUtils::GetAsBlock(aOurLineContainer);
+ NS_ASSERTION(ourBlock, "Not a block, but broke vertically?");
+
+ while (true) {
+ ourBlock->ReparentFloats(aFrame, frameBlock, false);
+
+ if (!aReparentSiblings)
+ return;
+ nsIFrame* next = aFrame->GetNextSibling();
+ if (!next)
+ return;
+ if (next->GetParent() == aFrame->GetParent()) {
+ aFrame = next;
+ continue;
+ }
+ // This is paranoid and will hardly ever get hit ... but we can't actually
+ // trust that the frames in the sibling chain all have the same parent,
+ // because lazy reparenting may be going on. If we find a different
+ // parent we need to redo our analysis.
+ ReparentFloatsForInlineChild(aOurLineContainer, next, aReparentSiblings);
+ return;
+ }
+}
+
+static void
+ReparentChildListStyle(nsPresContext* aPresContext,
+ const nsFrameList::Slice& aFrames,
+ nsIFrame* aParentFrame)
+{
+ RestyleManagerHandle restyleManager = aPresContext->RestyleManager();
+
+ for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
+ NS_ASSERTION(e.get()->GetParent() == aParentFrame, "Bogus parentage");
+ restyleManager->ReparentStyleContext(e.get());
+ nsLayoutUtils::MarkDescendantsDirty(e.get());
+ }
+}
+
+void
+nsInlineFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsInlineFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+ if (nullptr == aReflowInput.mLineLayout) {
+ NS_ERROR("must have non-null aReflowInput.mLineLayout");
+ return;
+ }
+ if (IsFrameTreeTooDeep(aReflowInput, aMetrics, aStatus)) {
+ return;
+ }
+
+ bool lazilySetParentPointer = false;
+
+ nsIFrame* lineContainer = aReflowInput.mLineLayout->LineContainerFrame();
+
+ // Check for an overflow list with our prev-in-flow
+ nsInlineFrame* prevInFlow = (nsInlineFrame*)GetPrevInFlow();
+ if (prevInFlow) {
+ AutoFrameListPtr prevOverflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
+ if (prevOverflowFrames) {
+ // When pushing and pulling frames we need to check for whether any
+ // views need to be reparented.
+ nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
+ this);
+
+ // Check if we should do the lazilySetParentPointer optimization.
+ // Only do it in simple cases where we're being reflowed for the
+ // first time, nothing (e.g. bidi resolution) has already given
+ // us children, and there's no next-in-flow, so all our frames
+ // will be taken from prevOverflowFrames.
+ if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && mFrames.IsEmpty() &&
+ !GetNextInFlow()) {
+ // If our child list is empty, just put the new frames into it.
+ // Note that we don't set the parent pointer for the new frames. Instead wait
+ // to do this until we actually reflow the frame. If the overflow list contains
+ // thousands of frames this is a big performance issue (see bug #5588)
+ mFrames.SetFrames(*prevOverflowFrames);
+ lazilySetParentPointer = true;
+ } else {
+ // Assign all floats to our block if necessary
+ if (lineContainer && lineContainer->GetPrevContinuation()) {
+ ReparentFloatsForInlineChild(lineContainer,
+ prevOverflowFrames->FirstChild(),
+ true);
+ }
+ // Insert the new frames at the beginning of the child list
+ // and set their parent pointer
+ const nsFrameList::Slice& newFrames =
+ mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
+ // If our prev in flow was under the first continuation of a first-line
+ // frame then we need to reparent the style contexts to remove the
+ // the special first-line styling. In the lazilySetParentPointer case
+ // we reparent the style contexts when we set their parents in
+ // nsInlineFrame::ReflowFrames and nsInlineFrame::ReflowInlineFrame.
+ if (aReflowInput.mLineLayout->GetInFirstLine()) {
+ ReparentChildListStyle(aPresContext, newFrames, this);
+ }
+ }
+ }
+ }
+
+ // It's also possible that we have an overflow list for ourselves
+#ifdef DEBUG
+ if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+ // If it's our initial reflow, then we should not have an overflow list.
+ // However, add an assertion in case we get reflowed more than once with
+ // the initial reflow reason
+ nsFrameList* overflowFrames = GetOverflowFrames();
+ NS_ASSERTION(!overflowFrames || overflowFrames->IsEmpty(),
+ "overflow list is not empty for initial reflow");
+ }
+#endif
+ if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+ DrainFlags flags =
+ lazilySetParentPointer ? eDontReparentFrames : DrainFlags(0);
+ if (aReflowInput.mLineLayout->GetInFirstLine()) {
+ flags = DrainFlags(flags | eInFirstLine);
+ }
+ DrainSelfOverflowListInternal(flags, lineContainer);
+ }
+
+ // Set our own reflow state (additional state above and beyond
+ // aReflowInput)
+ InlineReflowInput irs;
+ irs.mPrevFrame = nullptr;
+ irs.mLineContainer = lineContainer;
+ irs.mLineLayout = aReflowInput.mLineLayout;
+ irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();
+ irs.mSetParentPointer = lazilySetParentPointer;
+
+ if (mFrames.IsEmpty()) {
+ // Try to pull over one frame before starting so that we know
+ // whether we have an anonymous block or not.
+ bool complete;
+ (void) PullOneFrame(aPresContext, irs, &complete);
+ }
+
+ ReflowFrames(aPresContext, aReflowInput, irs, aMetrics, aStatus);
+
+ ReflowAbsoluteFrames(aPresContext, aMetrics, aReflowInput, aStatus);
+
+ // Note: the line layout code will properly compute our
+ // overflow-rect state for us.
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+nsresult
+nsInlineFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ nsresult rv =
+ nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (IsSVGText()) {
+ SVGTextFrame* f = static_cast<SVGTextFrame*>(
+ nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame));
+ f->HandleAttributeChangeInDescendant(mContent->AsElement(),
+ aNameSpaceID, aAttribute);
+ }
+
+ return NS_OK;
+}
+
+bool
+nsInlineFrame::DrainSelfOverflowListInternal(DrainFlags aFlags,
+ nsIFrame* aLineContainer)
+{
+ AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
+ if (overflowFrames) {
+ // The frames on our own overflowlist may have been pushed by a
+ // previous lazilySetParentPointer Reflow so we need to ensure the
+ // correct parent pointer. This is sometimes skipped by Reflow.
+ if (!(aFlags & eDontReparentFrames)) {
+ nsIFrame* firstChild = overflowFrames->FirstChild();
+ if (aLineContainer && aLineContainer->GetPrevContinuation()) {
+ ReparentFloatsForInlineChild(aLineContainer, firstChild, true);
+ }
+ const bool doReparentSC =
+ (aFlags & eInFirstLine) && !(aFlags & eForDestroy);
+ RestyleManagerHandle restyleManager = PresContext()->RestyleManager();
+ for (nsIFrame* f = firstChild; f; f = f->GetNextSibling()) {
+ f->SetParent(this);
+ if (doReparentSC) {
+ restyleManager->ReparentStyleContext(f);
+ nsLayoutUtils::MarkDescendantsDirty(f);
+ }
+ }
+ }
+ bool result = !overflowFrames->IsEmpty();
+ mFrames.AppendFrames(nullptr, *overflowFrames);
+ return result;
+ }
+ return false;
+}
+
+/* virtual */ bool
+nsInlineFrame::DrainSelfOverflowList()
+{
+ nsIFrame* lineContainer = nsLayoutUtils::FindNearestBlockAncestor(this);
+ // Add the eInFirstLine flag if we have a ::first-line ancestor frame.
+ // No need to look further than the nearest line container though.
+ DrainFlags flags = DrainFlags(0);
+ for (nsIFrame* p = GetParent(); p != lineContainer; p = p->GetParent()) {
+ if (p->GetType() == nsGkAtoms::lineFrame) {
+ flags = DrainFlags(flags | eInFirstLine);
+ break;
+ }
+ }
+ return DrainSelfOverflowListInternal(flags, lineContainer);
+}
+
+/* virtual */ bool
+nsInlineFrame::CanContinueTextRun() const
+{
+ // We can continue a text run through an inline frame
+ return true;
+}
+
+/* virtual */ void
+nsInlineFrame::PullOverflowsFromPrevInFlow()
+{
+ nsInlineFrame* prevInFlow = static_cast<nsInlineFrame*>(GetPrevInFlow());
+ if (prevInFlow) {
+ nsPresContext* presContext = PresContext();
+ AutoFrameListPtr prevOverflowFrames(presContext,
+ prevInFlow->StealOverflowFrames());
+ if (prevOverflowFrames) {
+ // Assume that our prev-in-flow has the same line container that we do.
+ nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
+ this);
+ mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
+ }
+ }
+}
+
+void
+nsInlineFrame::ReflowFrames(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ InlineReflowInput& irs,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus)
+{
+ aStatus = NS_FRAME_COMPLETE;
+
+ nsLineLayout* lineLayout = aReflowInput.mLineLayout;
+ bool inFirstLine = aReflowInput.mLineLayout->GetInFirstLine();
+ RestyleManagerHandle restyleManager = aPresContext->RestyleManager();
+ WritingMode frameWM = aReflowInput.GetWritingMode();
+ WritingMode lineWM = aReflowInput.mLineLayout->mRootSpan->mWritingMode;
+ LogicalMargin framePadding = aReflowInput.ComputedLogicalBorderPadding();
+ nscoord startEdge = 0;
+ const bool boxDecorationBreakClone =
+ MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone);
+ // Don't offset by our start borderpadding if we have a prev continuation or
+ // if we're in a part of an {ib} split other than the first one. For
+ // box-decoration-break:clone we always offset our start since all
+ // continuations have border/padding.
+ if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
+ boxDecorationBreakClone) {
+ startEdge = framePadding.IStart(frameWM);
+ }
+ nscoord availableISize = aReflowInput.AvailableISize();
+ NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
+ "should no longer use available widths");
+ // Subtract off inline axis border+padding from availableISize
+ availableISize -= startEdge;
+ availableISize -= framePadding.IEnd(frameWM);
+ lineLayout->BeginSpan(this, &aReflowInput, startEdge,
+ startEdge + availableISize, &mBaseline);
+
+ // First reflow our principal children.
+ nsIFrame* frame = mFrames.FirstChild();
+ bool done = false;
+ while (frame) {
+ // Check if we should lazily set the child frame's parent pointer.
+ if (irs.mSetParentPointer) {
+ bool havePrevBlock =
+ irs.mLineContainer && irs.mLineContainer->GetPrevContinuation();
+ nsIFrame* child = frame;
+ do {
+ // If our block is the first in flow, then any floats under the pulled
+ // frame must already belong to our block.
+ if (havePrevBlock) {
+ // This has to happen before we update frame's parent; we need to
+ // know frame's ancestry under its old block.
+ // The blockChildren.ContainsFrame check performed by
+ // ReparentFloatsForInlineChild here may be slow, but we can't
+ // easily avoid it because we don't know where 'frame' originally
+ // came from. If we really really have to optimize this we could
+ // cache whether frame->GetParent() is under its containing blocks
+ // overflowList or not.
+ ReparentFloatsForInlineChild(irs.mLineContainer, child, false);
+ }
+ child->SetParent(this);
+ if (inFirstLine) {
+ restyleManager->ReparentStyleContext(child);
+ nsLayoutUtils::MarkDescendantsDirty(child);
+ }
+ // We also need to do the same for |frame|'s next-in-flows that are in
+ // the sibling list. Otherwise, if we reflow |frame| and it's complete
+ // we'll crash when trying to delete its next-in-flow.
+ // This scenario doesn't happen often, but it can happen.
+ nsIFrame* nextSibling = child->GetNextSibling();
+ child = child->GetNextInFlow();
+ if (MOZ_UNLIKELY(child)) {
+ while (child != nextSibling && nextSibling) {
+ nextSibling = nextSibling->GetNextSibling();
+ }
+ if (!nextSibling) {
+ child = nullptr;
+ }
+ }
+ MOZ_ASSERT(!child || mFrames.ContainsFrame(child));
+ } while (child);
+
+ // Fix the parent pointer for ::first-letter child frame next-in-flows,
+ // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042).
+ nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame);
+ if (realFrame->GetType() == nsGkAtoms::letterFrame) {
+ nsIFrame* child = realFrame->PrincipalChildList().FirstChild();
+ if (child) {
+ NS_ASSERTION(child->GetType() == nsGkAtoms::textFrame,
+ "unexpected frame type");
+ nsIFrame* nextInFlow = child->GetNextInFlow();
+ for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) {
+ NS_ASSERTION(nextInFlow->GetType() == nsGkAtoms::textFrame,
+ "unexpected frame type");
+ if (mFrames.ContainsFrame(nextInFlow)) {
+ nextInFlow->SetParent(this);
+ if (inFirstLine) {
+ restyleManager->ReparentStyleContext(nextInFlow);
+ nsLayoutUtils::MarkDescendantsDirty(nextInFlow);
+ }
+ }
+ else {
+#ifdef DEBUG
+ // Once we find a next-in-flow that isn't ours none of the
+ // remaining next-in-flows should be either.
+ for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) {
+ NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow),
+ "unexpected letter frame flow");
+ }
+#endif
+ break;
+ }
+ }
+ }
+ }
+ }
+ MOZ_ASSERT(frame->GetParent() == this);
+
+ if (!done) {
+ bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
+ ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus);
+ done = NS_INLINE_IS_BREAK(aStatus) ||
+ (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus));
+ if (done) {
+ if (!irs.mSetParentPointer) {
+ break;
+ }
+ // Keep reparenting the remaining siblings, but don't reflow them.
+ nsFrameList* pushedFrames = GetOverflowFrames();
+ if (pushedFrames && pushedFrames->FirstChild() == frame) {
+ // Don't bother if |frame| was pushed to our overflow list.
+ break;
+ }
+ } else {
+ irs.mPrevFrame = frame;
+ }
+ }
+ frame = frame->GetNextSibling();
+ }
+
+ // Attempt to pull frames from our next-in-flow until we can't
+ if (!done && GetNextInFlow()) {
+ while (true) {
+ bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
+ bool isComplete;
+ if (!frame) { // Could be non-null if we pulled a first-letter frame and
+ // it created a continuation, since we don't push those.
+ frame = PullOneFrame(aPresContext, irs, &isComplete);
+ }
+#ifdef NOISY_PUSHING
+ printf("%p pulled up %p\n", this, frame);
+#endif
+ if (nullptr == frame) {
+ if (!isComplete) {
+ aStatus = NS_FRAME_NOT_COMPLETE;
+ }
+ break;
+ }
+ ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus);
+ if (NS_INLINE_IS_BREAK(aStatus) ||
+ (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) {
+ break;
+ }
+ irs.mPrevFrame = frame;
+ frame = frame->GetNextSibling();
+ }
+ }
+
+ NS_ASSERTION(!NS_FRAME_IS_COMPLETE(aStatus) || !GetOverflowFrames(),
+ "We can't be complete AND have overflow frames!");
+
+ // If after reflowing our children they take up no area then make
+ // sure that we don't either.
+ //
+ // Note: CSS demands that empty inline elements still affect the
+ // line-height calculations. However, continuations of an inline
+ // that are empty we force to empty so that things like collapsed
+ // whitespace in an inline element don't affect the line-height.
+ aMetrics.ISize(lineWM) = lineLayout->EndSpan(this);
+
+ // Compute final width.
+
+ // XXX Note that that the padding start and end are in the frame's
+ // writing mode, but the metrics' inline-size is in the line's
+ // writing mode. This makes sense if the line and frame are both
+ // vertical or both horizontal, but what should happen with
+ // orthogonal inlines?
+
+ // Make sure to not include our start border and padding if we have a prev
+ // continuation or if we're in a part of an {ib} split other than the first
+ // one. For box-decoration-break:clone we always include our start border
+ // and padding since all continuations have them.
+ if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
+ boxDecorationBreakClone) {
+ aMetrics.ISize(lineWM) += framePadding.IStart(frameWM);
+ }
+
+ /*
+ * We want to only apply the end border and padding if we're the last
+ * continuation and either not in an {ib} split or the last part of it. To
+ * be the last continuation we have to be complete (so that we won't get a
+ * next-in-flow) and have no non-fluid continuations on our continuation
+ * chain. For box-decoration-break:clone we always apply the end border and
+ * padding since all continuations have them.
+ */
+ if ((NS_FRAME_IS_COMPLETE(aStatus) &&
+ !LastInFlow()->GetNextContinuation() &&
+ !FrameIsNonLastInIBSplit()) ||
+ boxDecorationBreakClone) {
+ aMetrics.ISize(lineWM) += framePadding.IEnd(frameWM);
+ }
+
+ nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics,
+ framePadding, lineWM, frameWM);
+
+ // For now our overflow area is zero. The real value will be
+ // computed in |nsLineLayout::RelativePositionFrames|.
+ aMetrics.mOverflowAreas.Clear();
+
+#ifdef NOISY_FINAL_SIZE
+ ListTag(stdout);
+ printf(": metrics=%d,%d ascent=%d\n",
+ aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent());
+#endif
+}
+
+void
+nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ InlineReflowInput& irs,
+ nsIFrame* aFrame,
+ nsReflowStatus& aStatus)
+{
+ nsLineLayout* lineLayout = aReflowInput.mLineLayout;
+ bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
+ bool pushedFrame;
+ lineLayout->ReflowFrame(aFrame, aStatus, nullptr, pushedFrame);
+
+ if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) {
+ if (aFrame != mFrames.FirstChild()) {
+ // Change break-before status into break-after since we have
+ // already placed at least one child frame. This preserves the
+ // break-type so that it can be propagated upward.
+ aStatus = NS_FRAME_NOT_COMPLETE |
+ NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
+ (aStatus & NS_INLINE_BREAK_TYPE_MASK);
+ PushFrames(aPresContext, aFrame, irs.mPrevFrame, irs);
+ }
+ else {
+ // Preserve reflow status when breaking-before our first child
+ // and propagate it upward without modification.
+ }
+ return;
+ }
+
+ // Create a next-in-flow if needed.
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
+ CreateNextInFlow(aFrame);
+ }
+
+ if (NS_INLINE_IS_BREAK_AFTER(aStatus)) {
+ nsIFrame* nextFrame = aFrame->GetNextSibling();
+ if (nextFrame) {
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ PushFrames(aPresContext, nextFrame, aFrame, irs);
+ }
+ else {
+ // We must return an incomplete status if there are more child
+ // frames remaining in a next-in-flow that follows this frame.
+ nsInlineFrame* nextInFlow = static_cast<nsInlineFrame*>(GetNextInFlow());
+ while (nextInFlow) {
+ if (nextInFlow->mFrames.NotEmpty()) {
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ break;
+ }
+ nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
+ }
+ }
+ return;
+ }
+
+ if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !reflowingFirstLetter) {
+ nsIFrame* nextFrame = aFrame->GetNextSibling();
+ if (nextFrame) {
+ PushFrames(aPresContext, nextFrame, aFrame, irs);
+ }
+ }
+}
+
+nsIFrame*
+nsInlineFrame::PullOneFrame(nsPresContext* aPresContext,
+ InlineReflowInput& irs,
+ bool* aIsComplete)
+{
+ bool isComplete = true;
+
+ nsIFrame* frame = nullptr;
+ nsInlineFrame* nextInFlow = irs.mNextInFlow;
+ while (nextInFlow) {
+ frame = nextInFlow->mFrames.FirstChild();
+ if (!frame) {
+ // The nextInFlow's principal list has no frames, try its overflow list.
+ nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
+ if (overflowFrames) {
+ frame = overflowFrames->RemoveFirstChild();
+ if (overflowFrames->IsEmpty()) {
+ // We're stealing the only frame - delete the overflow list.
+ nextInFlow->DestroyOverflowList();
+ } else {
+ // We leave the remaining frames on the overflow list (rather than
+ // putting them on nextInFlow's principal list) so we don't have to
+ // set up the parent for them.
+ }
+ // ReparentFloatsForInlineChild needs it to be on a child list -
+ // we remove it again below.
+ nextInFlow->mFrames.SetFrames(frame);
+ }
+ }
+
+ if (frame) {
+ // If our block has no next continuation, then any floats belonging to
+ // the pulled frame must belong to our block already. This check ensures
+ // we do no extra work in the common non-vertical-breaking case.
+ if (irs.mLineContainer && irs.mLineContainer->GetNextContinuation()) {
+ // The blockChildren.ContainsFrame check performed by
+ // ReparentFloatsForInlineChild will be fast because frame's ancestor
+ // will be the first child of its containing block.
+ ReparentFloatsForInlineChild(irs.mLineContainer, frame, false);
+ }
+ nextInFlow->mFrames.RemoveFirstChild();
+ // nsFirstLineFrame::PullOneFrame calls ReparentStyleContext.
+
+ mFrames.InsertFrame(this, irs.mPrevFrame, frame);
+ isComplete = false;
+ if (irs.mLineLayout) {
+ irs.mLineLayout->SetDirtyNextLine();
+ }
+ nsContainerFrame::ReparentFrameView(frame, nextInFlow, this);
+ break;
+ }
+ nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
+ irs.mNextInFlow = nextInFlow;
+ }
+
+ *aIsComplete = isComplete;
+ return frame;
+}
+
+void
+nsInlineFrame::PushFrames(nsPresContext* aPresContext,
+ nsIFrame* aFromChild,
+ nsIFrame* aPrevSibling,
+ InlineReflowInput& aState)
+{
+ NS_PRECONDITION(aFromChild, "null pointer");
+ NS_PRECONDITION(aPrevSibling, "pushing first child");
+ NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
+
+#ifdef NOISY_PUSHING
+ printf("%p pushing aFromChild %p, disconnecting from prev sib %p\n",
+ this, aFromChild, aPrevSibling);
+#endif
+
+ // Add the frames to our overflow list (let our next in flow drain
+ // our overflow list when it is ready)
+ SetOverflowFrames(mFrames.RemoveFramesAfter(aPrevSibling));
+ if (aState.mLineLayout) {
+ aState.mLineLayout->SetDirtyNextLine();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+nsIFrame::LogicalSides
+nsInlineFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
+{
+ if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone)) {
+ return LogicalSides();
+ }
+
+ LogicalSides skip;
+ if (!IsFirst()) {
+ nsInlineFrame* prev = (nsInlineFrame*) GetPrevContinuation();
+ if ((GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET) ||
+ (prev && (prev->mRect.height || prev->mRect.width))) {
+ // Prev continuation is not empty therefore we don't render our start
+ // border edge.
+ skip |= eLogicalSideBitsIStart;
+ }
+ else {
+ // If the prev continuation is empty, then go ahead and let our start
+ // edge border render.
+ }
+ }
+ if (!IsLast()) {
+ nsInlineFrame* next = (nsInlineFrame*) GetNextContinuation();
+ if ((GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET) ||
+ (next && (next->mRect.height || next->mRect.width))) {
+ // Next continuation is not empty therefore we don't render our end
+ // border edge.
+ skip |= eLogicalSideBitsIEnd;
+ }
+ else {
+ // If the next continuation is empty, then go ahead and let our end
+ // edge border render.
+ }
+ }
+
+ if (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
+ // All but the last part of an {ib} split should skip the "end" side (as
+ // determined by this frame's direction) and all but the first part of such
+ // a split should skip the "start" side. But figuring out which part of
+ // the split we are involves getting our first continuation, which might be
+ // expensive. So don't bother if we already have the relevant bits set.
+ if (skip != LogicalSides(eLogicalSideBitsIBoth)) {
+ // We're missing one of the skip bits, so check whether we need to set it.
+ // Only get the first continuation once, as an optimization.
+ nsIFrame* firstContinuation = FirstContinuation();
+ if (firstContinuation->FrameIsNonLastInIBSplit()) {
+ skip |= eLogicalSideBitsIEnd;
+ }
+ if (firstContinuation->FrameIsNonFirstInIBSplit()) {
+ skip |= eLogicalSideBitsIStart;
+ }
+ }
+ }
+
+ return skip;
+}
+
+nscoord
+nsInlineFrame::GetLogicalBaseline(mozilla::WritingMode aWritingMode) const
+{
+ return mBaseline;
+}
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsInlineFrame::AccessibleType()
+{
+ // Broken image accessibles are created here, because layout
+ // replaces the image or image control frame with an inline frame
+ if (mContent->IsHTMLElement(nsGkAtoms::input)) // Broken <input type=image ... />
+ return a11y::eHTMLButtonType;
+ if (mContent->IsHTMLElement(nsGkAtoms::img)) // Create accessible for broken <img>
+ return a11y::eHyperTextType;
+
+ return a11y::eNoType;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+// nsLineFrame implementation
+
+nsFirstLineFrame*
+NS_NewFirstLineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsFirstLineFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsFirstLineFrame)
+
+void
+nsFirstLineFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsInlineFrame::Init(aContent, aParent, aPrevInFlow);
+ if (!aPrevInFlow) {
+ MOZ_ASSERT(StyleContext()->GetPseudo() == nsCSSPseudoElements::firstLine);
+ return;
+ }
+
+ // This frame is a continuation - fixup the style context if aPrevInFlow
+ // is the first-in-flow (the only one with a ::first-line pseudo).
+ if (aPrevInFlow->StyleContext()->GetPseudo() == nsCSSPseudoElements::firstLine) {
+ MOZ_ASSERT(FirstInFlow() == aPrevInFlow);
+ // Create a new style context that is a child of the parent
+ // style context thus removing the ::first-line style. This way
+ // we behave as if an anonymous (unstyled) span was the child
+ // of the parent frame.
+ nsStyleContext* parentContext = aParent->StyleContext();
+ RefPtr<nsStyleContext> newSC = PresContext()->StyleSet()->
+ ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozLineFrame, parentContext);
+ SetStyleContext(newSC);
+ } else {
+ MOZ_ASSERT(FirstInFlow() != aPrevInFlow);
+ MOZ_ASSERT(aPrevInFlow->StyleContext()->GetPseudo() ==
+ nsCSSAnonBoxes::mozLineFrame);
+ }
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsFirstLineFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Line"), aResult);
+}
+#endif
+
+nsIAtom*
+nsFirstLineFrame::GetType() const
+{
+ return nsGkAtoms::lineFrame;
+}
+
+nsIFrame*
+nsFirstLineFrame::PullOneFrame(nsPresContext* aPresContext, InlineReflowInput& irs,
+ bool* aIsComplete)
+{
+ nsIFrame* frame = nsInlineFrame::PullOneFrame(aPresContext, irs, aIsComplete);
+ if (frame && !GetPrevInFlow()) {
+ // We are a first-line frame. Fixup the child frames
+ // style-context that we just pulled.
+ NS_ASSERTION(frame->GetParent() == this, "Incorrect parent?");
+ aPresContext->RestyleManager()->ReparentStyleContext(frame);
+ nsLayoutUtils::MarkDescendantsDirty(frame);
+ }
+ return frame;
+}
+
+void
+nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ if (nullptr == aReflowInput.mLineLayout) {
+ return; // XXX does this happen? why?
+ }
+
+ nsIFrame* lineContainer = aReflowInput.mLineLayout->LineContainerFrame();
+
+ // Check for an overflow list with our prev-in-flow
+ nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
+ if (prevInFlow) {
+ AutoFrameListPtr prevOverflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
+ if (prevOverflowFrames) {
+ // Assign all floats to our block if necessary
+ if (lineContainer && lineContainer->GetPrevContinuation()) {
+ ReparentFloatsForInlineChild(lineContainer,
+ prevOverflowFrames->FirstChild(),
+ true);
+ }
+ const nsFrameList::Slice& newFrames =
+ mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
+ ReparentChildListStyle(aPresContext, newFrames, this);
+ }
+ }
+
+ // It's also possible that we have an overflow list for ourselves.
+ DrainSelfOverflowList();
+
+ // Set our own reflow state (additional state above and beyond
+ // aReflowInput)
+ InlineReflowInput irs;
+ irs.mPrevFrame = nullptr;
+ irs.mLineContainer = lineContainer;
+ irs.mLineLayout = aReflowInput.mLineLayout;
+ irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();
+
+ bool wasEmpty = mFrames.IsEmpty();
+ if (wasEmpty) {
+ // Try to pull over one frame before starting so that we know
+ // whether we have an anonymous block or not.
+ bool complete;
+ PullOneFrame(aPresContext, irs, &complete);
+ }
+
+ if (nullptr == GetPrevInFlow()) {
+ // XXX This is pretty sick, but what we do here is to pull-up, in
+ // advance, all of the next-in-flows children. We re-resolve their
+ // style while we are at at it so that when we reflow they have
+ // the right style.
+ //
+ // All of this is so that text-runs reflow properly.
+ irs.mPrevFrame = mFrames.LastChild();
+ for (;;) {
+ bool complete;
+ nsIFrame* frame = PullOneFrame(aPresContext, irs, &complete);
+ if (!frame) {
+ break;
+ }
+ irs.mPrevFrame = frame;
+ }
+ irs.mPrevFrame = nullptr;
+ }
+
+ NS_ASSERTION(!aReflowInput.mLineLayout->GetInFirstLine(),
+ "Nested first-line frames? BOGUS");
+ aReflowInput.mLineLayout->SetInFirstLine(true);
+ ReflowFrames(aPresContext, aReflowInput, irs, aMetrics, aStatus);
+ aReflowInput.mLineLayout->SetInFirstLine(false);
+
+ ReflowAbsoluteFrames(aPresContext, aMetrics, aReflowInput, aStatus);
+
+ // Note: the line layout code will properly compute our overflow state for us
+}
+
+/* virtual */ void
+nsFirstLineFrame::PullOverflowsFromPrevInFlow()
+{
+ nsFirstLineFrame* prevInFlow = static_cast<nsFirstLineFrame*>(GetPrevInFlow());
+ if (prevInFlow) {
+ nsPresContext* presContext = PresContext();
+ AutoFrameListPtr prevOverflowFrames(presContext,
+ prevInFlow->StealOverflowFrames());
+ if (prevOverflowFrames) {
+ // Assume that our prev-in-flow has the same line container that we do.
+ const nsFrameList::Slice& newFrames =
+ mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
+ ReparentChildListStyle(presContext, newFrames, this);
+ }
+ }
+}
+
+/* virtual */ bool
+nsFirstLineFrame::DrainSelfOverflowList()
+{
+ AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
+ if (overflowFrames) {
+ bool result = !overflowFrames->IsEmpty();
+ const nsFrameList::Slice& newFrames =
+ mFrames.AppendFrames(nullptr, *overflowFrames);
+ ReparentChildListStyle(PresContext(), newFrames, this);
+ return result;
+ }
+ return false;
+}
diff --git a/layout/generic/nsInlineFrame.h b/layout/generic/nsInlineFrame.h
new file mode 100644
index 000000000..00f89065c
--- /dev/null
+++ b/layout/generic/nsInlineFrame.h
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS display:inline objects */
+
+#ifndef nsInlineFrame_h___
+#define nsInlineFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+
+class nsLineLayout;
+
+/**
+ * Inline frame class.
+ *
+ * This class manages a list of child frames that are inline frames. Working with
+ * nsLineLayout, the class will reflow and place inline frames on a line.
+ */
+class nsInlineFrame : public nsContainerFrame
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsInlineFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsInlineFrame* NS_NewInlineFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+ // nsIFrame overrides
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ if (aFlags & eSupportsCSSTransforms) {
+ return false;
+ }
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eBidiInlineContainer | nsIFrame::eLineParticipant));
+ }
+
+ virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
+ virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
+
+ virtual bool IsEmpty() override;
+ virtual bool IsSelfEmpty() override;
+
+ virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual nsresult StealFrame(nsIFrame* aChild) override;
+
+ // nsIHTMLReflow overrides
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+ virtual nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+ virtual bool CanContinueTextRun() const override;
+
+ virtual void PullOverflowsFromPrevInFlow() override;
+ virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+ virtual bool DrainSelfOverflowList() override;
+
+ /**
+ * Return true if the frame is first visual frame or first continuation
+ */
+ bool IsFirst() const {
+ // If the frame's bidi visual state is set, return is-first state
+ // else return true if it's the first continuation.
+ return (GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
+ ? !!(GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST)
+ : (!GetPrevInFlow());
+ }
+
+ /**
+ * Return true if the frame is last visual frame or last continuation.
+ */
+ bool IsLast() const {
+ // If the frame's bidi visual state is set, return is-last state
+ // else return true if it's the last continuation.
+ return (GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
+ ? !!(GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST)
+ : (!GetNextInFlow());
+ }
+
+protected:
+ // Additional reflow state used during our reflow methods
+ struct InlineReflowInput {
+ nsIFrame* mPrevFrame;
+ nsInlineFrame* mNextInFlow;
+ nsIFrame* mLineContainer;
+ nsLineLayout* mLineLayout;
+ bool mSetParentPointer; // when reflowing child frame first set its
+ // parent frame pointer
+
+ InlineReflowInput() {
+ mPrevFrame = nullptr;
+ mNextInFlow = nullptr;
+ mLineContainer = nullptr;
+ mLineLayout = nullptr;
+ mSetParentPointer = false;
+ }
+ };
+
+ explicit nsInlineFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+
+ virtual LogicalSides GetLogicalSkipSides(const ReflowInput* aReflowInput = nullptr) const override;
+
+ void ReflowFrames(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ InlineReflowInput& rs,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus);
+
+ void ReflowInlineFrame(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ InlineReflowInput& rs,
+ nsIFrame* aFrame,
+ nsReflowStatus& aStatus);
+
+ /**
+ * Reparent floats whose placeholders are inline descendants of aFrame from
+ * whatever block they're currently parented by to aOurBlock.
+ * @param aReparentSiblings if this is true, we follow aFrame's
+ * GetNextSibling chain reparenting them all
+ */
+ void ReparentFloatsForInlineChild(nsIFrame* aOurBlock, nsIFrame* aFrame,
+ bool aReparentSiblings);
+
+ virtual nsIFrame* PullOneFrame(nsPresContext* aPresContext,
+ InlineReflowInput& rs,
+ bool* aIsComplete);
+
+ virtual void PushFrames(nsPresContext* aPresContext,
+ nsIFrame* aFromChild,
+ nsIFrame* aPrevSibling,
+ InlineReflowInput& aState);
+
+private:
+ // Helper method for DrainSelfOverflowList() to deal with lazy parenting
+ // (which we only do for nsInlineFrame, not nsFirstLineFrame).
+ enum DrainFlags {
+ eDontReparentFrames = 1, // skip reparenting the overflow list frames
+ eInFirstLine = 2, // the request is for an inline descendant of a nsFirstLineFrame
+ eForDestroy = 4, // the request is from DestroyFrom; in this case we do the
+ // minimal work required since the frame is about to be
+ // destroyed (just fixup parent pointers)
+ };
+ /**
+ * Move any frames on our overflow list to the end of our principal list.
+ * @param aFlags one or more of the above DrainFlags
+ * @param aLineContainer the nearest line container ancestor
+ * @return true if there were any overflow frames
+ */
+ bool DrainSelfOverflowListInternal(DrainFlags aFlags,
+ nsIFrame* aLineContainer);
+protected:
+ nscoord mBaseline;
+};
+
+//----------------------------------------------------------------------
+
+/**
+ * Variation on inline-frame used to manage lines for line layout in
+ * special situations (:first-line style in particular).
+ */
+class nsFirstLineFrame final : public nsInlineFrame {
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsFirstLineFrame* NS_NewFirstLineFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+ virtual nsIAtom* GetType() const override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual void PullOverflowsFromPrevInFlow() override;
+ virtual bool DrainSelfOverflowList() override;
+
+protected:
+ explicit nsFirstLineFrame(nsStyleContext* aContext) : nsInlineFrame(aContext) {}
+
+ virtual nsIFrame* PullOneFrame(nsPresContext* aPresContext,
+ InlineReflowInput& rs,
+ bool* aIsComplete) override;
+};
+
+#endif /* nsInlineFrame_h___ */
diff --git a/layout/generic/nsIntervalSet.cpp b/layout/generic/nsIntervalSet.cpp
new file mode 100644
index 000000000..cc72a811e
--- /dev/null
+++ b/layout/generic/nsIntervalSet.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:cindent:ts=8:et:sw=4:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* a set of ranges on a number-line */
+
+#include "nsIntervalSet.h"
+#include <new>
+#include <algorithm>
+
+nsIntervalSet::nsIntervalSet(IntervalSetAlloc aAlloc, IntervalSetFree aFree,
+ void* aAllocatorClosure)
+ : mList(nullptr),
+ mAlloc(aAlloc),
+ mFree(aFree),
+ mAllocatorClosure(aAllocatorClosure)
+{
+ NS_ASSERTION(mAlloc && mFree, "null callback params");
+}
+
+nsIntervalSet::~nsIntervalSet()
+{
+ Interval *current = mList;
+ while (current) {
+ Interval *trash = current;
+ current = current->mNext;
+ FreeInterval(trash);
+ }
+}
+
+void nsIntervalSet::FreeInterval(nsIntervalSet::Interval *aInterval)
+{
+ NS_ASSERTION(aInterval, "null interval");
+
+ aInterval->Interval::~Interval();
+ (*mFree)(sizeof(Interval), aInterval, mAllocatorClosure);
+}
+
+void nsIntervalSet::IncludeInterval(coord_type aBegin, coord_type aEnd)
+{
+ Interval *newInterval = static_cast<Interval*>
+ ((*mAlloc)(sizeof(Interval), mAllocatorClosure));
+ if (!newInterval) {
+ NS_NOTREACHED("allocation failure");
+ return;
+ }
+ new(newInterval) Interval(aBegin, aEnd);
+
+ Interval **current = &mList;
+ while (*current && (*current)->mEnd < aBegin)
+ current = &(*current)->mNext;
+
+ newInterval->mNext = *current;
+ *current = newInterval;
+
+ Interval *subsumed = newInterval->mNext;
+ while (subsumed && subsumed->mBegin <= aEnd) {
+ newInterval->mBegin = std::min(newInterval->mBegin, subsumed->mBegin);
+ newInterval->mEnd = std::max(newInterval->mEnd, subsumed->mEnd);
+ newInterval->mNext = subsumed->mNext;
+ FreeInterval(subsumed);
+ subsumed = newInterval->mNext;
+ }
+}
+
+bool nsIntervalSet::Intersects(coord_type aBegin, coord_type aEnd) const
+{
+ Interval *current = mList;
+ while (current && current->mBegin <= aEnd) {
+ if (current->mEnd >= aBegin)
+ return true;
+ current = current->mNext;
+ }
+ return false;
+}
+
+bool nsIntervalSet::Contains(coord_type aBegin, coord_type aEnd) const
+{
+ Interval *current = mList;
+ while (current && current->mBegin <= aBegin) {
+ if (current->mEnd >= aEnd)
+ return true;
+ current = current->mNext;
+ }
+ return false;
+}
diff --git a/layout/generic/nsIntervalSet.h b/layout/generic/nsIntervalSet.h
new file mode 100644
index 000000000..9941df447
--- /dev/null
+++ b/layout/generic/nsIntervalSet.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:cindent:ts=8:et:sw=4:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* a set of ranges on a number-line */
+
+#ifndef nsIntervalSet_h___
+#define nsIntervalSet_h___
+
+#include "nsCoord.h"
+
+typedef void *
+(* IntervalSetAlloc)(size_t aBytes, void *aClosure);
+
+typedef void
+(* IntervalSetFree) (size_t aBytes, void *aPtr, void *aClosure);
+
+/*
+ * A list-based class (hopefully tree-based when I get around to it)
+ * for representing a set of ranges on a number-line.
+ */
+class nsIntervalSet {
+
+public:
+
+ typedef nscoord coord_type;
+
+ nsIntervalSet(IntervalSetAlloc aAlloc, IntervalSetFree aFree,
+ void* aAllocatorClosure);
+ ~nsIntervalSet();
+
+ /*
+ * Include the interval [aBegin, aEnd] in the set.
+ *
+ * Removal of intervals added is not supported because that would
+ * require keeping track of the individual intervals that were
+ * added (nsIntervalMap should do that). It would be simple to
+ * implement ExcludeInterval if anyone wants it, though.
+ */
+ void IncludeInterval(coord_type aBegin, coord_type aEnd);
+
+ /*
+ * Are _some_ points in [aBegin, aEnd] contained within the set
+ * of intervals?
+ */
+ bool Intersects(coord_type aBegin, coord_type aEnd) const;
+
+ /*
+ * Are _all_ points in [aBegin, aEnd] contained within the set
+ * of intervals?
+ */
+ bool Contains(coord_type aBegin, coord_type aEnd) const;
+
+ bool IsEmpty() const
+ {
+ return !mList;
+ }
+
+private:
+
+ class Interval {
+
+ public:
+ Interval(coord_type aBegin, coord_type aEnd)
+ : mBegin(aBegin),
+ mEnd(aEnd),
+ mPrev(nullptr),
+ mNext(nullptr)
+ {
+ }
+
+ coord_type mBegin;
+ coord_type mEnd;
+ Interval *mPrev;
+ Interval *mNext;
+ };
+
+ void FreeInterval(Interval *aInterval);
+
+ Interval *mList;
+ IntervalSetAlloc mAlloc;
+ IntervalSetFree mFree;
+ void *mAllocatorClosure;
+
+};
+
+#endif // !defined(nsIntervalSet_h___)
diff --git a/layout/generic/nsLeafFrame.cpp b/layout/generic/nsLeafFrame.cpp
new file mode 100644
index 000000000..634eee04b
--- /dev/null
+++ b/layout/generic/nsLeafFrame.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class for rendering objects that do not have child lists */
+
+#include "nsLeafFrame.h"
+#include "nsPresContext.h"
+
+using namespace mozilla;
+
+nsLeafFrame::~nsLeafFrame()
+{
+}
+
+/* virtual */ nscoord
+nsLeafFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_MIN_WIDTH(this, result);
+ result = GetIntrinsicISize();
+ return result;
+}
+
+/* virtual */ nscoord
+nsLeafFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_PREF_WIDTH(this, result);
+ result = GetIntrinsicISize();
+ return result;
+}
+
+/* virtual */
+LogicalSize
+nsLeafFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ const WritingMode wm = GetWritingMode();
+ LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
+ return result.ConvertTo(aWM, wm);
+}
+
+void
+nsLeafFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsLeafFrame");
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("enter nsLeafFrame::Reflow: aMaxSize=%d,%d",
+ aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
+
+ DoReflow(aPresContext, aMetrics, aReflowInput, aStatus);
+
+ FinishAndStoreOverflow(&aMetrics);
+}
+
+void
+nsLeafFrame::DoReflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ NS_ASSERTION(aReflowInput.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
+ "Shouldn't have unconstrained stuff here "
+ "thanks to the rules of reflow");
+ NS_ASSERTION(NS_INTRINSICSIZE != aReflowInput.ComputedHeight(),
+ "Shouldn't have unconstrained stuff here "
+ "thanks to ComputeAutoSize");
+
+ // XXX how should border&padding effect baseline alignment?
+ // => descent = borderPadding.bottom for example
+ WritingMode wm = aReflowInput.GetWritingMode();
+ aMetrics.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding());
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("exit nsLeafFrame::DoReflow: size=%d,%d",
+ aMetrics.ISize(wm), aMetrics.BSize(wm)));
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+
+ aMetrics.SetOverflowAreasToDesiredBounds();
+}
+
+nscoord
+nsLeafFrame::GetIntrinsicBSize()
+{
+ NS_NOTREACHED("Someone didn't override Reflow or ComputeAutoSize");
+ return 0;
+}
+
+void
+nsLeafFrame::SizeToAvailSize(const ReflowInput& aReflowInput,
+ ReflowOutput& aDesiredSize)
+{
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalSize size(wm, aReflowInput.AvailableISize(), // FRAME
+ aReflowInput.AvailableBSize());
+ aDesiredSize.SetSize(wm, size);
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aDesiredSize);
+}
diff --git a/layout/generic/nsLeafFrame.h b/layout/generic/nsLeafFrame.h
new file mode 100644
index 000000000..407f9846d
--- /dev/null
+++ b/layout/generic/nsLeafFrame.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* base class for rendering objects that do not have child lists */
+
+#ifndef nsLeafFrame_h___
+#define nsLeafFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsFrame.h"
+#include "nsDisplayList.h"
+
+/**
+ * Abstract class that provides simple fixed-size layout for leaf objects
+ * (e.g. images, form elements, etc.). Deriviations provide the implementation
+ * of the GetDesiredSize method. The rendering method knows how to render
+ * borders and backgrounds.
+ */
+class nsLeafFrame : public nsFrame {
+public:
+ NS_DECL_ABSTRACT_FRAME(nsLeafFrame)
+
+ // nsIFrame replacements
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override {
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsLeafFrame");
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+ }
+
+ /**
+ * Both GetMinISize and GetPrefISize will return whatever GetIntrinsicISize
+ * returns.
+ */
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+
+ /**
+ * Our auto size is just intrinsic width and intrinsic height.
+ */
+ virtual mozilla::LogicalSize
+ ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ /**
+ * Reflow our frame. This will use the computed width plus borderpadding for
+ * the desired width, and use the return value of GetIntrinsicBSize plus
+ * borderpadding for the desired height. Ascent will be set to the height,
+ * and descent will be set to 0.
+ */
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ /**
+ * This method does most of the work that Reflow() above need done.
+ */
+ virtual void DoReflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus);
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ // We don't actually contain a block, but we do always want a
+ // computed width, so tell a little white lie here.
+ return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplacedContainsBlock));
+ }
+
+protected:
+ explicit nsLeafFrame(nsStyleContext* aContext) : nsFrame(aContext) {}
+ virtual ~nsLeafFrame();
+
+ /**
+ * Return the intrinsic isize of the frame's content area. Note that this
+ * should not include borders or padding and should not depend on the applied
+ * styles.
+ */
+ virtual nscoord GetIntrinsicISize() = 0;
+
+ /**
+ * Return the intrinsic bsize of the frame's content area. This should not
+ * include border or padding. This will only matter if the specified bsize
+ * is auto. Note that subclasses must either implement this or override
+ * Reflow and ComputeAutoSize; the default Reflow and ComputeAutoSize impls
+ * call this method.
+ */
+ virtual nscoord GetIntrinsicBSize();
+
+ /**
+ * Set aDesiredSize to be the available size
+ */
+ void SizeToAvailSize(const ReflowInput& aReflowInput,
+ ReflowOutput& aDesiredSize);
+};
+
+#endif /* nsLeafFrame_h___ */
diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp
new file mode 100644
index 000000000..7244b7f53
--- /dev/null
+++ b/layout/generic/nsLineBox.cpp
@@ -0,0 +1,989 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of one line within a block frame, a CSS line box */
+
+#include "nsLineBox.h"
+
+#include "mozilla/ArenaObjectID.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Likely.h"
+#include "mozilla/WritingModes.h"
+#include "nsBidiPresUtils.h"
+#include "nsFrame.h"
+#include "nsIFrameInlines.h"
+#include "nsPresArena.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Sprintf.h"
+
+#ifdef DEBUG
+static int32_t ctorCount;
+int32_t nsLineBox::GetCtorCount() { return ctorCount; }
+#endif
+
+#ifndef _MSC_VER
+// static nsLineBox constant; initialized in the header file.
+const uint32_t nsLineBox::kMinChildCountForHashtable;
+#endif
+
+using namespace mozilla;
+
+nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
+ : mFirstChild(aFrame)
+ , mContainerSize(-1, -1)
+ , mBounds(WritingMode()) // mBounds will be initialized with the correct
+ // writing mode when it is set
+// NOTE: memory is already zeroed since we allocate with AllocateByObjectID.
+{
+ MOZ_COUNT_CTOR(nsLineBox);
+#ifdef DEBUG
+ ++ctorCount;
+ NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
+ nsIFrame* f = aFrame;
+ for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
+ NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
+ "wrong kind of child frame");
+ }
+#endif
+ static_assert(static_cast<int>(StyleClear::Max) <= 15,
+ "FlagBits needs more bits to store the full range of "
+ "break type ('clear') values");
+ mChildCount = aCount;
+ MarkDirty();
+ mFlags.mBlock = aIsBlock;
+}
+
+nsLineBox::~nsLineBox()
+{
+ MOZ_COUNT_DTOR(nsLineBox);
+ if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
+ delete mFrames;
+ }
+ Cleanup();
+}
+
+nsLineBox*
+NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
+{
+ return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
+}
+
+nsLineBox*
+NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
+ nsIFrame* aFrame, int32_t aCount)
+{
+ nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
+ newLine->NoteFramesMovedFrom(aFromLine);
+ newLine->mContainerSize = aFromLine->mContainerSize;
+ return newLine;
+}
+
+void
+nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
+{
+ MOZ_ASSERT(!mFlags.mHasHashedFrames);
+ MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
+ mFrames = aFromLine->mFrames;
+ mFlags.mHasHashedFrames = 1;
+ aFromLine->mFlags.mHasHashedFrames = 0;
+ aFromLine->mChildCount = aFromLineNewCount;
+ // remove aFromLine's frames that aren't on this line
+ nsIFrame* f = aFromLine->mFirstChild;
+ for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
+ mFrames->RemoveEntry(f);
+ }
+}
+
+void
+nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
+{
+ uint32_t fromCount = aFromLine->GetChildCount();
+ uint32_t toCount = GetChildCount();
+ MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
+ uint32_t fromNewCount = fromCount - toCount;
+ if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
+ aFromLine->mChildCount = fromNewCount;
+ MOZ_ASSERT(toCount < kMinChildCountForHashtable);
+ } else if (fromNewCount < kMinChildCountForHashtable) {
+ // aFromLine has a hash table but will not have it after moving the frames
+ // so this line can steal the hash table if it needs it.
+ if (toCount >= kMinChildCountForHashtable) {
+ StealHashTableFrom(aFromLine, fromNewCount);
+ } else {
+ delete aFromLine->mFrames;
+ aFromLine->mFlags.mHasHashedFrames = 0;
+ aFromLine->mChildCount = fromNewCount;
+ }
+ } else {
+ // aFromLine still needs a hash table.
+ if (toCount < kMinChildCountForHashtable) {
+ // remove the moved frames from it
+ nsIFrame* f = mFirstChild;
+ for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
+ aFromLine->mFrames->RemoveEntry(f);
+ }
+ } else if (toCount <= fromNewCount) {
+ // This line needs a hash table, allocate a hash table for it since that
+ // means fewer hash ops.
+ nsIFrame* f = mFirstChild;
+ for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
+ aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
+ }
+ SwitchToHashtable(); // toCount PutEntry
+ } else {
+ // This line needs a hash table, but it's fewer hash ops to steal
+ // aFromLine's hash table and allocate a new hash table for that line.
+ StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
+ aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
+ }
+ }
+}
+
+void*
+nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell)
+{
+ return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
+}
+
+void
+nsLineBox::Destroy(nsIPresShell* aPresShell)
+{
+ this->nsLineBox::~nsLineBox();
+ aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this);
+}
+
+void
+nsLineBox::Cleanup()
+{
+ if (mData) {
+ if (IsBlock()) {
+ delete mBlockData;
+ }
+ else {
+ delete mInlineData;
+ }
+ mData = nullptr;
+ }
+}
+
+#ifdef DEBUG_FRAME_DUMP
+static void
+ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats)
+{
+ nsFloatCache* fc = aFloats.Head();
+ while (fc) {
+ nsCString str(aPrefix);
+ nsIFrame* frame = fc->mFloat;
+ str += nsPrintfCString("floatframe@%p ", static_cast<void*>(frame));
+ if (frame) {
+ nsAutoString frameName;
+ frame->GetFrameName(frameName);
+ str += NS_ConvertUTF16toUTF8(frameName).get();
+ }
+ else {
+ str += "\n###!!! NULL out-of-flow frame";
+ }
+ fprintf_stderr(out, "%s\n", str.get());
+ fc = fc->Next();
+ }
+}
+
+const char*
+nsLineBox::BreakTypeToString(StyleClear aBreakType) const
+{
+ switch (aBreakType) {
+ case StyleClear::None: return "nobr";
+ case StyleClear::Left: return "leftbr";
+ case StyleClear::Right: return "rightbr";
+ case StyleClear::InlineStart: return "inlinestartbr";
+ case StyleClear::InlineEnd: return "inlineendbr";
+ case StyleClear::Both: return "leftbr+rightbr";
+ case StyleClear::Line: return "linebr";
+ case StyleClear::Max: return "leftbr+rightbr+linebr";
+ }
+ return "unknown";
+}
+
+char*
+nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
+{
+ snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
+ IsBlock() ? "block" : "inline",
+ IsDirty() ? "dirty" : "clean",
+ IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
+ IsImpactedByFloat() ? "impacted" : "not impacted",
+ IsLineWrapped() ? "wrapped" : "not wrapped",
+ BreakTypeToString(GetBreakTypeBefore()),
+ BreakTypeToString(GetBreakTypeAfter()),
+ mAllFlags);
+ return aBuf;
+}
+
+void
+nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
+{
+ nsCString str;
+ while (aIndent-- > 0) {
+ str += " ";
+ }
+ List(out, str.get(), aFlags);
+}
+
+void
+nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str(aPrefix);
+ char cbuf[100];
+ str += nsPrintfCString("line %p: count=%d state=%s ",
+ static_cast<const void*>(this), GetChildCount(),
+ StateToString(cbuf, sizeof(cbuf)));
+ if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) {
+ str += nsPrintfCString("bm=%d ", GetCarriedOutBEndMargin().get());
+ }
+ nsRect bounds = GetPhysicalBounds();
+ str += nsPrintfCString("{%d,%d,%d,%d} ",
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) {
+ str += nsPrintfCString("{%s: %d,%d,%d,%d; cs=%d,%d} ",
+ mWritingMode.DebugString(),
+ IStart(), BStart(), ISize(), BSize(),
+ mContainerSize.width, mContainerSize.height);
+ }
+ if (mData &&
+ (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
+ !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
+ str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
+ mData->mOverflowAreas.VisualOverflow().x,
+ mData->mOverflowAreas.VisualOverflow().y,
+ mData->mOverflowAreas.VisualOverflow().width,
+ mData->mOverflowAreas.VisualOverflow().height,
+ mData->mOverflowAreas.ScrollableOverflow().x,
+ mData->mOverflowAreas.ScrollableOverflow().y,
+ mData->mOverflowAreas.ScrollableOverflow().width,
+ mData->mOverflowAreas.ScrollableOverflow().height);
+ }
+ fprintf_stderr(out, "%s<\n", str.get());
+
+ nsIFrame* frame = mFirstChild;
+ int32_t n = GetChildCount();
+ nsCString pfx(aPrefix);
+ pfx += " ";
+ while (--n >= 0) {
+ frame->List(out, pfx.get(), aFlags);
+ frame = frame->GetNextSibling();
+ }
+
+ if (HasFloats()) {
+ fprintf_stderr(out, "%s> floats <\n", aPrefix);
+ ListFloats(out, pfx.get(), mInlineData->mFloats);
+ }
+ fprintf_stderr(out, "%s>\n", aPrefix);
+}
+
+nsIFrame*
+nsLineBox::LastChild() const
+{
+ nsIFrame* frame = mFirstChild;
+ int32_t n = GetChildCount() - 1;
+ while (--n >= 0) {
+ frame = frame->GetNextSibling();
+ }
+ return frame;
+}
+#endif
+
+int32_t
+nsLineBox::IndexOf(nsIFrame* aFrame) const
+{
+ int32_t i, n = GetChildCount();
+ nsIFrame* frame = mFirstChild;
+ for (i = 0; i < n; i++) {
+ if (frame == aFrame) {
+ return i;
+ }
+ frame = frame->GetNextSibling();
+ }
+ return -1;
+}
+
+bool
+nsLineBox::IsEmpty() const
+{
+ if (IsBlock())
+ return mFirstChild->IsEmpty();
+
+ int32_t n;
+ nsIFrame *kid;
+ for (n = GetChildCount(), kid = mFirstChild;
+ n > 0;
+ --n, kid = kid->GetNextSibling())
+ {
+ if (!kid->IsEmpty())
+ return false;
+ }
+ if (HasBullet()) {
+ return false;
+ }
+ return true;
+}
+
+bool
+nsLineBox::CachedIsEmpty()
+{
+ if (mFlags.mDirty) {
+ return IsEmpty();
+ }
+
+ if (mFlags.mEmptyCacheValid) {
+ return mFlags.mEmptyCacheState;
+ }
+
+ bool result;
+ if (IsBlock()) {
+ result = mFirstChild->CachedIsEmpty();
+ } else {
+ int32_t n;
+ nsIFrame *kid;
+ result = true;
+ for (n = GetChildCount(), kid = mFirstChild;
+ n > 0;
+ --n, kid = kid->GetNextSibling())
+ {
+ if (!kid->CachedIsEmpty()) {
+ result = false;
+ break;
+ }
+ }
+ if (HasBullet()) {
+ result = false;
+ }
+ }
+
+ mFlags.mEmptyCacheValid = true;
+ mFlags.mEmptyCacheState = result;
+ return result;
+}
+
+void
+nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
+ nsIFrame* aDestructRoot, nsFrameList* aFrames)
+{
+ nsIPresShell* shell = aPresContext->PresShell();
+
+ // Keep our line list and frame list up to date as we
+ // remove frames, in case something wants to traverse the
+ // frame tree while we're destroying.
+ while (!aLines.empty()) {
+ nsLineBox* line = aLines.front();
+ if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
+ line->SwitchToCounter(); // Avoid expensive has table removals.
+ }
+ while (line->GetChildCount() > 0) {
+ nsIFrame* child = aFrames->RemoveFirstChild();
+ MOZ_ASSERT(child == line->mFirstChild, "Lines out of sync");
+ line->mFirstChild = aFrames->FirstChild();
+ line->NoteFrameRemoved(child);
+ child->DestroyFrom(aDestructRoot);
+ }
+
+ aLines.pop_front();
+ line->Destroy(shell);
+ }
+}
+
+bool
+nsLineBox::RFindLineContaining(nsIFrame* aFrame,
+ const nsLineList::iterator& aBegin,
+ nsLineList::iterator& aEnd,
+ nsIFrame* aLastFrameBeforeEnd,
+ int32_t* aFrameIndexInLine)
+{
+ NS_PRECONDITION(aFrame, "null ptr");
+
+ nsIFrame* curFrame = aLastFrameBeforeEnd;
+ while (aBegin != aEnd) {
+ --aEnd;
+ NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
+ if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
+ !aEnd->Contains(aFrame)) {
+ if (aEnd->mFirstChild) {
+ curFrame = aEnd->mFirstChild->GetPrevSibling();
+ }
+ continue;
+ }
+ // i is the index of curFrame in aEnd
+ int32_t i = aEnd->GetChildCount() - 1;
+ while (i >= 0) {
+ if (curFrame == aFrame) {
+ *aFrameIndexInLine = i;
+ return true;
+ }
+ --i;
+ curFrame = curFrame->GetPrevSibling();
+ }
+ MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
+ }
+ *aFrameIndexInLine = -1;
+ return false;
+}
+
+nsCollapsingMargin
+nsLineBox::GetCarriedOutBEndMargin() const
+{
+ NS_ASSERTION(IsBlock(),
+ "GetCarriedOutBEndMargin called on non-block line.");
+ return (IsBlock() && mBlockData)
+ ? mBlockData->mCarriedOutBEndMargin
+ : nsCollapsingMargin();
+}
+
+bool
+nsLineBox::SetCarriedOutBEndMargin(nsCollapsingMargin aValue)
+{
+ bool changed = false;
+ if (IsBlock()) {
+ if (!aValue.IsZero()) {
+ if (!mBlockData) {
+ mBlockData = new ExtraBlockData(GetPhysicalBounds());
+ }
+ changed = aValue != mBlockData->mCarriedOutBEndMargin;
+ mBlockData->mCarriedOutBEndMargin = aValue;
+ }
+ else if (mBlockData) {
+ changed = aValue != mBlockData->mCarriedOutBEndMargin;
+ mBlockData->mCarriedOutBEndMargin = aValue;
+ MaybeFreeData();
+ }
+ }
+ return changed;
+}
+
+void
+nsLineBox::MaybeFreeData()
+{
+ nsRect bounds = GetPhysicalBounds();
+ if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
+ if (IsInline()) {
+ if (mInlineData->mFloats.IsEmpty()) {
+ delete mInlineData;
+ mInlineData = nullptr;
+ }
+ }
+ else if (mBlockData->mCarriedOutBEndMargin.IsZero()) {
+ delete mBlockData;
+ mBlockData = nullptr;
+ }
+ }
+}
+
+// XXX get rid of this???
+nsFloatCache*
+nsLineBox::GetFirstFloat()
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ return mInlineData ? mInlineData->mFloats.Head() : nullptr;
+}
+
+// XXX this might be too eager to free memory
+void
+nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ if (IsInline() && mInlineData) {
+ if (mInlineData->mFloats.NotEmpty()) {
+ aFreeList.Append(mInlineData->mFloats);
+ }
+ MaybeFreeData();
+ }
+}
+
+void
+nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ if (IsInline()) {
+ if (aFreeList.NotEmpty()) {
+ if (!mInlineData) {
+ mInlineData = new ExtraInlineData(GetPhysicalBounds());
+ }
+ mInlineData->mFloats.Append(aFreeList);
+ }
+ }
+}
+
+bool
+nsLineBox::RemoveFloat(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ if (IsInline() && mInlineData) {
+ nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
+ if (fc) {
+ // Note: the placeholder is part of the line's child list
+ // and will be removed later.
+ mInlineData->mFloats.Remove(fc);
+ delete fc;
+ MaybeFreeData();
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
+{
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
+ "illegal width for combined area");
+ NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
+ "illegal height for combined area");
+ }
+ nsRect bounds = GetPhysicalBounds();
+ if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
+ !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
+ if (!mData) {
+ if (IsInline()) {
+ mInlineData = new ExtraInlineData(bounds);
+ }
+ else {
+ mBlockData = new ExtraBlockData(bounds);
+ }
+ }
+ mData->mOverflowAreas = aOverflowAreas;
+ }
+ else if (mData) {
+ // Store away new value so that MaybeFreeData compares against
+ // the right value.
+ mData->mOverflowAreas = aOverflowAreas;
+ MaybeFreeData();
+ }
+}
+
+//----------------------------------------------------------------------
+
+
+static nsLineBox* gDummyLines[1];
+
+nsLineIterator::nsLineIterator()
+{
+ mLines = gDummyLines;
+ mNumLines = 0;
+ mIndex = 0;
+ mRightToLeft = false;
+}
+
+nsLineIterator::~nsLineIterator()
+{
+ if (mLines != gDummyLines) {
+ delete [] mLines;
+ }
+}
+
+/* virtual */ void
+nsLineIterator::DisposeLineIterator()
+{
+ delete this;
+}
+
+nsresult
+nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
+{
+ mRightToLeft = aRightToLeft;
+
+ // Count the lines
+ int32_t numLines = aLines.size();
+ if (0 == numLines) {
+ // Use gDummyLines so that we don't need null pointer checks in
+ // the accessor methods
+ mLines = gDummyLines;
+ return NS_OK;
+ }
+
+ // Make a linear array of the lines
+ mLines = new nsLineBox*[numLines];
+ if (!mLines) {
+ // Use gDummyLines so that we don't need null pointer checks in
+ // the accessor methods
+ mLines = gDummyLines;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ nsLineBox** lp = mLines;
+ for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
+ line != line_end;
+ ++line)
+ {
+ *lp++ = line;
+ }
+ mNumLines = numLines;
+ return NS_OK;
+}
+
+int32_t
+nsLineIterator::GetNumLines()
+{
+ return mNumLines;
+}
+
+bool
+nsLineIterator::GetDirection()
+{
+ return mRightToLeft;
+}
+
+NS_IMETHODIMP
+nsLineIterator::GetLine(int32_t aLineNumber,
+ nsIFrame** aFirstFrameOnLine,
+ int32_t* aNumFramesOnLine,
+ nsRect& aLineBounds)
+{
+ NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
+ NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
+
+ if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
+ *aFirstFrameOnLine = nullptr;
+ *aNumFramesOnLine = 0;
+ aLineBounds.SetRect(0, 0, 0, 0);
+ return NS_OK;
+ }
+ nsLineBox* line = mLines[aLineNumber];
+ *aFirstFrameOnLine = line->mFirstChild;
+ *aNumFramesOnLine = line->GetChildCount();
+ aLineBounds = line->GetPhysicalBounds();
+
+ return NS_OK;
+}
+
+int32_t
+nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
+{
+ NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
+ int32_t lineNumber = aStartLine;
+ while (lineNumber != mNumLines) {
+ nsLineBox* line = mLines[lineNumber];
+ if (line->Contains(aFrame)) {
+ return lineNumber;
+ }
+ ++lineNumber;
+ }
+ return -1;
+}
+
+NS_IMETHODIMP
+nsLineIterator::CheckLineOrder(int32_t aLine,
+ bool *aIsReordered,
+ nsIFrame **aFirstVisual,
+ nsIFrame **aLastVisual)
+{
+ NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
+ nsLineBox* line = mLines[aLine];
+
+ if (!line->mFirstChild) { // empty line
+ *aIsReordered = false;
+ *aFirstVisual = nullptr;
+ *aLastVisual = nullptr;
+ return NS_OK;
+ }
+
+ nsIFrame* leftmostFrame;
+ nsIFrame* rightmostFrame;
+ *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
+
+ // map leftmost/rightmost to first/last according to paragraph direction
+ *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
+ *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLineIterator::FindFrameAt(int32_t aLineNumber,
+ nsPoint aPos,
+ nsIFrame** aFrameFound,
+ bool* aPosIsBeforeFirstFrame,
+ bool* aPosIsAfterLastFrame)
+{
+ NS_PRECONDITION(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame,
+ "null OUT ptr");
+ if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsLineBox* line = mLines[aLineNumber];
+ if (!line) {
+ *aFrameFound = nullptr;
+ *aPosIsBeforeFirstFrame = true;
+ *aPosIsAfterLastFrame = false;
+ return NS_OK;
+ }
+
+ if (line->ISize() == 0 && line->BSize() == 0)
+ return NS_ERROR_FAILURE;
+
+ nsIFrame* frame = line->mFirstChild;
+ nsIFrame* closestFromStart = nullptr;
+ nsIFrame* closestFromEnd = nullptr;
+
+ WritingMode wm = line->mWritingMode;
+ nsSize containerSize = line->mContainerSize;
+
+ LogicalPoint pos(wm, aPos, containerSize);
+
+ int32_t n = line->GetChildCount();
+ while (n--) {
+ LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
+ if (rect.ISize(wm) > 0) {
+ // If pos.I() is inside this frame - this is it
+ if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
+ closestFromStart = closestFromEnd = frame;
+ break;
+ }
+ if (rect.IStart(wm) < pos.I(wm)) {
+ if (!closestFromStart ||
+ rect.IEnd(wm) > closestFromStart->
+ GetLogicalRect(wm, containerSize).IEnd(wm))
+ closestFromStart = frame;
+ }
+ else {
+ if (!closestFromEnd ||
+ rect.IStart(wm) < closestFromEnd->
+ GetLogicalRect(wm, containerSize).IStart(wm))
+ closestFromEnd = frame;
+ }
+ }
+ frame = frame->GetNextSibling();
+ }
+ if (!closestFromStart && !closestFromEnd) {
+ // All frames were zero-width. Just take the first one.
+ closestFromStart = closestFromEnd = line->mFirstChild;
+ }
+ *aPosIsBeforeFirstFrame = mRightToLeft ? !closestFromEnd : !closestFromStart;
+ *aPosIsAfterLastFrame = mRightToLeft ? !closestFromStart : !closestFromEnd;
+ if (closestFromStart == closestFromEnd) {
+ *aFrameFound = closestFromStart;
+ }
+ else if (!closestFromStart) {
+ *aFrameFound = closestFromEnd;
+ }
+ else if (!closestFromEnd) {
+ *aFrameFound = closestFromStart;
+ }
+ else { // we're between two frames
+ nscoord delta =
+ closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
+ closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
+ if (pos.I(wm) < closestFromStart->
+ GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
+ *aFrameFound = closestFromStart;
+ } else {
+ *aFrameFound = closestFromEnd;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
+{
+ aFrame = aFrame->GetNextSibling();
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatCacheList::nsFloatCacheList() :
+ mHead(nullptr)
+{
+ MOZ_COUNT_CTOR(nsFloatCacheList);
+}
+#endif
+
+nsFloatCacheList::~nsFloatCacheList()
+{
+ DeleteAll();
+ MOZ_COUNT_DTOR(nsFloatCacheList);
+}
+
+void
+nsFloatCacheList::DeleteAll()
+{
+ nsFloatCache* c = mHead;
+ while (c) {
+ nsFloatCache* next = c->Next();
+ delete c;
+ c = next;
+ }
+ mHead = nullptr;
+}
+
+nsFloatCache*
+nsFloatCacheList::Tail() const
+{
+ nsFloatCache* fc = mHead;
+ while (fc) {
+ if (!fc->mNext) {
+ break;
+ }
+ fc = fc->mNext;
+ }
+ return fc;
+}
+
+void
+nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
+{
+ NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
+
+ nsFloatCache* tail = Tail();
+ if (tail) {
+ NS_ASSERTION(!tail->mNext, "Bogus!");
+ tail->mNext = aList.mHead;
+ }
+ else {
+ NS_ASSERTION(!mHead, "Bogus!");
+ mHead = aList.mHead;
+ }
+ aList.mHead = nullptr;
+ aList.mTail = nullptr;
+}
+
+nsFloatCache*
+nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
+{
+ nsFloatCache* fc = mHead;
+ while (fc) {
+ if (fc->mFloat == aOutOfFlowFrame) {
+ break;
+ }
+ fc = fc->Next();
+ }
+ return fc;
+}
+
+nsFloatCache*
+nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
+{
+ nsFloatCache* fc = mHead;
+ nsFloatCache* prev = nullptr;
+ while (fc) {
+ if (fc == aElement) {
+ if (prev) {
+ prev->mNext = fc->mNext;
+ } else {
+ mHead = fc->mNext;
+ }
+ return prev;
+ }
+ prev = fc;
+ fc = fc->mNext;
+ }
+ return nullptr;
+}
+
+//----------------------------------------------------------------------
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatCacheFreeList::nsFloatCacheFreeList() :
+ mTail(nullptr)
+{
+ MOZ_COUNT_CTOR(nsFloatCacheFreeList);
+}
+
+nsFloatCacheFreeList::~nsFloatCacheFreeList()
+{
+ MOZ_COUNT_DTOR(nsFloatCacheFreeList);
+}
+#endif
+
+void
+nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
+{
+ NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
+
+ if (mTail) {
+ NS_ASSERTION(!mTail->mNext, "Bogus");
+ mTail->mNext = aList.mHead;
+ }
+ else {
+ NS_ASSERTION(!mHead, "Bogus");
+ mHead = aList.mHead;
+ }
+ mTail = aList.Tail();
+ aList.mHead = nullptr;
+}
+
+void
+nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
+{
+ nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
+ if (mTail == aElement) {
+ mTail = prev;
+ }
+}
+
+void
+nsFloatCacheFreeList::DeleteAll()
+{
+ nsFloatCacheList::DeleteAll();
+ mTail = nullptr;
+}
+
+nsFloatCache*
+nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
+{
+ NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "This is a float cache, why isn't the frame out-of-flow?");
+ nsFloatCache* fc = mHead;
+ if (mHead) {
+ if (mHead == mTail) {
+ mHead = mTail = nullptr;
+ }
+ else {
+ mHead = fc->mNext;
+ }
+ fc->mNext = nullptr;
+ }
+ else {
+ fc = new nsFloatCache();
+ }
+ fc->mFloat = aFloat;
+ return fc;
+}
+
+void
+nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
+{
+ NS_ASSERTION(!aFloat->mNext, "Bogus!");
+ aFloat->mNext = nullptr;
+ if (mTail) {
+ NS_ASSERTION(!mTail->mNext, "Bogus!");
+ mTail->mNext = aFloat;
+ mTail = aFloat;
+ }
+ else {
+ NS_ASSERTION(!mHead, "Bogus!");
+ mHead = mTail = aFloat;
+ }
+}
+
+//----------------------------------------------------------------------
+
+nsFloatCache::nsFloatCache()
+ : mFloat(nullptr),
+ mNext(nullptr)
+{
+ MOZ_COUNT_CTOR(nsFloatCache);
+}
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatCache::~nsFloatCache()
+{
+ MOZ_COUNT_DTOR(nsFloatCache);
+}
+#endif
diff --git a/layout/generic/nsLineBox.h b/layout/generic/nsLineBox.h
new file mode 100644
index 000000000..8f42b9e93
--- /dev/null
+++ b/layout/generic/nsLineBox.h
@@ -0,0 +1,1782 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of one line within a block frame, a CSS line box */
+
+#ifndef nsLineBox_h___
+#define nsLineBox_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#include "nsILineIterator.h"
+#include "nsIFrame.h"
+#include <algorithm>
+
+class nsLineBox;
+class nsFloatCache;
+class nsFloatCacheList;
+class nsFloatCacheFreeList;
+
+// State cached after reflowing a float. This state is used during
+// incremental reflow when we avoid reflowing a float.
+class nsFloatCache {
+public:
+ nsFloatCache();
+#ifdef NS_BUILD_REFCNT_LOGGING
+ ~nsFloatCache();
+#else
+ ~nsFloatCache() { }
+#endif
+
+ nsFloatCache* Next() const { return mNext; }
+
+ nsIFrame* mFloat; // floating frame
+
+protected:
+ nsFloatCache* mNext;
+
+ friend class nsFloatCacheList;
+ friend class nsFloatCacheFreeList;
+};
+
+//----------------------------------------
+
+class nsFloatCacheList {
+public:
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsFloatCacheList();
+#else
+ nsFloatCacheList() : mHead(nullptr) { }
+#endif
+ ~nsFloatCacheList();
+
+ bool IsEmpty() const {
+ return nullptr == mHead;
+ }
+
+ bool NotEmpty() const {
+ return nullptr != mHead;
+ }
+
+ nsFloatCache* Head() const {
+ return mHead;
+ }
+
+ nsFloatCache* Tail() const;
+
+ void DeleteAll();
+
+ nsFloatCache* Find(nsIFrame* aOutOfFlowFrame);
+
+ // Remove a nsFloatCache from this list. Deleting this nsFloatCache
+ // becomes the caller's responsibility.
+ void Remove(nsFloatCache* aElement) { RemoveAndReturnPrev(aElement); }
+
+ // Steal away aList's nsFloatCache objects and put them in this
+ // list. aList must not be empty.
+ void Append(nsFloatCacheFreeList& aList);
+
+protected:
+ nsFloatCache* mHead;
+
+ // Remove a nsFloatCache from this list. Deleting this nsFloatCache
+ // becomes the caller's responsibility. Returns the nsFloatCache that was
+ // before aElement, or nullptr if aElement was the first.
+ nsFloatCache* RemoveAndReturnPrev(nsFloatCache* aElement);
+
+ friend class nsFloatCacheFreeList;
+};
+
+//---------------------------------------
+// Like nsFloatCacheList, but with fast access to the tail
+
+class nsFloatCacheFreeList : private nsFloatCacheList {
+public:
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsFloatCacheFreeList();
+ ~nsFloatCacheFreeList();
+#else
+ nsFloatCacheFreeList() : mTail(nullptr) { }
+ ~nsFloatCacheFreeList() { }
+#endif
+
+ // Reimplement trivial functions
+ bool IsEmpty() const {
+ return nullptr == mHead;
+ }
+
+ nsFloatCache* Head() const {
+ return mHead;
+ }
+
+ nsFloatCache* Tail() const {
+ return mTail;
+ }
+
+ bool NotEmpty() const {
+ return nullptr != mHead;
+ }
+
+ void DeleteAll();
+
+ // Steal away aList's nsFloatCache objects and put them on this
+ // free-list. aList must not be empty.
+ void Append(nsFloatCacheList& aList);
+
+ void Append(nsFloatCache* aFloatCache);
+
+ void Remove(nsFloatCache* aElement);
+
+ // Remove an nsFloatCache object from this list and return it, or create
+ // a new one if this one is empty; Set its mFloat to aFloat.
+ nsFloatCache* Alloc(nsIFrame* aFloat);
+
+protected:
+ nsFloatCache* mTail;
+
+ friend class nsFloatCacheList;
+};
+
+//----------------------------------------------------------------------
+
+#define LINE_MAX_CHILD_COUNT INT32_MAX
+
+/**
+ * Function to create a line box and initialize it with a single frame.
+ * The allocation is infallible.
+ * If the frame was moved from another line then you're responsible
+ * for notifying that line using NoteFrameRemoved(). Alternatively,
+ * it's better to use the next function that does that for you in an
+ * optimal way.
+ */
+nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame,
+ bool aIsBlock);
+/**
+ * Function to create a line box and initialize it with aCount frames
+ * that are currently on aFromLine. The allocation is infallible.
+ */
+nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
+ nsIFrame* aFrame, int32_t aCount);
+
+class nsLineList;
+
+// don't use the following names outside of this file. Instead, use
+// nsLineList::iterator, etc. These are just here to allow them to
+// be specified as parameters to methods of nsLineBox.
+class nsLineList_iterator;
+class nsLineList_const_iterator;
+class nsLineList_reverse_iterator;
+class nsLineList_const_reverse_iterator;
+
+/**
+ * Users must have the class that is to be part of the list inherit
+ * from nsLineLink. If they want to be efficient, it should be the
+ * first base class. (This was originally nsCLink in a templatized
+ * nsCList, but it's still useful separately.)
+ */
+
+class nsLineLink {
+
+ public:
+ friend class nsLineList;
+ friend class nsLineList_iterator;
+ friend class nsLineList_reverse_iterator;
+ friend class nsLineList_const_iterator;
+ friend class nsLineList_const_reverse_iterator;
+
+ private:
+ nsLineLink *_mNext; // or head
+ nsLineLink *_mPrev; // or tail
+
+};
+
+
+/**
+ * The nsLineBox class represents a horizontal line of frames. It contains
+ * enough state to support incremental reflow of the frames, event handling
+ * for the frames, and rendering of the frames.
+ */
+class nsLineBox final : public nsLineLink {
+private:
+ nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock);
+ ~nsLineBox();
+
+ // Infallible overloaded new operator. Uses an arena (which comes from the
+ // presShell) to perform the allocation.
+ void* operator new(size_t sz, nsIPresShell* aPresShell);
+ void operator delete(void* aPtr, size_t sz) = delete;
+
+public:
+ // Use these functions to allocate and destroy line boxes
+ friend nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame,
+ bool aIsBlock);
+ friend nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
+ nsIFrame* aFrame, int32_t aCount);
+ void Destroy(nsIPresShell* aPresShell);
+
+ // mBlock bit
+ bool IsBlock() const {
+ return mFlags.mBlock;
+ }
+ bool IsInline() const {
+ return 0 == mFlags.mBlock;
+ }
+
+ // mDirty bit
+ void MarkDirty() {
+ mFlags.mDirty = 1;
+ }
+ void ClearDirty() {
+ mFlags.mDirty = 0;
+ }
+ bool IsDirty() const {
+ return mFlags.mDirty;
+ }
+
+ // mPreviousMarginDirty bit
+ void MarkPreviousMarginDirty() {
+ mFlags.mPreviousMarginDirty = 1;
+ }
+ void ClearPreviousMarginDirty() {
+ mFlags.mPreviousMarginDirty = 0;
+ }
+ bool IsPreviousMarginDirty() const {
+ return mFlags.mPreviousMarginDirty;
+ }
+
+ // mHasClearance bit
+ void SetHasClearance() {
+ mFlags.mHasClearance = 1;
+ }
+ void ClearHasClearance() {
+ mFlags.mHasClearance = 0;
+ }
+ bool HasClearance() const {
+ return mFlags.mHasClearance;
+ }
+
+ // mImpactedByFloat bit
+ void SetLineIsImpactedByFloat(bool aValue) {
+ mFlags.mImpactedByFloat = aValue;
+ }
+ bool IsImpactedByFloat() const {
+ return mFlags.mImpactedByFloat;
+ }
+
+ // mLineWrapped bit
+ void SetLineWrapped(bool aOn) {
+ mFlags.mLineWrapped = aOn;
+ }
+ bool IsLineWrapped() const {
+ return mFlags.mLineWrapped;
+ }
+
+ // mInvalidateTextRuns bit
+ void SetInvalidateTextRuns(bool aOn) {
+ mFlags.mInvalidateTextRuns = aOn;
+ }
+ bool GetInvalidateTextRuns() const {
+ return mFlags.mInvalidateTextRuns;
+ }
+
+ // mResizeReflowOptimizationDisabled bit
+ void DisableResizeReflowOptimization() {
+ mFlags.mResizeReflowOptimizationDisabled = true;
+ }
+ void EnableResizeReflowOptimization() {
+ mFlags.mResizeReflowOptimizationDisabled = false;
+ }
+ bool ResizeReflowOptimizationDisabled() const {
+ return mFlags.mResizeReflowOptimizationDisabled;
+ }
+
+ // mHasBullet bit
+ void SetHasBullet() {
+ mFlags.mHasBullet = true;
+ InvalidateCachedIsEmpty();
+ }
+ void ClearHasBullet() {
+ mFlags.mHasBullet = false;
+ InvalidateCachedIsEmpty();
+ }
+ bool HasBullet() const {
+ return mFlags.mHasBullet;
+ }
+
+ // mHadFloatPushed bit
+ void SetHadFloatPushed() {
+ mFlags.mHadFloatPushed = true;
+ }
+ void ClearHadFloatPushed() {
+ mFlags.mHadFloatPushed = false;
+ }
+ bool HadFloatPushed() const {
+ return mFlags.mHadFloatPushed;
+ }
+
+private:
+ // Add a hash table for fast lookup when the line has more frames than this.
+ static const uint32_t kMinChildCountForHashtable = 200;
+
+ /**
+ * Take ownership of aFromLine's hash table and remove the frames that
+ * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with
+ * mFirstChild. This method is used to optimize moving a large number
+ * of frames from one line to the next.
+ */
+ void StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount);
+
+ /**
+ * Does the equivalent of this->NoteFrameAdded and aFromLine->NoteFrameRemoved
+ * for each frame on this line, but in a optimized way.
+ */
+ void NoteFramesMovedFrom(nsLineBox* aFromLine);
+
+ void SwitchToHashtable()
+ {
+ MOZ_ASSERT(!mFlags.mHasHashedFrames);
+ uint32_t count = GetChildCount();
+ mFlags.mHasHashedFrames = 1;
+ uint32_t minLength = std::max(kMinChildCountForHashtable,
+ uint32_t(PLDHashTable::kDefaultInitialLength));
+ mFrames = new nsTHashtable< nsPtrHashKey<nsIFrame> >(std::max(count, minLength));
+ for (nsIFrame* f = mFirstChild; count-- > 0; f = f->GetNextSibling()) {
+ mFrames->PutEntry(f);
+ }
+ }
+ void SwitchToCounter() {
+ MOZ_ASSERT(mFlags.mHasHashedFrames);
+ uint32_t count = GetChildCount();
+ delete mFrames;
+ mFlags.mHasHashedFrames = 0;
+ mChildCount = count;
+ }
+
+public:
+ int32_t GetChildCount() const {
+ return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Count() : mChildCount;
+ }
+
+ /**
+ * Register that aFrame is now on this line.
+ */
+ void NoteFrameAdded(nsIFrame* aFrame) {
+ if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
+ mFrames->PutEntry(aFrame);
+ } else {
+ if (++mChildCount >= kMinChildCountForHashtable) {
+ SwitchToHashtable();
+ }
+ }
+ }
+
+ /**
+ * Register that aFrame is not on this line anymore.
+ */
+ void NoteFrameRemoved(nsIFrame* aFrame) {
+ MOZ_ASSERT(GetChildCount() > 0);
+ if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
+ mFrames->RemoveEntry(aFrame);
+ if (mFrames->Count() < kMinChildCountForHashtable) {
+ SwitchToCounter();
+ }
+ } else {
+ --mChildCount;
+ }
+ }
+
+ // mBreakType value
+ // Break information is applied *before* the line if the line is a block,
+ // or *after* the line if the line is an inline. Confusing, I know, but
+ // using different names should help.
+ using StyleClear = mozilla::StyleClear;
+ bool HasBreakBefore() const {
+ return IsBlock() && StyleClear::None != BreakType();
+ }
+ void SetBreakTypeBefore(StyleClear aBreakType) {
+ MOZ_ASSERT(IsBlock(), "Only blocks have break-before");
+ MOZ_ASSERT(aBreakType == StyleClear::None ||
+ aBreakType == StyleClear::Left ||
+ aBreakType == StyleClear::Right ||
+ aBreakType == StyleClear::Both,
+ "Only float break types are allowed before a line");
+ mFlags.mBreakType = static_cast<int>(aBreakType);
+ }
+ StyleClear GetBreakTypeBefore() const {
+ return IsBlock() ? BreakType() : StyleClear::None;
+ }
+
+ bool HasBreakAfter() const {
+ return !IsBlock() && StyleClear::None != BreakType();
+ }
+ void SetBreakTypeAfter(StyleClear aBreakType) {
+ MOZ_ASSERT(!IsBlock(), "Only inlines have break-after");
+ mFlags.mBreakType = static_cast<int>(aBreakType);
+ }
+ bool HasFloatBreakAfter() const {
+ return !IsBlock() &&
+ (StyleClear::Left == BreakType() ||
+ StyleClear::Right == BreakType() ||
+ StyleClear::Both == BreakType());
+ }
+ StyleClear GetBreakTypeAfter() const {
+ return !IsBlock() ? BreakType() : StyleClear::None;
+ }
+
+ // mCarriedOutBEndMargin value
+ nsCollapsingMargin GetCarriedOutBEndMargin() const;
+ // Returns true if the margin changed
+ bool SetCarriedOutBEndMargin(nsCollapsingMargin aValue);
+
+ // mFloats
+ bool HasFloats() const {
+ return (IsInline() && mInlineData) && mInlineData->mFloats.NotEmpty();
+ }
+ nsFloatCache* GetFirstFloat();
+ void FreeFloats(nsFloatCacheFreeList& aFreeList);
+ void AppendFloats(nsFloatCacheFreeList& aFreeList);
+ bool RemoveFloat(nsIFrame* aFrame);
+
+ // Combined area is the area of the line that should influence the
+ // overflow area of its parent block. The combined area should be
+ // used for painting-related things, but should never be used for
+ // layout (except for handling of 'overflow').
+ void SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
+ mozilla::LogicalRect GetOverflowArea(nsOverflowType aType,
+ mozilla::WritingMode aWM,
+ const nsSize& aContainerSize)
+ {
+ return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize);
+ }
+ nsRect GetOverflowArea(nsOverflowType aType) {
+ return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds();
+ }
+ nsOverflowAreas GetOverflowAreas() {
+ if (mData) {
+ return mData->mOverflowAreas;
+ }
+ nsRect bounds = GetPhysicalBounds();
+ return nsOverflowAreas(bounds, bounds);
+ }
+ nsRect GetVisualOverflowArea()
+ { return GetOverflowArea(eVisualOverflow); }
+ nsRect GetScrollableOverflowArea()
+ { return GetOverflowArea(eScrollableOverflow); }
+
+ void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) {
+ NS_ASSERTION(aContainerSize == mContainerSize ||
+ mContainerSize == nsSize(-1, -1),
+ "container size doesn't match");
+ mContainerSize = aContainerSize;
+ mBounds.BStart(mWritingMode) += aDBCoord;
+ if (mData) {
+ // Use a null containerSize to convert vector from logical to physical.
+ const nsSize nullContainerSize;
+ nsPoint physicalDelta =
+ mozilla::LogicalPoint(mWritingMode, 0, aDBCoord).
+ GetPhysicalPoint(mWritingMode, nullContainerSize);
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ mData->mOverflowAreas.Overflow(otype) += physicalDelta;
+ }
+ }
+ }
+
+ // Container-size for the line is changing (and therefore if writing mode
+ // was vertical-rl, the line will move physically; this is like SlideBy,
+ // but it is the container size instead of the line's own logical coord
+ // that is changing.
+ nsSize UpdateContainerSize(const nsSize aNewContainerSize)
+ {
+ NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set");
+ nsSize delta = mContainerSize - aNewContainerSize;
+ mContainerSize = aNewContainerSize;
+ // this has a physical-coordinate effect only in vertical-rl mode
+ if (mWritingMode.IsVerticalRL() && mData) {
+ nsPoint physicalDelta(-delta.width, 0);
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ mData->mOverflowAreas.Overflow(otype) += physicalDelta;
+ }
+ }
+ return delta;
+ }
+
+ void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) {
+ NS_ASSERTION(aContainerSize == mContainerSize ||
+ mContainerSize == nsSize(-1, -1),
+ "container size doesn't match");
+ mContainerSize = aContainerSize;
+ mBounds.IStart(mWritingMode) += aDICoord;
+ }
+
+ void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) {
+ NS_ASSERTION(aContainerSize == mContainerSize ||
+ mContainerSize == nsSize(-1, -1),
+ "container size doesn't match");
+ mContainerSize = aContainerSize;
+ mBounds.ISize(mWritingMode) += aDISize;
+ }
+
+ /**
+ * The logical ascent (distance from block-start to baseline) of the
+ * linebox is the logical ascent of the anonymous inline box (for
+ * which we don't actually create a frame) that wraps all the
+ * consecutive inline children of a block.
+ *
+ * This is currently unused for block lines.
+ */
+ nscoord GetLogicalAscent() const { return mAscent; }
+ void SetLogicalAscent(nscoord aAscent) { mAscent = aAscent; }
+
+ nscoord BStart() const {
+ return mBounds.BStart(mWritingMode);
+ }
+ nscoord BSize() const {
+ return mBounds.BSize(mWritingMode);
+ }
+ nscoord BEnd() const {
+ return mBounds.BEnd(mWritingMode);
+ }
+ nscoord IStart() const {
+ return mBounds.IStart(mWritingMode);
+ }
+ nscoord ISize() const {
+ return mBounds.ISize(mWritingMode);
+ }
+ nscoord IEnd() const {
+ return mBounds.IEnd(mWritingMode);
+ }
+ void SetBoundsEmpty() {
+ mBounds.IStart(mWritingMode) = 0;
+ mBounds.ISize(mWritingMode) = 0;
+ mBounds.BStart(mWritingMode) = 0;
+ mBounds.BSize(mWritingMode) = 0;
+ }
+
+ static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
+ nsIFrame* aDestructRoot, nsFrameList* aFrames);
+
+ // search from end to beginning of [aBegin, aEnd)
+ // Returns true if it found the line and false if not.
+ // Moves aEnd as it searches so that aEnd points to the resulting line.
+ // aLastFrameBeforeEnd is the last frame before aEnd (so if aEnd is
+ // the end of the line list, it's just the last frame in the frame
+ // list).
+ static bool RFindLineContaining(nsIFrame* aFrame,
+ const nsLineList_iterator& aBegin,
+ nsLineList_iterator& aEnd,
+ nsIFrame* aLastFrameBeforeEnd,
+ int32_t* aFrameIndexInLine);
+
+#ifdef DEBUG_FRAME_DUMP
+ const char* BreakTypeToString(StyleClear aBreakType) const;
+ char* StateToString(char* aBuf, int32_t aBufSize) const;
+
+ void List(FILE* out, int32_t aIndent, uint32_t aFlags = 0) const;
+ void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const;
+ nsIFrame* LastChild() const;
+#endif
+
+private:
+ int32_t IndexOf(nsIFrame* aFrame) const;
+public:
+
+ bool Contains(nsIFrame* aFrame) const {
+ return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame)
+ : IndexOf(aFrame) >= 0;
+ }
+
+ // whether the line box is "logically" empty (just like nsIFrame::IsEmpty)
+ bool IsEmpty() const;
+
+ // Call this only while in Reflow() for the block the line belongs
+ // to, only between reflowing the line (or sliding it, if we skip
+ // reflowing it) and the end of reflowing the block.
+ bool CachedIsEmpty();
+
+ void InvalidateCachedIsEmpty() {
+ mFlags.mEmptyCacheValid = false;
+ }
+
+ // For debugging purposes
+ bool IsValidCachedIsEmpty() {
+ return mFlags.mEmptyCacheValid;
+ }
+
+#ifdef DEBUG
+ static int32_t GetCtorCount();
+#endif
+
+ nsIFrame* mFirstChild;
+
+ mozilla::WritingMode mWritingMode;
+
+ // Physical size. Use only for physical <-> logical coordinate conversion.
+ nsSize mContainerSize;
+
+ private:
+ mozilla::LogicalRect mBounds;
+
+ public:
+ const mozilla::LogicalRect& GetBounds() { return mBounds; }
+ nsRect GetPhysicalBounds() const
+ {
+ if (mBounds.IsAllZero()) {
+ return nsRect(0, 0, 0, 0);
+ }
+
+ NS_ASSERTION(mContainerSize != nsSize(-1, -1),
+ "mContainerSize not initialized");
+ return mBounds.GetPhysicalRect(mWritingMode, mContainerSize);
+ }
+ void SetBounds(mozilla::WritingMode aWritingMode,
+ nscoord aIStart, nscoord aBStart,
+ nscoord aISize, nscoord aBSize,
+ const nsSize& aContainerSize)
+ {
+ mWritingMode = aWritingMode;
+ mContainerSize = aContainerSize;
+ mBounds = mozilla::LogicalRect(aWritingMode, aIStart, aBStart,
+ aISize, aBSize);
+ }
+ void SetBounds(mozilla::WritingMode aWritingMode,
+ nsRect aRect, const nsSize& aContainerSize)
+ {
+ mWritingMode = aWritingMode;
+ mContainerSize = aContainerSize;
+ mBounds = mozilla::LogicalRect(aWritingMode, aRect, aContainerSize);
+ }
+
+ // mFlags.mHasHashedFrames says which one to use
+ union {
+ nsTHashtable< nsPtrHashKey<nsIFrame> >* mFrames;
+ uint32_t mChildCount;
+ };
+
+ struct FlagBits {
+ uint32_t mDirty : 1;
+ uint32_t mPreviousMarginDirty : 1;
+ uint32_t mHasClearance : 1;
+ uint32_t mBlock : 1;
+ uint32_t mImpactedByFloat : 1;
+ uint32_t mLineWrapped: 1;
+ uint32_t mInvalidateTextRuns : 1;
+ // default 0 = means that the opt potentially applies to this line.
+ // 1 = never skip reflowing this line for a resize reflow
+ uint32_t mResizeReflowOptimizationDisabled: 1;
+ uint32_t mEmptyCacheValid: 1;
+ uint32_t mEmptyCacheState: 1;
+ // mHasBullet indicates that this is an inline line whose block's
+ // bullet is adjacent to this line and non-empty.
+ uint32_t mHasBullet : 1;
+ // Indicates that this line *may* have a placeholder for a float
+ // that was pushed to a later column or page.
+ uint32_t mHadFloatPushed : 1;
+ uint32_t mHasHashedFrames: 1;
+ uint32_t mBreakType : 4;
+ };
+
+ struct ExtraData {
+ explicit ExtraData(const nsRect& aBounds) : mOverflowAreas(aBounds, aBounds) {
+ }
+ nsOverflowAreas mOverflowAreas;
+ };
+
+ struct ExtraBlockData : public ExtraData {
+ explicit ExtraBlockData(const nsRect& aBounds)
+ : ExtraData(aBounds),
+ mCarriedOutBEndMargin()
+ {
+ }
+ nsCollapsingMargin mCarriedOutBEndMargin;
+ };
+
+ struct ExtraInlineData : public ExtraData {
+ explicit ExtraInlineData(const nsRect& aBounds) : ExtraData(aBounds) {
+ }
+ nsFloatCacheList mFloats;
+ };
+
+protected:
+ nscoord mAscent; // see |SetAscent| / |GetAscent|
+ static_assert(sizeof(FlagBits) <= sizeof(uint32_t),
+ "size of FlagBits should not be larger than size of uint32_t");
+ union {
+ uint32_t mAllFlags;
+ FlagBits mFlags;
+ };
+
+ StyleClear BreakType() const {
+ return static_cast<StyleClear>(mFlags.mBreakType);
+ };
+
+ union {
+ ExtraData* mData;
+ ExtraBlockData* mBlockData;
+ ExtraInlineData* mInlineData;
+ };
+
+ void Cleanup();
+ void MaybeFreeData();
+};
+
+/**
+ * A linked list type where the items in the list must inherit from
+ * a link type to fuse allocations.
+ *
+ * API heavily based on the |list| class in the C++ standard.
+ */
+
+class nsLineList_iterator {
+ public:
+ friend class nsLineList;
+ friend class nsLineList_reverse_iterator;
+ friend class nsLineList_const_iterator;
+ friend class nsLineList_const_reverse_iterator;
+
+ typedef nsLineList_iterator iterator_self_type;
+ typedef nsLineList_reverse_iterator iterator_reverse_type;
+
+ typedef nsLineBox& reference;
+ typedef const nsLineBox& const_reference;
+
+ typedef nsLineBox* pointer;
+ typedef const nsLineBox* const_pointer;
+
+ typedef uint32_t size_type;
+ typedef int32_t difference_type;
+
+ typedef nsLineLink link_type;
+
+#ifdef DEBUG
+ nsLineList_iterator() { memset(&mCurrent, 0xcd, sizeof(mCurrent)); }
+#else
+ // Auto generated default constructor OK.
+#endif
+ // Auto generated copy-constructor OK.
+
+ inline iterator_self_type&
+ operator=(const iterator_self_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_reverse_type& aOther);
+
+ iterator_self_type& operator++()
+ {
+ mCurrent = mCurrent->_mNext;
+ return *this;
+ }
+
+ iterator_self_type operator++(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mNext;
+ return rv;
+ }
+
+ iterator_self_type& operator--()
+ {
+ mCurrent = mCurrent->_mPrev;
+ return *this;
+ }
+
+ iterator_self_type operator--(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mPrev;
+ return rv;
+ }
+
+ reference operator*()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return *static_cast<pointer>(mCurrent);
+ }
+
+ pointer operator->()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<pointer>(mCurrent);
+ }
+
+ pointer get()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<pointer>(mCurrent);
+ }
+
+ operator pointer()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<pointer>(mCurrent);
+ }
+
+ const_reference operator*() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return *static_cast<const_pointer>(mCurrent);
+ }
+
+ const_pointer operator->() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+
+#ifndef __MWERKS__
+ operator const_pointer() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+#endif /* !__MWERKS__ */
+
+ iterator_self_type next()
+ {
+ iterator_self_type copy(*this);
+ return ++copy;
+ }
+
+ const iterator_self_type next() const
+ {
+ iterator_self_type copy(*this);
+ return ++copy;
+ }
+
+ iterator_self_type prev()
+ {
+ iterator_self_type copy(*this);
+ return --copy;
+ }
+
+ const iterator_self_type prev() const
+ {
+ iterator_self_type copy(*this);
+ return --copy;
+ }
+
+ // Passing by value rather than by reference and reference to const
+ // to keep AIX happy.
+ bool operator==(const iterator_self_type aOther) const
+ {
+ MOZ_ASSERT(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther) const
+ {
+ MOZ_ASSERT(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+ bool operator==(const iterator_self_type aOther)
+ {
+ MOZ_ASSERT(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther)
+ {
+ MOZ_ASSERT(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+
+ private:
+ link_type *mCurrent;
+#ifdef DEBUG
+ link_type *mListLink; // the list's link, i.e., the end
+#endif
+};
+
+class nsLineList_reverse_iterator {
+
+ public:
+
+ friend class nsLineList;
+ friend class nsLineList_iterator;
+ friend class nsLineList_const_iterator;
+ friend class nsLineList_const_reverse_iterator;
+
+ typedef nsLineList_reverse_iterator iterator_self_type;
+ typedef nsLineList_iterator iterator_reverse_type;
+
+ typedef nsLineBox& reference;
+ typedef const nsLineBox& const_reference;
+
+ typedef nsLineBox* pointer;
+ typedef const nsLineBox* const_pointer;
+
+ typedef uint32_t size_type;
+ typedef int32_t difference_type;
+
+ typedef nsLineLink link_type;
+
+#ifdef DEBUG
+ nsLineList_reverse_iterator() { memset(&mCurrent, 0xcd, sizeof(mCurrent)); }
+#else
+ // Auto generated default constructor OK.
+#endif
+ // Auto generated copy-constructor OK.
+
+ inline iterator_self_type&
+ operator=(const iterator_reverse_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_self_type& aOther);
+
+ iterator_self_type& operator++()
+ {
+ mCurrent = mCurrent->_mPrev;
+ return *this;
+ }
+
+ iterator_self_type operator++(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mPrev;
+ return rv;
+ }
+
+ iterator_self_type& operator--()
+ {
+ mCurrent = mCurrent->_mNext;
+ return *this;
+ }
+
+ iterator_self_type operator--(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mNext;
+ return rv;
+ }
+
+ reference operator*()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return *static_cast<pointer>(mCurrent);
+ }
+
+ pointer operator->()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<pointer>(mCurrent);
+ }
+
+ pointer get()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<pointer>(mCurrent);
+ }
+
+ operator pointer()
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<pointer>(mCurrent);
+ }
+
+ const_reference operator*() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return *static_cast<const_pointer>(mCurrent);
+ }
+
+ const_pointer operator->() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+
+#ifndef __MWERKS__
+ operator const_pointer() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+#endif /* !__MWERKS__ */
+
+ // Passing by value rather than by reference and reference to const
+ // to keep AIX happy.
+ bool operator==(const iterator_self_type aOther) const
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther) const
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+ bool operator==(const iterator_self_type aOther)
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther)
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+
+ private:
+ link_type *mCurrent;
+#ifdef DEBUG
+ link_type *mListLink; // the list's link, i.e., the end
+#endif
+};
+
+class nsLineList_const_iterator {
+ public:
+
+ friend class nsLineList;
+ friend class nsLineList_iterator;
+ friend class nsLineList_reverse_iterator;
+ friend class nsLineList_const_reverse_iterator;
+
+ typedef nsLineList_const_iterator iterator_self_type;
+ typedef nsLineList_const_reverse_iterator iterator_reverse_type;
+ typedef nsLineList_iterator iterator_nonconst_type;
+ typedef nsLineList_reverse_iterator iterator_nonconst_reverse_type;
+
+ typedef nsLineBox& reference;
+ typedef const nsLineBox& const_reference;
+
+ typedef nsLineBox* pointer;
+ typedef const nsLineBox* const_pointer;
+
+ typedef uint32_t size_type;
+ typedef int32_t difference_type;
+
+ typedef nsLineLink link_type;
+
+#ifdef DEBUG
+ nsLineList_const_iterator() { memset(&mCurrent, 0xcd, sizeof(mCurrent)); }
+#else
+ // Auto generated default constructor OK.
+#endif
+ // Auto generated copy-constructor OK.
+
+ inline iterator_self_type&
+ operator=(const iterator_nonconst_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_nonconst_reverse_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_self_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_reverse_type& aOther);
+
+ iterator_self_type& operator++()
+ {
+ mCurrent = mCurrent->_mNext;
+ return *this;
+ }
+
+ iterator_self_type operator++(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mNext;
+ return rv;
+ }
+
+ iterator_self_type& operator--()
+ {
+ mCurrent = mCurrent->_mPrev;
+ return *this;
+ }
+
+ iterator_self_type operator--(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mPrev;
+ return rv;
+ }
+
+ const_reference operator*() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return *static_cast<const_pointer>(mCurrent);
+ }
+
+ const_pointer operator->() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+
+ const_pointer get() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+
+#ifndef __MWERKS__
+ operator const_pointer() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+#endif /* !__MWERKS__ */
+
+ const iterator_self_type next() const
+ {
+ iterator_self_type copy(*this);
+ return ++copy;
+ }
+
+ const iterator_self_type prev() const
+ {
+ iterator_self_type copy(*this);
+ return --copy;
+ }
+
+ // Passing by value rather than by reference and reference to const
+ // to keep AIX happy.
+ bool operator==(const iterator_self_type aOther) const
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther) const
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+ bool operator==(const iterator_self_type aOther)
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther)
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+
+ private:
+ const link_type *mCurrent;
+#ifdef DEBUG
+ const link_type *mListLink; // the list's link, i.e., the end
+#endif
+};
+
+class nsLineList_const_reverse_iterator {
+ public:
+
+ friend class nsLineList;
+ friend class nsLineList_iterator;
+ friend class nsLineList_reverse_iterator;
+ friend class nsLineList_const_iterator;
+
+ typedef nsLineList_const_reverse_iterator iterator_self_type;
+ typedef nsLineList_const_iterator iterator_reverse_type;
+ typedef nsLineList_iterator iterator_nonconst_reverse_type;
+ typedef nsLineList_reverse_iterator iterator_nonconst_type;
+
+ typedef nsLineBox& reference;
+ typedef const nsLineBox& const_reference;
+
+ typedef nsLineBox* pointer;
+ typedef const nsLineBox* const_pointer;
+
+ typedef uint32_t size_type;
+ typedef int32_t difference_type;
+
+ typedef nsLineLink link_type;
+
+#ifdef DEBUG
+ nsLineList_const_reverse_iterator() { memset(&mCurrent, 0xcd, sizeof(mCurrent)); }
+#else
+ // Auto generated default constructor OK.
+#endif
+ // Auto generated copy-constructor OK.
+
+ inline iterator_self_type&
+ operator=(const iterator_nonconst_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_nonconst_reverse_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_self_type& aOther);
+ inline iterator_self_type&
+ operator=(const iterator_reverse_type& aOther);
+
+ iterator_self_type& operator++()
+ {
+ mCurrent = mCurrent->_mPrev;
+ return *this;
+ }
+
+ iterator_self_type operator++(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mPrev;
+ return rv;
+ }
+
+ iterator_self_type& operator--()
+ {
+ mCurrent = mCurrent->_mNext;
+ return *this;
+ }
+
+ iterator_self_type operator--(int)
+ {
+ iterator_self_type rv(*this);
+ mCurrent = mCurrent->_mNext;
+ return rv;
+ }
+
+ const_reference operator*() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return *static_cast<const_pointer>(mCurrent);
+ }
+
+ const_pointer operator->() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+
+ const_pointer get() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+
+#ifndef __MWERKS__
+ operator const_pointer() const
+ {
+ MOZ_ASSERT(mCurrent != mListLink, "running past end");
+ return static_cast<const_pointer>(mCurrent);
+ }
+#endif /* !__MWERKS__ */
+
+ // Passing by value rather than by reference and reference to const
+ // to keep AIX happy.
+ bool operator==(const iterator_self_type aOther) const
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther) const
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+ bool operator==(const iterator_self_type aOther)
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent == aOther.mCurrent;
+ }
+ bool operator!=(const iterator_self_type aOther)
+ {
+ NS_ASSERTION(mListLink == aOther.mListLink, "comparing iterators over different lists");
+ return mCurrent != aOther.mCurrent;
+ }
+
+//private:
+ const link_type *mCurrent;
+#ifdef DEBUG
+ const link_type *mListLink; // the list's link, i.e., the end
+#endif
+};
+
+class nsLineList {
+
+ public:
+
+ friend class nsLineList_iterator;
+ friend class nsLineList_reverse_iterator;
+ friend class nsLineList_const_iterator;
+ friend class nsLineList_const_reverse_iterator;
+
+ typedef uint32_t size_type;
+ typedef int32_t difference_type;
+
+ typedef nsLineLink link_type;
+
+ private:
+ link_type mLink;
+
+ public:
+ typedef nsLineList self_type;
+
+ typedef nsLineBox& reference;
+ typedef const nsLineBox& const_reference;
+
+ typedef nsLineBox* pointer;
+ typedef const nsLineBox* const_pointer;
+
+ typedef nsLineList_iterator iterator;
+ typedef nsLineList_reverse_iterator reverse_iterator;
+ typedef nsLineList_const_iterator const_iterator;
+ typedef nsLineList_const_reverse_iterator const_reverse_iterator;
+
+ nsLineList()
+ {
+ MOZ_COUNT_CTOR(nsLineList);
+ clear();
+ }
+
+ ~nsLineList()
+ {
+ MOZ_COUNT_DTOR(nsLineList);
+ }
+
+ const_iterator begin() const
+ {
+ const_iterator rv;
+ rv.mCurrent = mLink._mNext;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ iterator begin()
+ {
+ iterator rv;
+ rv.mCurrent = mLink._mNext;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ iterator begin(nsLineBox* aLine)
+ {
+ iterator rv;
+ rv.mCurrent = aLine;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ const_iterator end() const
+ {
+ const_iterator rv;
+ rv.mCurrent = &mLink;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ iterator end()
+ {
+ iterator rv;
+ rv.mCurrent = &mLink;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ const_reverse_iterator rbegin() const
+ {
+ const_reverse_iterator rv;
+ rv.mCurrent = mLink._mPrev;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ reverse_iterator rbegin()
+ {
+ reverse_iterator rv;
+ rv.mCurrent = mLink._mPrev;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ reverse_iterator rbegin(nsLineBox* aLine)
+ {
+ reverse_iterator rv;
+ rv.mCurrent = aLine;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ const_reverse_iterator rend() const
+ {
+ const_reverse_iterator rv;
+ rv.mCurrent = &mLink;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ reverse_iterator rend()
+ {
+ reverse_iterator rv;
+ rv.mCurrent = &mLink;
+#ifdef DEBUG
+ rv.mListLink = &mLink;
+#endif
+ return rv;
+ }
+
+ bool empty() const
+ {
+ return mLink._mNext == &mLink;
+ }
+
+ // NOTE: O(N).
+ size_type size() const
+ {
+ size_type count = 0;
+ for (const link_type *cur = mLink._mNext;
+ cur != &mLink;
+ cur = cur->_mNext)
+ {
+ ++count;
+ }
+ return count;
+ }
+
+ pointer front()
+ {
+ NS_ASSERTION(!empty(), "no element to return");
+ return static_cast<pointer>(mLink._mNext);
+ }
+
+ const_pointer front() const
+ {
+ NS_ASSERTION(!empty(), "no element to return");
+ return static_cast<const_pointer>(mLink._mNext);
+ }
+
+ pointer back()
+ {
+ NS_ASSERTION(!empty(), "no element to return");
+ return static_cast<pointer>(mLink._mPrev);
+ }
+
+ const_pointer back() const
+ {
+ NS_ASSERTION(!empty(), "no element to return");
+ return static_cast<const_pointer>(mLink._mPrev);
+ }
+
+ void push_front(pointer aNew)
+ {
+ aNew->_mNext = mLink._mNext;
+ mLink._mNext->_mPrev = aNew;
+ aNew->_mPrev = &mLink;
+ mLink._mNext = aNew;
+ }
+
+ void pop_front()
+ // NOTE: leaves dangling next/prev pointers
+ {
+ NS_ASSERTION(!empty(), "no element to pop");
+ link_type *newFirst = mLink._mNext->_mNext;
+ newFirst->_mPrev = &mLink;
+ // mLink._mNext->_mNext = nullptr;
+ // mLink._mNext->_mPrev = nullptr;
+ mLink._mNext = newFirst;
+ }
+
+ void push_back(pointer aNew)
+ {
+ aNew->_mPrev = mLink._mPrev;
+ mLink._mPrev->_mNext = aNew;
+ aNew->_mNext = &mLink;
+ mLink._mPrev = aNew;
+ }
+
+ void pop_back()
+ // NOTE: leaves dangling next/prev pointers
+ {
+ NS_ASSERTION(!empty(), "no element to pop");
+ link_type *newLast = mLink._mPrev->_mPrev;
+ newLast->_mNext = &mLink;
+ // mLink._mPrev->_mPrev = nullptr;
+ // mLink._mPrev->_mNext = nullptr;
+ mLink._mPrev = newLast;
+ }
+
+ // inserts x before position
+ iterator before_insert(iterator position, pointer x)
+ {
+ // use |mCurrent| to prevent DEBUG_PASS_END assertions
+ x->_mPrev = position.mCurrent->_mPrev;
+ x->_mNext = position.mCurrent;
+ position.mCurrent->_mPrev->_mNext = x;
+ position.mCurrent->_mPrev = x;
+ return --position;
+ }
+
+ // inserts x after position
+ iterator after_insert(iterator position, pointer x)
+ {
+ // use |mCurrent| to prevent DEBUG_PASS_END assertions
+ x->_mNext = position.mCurrent->_mNext;
+ x->_mPrev = position.mCurrent;
+ position.mCurrent->_mNext->_mPrev = x;
+ position.mCurrent->_mNext = x;
+ return ++position;
+ }
+
+ // returns iterator pointing to after the element
+ iterator erase(iterator position)
+ // NOTE: leaves dangling next/prev pointers
+ {
+ position->_mPrev->_mNext = position->_mNext;
+ position->_mNext->_mPrev = position->_mPrev;
+ return ++position;
+ }
+
+ void swap(self_type& y)
+ {
+ link_type tmp(y.mLink);
+ y.mLink = mLink;
+ mLink = tmp;
+
+ if (!empty()) {
+ mLink._mNext->_mPrev = &mLink;
+ mLink._mPrev->_mNext = &mLink;
+ }
+
+ if (!y.empty()) {
+ y.mLink._mNext->_mPrev = &y.mLink;
+ y.mLink._mPrev->_mNext = &y.mLink;
+ }
+ }
+
+ void clear()
+ // NOTE: leaves dangling next/prev pointers
+ {
+ mLink._mNext = &mLink;
+ mLink._mPrev = &mLink;
+ }
+
+ // inserts the conts of x before position and makes x empty
+ void splice(iterator position, self_type& x)
+ {
+ // use |mCurrent| to prevent DEBUG_PASS_END assertions
+ position.mCurrent->_mPrev->_mNext = x.mLink._mNext;
+ x.mLink._mNext->_mPrev = position.mCurrent->_mPrev;
+ x.mLink._mPrev->_mNext = position.mCurrent;
+ position.mCurrent->_mPrev = x.mLink._mPrev;
+ x.clear();
+ }
+
+ // Inserts element *i from list x before position and removes
+ // it from x.
+ void splice(iterator position, self_type& x, iterator i)
+ {
+ NS_ASSERTION(!x.empty(), "Can't insert from empty list.");
+ NS_ASSERTION(position != i && position.mCurrent != i->_mNext,
+ "We don't check for this case.");
+
+ // remove from |x|
+ i->_mPrev->_mNext = i->_mNext;
+ i->_mNext->_mPrev = i->_mPrev;
+
+ // use |mCurrent| to prevent DEBUG_PASS_END assertions
+ // link into |this|, before-side
+ i->_mPrev = position.mCurrent->_mPrev;
+ position.mCurrent->_mPrev->_mNext = i.get();
+
+ // link into |this|, after-side
+ i->_mNext = position.mCurrent;
+ position.mCurrent->_mPrev = i.get();
+ }
+
+ // Inserts elements in [|first|, |last|), which are in |x|,
+ // into |this| before |position| and removes them from |x|.
+ void splice(iterator position, self_type& x, iterator first,
+ iterator last)
+ {
+ NS_ASSERTION(!x.empty(), "Can't insert from empty list.");
+
+ if (first == last)
+ return;
+
+ --last; // so we now want to move [first, last]
+ // remove from |x|
+ first->_mPrev->_mNext = last->_mNext;
+ last->_mNext->_mPrev = first->_mPrev;
+
+ // use |mCurrent| to prevent DEBUG_PASS_END assertions
+ // link into |this|, before-side
+ first->_mPrev = position.mCurrent->_mPrev;
+ position.mCurrent->_mPrev->_mNext = first.get();
+
+ // link into |this|, after-side
+ last->_mNext = position.mCurrent;
+ position.mCurrent->_mPrev = last.get();
+ }
+
+};
+
+
+// Many of these implementations of operator= don't work yet. I don't
+// know why.
+
+#ifdef DEBUG
+
+ // NOTE: ASSIGN_FROM is meant to be used *only* as the entire body
+ // of a function and therefore lacks PR_{BEGIN,END}_MACRO
+#define ASSIGN_FROM(other_) \
+ mCurrent = other_.mCurrent; \
+ mListLink = other_.mListLink; \
+ return *this;
+
+#else /* !NS_LINELIST_DEBUG_PASS_END */
+
+#define ASSIGN_FROM(other_) \
+ mCurrent = other_.mCurrent; \
+ return *this;
+
+#endif /* !NS_LINELIST_DEBUG_PASS_END */
+
+inline
+nsLineList_iterator&
+nsLineList_iterator::operator=(const nsLineList_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_iterator&
+nsLineList_iterator::operator=(const nsLineList_reverse_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_reverse_iterator&
+nsLineList_reverse_iterator::operator=(const nsLineList_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_reverse_iterator&
+nsLineList_reverse_iterator::operator=(const nsLineList_reverse_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_iterator&
+nsLineList_const_iterator::operator=(const nsLineList_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_iterator&
+nsLineList_const_iterator::operator=(const nsLineList_reverse_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_iterator&
+nsLineList_const_iterator::operator=(const nsLineList_const_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_iterator&
+nsLineList_const_iterator::operator=(const nsLineList_const_reverse_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_reverse_iterator&
+nsLineList_const_reverse_iterator::operator=(const nsLineList_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_reverse_iterator&
+nsLineList_const_reverse_iterator::operator=(const nsLineList_reverse_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_reverse_iterator&
+nsLineList_const_reverse_iterator::operator=(const nsLineList_const_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+inline
+nsLineList_const_reverse_iterator&
+nsLineList_const_reverse_iterator::operator=(const nsLineList_const_reverse_iterator& aOther)
+{
+ ASSIGN_FROM(aOther)
+}
+
+
+//----------------------------------------------------------------------
+
+class nsLineIterator final : public nsILineIterator
+{
+public:
+ nsLineIterator();
+ ~nsLineIterator();
+
+ virtual void DisposeLineIterator() override;
+
+ virtual int32_t GetNumLines() override;
+ virtual bool GetDirection() override;
+ NS_IMETHOD GetLine(int32_t aLineNumber,
+ nsIFrame** aFirstFrameOnLine,
+ int32_t* aNumFramesOnLine,
+ nsRect& aLineBounds) override;
+ virtual int32_t FindLineContaining(nsIFrame* aFrame, int32_t aStartLine = 0) override;
+ NS_IMETHOD FindFrameAt(int32_t aLineNumber,
+ nsPoint aPos,
+ nsIFrame** aFrameFound,
+ bool* aPosIsBeforeFirstFrame,
+ bool* aPosIsAfterLastFrame) override;
+
+ NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) override;
+ NS_IMETHOD CheckLineOrder(int32_t aLine,
+ bool *aIsReordered,
+ nsIFrame **aFirstVisual,
+ nsIFrame **aLastVisual) override;
+ nsresult Init(nsLineList& aLines, bool aRightToLeft);
+
+private:
+ nsLineBox* PrevLine() {
+ if (0 == mIndex) {
+ return nullptr;
+ }
+ return mLines[--mIndex];
+ }
+
+ nsLineBox* NextLine() {
+ if (mIndex >= mNumLines - 1) {
+ return nullptr;
+ }
+ return mLines[++mIndex];
+ }
+
+ nsLineBox* LineAt(int32_t aIndex) {
+ if ((aIndex < 0) || (aIndex >= mNumLines)) {
+ return nullptr;
+ }
+ return mLines[aIndex];
+ }
+
+ nsLineBox** mLines;
+ int32_t mIndex;
+ int32_t mNumLines;
+ bool mRightToLeft;
+};
+
+#endif /* nsLineBox_h___ */
diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp
new file mode 100644
index 000000000..138d0b871
--- /dev/null
+++ b/layout/generic/nsLineLayout.cpp
@@ -0,0 +1,3400 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* state and methods used while laying out a single line of a block frame */
+
+// This has to be defined before nsLineLayout.h is included, because
+// nsLineLayout.h has a #include for plarena.h, which needs this defined:
+#define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
+#include "nsLineLayout.h"
+
+#include "LayoutLogging.h"
+#include "SVGTextFrame.h"
+#include "nsBlockFrame.h"
+#include "nsFontMetrics.h"
+#include "nsStyleConsts.h"
+#include "nsContainerFrame.h"
+#include "nsFloatManager.h"
+#include "nsStyleContext.h"
+#include "nsPresContext.h"
+#include "nsGkAtoms.h"
+#include "nsIContent.h"
+#include "nsLayoutUtils.h"
+#include "nsTextFrame.h"
+#include "nsStyleStructInlines.h"
+#include "nsBidiPresUtils.h"
+#include "nsRubyFrame.h"
+#include "nsRubyTextFrame.h"
+#include "RubyUtils.h"
+#include <algorithm>
+
+#ifdef DEBUG
+#undef NOISY_INLINEDIR_ALIGN
+#undef NOISY_BLOCKDIR_ALIGN
+#undef NOISY_REFLOW
+#undef REALLY_NOISY_REFLOW
+#undef NOISY_PUSHING
+#undef REALLY_NOISY_PUSHING
+#undef NOISY_CAN_PLACE_FRAME
+#undef NOISY_TRIM
+#undef REALLY_NOISY_TRIM
+#endif
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+#define FIX_BUG_50257
+
+nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
+ nsFloatManager* aFloatManager,
+ const ReflowInput* aOuterReflowInput,
+ const nsLineList::iterator* aLine,
+ nsLineLayout* aBaseLineLayout)
+ : mPresContext(aPresContext),
+ mFloatManager(aFloatManager),
+ mBlockReflowInput(aOuterReflowInput),
+ mBaseLineLayout(aBaseLineLayout),
+ mLastOptionalBreakFrame(nullptr),
+ mForceBreakFrame(nullptr),
+ mBlockRI(nullptr),/* XXX temporary */
+ mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak),
+ mLastOptionalBreakFrameOffset(-1),
+ mForceBreakFrameOffset(-1),
+ mMinLineBSize(0),
+ mTextIndent(0),
+ mFirstLetterStyleOK(false),
+ mIsTopOfPage(false),
+ mImpactedByFloats(false),
+ mLastFloatWasLetterFrame(false),
+ mLineIsEmpty(false),
+ mLineEndsInBR(false),
+ mNeedBackup(false),
+ mInFirstLine(false),
+ mGotLineBox(false),
+ mInFirstLetter(false),
+ mHasBullet(false),
+ mDirtyNextLine(false),
+ mLineAtStart(false),
+ mHasRuby(false),
+ mSuppressLineWrap(aOuterReflowInput->mFrame->IsSVGText())
+{
+ MOZ_ASSERT(aOuterReflowInput, "aOuterReflowInput must not be null");
+ NS_ASSERTION(aFloatManager || aOuterReflowInput->mFrame->GetType() ==
+ nsGkAtoms::letterFrame,
+ "float manager should be present");
+ MOZ_ASSERT((!!mBaseLineLayout) ==
+ (aOuterReflowInput->mFrame->GetType() ==
+ nsGkAtoms::rubyTextContainerFrame),
+ "Only ruby text container frames have "
+ "a different base line layout");
+ MOZ_COUNT_CTOR(nsLineLayout);
+
+ // Stash away some style data that we need
+ nsBlockFrame* blockFrame = do_QueryFrame(aOuterReflowInput->mFrame);
+ if (blockFrame)
+ mStyleText = blockFrame->StyleTextForLineLayout();
+ else
+ mStyleText = aOuterReflowInput->mFrame->StyleText();
+
+ mLineNumber = 0;
+ mTotalPlacedFrames = 0;
+ mBStartEdge = 0;
+ mTrimmableISize = 0;
+
+ mInflationMinFontSize =
+ nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowInput->mFrame);
+
+ // Instead of always pre-initializing the free-lists for frames and
+ // spans, we do it on demand so that situations that only use a few
+ // frames and spans won't waste a lot of time in unneeded
+ // initialization.
+ PL_INIT_ARENA_POOL(&mArena, "nsLineLayout", 1024);
+ mFrameFreeList = nullptr;
+ mSpanFreeList = nullptr;
+
+ mCurrentSpan = mRootSpan = nullptr;
+ mSpanDepth = 0;
+
+ if (aLine) {
+ mGotLineBox = true;
+ mLineBox = *aLine;
+ }
+}
+
+nsLineLayout::~nsLineLayout()
+{
+ MOZ_COUNT_DTOR(nsLineLayout);
+
+ NS_ASSERTION(nullptr == mRootSpan, "bad line-layout user");
+
+ PL_FinishArenaPool(&mArena);
+}
+
+// Find out if the frame has a non-null prev-in-flow, i.e., whether it
+// is a continuation.
+inline bool
+HasPrevInFlow(nsIFrame *aFrame)
+{
+ nsIFrame *prevInFlow = aFrame->GetPrevInFlow();
+ return prevInFlow != nullptr;
+}
+
+void
+nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
+ nscoord aISize, nscoord aBSize,
+ bool aImpactedByFloats,
+ bool aIsTopOfPage,
+ WritingMode aWritingMode,
+ const nsSize& aContainerSize)
+{
+ NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user");
+ LAYOUT_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained width; this should only result from "
+ "very large sizes, not attempts at intrinsic width "
+ "calculation");
+#ifdef DEBUG
+ if ((aISize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aISize) &&
+ !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) {
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": Init: bad caller: width WAS %d(0x%x)\n",
+ aISize, aISize);
+ }
+ if ((aBSize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aBSize) &&
+ !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) {
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": Init: bad caller: height WAS %d(0x%x)\n",
+ aBSize, aBSize);
+ }
+#endif
+#ifdef NOISY_REFLOW
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
+ aICoord, aBCoord, aISize, aBSize,
+ aImpactedByFloats?"true":"false",
+ aIsTopOfPage ? "top-of-page" : "");
+#endif
+#ifdef DEBUG
+ mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0;
+#endif
+
+ mFirstLetterStyleOK = false;
+ mIsTopOfPage = aIsTopOfPage;
+ mImpactedByFloats = aImpactedByFloats;
+ mTotalPlacedFrames = 0;
+ if (!mBaseLineLayout) {
+ mLineIsEmpty = true;
+ mLineAtStart = true;
+ } else {
+ mLineIsEmpty = false;
+ mLineAtStart = false;
+ }
+ mLineEndsInBR = false;
+ mSpanDepth = 0;
+ mMaxStartBoxBSize = mMaxEndBoxBSize = 0;
+
+ if (mGotLineBox) {
+ mLineBox->ClearHasBullet();
+ }
+
+ PerSpanData* psd = NewPerSpanData();
+ mCurrentSpan = mRootSpan = psd;
+ psd->mReflowInput = mBlockReflowInput;
+ psd->mIStart = aICoord;
+ psd->mICoord = aICoord;
+ psd->mIEnd = aICoord + aISize;
+ mContainerSize = aContainerSize;
+
+ mBStartEdge = aBCoord;
+
+ psd->mNoWrap = !mStyleText->WhiteSpaceCanWrapStyle() || mSuppressLineWrap;
+ psd->mWritingMode = aWritingMode;
+
+ // If this is the first line of a block then see if the text-indent
+ // property amounts to anything.
+
+ if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowInput->mFrame)) {
+ const nsStyleCoord &textIndent = mStyleText->mTextIndent;
+ nscoord pctBasis = 0;
+ if (textIndent.HasPercent()) {
+ pctBasis =
+ mBlockReflowInput->GetContainingBlockContentISize(aWritingMode);
+ }
+ nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
+
+ mTextIndent = indent;
+
+ psd->mICoord += indent;
+ }
+
+ PerFrameData* pfd = NewPerFrameData(mBlockReflowInput->mFrame);
+ pfd->mAscent = 0;
+ pfd->mSpan = psd;
+ psd->mFrame = pfd;
+ nsIFrame* frame = mBlockReflowInput->mFrame;
+ if (frame->GetType() == nsGkAtoms::rubyTextContainerFrame) {
+ // Ruby text container won't be reflowed via ReflowFrame, hence the
+ // relative positioning information should be recorded here.
+ MOZ_ASSERT(mBaseLineLayout != this);
+ pfd->mRelativePos =
+ mBlockReflowInput->mStyleDisplay->IsRelativelyPositionedStyle();
+ if (pfd->mRelativePos) {
+ MOZ_ASSERT(
+ mBlockReflowInput->GetWritingMode() == pfd->mWritingMode,
+ "mBlockReflowInput->frame == frame, "
+ "hence they should have identical writing mode");
+ pfd->mOffsets = mBlockReflowInput->ComputedLogicalOffsets();
+ }
+ }
+}
+
+void
+nsLineLayout::EndLineReflow()
+{
+#ifdef NOISY_REFLOW
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart);
+#endif
+
+ NS_ASSERTION(!mBaseLineLayout ||
+ (!mSpansAllocated && !mSpansFreed && !mSpanFreeList &&
+ !mFramesAllocated && !mFramesFreed && !mFrameFreeList),
+ "Allocated frames or spans on non-base line layout?");
+
+ UnlinkFrame(mRootSpan->mFrame);
+ mCurrentSpan = mRootSpan = nullptr;
+
+ NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak");
+ NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak");
+
+#if 0
+ static int32_t maxSpansAllocated = NS_LINELAYOUT_NUM_SPANS;
+ static int32_t maxFramesAllocated = NS_LINELAYOUT_NUM_FRAMES;
+ if (mSpansAllocated > maxSpansAllocated) {
+ printf("XXX: saw a line with %d spans\n", mSpansAllocated);
+ maxSpansAllocated = mSpansAllocated;
+ }
+ if (mFramesAllocated > maxFramesAllocated) {
+ printf("XXX: saw a line with %d frames\n", mFramesAllocated);
+ maxFramesAllocated = mFramesAllocated;
+ }
+#endif
+}
+
+// XXX swtich to a single mAvailLineWidth that we adjust as each frame
+// on the line is placed. Each span can still have a per-span mICoord that
+// tracks where a child frame is going in its span; they don't need a
+// per-span mIStart?
+
+void
+nsLineLayout::UpdateBand(WritingMode aWM,
+ const LogicalRect& aNewAvailSpace,
+ nsIFrame* aFloatFrame)
+{
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ // need to convert to our writing mode, because we might have a different
+ // mode from the caller due to dir: auto
+ LogicalRect availSpace = aNewAvailSpace.ConvertTo(lineWM, aWM,
+ ContainerSize());
+#ifdef REALLY_NOISY_REFLOW
+ printf("nsLL::UpdateBand %d, %d, %d, %d, (converted to %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n",
+ aNewAvailSpace.IStart(aWM), aNewAvailSpace.BStart(aWM),
+ aNewAvailSpace.ISize(aWM), aNewAvailSpace.BSize(aWM),
+ availSpace.IStart(lineWM), availSpace.BStart(lineWM),
+ availSpace.ISize(lineWM), availSpace.BSize(lineWM),
+ aFloatFrame);
+#endif
+#ifdef DEBUG
+ if ((availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE) &&
+ CRAZY_SIZE(availSpace.ISize(lineWM)) &&
+ !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) {
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n",
+ availSpace.ISize(lineWM), availSpace.ISize(lineWM));
+ }
+ if ((availSpace.BSize(lineWM) != NS_UNCONSTRAINEDSIZE) &&
+ CRAZY_SIZE(availSpace.BSize(lineWM)) &&
+ !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) {
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n",
+ availSpace.BSize(lineWM), availSpace.BSize(lineWM));
+ }
+#endif
+
+ // Compute the difference between last times width and the new width
+ NS_WARNING_ASSERTION(
+ mRootSpan->mIEnd != NS_UNCONSTRAINEDSIZE &&
+ availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained inline size; this should only result from very large "
+ "sizes, not attempts at intrinsic width calculation");
+ // The root span's mIStart moves to aICoord
+ nscoord deltaICoord = availSpace.IStart(lineWM) - mRootSpan->mIStart;
+ // The inline size of all spans changes by this much (the root span's
+ // mIEnd moves to aICoord + aISize, its new inline size is aISize)
+ nscoord deltaISize = availSpace.ISize(lineWM) -
+ (mRootSpan->mIEnd - mRootSpan->mIStart);
+#ifdef NOISY_REFLOW
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n",
+ availSpace.IStart(lineWM), availSpace.BStart(lineWM),
+ availSpace.ISize(lineWM), availSpace.BSize(lineWM),
+ deltaISize, deltaICoord);
+#endif
+
+ // Update the root span position
+ mRootSpan->mIStart += deltaICoord;
+ mRootSpan->mIEnd += deltaICoord;
+ mRootSpan->mICoord += deltaICoord;
+
+ // Now update the right edges of the open spans to account for any
+ // change in available space width
+ for (PerSpanData* psd = mCurrentSpan; psd; psd = psd->mParent) {
+ psd->mIEnd += deltaISize;
+ psd->mContainsFloat = true;
+#ifdef NOISY_REFLOW
+ printf(" span %p: oldIEnd=%d newIEnd=%d\n",
+ psd, psd->mIEnd - deltaISize, psd->mIEnd);
+#endif
+ }
+ NS_ASSERTION(mRootSpan->mContainsFloat &&
+ mRootSpan->mIStart == availSpace.IStart(lineWM) &&
+ mRootSpan->mIEnd == availSpace.IEnd(lineWM),
+ "root span was updated incorrectly?");
+
+ // Update frame bounds
+ // Note: Only adjust the outermost frames (the ones that are direct
+ // children of the block), not the ones in the child spans. The reason
+ // is simple: the frames in the spans have coordinates local to their
+ // parent therefore they are moved when their parent span is moved.
+ if (deltaICoord != 0) {
+ for (PerFrameData* pfd = mRootSpan->mFirstFrame; pfd; pfd = pfd->mNext) {
+ pfd->mBounds.IStart(lineWM) += deltaICoord;
+ }
+ }
+
+ mBStartEdge = availSpace.BStart(lineWM);
+ mImpactedByFloats = true;
+
+ mLastFloatWasLetterFrame = nsGkAtoms::letterFrame == aFloatFrame->GetType();
+}
+
+nsLineLayout::PerSpanData*
+nsLineLayout::NewPerSpanData()
+{
+ nsLineLayout* outerLineLayout = GetOutermostLineLayout();
+ PerSpanData* psd = outerLineLayout->mSpanFreeList;
+ if (!psd) {
+ void *mem;
+ size_t sz = sizeof(PerSpanData);
+ PL_ARENA_ALLOCATE(mem, &outerLineLayout->mArena, sz);
+ if (!mem) {
+ NS_ABORT_OOM(sz);
+ }
+ psd = reinterpret_cast<PerSpanData*>(mem);
+ }
+ else {
+ outerLineLayout->mSpanFreeList = psd->mNextFreeSpan;
+ }
+ psd->mParent = nullptr;
+ psd->mFrame = nullptr;
+ psd->mFirstFrame = nullptr;
+ psd->mLastFrame = nullptr;
+ psd->mContainsFloat = false;
+ psd->mHasNonemptyContent = false;
+
+#ifdef DEBUG
+ outerLineLayout->mSpansAllocated++;
+#endif
+ return psd;
+}
+
+void
+nsLineLayout::BeginSpan(nsIFrame* aFrame,
+ const ReflowInput* aSpanReflowInput,
+ nscoord aIStart, nscoord aIEnd,
+ nscoord* aBaseline)
+{
+ NS_ASSERTION(aIEnd != NS_UNCONSTRAINEDSIZE,
+ "should no longer be using unconstrained sizes");
+#ifdef NOISY_REFLOW
+ nsFrame::IndentBy(stdout, mSpanDepth+1);
+ nsFrame::ListTag(stdout, aFrame);
+ printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart, aIEnd);
+#endif
+
+ PerSpanData* psd = NewPerSpanData();
+ // Link up span frame's pfd to point to its child span data
+ PerFrameData* pfd = mCurrentSpan->mLastFrame;
+ NS_ASSERTION(pfd->mFrame == aFrame, "huh?");
+ pfd->mSpan = psd;
+
+ // Init new span
+ psd->mFrame = pfd;
+ psd->mParent = mCurrentSpan;
+ psd->mReflowInput = aSpanReflowInput;
+ psd->mIStart = aIStart;
+ psd->mICoord = aIStart;
+ psd->mIEnd = aIEnd;
+ psd->mBaseline = aBaseline;
+
+ nsIFrame* frame = aSpanReflowInput->mFrame;
+ psd->mNoWrap = !frame->StyleText()->WhiteSpaceCanWrap(frame) ||
+ mSuppressLineWrap ||
+ frame->StyleContext()->ShouldSuppressLineBreak();
+ psd->mWritingMode = aSpanReflowInput->GetWritingMode();
+
+ // Switch to new span
+ mCurrentSpan = psd;
+ mSpanDepth++;
+}
+
+nscoord
+nsLineLayout::EndSpan(nsIFrame* aFrame)
+{
+ NS_ASSERTION(mSpanDepth > 0, "end-span without begin-span");
+#ifdef NOISY_REFLOW
+ nsFrame::IndentBy(stdout, mSpanDepth);
+ nsFrame::ListTag(stdout, aFrame);
+ printf(": EndSpan width=%d\n", mCurrentSpan->mICoord - mCurrentSpan->mIStart);
+#endif
+ PerSpanData* psd = mCurrentSpan;
+ nscoord iSizeResult = psd->mLastFrame ? (psd->mICoord - psd->mIStart) : 0;
+
+ mSpanDepth--;
+ mCurrentSpan->mReflowInput = nullptr; // no longer valid so null it out!
+ mCurrentSpan = mCurrentSpan->mParent;
+ return iSizeResult;
+}
+
+void
+nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData* aFrame)
+{
+ NS_PRECONDITION(mBaseLineLayout,
+ "This method must not be called in a base line layout.");
+
+ PerFrameData* baseFrame = mBaseLineLayout->LastFrame();
+ MOZ_ASSERT(aFrame && baseFrame);
+ MOZ_ASSERT(!aFrame->mIsLinkedToBase,
+ "The frame must not have been linked with the base");
+#ifdef DEBUG
+ nsIAtom* baseType = baseFrame->mFrame->GetType();
+ nsIAtom* annotationType = aFrame->mFrame->GetType();
+ MOZ_ASSERT((baseType == nsGkAtoms::rubyBaseContainerFrame &&
+ annotationType == nsGkAtoms::rubyTextContainerFrame) ||
+ (baseType == nsGkAtoms::rubyBaseFrame &&
+ annotationType == nsGkAtoms::rubyTextFrame));
+#endif
+
+ aFrame->mNextAnnotation = baseFrame->mNextAnnotation;
+ baseFrame->mNextAnnotation = aFrame;
+ aFrame->mIsLinkedToBase = true;
+}
+
+int32_t
+nsLineLayout::GetCurrentSpanCount() const
+{
+ NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
+ int32_t count = 0;
+ PerFrameData* pfd = mRootSpan->mFirstFrame;
+ while (nullptr != pfd) {
+ count++;
+ pfd = pfd->mNext;
+ }
+ return count;
+}
+
+void
+nsLineLayout::SplitLineTo(int32_t aNewCount)
+{
+ NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
+
+#ifdef REALLY_NOISY_PUSHING
+ printf("SplitLineTo %d (current count=%d); before:\n", aNewCount,
+ GetCurrentSpanCount());
+ DumpPerSpanData(mRootSpan, 1);
+#endif
+ PerSpanData* psd = mRootSpan;
+ PerFrameData* pfd = psd->mFirstFrame;
+ while (nullptr != pfd) {
+ if (--aNewCount == 0) {
+ // Truncate list at pfd (we keep pfd, but anything following is freed)
+ PerFrameData* next = pfd->mNext;
+ pfd->mNext = nullptr;
+ psd->mLastFrame = pfd;
+
+ // Now unlink all of the frames following pfd
+ UnlinkFrame(next);
+ break;
+ }
+ pfd = pfd->mNext;
+ }
+#ifdef NOISY_PUSHING
+ printf("SplitLineTo %d (current count=%d); after:\n", aNewCount,
+ GetCurrentSpanCount());
+ DumpPerSpanData(mRootSpan, 1);
+#endif
+}
+
+void
+nsLineLayout::PushFrame(nsIFrame* aFrame)
+{
+ PerSpanData* psd = mCurrentSpan;
+ NS_ASSERTION(psd->mLastFrame->mFrame == aFrame, "pushing non-last frame");
+
+#ifdef REALLY_NOISY_PUSHING
+ nsFrame::IndentBy(stdout, mSpanDepth);
+ printf("PushFrame %p, before:\n", psd);
+ DumpPerSpanData(psd, 1);
+#endif
+
+ // Take the last frame off of the span's frame list
+ PerFrameData* pfd = psd->mLastFrame;
+ if (pfd == psd->mFirstFrame) {
+ // We are pushing away the only frame...empty the list
+ psd->mFirstFrame = nullptr;
+ psd->mLastFrame = nullptr;
+ }
+ else {
+ PerFrameData* prevFrame = pfd->mPrev;
+ prevFrame->mNext = nullptr;
+ psd->mLastFrame = prevFrame;
+ }
+
+ // Now unlink the frame
+ MOZ_ASSERT(!pfd->mNext);
+ UnlinkFrame(pfd);
+#ifdef NOISY_PUSHING
+ nsFrame::IndentBy(stdout, mSpanDepth);
+ printf("PushFrame: %p after:\n", psd);
+ DumpPerSpanData(psd, 1);
+#endif
+}
+
+void
+nsLineLayout::UnlinkFrame(PerFrameData* pfd)
+{
+ while (nullptr != pfd) {
+ PerFrameData* next = pfd->mNext;
+ if (pfd->mIsLinkedToBase) {
+ // This frame is linked to a ruby base, and should not be freed
+ // now. Just unlink it from the span. It will be freed when its
+ // base frame gets unlinked.
+ pfd->mNext = pfd->mPrev = nullptr;
+ pfd = next;
+ continue;
+ }
+
+ // It is a ruby base frame. If there are any annotations
+ // linked to this frame, free them first.
+ PerFrameData* annotationPFD = pfd->mNextAnnotation;
+ while (annotationPFD) {
+ PerFrameData* nextAnnotation = annotationPFD->mNextAnnotation;
+ MOZ_ASSERT(annotationPFD->mNext == nullptr &&
+ annotationPFD->mPrev == nullptr,
+ "PFD in annotations should have been unlinked.");
+ FreeFrame(annotationPFD);
+ annotationPFD = nextAnnotation;
+ }
+
+ FreeFrame(pfd);
+ pfd = next;
+ }
+}
+
+void
+nsLineLayout::FreeFrame(PerFrameData* pfd)
+{
+ if (nullptr != pfd->mSpan) {
+ FreeSpan(pfd->mSpan);
+ }
+ nsLineLayout* outerLineLayout = GetOutermostLineLayout();
+ pfd->mNext = outerLineLayout->mFrameFreeList;
+ outerLineLayout->mFrameFreeList = pfd;
+#ifdef DEBUG
+ outerLineLayout->mFramesFreed++;
+#endif
+}
+
+void
+nsLineLayout::FreeSpan(PerSpanData* psd)
+{
+ // Unlink its frames
+ UnlinkFrame(psd->mFirstFrame);
+
+ nsLineLayout* outerLineLayout = GetOutermostLineLayout();
+ // Now put the span on the free list since it's free too
+ psd->mNextFreeSpan = outerLineLayout->mSpanFreeList;
+ outerLineLayout->mSpanFreeList = psd;
+#ifdef DEBUG
+ outerLineLayout->mSpansFreed++;
+#endif
+}
+
+bool
+nsLineLayout::IsZeroBSize()
+{
+ PerSpanData* psd = mCurrentSpan;
+ PerFrameData* pfd = psd->mFirstFrame;
+ while (nullptr != pfd) {
+ if (0 != pfd->mBounds.BSize(psd->mWritingMode)) {
+ return false;
+ }
+ pfd = pfd->mNext;
+ }
+ return true;
+}
+
+nsLineLayout::PerFrameData*
+nsLineLayout::NewPerFrameData(nsIFrame* aFrame)
+{
+ nsLineLayout* outerLineLayout = GetOutermostLineLayout();
+ PerFrameData* pfd = outerLineLayout->mFrameFreeList;
+ if (!pfd) {
+ void *mem;
+ size_t sz = sizeof(PerFrameData);
+ PL_ARENA_ALLOCATE(mem, &outerLineLayout->mArena, sz);
+ if (!mem) {
+ NS_ABORT_OOM(sz);
+ }
+ pfd = reinterpret_cast<PerFrameData*>(mem);
+ }
+ else {
+ outerLineLayout->mFrameFreeList = pfd->mNext;
+ }
+ pfd->mSpan = nullptr;
+ pfd->mNext = nullptr;
+ pfd->mPrev = nullptr;
+ pfd->mNextAnnotation = nullptr;
+ pfd->mFrame = aFrame;
+
+ // all flags default to false
+ pfd->mRelativePos = false;
+ pfd->mIsTextFrame = false;
+ pfd->mIsNonEmptyTextFrame = false;
+ pfd->mIsNonWhitespaceTextFrame = false;
+ pfd->mIsLetterFrame = false;
+ pfd->mRecomputeOverflow = false;
+ pfd->mIsBullet = false;
+ pfd->mSkipWhenTrimmingWhitespace = false;
+ pfd->mIsEmpty = false;
+ pfd->mIsLinkedToBase = false;
+
+ pfd->mWritingMode = aFrame->GetWritingMode();
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ pfd->mBounds = LogicalRect(lineWM);
+ pfd->mOverflowAreas.Clear();
+ pfd->mMargin = LogicalMargin(lineWM);
+ pfd->mBorderPadding = LogicalMargin(lineWM);
+ pfd->mOffsets = LogicalMargin(pfd->mWritingMode);
+
+ pfd->mJustificationInfo = JustificationInfo();
+ pfd->mJustificationAssignment = JustificationAssignment();
+
+#ifdef DEBUG
+ pfd->mBlockDirAlign = 0xFF;
+ outerLineLayout->mFramesAllocated++;
+#endif
+ return pfd;
+}
+
+bool
+nsLineLayout::LineIsBreakable() const
+{
+ // XXX mTotalPlacedFrames should go away and we should just use
+ // mLineIsEmpty here instead
+ if ((0 != mTotalPlacedFrames) || mImpactedByFloats) {
+ return true;
+ }
+ return false;
+}
+
+// Checks all four sides for percentage units. This means it should
+// only be used for things (margin, padding) where percentages on top
+// and bottom depend on the *width* just like percentages on left and
+// right.
+static bool
+HasPercentageUnitSide(const nsStyleSides& aSides)
+{
+ NS_FOR_CSS_SIDES(side) {
+ if (aSides.Get(side).HasPercent())
+ return true;
+ }
+ return false;
+}
+
+static bool
+IsPercentageAware(const nsIFrame* aFrame)
+{
+ NS_ASSERTION(aFrame, "null frame is not allowed");
+
+ nsIAtom *fType = aFrame->GetType();
+ if (fType == nsGkAtoms::textFrame) {
+ // None of these things can ever be true for text frames.
+ return false;
+ }
+
+ // Some of these things don't apply to non-replaced inline frames
+ // (that is, fType == nsGkAtoms::inlineFrame), but we won't bother making
+ // things unnecessarily complicated, since they'll probably be set
+ // quite rarely.
+
+ const nsStyleMargin* margin = aFrame->StyleMargin();
+ if (HasPercentageUnitSide(margin->mMargin)) {
+ return true;
+ }
+
+ const nsStylePadding* padding = aFrame->StylePadding();
+ if (HasPercentageUnitSide(padding->mPadding)) {
+ return true;
+ }
+
+ // Note that borders can't be aware of percentages
+
+ const nsStylePosition* pos = aFrame->StylePosition();
+
+ if ((pos->WidthDependsOnContainer() &&
+ pos->mWidth.GetUnit() != eStyleUnit_Auto) ||
+ pos->MaxWidthDependsOnContainer() ||
+ pos->MinWidthDependsOnContainer() ||
+ pos->OffsetHasPercent(NS_SIDE_RIGHT) ||
+ pos->OffsetHasPercent(NS_SIDE_LEFT)) {
+ return true;
+ }
+
+ if (eStyleUnit_Auto == pos->mWidth.GetUnit()) {
+ // We need to check for frames that shrink-wrap when they're auto
+ // width.
+ const nsStyleDisplay* disp = aFrame->StyleDisplay();
+ if (disp->mDisplay == StyleDisplay::InlineBlock ||
+ disp->mDisplay == StyleDisplay::InlineTable ||
+ fType == nsGkAtoms::HTMLButtonControlFrame ||
+ fType == nsGkAtoms::gfxButtonControlFrame ||
+ fType == nsGkAtoms::fieldSetFrame ||
+ fType == nsGkAtoms::comboboxDisplayFrame) {
+ return true;
+ }
+
+ // Per CSS 2.1, section 10.3.2:
+ // If 'height' and 'width' both have computed values of 'auto' and
+ // the element has an intrinsic ratio but no intrinsic height or
+ // width and the containing block's width does not itself depend
+ // on the replaced element's width, then the used value of 'width'
+ // is calculated from the constraint equation used for
+ // block-level, non-replaced elements in normal flow.
+ nsIFrame *f = const_cast<nsIFrame*>(aFrame);
+ if (f->GetIntrinsicRatio() != nsSize(0, 0) &&
+ // Some percents are treated like 'auto', so check != coord
+ pos->mHeight.GetUnit() != eStyleUnit_Coord) {
+ const IntrinsicSize &intrinsicSize = f->GetIntrinsicSize();
+ if (intrinsicSize.width.GetUnit() == eStyleUnit_None &&
+ intrinsicSize.height.GetUnit() == eStyleUnit_None) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+nsLineLayout::ReflowFrame(nsIFrame* aFrame,
+ nsReflowStatus& aReflowStatus,
+ ReflowOutput* aMetrics,
+ bool& aPushedFrame)
+{
+ // Initialize OUT parameter
+ aPushedFrame = false;
+
+ PerFrameData* pfd = NewPerFrameData(aFrame);
+ PerSpanData* psd = mCurrentSpan;
+ psd->AppendFrame(pfd);
+
+#ifdef REALLY_NOISY_REFLOW
+ nsFrame::IndentBy(stdout, mSpanDepth);
+ printf("%p: Begin ReflowFrame pfd=%p ", psd, pfd);
+ nsFrame::ListTag(stdout, aFrame);
+ printf("\n");
+#endif
+
+ if (mCurrentSpan == mRootSpan) {
+ pfd->mFrame->Properties().Remove(nsIFrame::LineBaselineOffset());
+ } else {
+#ifdef DEBUG
+ bool hasLineOffset;
+ pfd->mFrame->Properties().Get(nsIFrame::LineBaselineOffset(), &hasLineOffset);
+ NS_ASSERTION(!hasLineOffset, "LineBaselineOffset was set but was not expected");
+#endif
+ }
+
+ mJustificationInfo = JustificationInfo();
+
+ // Stash copies of some of the computed state away for later
+ // (block-direction alignment, for example)
+ WritingMode frameWM = pfd->mWritingMode;
+ WritingMode lineWM = mRootSpan->mWritingMode;
+
+ // NOTE: While the inline direction coordinate remains relative to the
+ // parent span, the block direction coordinate is fixed at the top
+ // edge for the line. During VerticalAlignFrames we will repair this
+ // so that the block direction coordinate is properly set and relative
+ // to the appropriate span.
+ pfd->mBounds.IStart(lineWM) = psd->mICoord;
+ pfd->mBounds.BStart(lineWM) = mBStartEdge;
+
+ // We want to guarantee that we always make progress when
+ // formatting. Therefore, if the object being placed on the line is
+ // too big for the line, but it is the only thing on the line and is not
+ // impacted by a float, then we go ahead and place it anyway. (If the line
+ // is impacted by one or more floats, then it is safe to break because
+ // we can move the line down below float(s).)
+ //
+ // Capture this state *before* we reflow the frame in case it clears
+ // the state out. We need to know how to treat the current frame
+ // when breaking.
+ bool notSafeToBreak = LineIsEmpty() && !mImpactedByFloats;
+
+ // Figure out whether we're talking about a textframe here
+ nsIAtom* frameType = aFrame->GetType();
+ bool isText = frameType == nsGkAtoms::textFrame;
+
+ // Inline-ish and text-ish things don't compute their width;
+ // everything else does. We need to give them an available width that
+ // reflects the space left on the line.
+ LAYOUT_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained width; this should only result from "
+ "very large sizes, not attempts at intrinsic width "
+ "calculation");
+ nscoord availableSpaceOnLine = psd->mIEnd - psd->mICoord;
+
+ // Setup reflow state for reflowing the frame
+ Maybe<ReflowInput> reflowInputHolder;
+ if (!isText) {
+ // Compute the available size for the frame. This available width
+ // includes room for the side margins.
+ // For now, set the available block-size to unconstrained always.
+ LogicalSize availSize = mBlockReflowInput->ComputedSize(frameWM);
+ availSize.BSize(frameWM) = NS_UNCONSTRAINEDSIZE;
+ reflowInputHolder.emplace(mPresContext, *psd->mReflowInput,
+ aFrame, availSize);
+ ReflowInput& reflowInput = *reflowInputHolder;
+ reflowInput.mLineLayout = this;
+ reflowInput.mFlags.mIsTopOfPage = mIsTopOfPage;
+ if (reflowInput.ComputedISize() == NS_UNCONSTRAINEDSIZE) {
+ reflowInput.AvailableISize() = availableSpaceOnLine;
+ }
+ WritingMode stateWM = reflowInput.GetWritingMode();
+ pfd->mMargin =
+ reflowInput.ComputedLogicalMargin().ConvertTo(lineWM, stateWM);
+ pfd->mBorderPadding =
+ reflowInput.ComputedLogicalBorderPadding().ConvertTo(lineWM, stateWM);
+ pfd->mRelativePos =
+ reflowInput.mStyleDisplay->IsRelativelyPositionedStyle();
+ if (pfd->mRelativePos) {
+ pfd->mOffsets =
+ reflowInput.ComputedLogicalOffsets().ConvertTo(frameWM, stateWM);
+ }
+
+ // Calculate whether the the frame should have a start margin and
+ // subtract the margin from the available width if necessary.
+ // The margin will be applied to the starting inline coordinates of
+ // the frame in CanPlaceFrame() after reflowing the frame.
+ AllowForStartMargin(pfd, reflowInput);
+ }
+ // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent,
+ // because reflow doesn't look at the dirty bits on the frame being reflowed.
+
+ // See if this frame depends on the width of its containing block. If
+ // so, disable resize reflow optimizations for the line. (Note that,
+ // to be conservative, we do this if we *try* to fit a frame on a
+ // line, even if we don't succeed.) (Note also that we can only make
+ // this IsPercentageAware check *after* we've constructed our
+ // ReflowInput, because that construction may be what forces aFrame
+ // to lazily initialize its (possibly-percent-valued) intrinsic size.)
+ if (mGotLineBox && IsPercentageAware(aFrame)) {
+ mLineBox->DisableResizeReflowOptimization();
+ }
+
+ // Note that we don't bother positioning the frame yet, because we're probably
+ // going to end up moving it when we do the block-direction alignment.
+
+ // Adjust spacemanager coordinate system for the frame.
+ ReflowOutput reflowOutput(lineWM);
+#ifdef DEBUG
+ reflowOutput.ISize(lineWM) = nscoord(0xdeadbeef);
+ reflowOutput.BSize(lineWM) = nscoord(0xdeadbeef);
+#endif
+ nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerSize());
+ nscoord tB = pfd->mBounds.BStart(lineWM);
+ mFloatManager->Translate(tI, tB);
+
+ int32_t savedOptionalBreakOffset;
+ gfxBreakPriority savedOptionalBreakPriority;
+ nsIFrame* savedOptionalBreakFrame =
+ GetLastOptionalBreakPosition(&savedOptionalBreakOffset,
+ &savedOptionalBreakPriority);
+
+ if (!isText) {
+ aFrame->Reflow(mPresContext, reflowOutput, *reflowInputHolder, aReflowStatus);
+ } else {
+ static_cast<nsTextFrame*>(aFrame)->
+ ReflowText(*this, availableSpaceOnLine,
+ psd->mReflowInput->mRenderingContext->GetDrawTarget(),
+ reflowOutput, aReflowStatus);
+ }
+
+ pfd->mJustificationInfo = mJustificationInfo;
+ mJustificationInfo = JustificationInfo();
+
+ // See if the frame is a placeholderFrame and if it is process
+ // the float. At the same time, check if the frame has any non-collapsed-away
+ // content.
+ bool placedFloat = false;
+ bool isEmpty;
+ if (!frameType) {
+ isEmpty = pfd->mFrame->IsEmpty();
+ } else {
+ if (nsGkAtoms::placeholderFrame == frameType) {
+ isEmpty = true;
+ pfd->mSkipWhenTrimmingWhitespace = true;
+ nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame);
+ if (outOfFlowFrame) {
+ // Add mTrimmableISize to the available width since if the line ends
+ // here, the width of the inline content will be reduced by
+ // mTrimmableISize.
+ nscoord availableISize = psd->mIEnd - (psd->mICoord - mTrimmableISize);
+ if (psd->mNoWrap) {
+ // If we place floats after inline content where there's
+ // no break opportunity, we don't know how much additional
+ // width is required for the non-breaking content after the float,
+ // so we can't know whether the float plus that content will fit
+ // on the line. So for now, don't place floats after inline
+ // content where there's no break opportunity. This is incorrect
+ // but hopefully rare. Fixing it will require significant
+ // restructuring of line layout.
+ // We might as well allow zero-width floats to be placed, though.
+ availableISize = 0;
+ }
+ placedFloat = GetOutermostLineLayout()->
+ AddFloat(outOfFlowFrame, availableISize);
+ NS_ASSERTION(!(outOfFlowFrame->GetType() == nsGkAtoms::letterFrame &&
+ GetFirstLetterStyleOK()),
+ "FirstLetterStyle set on line with floating first letter");
+ }
+ }
+ else if (isText) {
+ // Note non-empty text-frames for inline frame compatibility hackery
+ pfd->mIsTextFrame = true;
+ nsTextFrame* textFrame = static_cast<nsTextFrame*>(pfd->mFrame);
+ isEmpty = !textFrame->HasNoncollapsedCharacters();
+ if (!isEmpty) {
+ pfd->mIsNonEmptyTextFrame = true;
+ nsIContent* content = textFrame->GetContent();
+
+ const nsTextFragment* frag = content->GetText();
+ if (frag) {
+ pfd->mIsNonWhitespaceTextFrame = !content->TextIsOnlyWhitespace();
+ }
+ }
+ }
+ else if (nsGkAtoms::brFrame == frameType) {
+ pfd->mSkipWhenTrimmingWhitespace = true;
+ isEmpty = false;
+ } else {
+ if (nsGkAtoms::letterFrame==frameType) {
+ pfd->mIsLetterFrame = true;
+ }
+ if (pfd->mSpan) {
+ isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty();
+ } else {
+ isEmpty = pfd->mFrame->IsEmpty();
+ }
+ }
+ }
+ pfd->mIsEmpty = isEmpty;
+
+ mFloatManager->Translate(-tI, -tB);
+
+ NS_ASSERTION(reflowOutput.ISize(lineWM) >= 0, "bad inline size");
+ NS_ASSERTION(reflowOutput.BSize(lineWM) >= 0,"bad block size");
+ if (reflowOutput.ISize(lineWM) < 0) {
+ reflowOutput.ISize(lineWM) = 0;
+ }
+ if (reflowOutput.BSize(lineWM) < 0) {
+ reflowOutput.BSize(lineWM) = 0;
+ }
+
+#ifdef DEBUG
+ // Note: break-before means ignore the reflow metrics since the
+ // frame will be reflowed another time.
+ if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
+ if ((CRAZY_SIZE(reflowOutput.ISize(lineWM)) ||
+ CRAZY_SIZE(reflowOutput.BSize(lineWM))) &&
+ !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) {
+ printf("nsLineLayout: ");
+ nsFrame::ListTag(stdout, aFrame);
+ printf(" metrics=%d,%d!\n", reflowOutput.Width(), reflowOutput.Height());
+ }
+ if ((reflowOutput.Width() == nscoord(0xdeadbeef)) ||
+ (reflowOutput.Height() == nscoord(0xdeadbeef))) {
+ printf("nsLineLayout: ");
+ nsFrame::ListTag(stdout, aFrame);
+ printf(" didn't set w/h %d,%d!\n", reflowOutput.Width(), reflowOutput.Height());
+ }
+ }
+#endif
+
+ // Unlike with non-inline reflow, the overflow area here does *not*
+ // include the accumulation of the frame's bounds and its inline
+ // descendants' bounds. Nor does it include the outline area; it's
+ // just the union of the bounds of any absolute children. That is
+ // added in later by nsLineLayout::ReflowInlineFrames.
+ pfd->mOverflowAreas = reflowOutput.mOverflowAreas;
+
+ pfd->mBounds.ISize(lineWM) = reflowOutput.ISize(lineWM);
+ pfd->mBounds.BSize(lineWM) = reflowOutput.BSize(lineWM);
+
+ // Size the frame, but |RelativePositionFrames| will size the view.
+ aFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd));
+
+ // Tell the frame that we're done reflowing it
+ aFrame->DidReflow(mPresContext,
+ isText ? nullptr : reflowInputHolder.ptr(),
+ nsDidReflowStatus::FINISHED);
+
+ if (aMetrics) {
+ *aMetrics = reflowOutput;
+ }
+
+ if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
+ // If frame is complete and has a next-in-flow, we need to delete
+ // them now. Do not do this when a break-before is signaled because
+ // the frame is going to get reflowed again (and may end up wanting
+ // a next-in-flow where it ends up).
+ if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
+ nsIFrame* kidNextInFlow = aFrame->GetNextInFlow();
+ if (nullptr != kidNextInFlow) {
+ // Remove all of the childs next-in-flows. Make sure that we ask
+ // the right parent to do the removal (it's possible that the
+ // parent is not this because we are executing pullup code)
+ kidNextInFlow->GetParent()->
+ DeleteNextInFlowChild(kidNextInFlow, true);
+ }
+ }
+
+ // Check whether this frame breaks up text runs. All frames break up text
+ // runs (hence return false here) except for text frames and inline containers.
+ bool continuingTextRun = aFrame->CanContinueTextRun();
+
+ // Clear any residual mTrimmableISize if this isn't a text frame
+ if (!continuingTextRun && !pfd->mSkipWhenTrimmingWhitespace) {
+ mTrimmableISize = 0;
+ }
+
+ // See if we can place the frame. If we can't fit it, then we
+ // return now.
+ bool optionalBreakAfterFits;
+ NS_ASSERTION(isText ||
+ !reflowInputHolder->IsFloating(),
+ "How'd we get a floated inline frame? "
+ "The frame ctor should've dealt with this.");
+ if (CanPlaceFrame(pfd, notSafeToBreak, continuingTextRun,
+ savedOptionalBreakFrame != nullptr, reflowOutput,
+ aReflowStatus, &optionalBreakAfterFits)) {
+ if (!isEmpty) {
+ psd->mHasNonemptyContent = true;
+ mLineIsEmpty = false;
+ if (!pfd->mSpan) {
+ // nonempty leaf content has been placed
+ mLineAtStart = false;
+ }
+ if (nsGkAtoms::rubyFrame == frameType) {
+ mHasRuby = true;
+ SyncAnnotationBounds(pfd);
+ }
+ }
+
+ // Place the frame, updating aBounds with the final size and
+ // location. Then apply the bottom+right margins (as
+ // appropriate) to the frame.
+ PlaceFrame(pfd, reflowOutput);
+ PerSpanData* span = pfd->mSpan;
+ if (span) {
+ // The frame we just finished reflowing is an inline
+ // container. It needs its child frames aligned in the block direction,
+ // so do most of it now.
+ VerticalAlignFrames(span);
+ }
+
+ if (!continuingTextRun) {
+ if (!psd->mNoWrap && (!LineIsEmpty() || placedFloat)) {
+ // record soft break opportunity after this content that can't be
+ // part of a text run. This is not a text frame so we know
+ // that offset INT32_MAX means "after the content".
+ if (NotifyOptionalBreakPosition(aFrame, INT32_MAX,
+ optionalBreakAfterFits,
+ gfxBreakPriority::eNormalBreak)) {
+ // If this returns true then we are being told to actually break here.
+ aReflowStatus = NS_INLINE_LINE_BREAK_AFTER(aReflowStatus);
+ }
+ }
+ }
+ }
+ else {
+ PushFrame(aFrame);
+ aPushedFrame = true;
+ // Undo any saved break positions that the frame might have told us about,
+ // since we didn't end up placing it
+ RestoreSavedBreakPosition(savedOptionalBreakFrame,
+ savedOptionalBreakOffset,
+ savedOptionalBreakPriority);
+ }
+ }
+ else {
+ PushFrame(aFrame);
+ aPushedFrame = true;
+ }
+
+#ifdef REALLY_NOISY_REFLOW
+ nsFrame::IndentBy(stdout, mSpanDepth);
+ printf("End ReflowFrame ");
+ nsFrame::ListTag(stdout, aFrame);
+ printf(" status=%x\n", aReflowStatus);
+#endif
+}
+
+void
+nsLineLayout::AllowForStartMargin(PerFrameData* pfd,
+ ReflowInput& aReflowInput)
+{
+ NS_ASSERTION(!aReflowInput.IsFloating(),
+ "How'd we get a floated inline frame? "
+ "The frame ctor should've dealt with this.");
+
+ WritingMode lineWM = mRootSpan->mWritingMode;
+
+ // Only apply start-margin on the first-in flow for inline frames,
+ // and make sure to not apply it to any inline other than the first
+ // in an ib split. Note that the ib sibling (block-in-inline
+ // sibling) annotations only live on the first continuation, but we
+ // don't want to apply the start margin for later continuations
+ // anyway. For box-decoration-break:clone we apply the start-margin
+ // on all continuations.
+ if ((pfd->mFrame->GetPrevContinuation() ||
+ pfd->mFrame->FrameIsNonFirstInIBSplit()) &&
+ aReflowInput.mStyleBorder->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Slice) {
+ // Zero this out so that when we compute the max-element-width of
+ // the frame we will properly avoid adding in the starting margin.
+ pfd->mMargin.IStart(lineWM) = 0;
+ } else if (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedISize()) {
+ NS_WARNING_ASSERTION(
+ NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableISize(),
+ "have unconstrained inline-size; this should only result from very "
+ "large sizes, not attempts at intrinsic inline-size calculation");
+ // For inline-ish and text-ish things (which don't compute widths
+ // in the reflow state), adjust available inline-size to account
+ // for the start margin. The end margin will be accounted for when
+ // we finish flowing the frame.
+ WritingMode wm = aReflowInput.GetWritingMode();
+ aReflowInput.AvailableISize() -=
+ pfd->mMargin.ConvertTo(wm, lineWM).IStart(wm);
+ }
+}
+
+nscoord
+nsLineLayout::GetCurrentFrameInlineDistanceFromBlock()
+{
+ PerSpanData* psd;
+ nscoord x = 0;
+ for (psd = mCurrentSpan; psd; psd = psd->mParent) {
+ x += psd->mICoord;
+ }
+ return x;
+}
+
+/**
+ * This method syncs bounds of ruby annotations and ruby annotation
+ * containers from their rect. It is necessary because:
+ * Containers are not part of the line in their levels, which means
+ * their bounds are not set properly before.
+ * Ruby annotations' position may have been changed when reflowing
+ * their containers.
+ */
+void
+nsLineLayout::SyncAnnotationBounds(PerFrameData* aRubyFrame)
+{
+ MOZ_ASSERT(aRubyFrame->mFrame->GetType() == nsGkAtoms::rubyFrame);
+ MOZ_ASSERT(aRubyFrame->mSpan);
+
+ PerSpanData* span = aRubyFrame->mSpan;
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ for (PerFrameData* pfd = span->mFirstFrame; pfd; pfd = pfd->mNext) {
+ for (PerFrameData* rtc = pfd->mNextAnnotation;
+ rtc; rtc = rtc->mNextAnnotation) {
+ // When the annotation container is reflowed, the width of the
+ // ruby container is unknown so we use a dummy container size;
+ // in the case of RTL block direction, the final position will be
+ // fixed up later.
+ const nsSize dummyContainerSize;
+ LogicalRect rtcBounds(lineWM, rtc->mFrame->GetRect(),
+ dummyContainerSize);
+ rtc->mBounds = rtcBounds;
+ nsSize rtcSize = rtcBounds.Size(lineWM).GetPhysicalSize(lineWM);
+ for (PerFrameData* rt = rtc->mSpan->mFirstFrame; rt; rt = rt->mNext) {
+ LogicalRect rtBounds = rt->mFrame->GetLogicalRect(lineWM, rtcSize);
+ MOZ_ASSERT(rt->mBounds.Size(lineWM) == rtBounds.Size(lineWM),
+ "Size of the annotation should not have been changed");
+ rt->mBounds.SetOrigin(lineWM, rtBounds.Origin(lineWM));
+ }
+ }
+ }
+}
+
+/**
+ * See if the frame can be placed now that we know it's desired size.
+ * We can always place the frame if the line is empty. Note that we
+ * know that the reflow-status is not a break-before because if it was
+ * ReflowFrame above would have returned false, preventing this method
+ * from being called. The logic in this method assumes that.
+ *
+ * Note that there is no check against the Y coordinate because we
+ * assume that the caller will take care of that.
+ */
+bool
+nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
+ bool aNotSafeToBreak,
+ bool aFrameCanContinueTextRun,
+ bool aCanRollBackBeforeFrame,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus,
+ bool* aOptionalBreakAfterFits)
+{
+ NS_PRECONDITION(pfd && pfd->mFrame, "bad args, null pointers for frame data");
+
+ *aOptionalBreakAfterFits = true;
+
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ /*
+ * We want to only apply the end margin if we're the last continuation and
+ * either not in an {ib} split or the last inline in it. In all other
+ * cases we want to zero it out. That means zeroing it out if any of these
+ * conditions hold:
+ * 1) The frame is not complete (in this case it will get a next-in-flow)
+ * 2) The frame is complete but has a non-fluid continuation on its
+ * continuation chain. Note that if it has a fluid continuation, that
+ * continuation will get destroyed later, so we don't want to drop the
+ * end-margin in that case.
+ * 3) The frame is in an {ib} split and is not the last part.
+ *
+ * However, none of that applies if this is a letter frame (XXXbz why?)
+ *
+ * For box-decoration-break:clone we apply the end margin on all
+ * continuations (that are not letter frames).
+ */
+ if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) ||
+ pfd->mFrame->LastInFlow()->GetNextContinuation() ||
+ pfd->mFrame->FrameIsNonLastInIBSplit()) &&
+ !pfd->mIsLetterFrame &&
+ pfd->mFrame->StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Slice) {
+ pfd->mMargin.IEnd(lineWM) = 0;
+ }
+
+ // Apply the start margin to the frame bounds.
+ nscoord startMargin = pfd->mMargin.IStart(lineWM);
+ nscoord endMargin = pfd->mMargin.IEnd(lineWM);
+
+ pfd->mBounds.IStart(lineWM) += startMargin;
+
+ PerSpanData* psd = mCurrentSpan;
+ if (psd->mNoWrap) {
+ // When wrapping is off, everything fits.
+ return true;
+ }
+
+#ifdef NOISY_CAN_PLACE_FRAME
+ if (nullptr != psd->mFrame) {
+ nsFrame::ListTag(stdout, psd->mFrame->mFrame);
+ }
+ printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak ? "true" : "false");
+ nsFrame::ListTag(stdout, pfd->mFrame);
+ printf(" frameWidth=%d, margins=%d,%d\n",
+ pfd->mBounds.IEnd(lineWM) + endMargin - psd->mICoord,
+ startMargin, endMargin);
+#endif
+
+ // Set outside to true if the result of the reflow leads to the
+ // frame sticking outside of our available area.
+ bool outside = pfd->mBounds.IEnd(lineWM) - mTrimmableISize + endMargin >
+ psd->mIEnd;
+ if (!outside) {
+ // If it fits, it fits
+#ifdef NOISY_CAN_PLACE_FRAME
+ printf(" ==> inside\n");
+#endif
+ return true;
+ }
+ *aOptionalBreakAfterFits = false;
+
+ // When it doesn't fit, check for a few special conditions where we
+ // allow it to fit anyway.
+ if (0 == startMargin + pfd->mBounds.ISize(lineWM) + endMargin) {
+ // Empty frames always fit right where they are
+#ifdef NOISY_CAN_PLACE_FRAME
+ printf(" ==> empty frame fits\n");
+#endif
+ return true;
+ }
+
+#ifdef FIX_BUG_50257
+ // another special case: always place a BR
+ if (nsGkAtoms::brFrame == pfd->mFrame->GetType()) {
+#ifdef NOISY_CAN_PLACE_FRAME
+ printf(" ==> BR frame fits\n");
+#endif
+ return true;
+ }
+#endif
+
+ if (aNotSafeToBreak) {
+ // There are no frames on the line that take up width and the line is
+ // not impacted by floats, so we must allow the current frame to be
+ // placed on the line
+#ifdef NOISY_CAN_PLACE_FRAME
+ printf(" ==> not-safe and not-impacted fits: ");
+ while (nullptr != psd) {
+ printf("<psd=%p x=%d left=%d> ", psd, psd->mICoord, psd->mIStart);
+ psd = psd->mParent;
+ }
+ printf("\n");
+#endif
+ return true;
+ }
+
+ // Special check for span frames
+ if (pfd->mSpan && pfd->mSpan->mContainsFloat) {
+ // If the span either directly or indirectly contains a float then
+ // it fits. Why? It's kind of complicated, but here goes:
+ //
+ // 1. CanPlaceFrame is used for all frame placements on a line,
+ // and in a span. This includes recursively placement of frames
+ // inside of spans, and the span itself. Because the logic always
+ // checks for room before proceeding (the code above here), the
+ // only things on a line will be those things that "fit".
+ //
+ // 2. Before a float is placed on a line, the line has to be empty
+ // (otherwise it's a "below current line" float and will be placed
+ // after the line).
+ //
+ // Therefore, if the span directly or indirectly has a float
+ // then it means that at the time of the placement of the float
+ // the line was empty. Because of #1, only the frames that fit can
+ // be added after that point, therefore we can assume that the
+ // current span being placed has fit.
+ //
+ // So how do we get here and have a span that should already fit
+ // and yet doesn't: Simple: span's that have the no-wrap attribute
+ // set on them and contain a float and are placed where they
+ // don't naturally fit.
+ return true;
+ }
+
+ if (aFrameCanContinueTextRun) {
+ // Let it fit, but we reserve the right to roll back.
+ // Note that we usually won't get here because a text frame will break
+ // itself to avoid exceeding the available width.
+ // We'll only get here for text frames that couldn't break early enough.
+#ifdef NOISY_CAN_PLACE_FRAME
+ printf(" ==> placing overflowing textrun, requesting backup\n");
+#endif
+
+ // We will want to try backup.
+ mNeedBackup = true;
+ return true;
+ }
+
+#ifdef NOISY_CAN_PLACE_FRAME
+ printf(" ==> didn't fit\n");
+#endif
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return false;
+}
+
+/**
+ * Place the frame. Update running counters.
+ */
+void
+nsLineLayout::PlaceFrame(PerFrameData* pfd, ReflowOutput& aMetrics)
+{
+ WritingMode lineWM = mRootSpan->mWritingMode;
+
+ // If the frame's block direction does not match the line's, we can't use
+ // its ascent; instead, treat it as a block with baseline at the block-end
+ // edge (or block-begin in the case of an "inverted" line).
+ if (pfd->mWritingMode.GetBlockDir() != lineWM.GetBlockDir()) {
+ pfd->mAscent = lineWM.IsLineInverted() ? 0 : aMetrics.BSize(lineWM);
+ } else {
+ if (aMetrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
+ pfd->mAscent = pfd->mFrame->GetLogicalBaseline(lineWM);
+ } else {
+ pfd->mAscent = aMetrics.BlockStartAscent();
+ }
+ }
+
+ // Advance to next inline coordinate
+ mCurrentSpan->mICoord = pfd->mBounds.IEnd(lineWM) +
+ pfd->mMargin.IEnd(lineWM);
+
+ // Count the number of non-placeholder frames on the line...
+ if (pfd->mFrame->GetType() == nsGkAtoms::placeholderFrame) {
+ NS_ASSERTION(pfd->mBounds.ISize(lineWM) == 0 &&
+ pfd->mBounds.BSize(lineWM) == 0,
+ "placeholders should have 0 width/height (checking "
+ "placeholders were never counted by the old code in "
+ "this function)");
+ } else {
+ mTotalPlacedFrames++;
+ }
+}
+
+void
+nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
+ const ReflowOutput& aMetrics)
+{
+ NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
+ NS_ASSERTION(mGotLineBox, "must have line box");
+
+ nsIFrame *blockFrame = mBlockReflowInput->mFrame;
+ NS_ASSERTION(blockFrame->IsFrameOfType(nsIFrame::eBlockFrame),
+ "must be for block");
+ if (!static_cast<nsBlockFrame*>(blockFrame)->BulletIsEmpty()) {
+ mHasBullet = true;
+ mLineBox->SetHasBullet();
+ }
+
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ PerFrameData* pfd = NewPerFrameData(aFrame);
+ mRootSpan->AppendFrame(pfd);
+ pfd->mIsBullet = true;
+ if (aMetrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
+ pfd->mAscent = aFrame->GetLogicalBaseline(lineWM);
+ } else {
+ pfd->mAscent = aMetrics.BlockStartAscent();
+ }
+
+ // Note: block-coord value will be updated during block-direction alignment
+ pfd->mBounds = LogicalRect(lineWM, aFrame->GetRect(), ContainerSize());
+ pfd->mOverflowAreas = aMetrics.mOverflowAreas;
+}
+
+#ifdef DEBUG
+void
+nsLineLayout::DumpPerSpanData(PerSpanData* psd, int32_t aIndent)
+{
+ nsFrame::IndentBy(stdout, aIndent);
+ printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd),
+ psd->mIStart, psd->mICoord, psd->mIEnd);
+ PerFrameData* pfd = psd->mFirstFrame;
+ while (nullptr != pfd) {
+ nsFrame::IndentBy(stdout, aIndent+1);
+ nsFrame::ListTag(stdout, pfd->mFrame);
+ nsRect rect = pfd->mBounds.GetPhysicalRect(psd->mWritingMode,
+ ContainerSize());
+ printf(" %d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
+ if (pfd->mSpan) {
+ DumpPerSpanData(pfd->mSpan, aIndent + 1);
+ }
+ pfd = pfd->mNext;
+ }
+}
+#endif
+
+#define VALIGN_OTHER 0
+#define VALIGN_TOP 1
+#define VALIGN_BOTTOM 2
+
+void
+nsLineLayout::VerticalAlignLine()
+{
+ // Partially place the children of the block frame. The baseline for
+ // this operation is set to zero so that the y coordinates for all
+ // of the placed children will be relative to there.
+ PerSpanData* psd = mRootSpan;
+ VerticalAlignFrames(psd);
+
+ // *** Note that comments here still use the anachronistic term
+ // "line-height" when we really mean "size of the line in the block
+ // direction", "vertical-align" when we really mean "alignment in
+ // the block direction", and "top" and "bottom" when we really mean
+ // "block start" and "block end". This is partly for brevity and
+ // partly to retain the association with the CSS line-height and
+ // vertical-align properties.
+ //
+ // Compute the line-height. The line-height will be the larger of:
+ //
+ // [1] maxBCoord - minBCoord (the distance between the first child's
+ // block-start edge and the last child's block-end edge)
+ //
+ // [2] the maximum logical box block size (since not every frame may have
+ // participated in #1; for example: "top" and "botttom" aligned frames)
+ //
+ // [3] the minimum line height ("line-height" property set on the
+ // block frame)
+ nscoord lineBSize = psd->mMaxBCoord - psd->mMinBCoord;
+
+ // Now that the line-height is computed, we need to know where the
+ // baseline is in the line. Position baseline so that mMinBCoord is just
+ // inside the start of the line box.
+ nscoord baselineBCoord;
+ if (psd->mMinBCoord < 0) {
+ baselineBCoord = mBStartEdge - psd->mMinBCoord;
+ }
+ else {
+ baselineBCoord = mBStartEdge;
+ }
+
+ // It's also possible that the line block-size isn't tall enough because
+ // of "top" and "bottom" aligned elements that were not accounted for in
+ // min/max BCoord.
+ //
+ // The CSS2 spec doesn't really say what happens when to the
+ // baseline in this situations. What we do is if the largest start
+ // aligned box block size is greater than the line block-size then we leave
+ // the baseline alone. If the largest end aligned box is greater
+ // than the line block-size then we slide the baseline forward by the extra
+ // amount.
+ //
+ // Navigator 4 gives precedence to the first top/bottom aligned
+ // object. We just let block end aligned objects win.
+ if (lineBSize < mMaxEndBoxBSize) {
+ // When the line is shorter than the maximum block start aligned box
+ nscoord extra = mMaxEndBoxBSize - lineBSize;
+ baselineBCoord += extra;
+ lineBSize = mMaxEndBoxBSize;
+ }
+ if (lineBSize < mMaxStartBoxBSize) {
+ lineBSize = mMaxStartBoxBSize;
+ }
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize, baselineBCoord);
+#endif
+
+ // Now position all of the frames in the root span. We will also
+ // recurse over the child spans and place any frames we find with
+ // vertical-align: top or bottom.
+ // XXX PERFORMANCE: set a bit per-span to avoid the extra work
+ // (propagate it upward too)
+ WritingMode lineWM = psd->mWritingMode;
+ for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
+ if (pfd->mBlockDirAlign == VALIGN_OTHER) {
+ pfd->mBounds.BStart(lineWM) += baselineBCoord;
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSize());
+ }
+ }
+ PlaceTopBottomFrames(psd, -mBStartEdge, lineBSize);
+
+ mFinalLineBSize = lineBSize;
+ if (mGotLineBox) {
+ // Fill in returned line-box and max-element-width data
+ mLineBox->SetBounds(lineWM,
+ psd->mIStart, mBStartEdge,
+ psd->mICoord - psd->mIStart, lineBSize,
+ ContainerSize());
+
+ mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge);
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(
+ " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
+ mLineBox->GetBounds().IStart(lineWM), mLineBox->GetBounds().BStart(lineWM),
+ mLineBox->GetBounds().ISize(lineWM), mLineBox->GetBounds().BSize(lineWM),
+ mFinalLineBSize, mLineBox->GetLogicalAscent());
+#endif
+ }
+}
+
+// Place frames with CSS property vertical-align: top or bottom.
+void
+nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
+ nscoord aDistanceFromStart,
+ nscoord aLineBSize)
+{
+ for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
+ PerSpanData* span = pfd->mSpan;
+#ifdef DEBUG
+ NS_ASSERTION(0xFF != pfd->mBlockDirAlign, "umr");
+#endif
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ nsSize containerSize = ContainerSizeForSpan(psd);
+ switch (pfd->mBlockDirAlign) {
+ case VALIGN_TOP:
+ if (span) {
+ pfd->mBounds.BStart(lineWM) = -aDistanceFromStart - span->mMinBCoord;
+ }
+ else {
+ pfd->mBounds.BStart(lineWM) =
+ -aDistanceFromStart + pfd->mMargin.BStart(lineWM);
+ }
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds, containerSize);
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" ");
+ nsFrame::ListTag(stdout, pfd->mFrame);
+ printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
+ pfd->mBounds.BStart(lineWM), aDistanceFromStart,
+ span ? pfd->mBorderPadding.BStart(lineWM) : 0,
+ span ? span->mBStartLeading : 0);
+#endif
+ break;
+ case VALIGN_BOTTOM:
+ if (span) {
+ // Compute bottom leading
+ pfd->mBounds.BStart(lineWM) =
+ -aDistanceFromStart + aLineBSize - span->mMaxBCoord;
+ }
+ else {
+ pfd->mBounds.BStart(lineWM) = -aDistanceFromStart + aLineBSize -
+ pfd->mMargin.BEnd(lineWM) - pfd->mBounds.BSize(lineWM);
+ }
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds, containerSize);
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" ");
+ nsFrame::ListTag(stdout, pfd->mFrame);
+ printf(": y=%d\n", pfd->mBounds.BStart(lineWM));
+#endif
+ break;
+ }
+ if (span) {
+ nscoord fromStart = aDistanceFromStart + pfd->mBounds.BStart(lineWM);
+ PlaceTopBottomFrames(span, fromStart, aLineBSize);
+ }
+ }
+}
+
+static nscoord
+GetBSizeOfEmphasisMarks(nsIFrame* aSpanFrame, float aInflation)
+{
+ RefPtr<nsFontMetrics> fm = nsLayoutUtils::
+ GetFontMetricsOfEmphasisMarks(aSpanFrame->StyleContext(), aInflation);
+ return fm->MaxHeight();
+}
+
+void
+nsLineLayout::AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd,
+ const nsStyleText* aStyleText, float aInflation,
+ bool* aZeroEffectiveSpanBox)
+{
+ MOZ_ASSERT(spanFrame == psd->mFrame->mFrame);
+ nscoord requiredStartLeading = 0;
+ nscoord requiredEndLeading = 0;
+ if (spanFrame->GetType() == nsGkAtoms::rubyFrame) {
+ // We may need to extend leadings here for ruby annotations as
+ // required by section Line Spacing in the CSS Ruby spec.
+ // See http://dev.w3.org/csswg/css-ruby/#line-height
+ auto rubyFrame = static_cast<nsRubyFrame*>(spanFrame);
+ RubyBlockLeadings leadings = rubyFrame->GetBlockLeadings();
+ requiredStartLeading += leadings.mStart;
+ requiredEndLeading += leadings.mEnd;
+ }
+ if (aStyleText->HasTextEmphasis()) {
+ nscoord bsize = GetBSizeOfEmphasisMarks(spanFrame, aInflation);
+ LogicalSide side = aStyleText->TextEmphasisSide(mRootSpan->mWritingMode);
+ if (side == eLogicalSideBStart) {
+ requiredStartLeading += bsize;
+ } else {
+ MOZ_ASSERT(side == eLogicalSideBEnd,
+ "emphasis marks must be in block axis");
+ requiredEndLeading += bsize;
+ }
+ }
+
+ nscoord requiredLeading = requiredStartLeading + requiredEndLeading;
+ // If we do not require any additional leadings, don't touch anything
+ // here even if it is greater than the original leading, because the
+ // latter could be negative.
+ if (requiredLeading != 0) {
+ nscoord leading = psd->mBStartLeading + psd->mBEndLeading;
+ nscoord deltaLeading = requiredLeading - leading;
+ if (deltaLeading > 0) {
+ // If the total leading is not wide enough for ruby annotations
+ // and/or emphasis marks, extend the side which is not enough. If
+ // both sides are not wide enough, replace the leadings with the
+ // requested values.
+ if (requiredStartLeading < psd->mBStartLeading) {
+ psd->mBEndLeading += deltaLeading;
+ } else if (requiredEndLeading < psd->mBEndLeading) {
+ psd->mBStartLeading += deltaLeading;
+ } else {
+ psd->mBStartLeading = requiredStartLeading;
+ psd->mBEndLeading = requiredEndLeading;
+ }
+ psd->mLogicalBSize += deltaLeading;
+ // We have adjusted the leadings, it is no longer a zero
+ // effective span box.
+ *aZeroEffectiveSpanBox = false;
+ }
+ }
+}
+
+static float
+GetInflationForBlockDirAlignment(nsIFrame* aFrame,
+ nscoord aInflationMinFontSize)
+{
+ if (aFrame->IsSVGText()) {
+ const nsIFrame* container =
+ nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame);
+ NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
+ return
+ static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
+ }
+ return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize);
+}
+
+#define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX
+#define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN
+
+// Place frames in the block direction within a given span (CSS property
+// vertical-align) Note: this doesn't place frames with vertical-align:
+// top or bottom as those have to wait until the entire line box block
+// size is known. This is called after the span frame has finished being
+// reflowed so that we know its block size.
+void
+nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
+{
+ // Get parent frame info
+ PerFrameData* spanFramePFD = psd->mFrame;
+ nsIFrame* spanFrame = spanFramePFD->mFrame;
+
+ // Get the parent frame's font for all of the frames in this span
+ float inflation =
+ GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize);
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(spanFrame, inflation);
+
+ bool preMode = mStyleText->WhiteSpaceIsSignificant();
+
+ // See if the span is an empty continuation. It's an empty continuation iff:
+ // - it has a prev-in-flow
+ // - it has no next in flow
+ // - it's zero sized
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ bool emptyContinuation = psd != mRootSpan &&
+ spanFrame->GetPrevInFlow() && !spanFrame->GetNextInFlow() &&
+ spanFramePFD->mBounds.IsZeroSize();
+
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
+ nsFrame::ListTag(stdout, spanFrame);
+ printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
+ preMode ? "yes" : "no",
+ mPresContext->CompatibilityMode() != eCompatibility_NavQuirks ? "yes" : "no",
+ spanFramePFD->mBounds.ISize(lineWM),
+ spanFramePFD->mBounds.BSize(lineWM),
+ emptyContinuation ? "yes" : "no");
+ if (psd != mRootSpan) {
+ printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
+ spanFramePFD->mBorderPadding.Top(lineWM),
+ spanFramePFD->mBorderPadding.Right(lineWM),
+ spanFramePFD->mBorderPadding.Bottom(lineWM),
+ spanFramePFD->mBorderPadding.Left(lineWM),
+ spanFramePFD->mMargin.Top(lineWM),
+ spanFramePFD->mMargin.Right(lineWM),
+ spanFramePFD->mMargin.Bottom(lineWM),
+ spanFramePFD->mMargin.Left(lineWM));
+ }
+ printf("\n");
+#endif
+
+ // Compute the span's zeroEffectiveSpanBox flag. What we are trying
+ // to determine is how we should treat the span: should it act
+ // "normally" according to css2 or should it effectively
+ // "disappear".
+ //
+ // In general, if the document being processed is in full standards
+ // mode then it should act normally (with one exception). The
+ // exception case is when a span is continued and yet the span is
+ // empty (e.g. compressed whitespace). For this kind of span we treat
+ // it as if it were not there so that it doesn't impact the
+ // line block-size.
+ //
+ // In almost standards mode or quirks mode, we should sometimes make
+ // it disappear. The cases that matter are those where the span
+ // contains no real text elements that would provide an ascent and
+ // descent and height. However, if css style elements have been
+ // applied to the span (border/padding/margin) so that it's clear the
+ // document author is intending css2 behavior then we act as if strict
+ // mode is set.
+ //
+ // This code works correctly for preMode, because a blank line
+ // in PRE mode is encoded as a text node with a LF in it, since
+ // text nodes with only whitespace are considered in preMode.
+ //
+ // Much of this logic is shared with the various implementations of
+ // nsIFrame::IsEmpty since they need to duplicate the way it makes
+ // some lines empty. However, nsIFrame::IsEmpty can't be reused here
+ // since this code sets zeroEffectiveSpanBox even when there are
+ // non-empty children.
+ bool zeroEffectiveSpanBox = false;
+ // XXXldb If we really have empty continuations, then all these other
+ // checks don't make sense for them.
+ // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
+ // it agrees with this code. (If it doesn't agree, it probably should.)
+ if ((emptyContinuation ||
+ mPresContext->CompatibilityMode() != eCompatibility_FullStandards) &&
+ ((psd == mRootSpan) ||
+ (spanFramePFD->mBorderPadding.IsAllZero() &&
+ spanFramePFD->mMargin.IsAllZero()))) {
+ // This code handles an issue with compatibility with non-css
+ // conformant browsers. In particular, there are some cases
+ // where the font-size and line-height for a span must be
+ // ignored and instead the span must *act* as if it were zero
+ // sized. In general, if the span contains any non-compressed
+ // text then we don't use this logic.
+ // However, this is not propagated outwards, since (in compatibility
+ // mode) we don't want big line heights for things like
+ // <p><font size="-1">Text</font></p>
+
+ // We shouldn't include any whitespace that collapses, unless we're
+ // preformatted (in which case it shouldn't, but the width=0 test is
+ // perhaps incorrect). This includes whitespace at the beginning of
+ // a line and whitespace preceded (?) by other whitespace.
+ // See bug 134580 and bug 155333.
+ zeroEffectiveSpanBox = true;
+ for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
+ if (pfd->mIsTextFrame &&
+ (pfd->mIsNonWhitespaceTextFrame || preMode ||
+ pfd->mBounds.ISize(mRootSpan->mWritingMode) != 0)) {
+ zeroEffectiveSpanBox = false;
+ break;
+ }
+ }
+ }
+
+ // Setup baselineBCoord, minBCoord, and maxBCoord
+ nscoord baselineBCoord, minBCoord, maxBCoord;
+ if (psd == mRootSpan) {
+ // Use a zero baselineBCoord since we don't yet know where the baseline
+ // will be (until we know how tall the line is; then we will
+ // know). In addition, use extreme values for the minBCoord and maxBCoord
+ // values so that only the child frames will impact their values
+ // (since these are children of the block, there is no span box to
+ // provide initial values).
+ baselineBCoord = 0;
+ minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM;
+ maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM;
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf("[RootSpan]");
+ nsFrame::ListTag(stdout, spanFrame);
+ printf(": pass1 valign frames: topEdge=%d minLineBSize=%d zeroEffectiveSpanBox=%s\n",
+ mBStartEdge, mMinLineBSize,
+ zeroEffectiveSpanBox ? "yes" : "no");
+#endif
+ }
+ else {
+ // Compute the logical block size for this span. The logical block size
+ // is based on the "line-height" value, not the font-size. Also
+ // compute the top leading.
+ float inflation =
+ GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize);
+ nscoord logicalBSize = ReflowInput::
+ CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(),
+ mBlockReflowInput->ComputedHeight(),
+ inflation);
+ nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) -
+ spanFramePFD->mBorderPadding.BStartEnd(lineWM);
+
+ // Special-case for a ::first-letter frame, set the line height to
+ // the frame block size if the user has left line-height == normal
+ const nsStyleText* styleText = spanFrame->StyleText();
+ if (spanFramePFD->mIsLetterFrame &&
+ !spanFrame->GetPrevInFlow() &&
+ styleText->mLineHeight.GetUnit() == eStyleUnit_Normal) {
+ logicalBSize = spanFramePFD->mBounds.BSize(lineWM);
+ }
+
+ nscoord leading = logicalBSize - contentBSize;
+ psd->mBStartLeading = leading / 2;
+ psd->mBEndLeading = leading - psd->mBStartLeading;
+ psd->mLogicalBSize = logicalBSize;
+ AdjustLeadings(spanFrame, psd, styleText, inflation,
+ &zeroEffectiveSpanBox);
+
+ if (zeroEffectiveSpanBox) {
+ // When the span-box is to be ignored, zero out the initial
+ // values so that the span doesn't impact the final line
+ // height. The contents of the span can impact the final line
+ // height.
+
+ // Note that things are readjusted for this span after its children
+ // are reflowed
+ minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM;
+ maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM;
+ }
+ else {
+
+ // The initial values for the min and max block coord values are in the
+ // span's coordinate space, and cover the logical block size of the span.
+ // If there are child frames in this span that stick out of this area
+ // then the minBCoord and maxBCoord are updated by the amount of logical
+ // blockSize that is outside this range.
+ minBCoord = spanFramePFD->mBorderPadding.BStart(lineWM) -
+ psd->mBStartLeading;
+ maxBCoord = minBCoord + psd->mLogicalBSize;
+ }
+
+ // This is the distance from the top edge of the parents visual
+ // box to the baseline. The span already computed this for us,
+ // so just use it.
+ *psd->mBaseline = baselineBCoord = spanFramePFD->mAscent;
+
+
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
+ nsFrame::ListTag(stdout, spanFrame);
+ printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
+ baselineBCoord, psd->mLogicalBSize, psd->mBStartLeading,
+ spanFramePFD->mBounds.BSize(lineWM),
+ spanFramePFD->mBorderPadding.Top(lineWM),
+ spanFramePFD->mBorderPadding.Bottom(lineWM),
+ zeroEffectiveSpanBox ? "yes" : "no");
+#endif
+ }
+
+ nscoord maxStartBoxBSize = 0;
+ nscoord maxEndBoxBSize = 0;
+ PerFrameData* pfd = psd->mFirstFrame;
+ while (nullptr != pfd) {
+ nsIFrame* frame = pfd->mFrame;
+
+ // sanity check (see bug 105168, non-reproducible crashes from null frame)
+ NS_ASSERTION(frame, "null frame in PerFrameData - something is very very bad");
+ if (!frame) {
+ return;
+ }
+
+ // Compute the logical block size of the frame
+ nscoord logicalBSize;
+ PerSpanData* frameSpan = pfd->mSpan;
+ if (frameSpan) {
+ // For span frames the logical-block-size and start-leading were
+ // pre-computed when the span was reflowed.
+ logicalBSize = frameSpan->mLogicalBSize;
+ }
+ else {
+ // For other elements the logical block size is the same as the
+ // frame's block size plus its margins.
+ logicalBSize = pfd->mBounds.BSize(lineWM) +
+ pfd->mMargin.BStartEnd(lineWM);
+ if (logicalBSize < 0 &&
+ mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
+ pfd->mAscent -= logicalBSize;
+ logicalBSize = 0;
+ }
+ }
+
+ // Get vertical-align property ("vertical-align" is the CSS name for
+ // block-direction align)
+ const nsStyleCoord& verticalAlign = frame->StyleDisplay()->mVerticalAlign;
+ uint8_t verticalAlignEnum = frame->VerticalAlignEnum();
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [frame]");
+ nsFrame::ListTag(stdout, frame);
+ printf(": verticalAlignUnit=%d (enum == %d",
+ verticalAlign.GetUnit(),
+ ((eStyleUnit_Enumerated == verticalAlign.GetUnit())
+ ? verticalAlign.GetIntValue()
+ : -1));
+ if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) {
+ printf(", after SVG dominant-baseline conversion == %d",
+ verticalAlignEnum);
+ }
+ printf(")\n");
+#endif
+
+ if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) {
+ if (lineWM.IsVertical()) {
+ if (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_MIDDLE) {
+ // For vertical writing mode where the dominant baseline is centered
+ // (i.e. text-orientation is not sideways-*), we remap 'middle' to
+ // 'middle-with-baseline' so that images align sensibly with the
+ // center-baseline-aligned text.
+ if (!lineWM.IsSideways()) {
+ verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE;
+ }
+ } else if (lineWM.IsLineInverted()) {
+ // Swap the meanings of top and bottom when line is inverted
+ // relative to block direction.
+ switch (verticalAlignEnum) {
+ case NS_STYLE_VERTICAL_ALIGN_TOP:
+ verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BOTTOM;
+ break;
+ case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
+ verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TOP;
+ break;
+ case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
+ verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM;
+ break;
+ case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
+ verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TEXT_TOP;
+ break;
+ }
+ }
+ }
+
+ // baseline coord that may be adjusted for script offset
+ nscoord revisedBaselineBCoord = baselineBCoord;
+
+ // For superscript and subscript, raise or lower the baseline of the box
+ // to the proper offset of the parent's box, then proceed as for BASELINE
+ if (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUB ||
+ verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUPER) {
+ revisedBaselineBCoord += lineWM.FlowRelativeToLineRelativeFactor() *
+ (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUB
+ ? fm->SubscriptOffset() : -fm->SuperscriptOffset());
+ verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BASELINE;
+ }
+
+ switch (verticalAlignEnum) {
+ default:
+ case NS_STYLE_VERTICAL_ALIGN_BASELINE:
+ if (lineWM.IsVertical() && !lineWM.IsSideways()) {
+ if (frameSpan) {
+ pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord -
+ pfd->mBounds.BSize(lineWM)/2;
+ } else {
+ pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord -
+ logicalBSize/2 +
+ pfd->mMargin.BStart(lineWM);
+ }
+ } else {
+ pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
+ }
+ pfd->mBlockDirAlign = VALIGN_OTHER;
+ break;
+
+ case NS_STYLE_VERTICAL_ALIGN_TOP:
+ {
+ pfd->mBlockDirAlign = VALIGN_TOP;
+ nscoord subtreeBSize = logicalBSize;
+ if (frameSpan) {
+ subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord;
+ NS_ASSERTION(subtreeBSize >= logicalBSize,
+ "unexpected subtree block size");
+ }
+ if (subtreeBSize > maxStartBoxBSize) {
+ maxStartBoxBSize = subtreeBSize;
+ }
+ break;
+ }
+
+ case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
+ {
+ pfd->mBlockDirAlign = VALIGN_BOTTOM;
+ nscoord subtreeBSize = logicalBSize;
+ if (frameSpan) {
+ subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord;
+ NS_ASSERTION(subtreeBSize >= logicalBSize,
+ "unexpected subtree block size");
+ }
+ if (subtreeBSize > maxEndBoxBSize) {
+ maxEndBoxBSize = subtreeBSize;
+ }
+ break;
+ }
+
+ case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
+ {
+ // Align the midpoint of the frame with 1/2 the parents
+ // x-height above the baseline.
+ nscoord parentXHeight =
+ lineWM.FlowRelativeToLineRelativeFactor() * fm->XHeight();
+ if (frameSpan) {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord -
+ (parentXHeight + pfd->mBounds.BSize(lineWM))/2;
+ }
+ else {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord -
+ (parentXHeight + logicalBSize)/2 +
+ pfd->mMargin.BStart(lineWM);
+ }
+ pfd->mBlockDirAlign = VALIGN_OTHER;
+ break;
+ }
+
+ case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
+ {
+ // The top of the logical box is aligned with the top of
+ // the parent element's text.
+ // XXX For vertical text we will need a new API to get the logical
+ // max-ascent here
+ nscoord parentAscent =
+ lineWM.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
+ if (frameSpan) {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent -
+ pfd->mBorderPadding.BStart(lineWM) + frameSpan->mBStartLeading;
+ }
+ else {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent +
+ pfd->mMargin.BStart(lineWM);
+ }
+ pfd->mBlockDirAlign = VALIGN_OTHER;
+ break;
+ }
+
+ case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
+ {
+ // The bottom of the logical box is aligned with the
+ // bottom of the parent elements text.
+ nscoord parentDescent =
+ lineWM.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
+ if (frameSpan) {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
+ pfd->mBounds.BSize(lineWM) +
+ pfd->mBorderPadding.BEnd(lineWM) -
+ frameSpan->mBEndLeading;
+ }
+ else {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
+ pfd->mBounds.BSize(lineWM) -
+ pfd->mMargin.BEnd(lineWM);
+ }
+ pfd->mBlockDirAlign = VALIGN_OTHER;
+ break;
+ }
+
+ case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
+ {
+ // Align the midpoint of the frame with the baseline of the parent.
+ if (frameSpan) {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord -
+ pfd->mBounds.BSize(lineWM)/2;
+ }
+ else {
+ pfd->mBounds.BStart(lineWM) = baselineBCoord - logicalBSize/2 +
+ pfd->mMargin.BStart(lineWM);
+ }
+ pfd->mBlockDirAlign = VALIGN_OTHER;
+ break;
+ }
+ }
+ } else {
+ // We have either a coord, a percent, or a calc().
+ nscoord pctBasis = 0;
+ if (verticalAlign.HasPercent()) {
+ // Percentages are like lengths, except treated as a percentage
+ // of the elements line block size value.
+ float inflation =
+ GetInflationForBlockDirAlignment(frame, mInflationMinFontSize);
+ pctBasis = ReflowInput::CalcLineHeight(frame->GetContent(),
+ frame->StyleContext(), mBlockReflowInput->ComputedBSize(),
+ inflation);
+ }
+ nscoord offset =
+ nsRuleNode::ComputeCoordPercentCalc(verticalAlign, pctBasis);
+ // According to the CSS2 spec (10.8.1), a positive value
+ // "raises" the box by the given distance while a negative value
+ // "lowers" the box by the given distance (with zero being the
+ // baseline). Since Y coordinates increase towards the bottom of
+ // the screen we reverse the sign, unless the line orientation is
+ // inverted relative to block direction.
+ nscoord revisedBaselineBCoord = baselineBCoord - offset *
+ lineWM.FlowRelativeToLineRelativeFactor();
+ if (lineWM.IsVertical() && !lineWM.IsSideways()) {
+ // If we're using a dominant center baseline, we align with the center
+ // of the frame being placed (bug 1133945).
+ pfd->mBounds.BStart(lineWM) =
+ revisedBaselineBCoord - pfd->mBounds.BSize(lineWM)/2;
+ } else {
+ pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
+ }
+ pfd->mBlockDirAlign = VALIGN_OTHER;
+ }
+
+ // Update minBCoord/maxBCoord for frames that we just placed. Do not factor
+ // text into the equation.
+ if (pfd->mBlockDirAlign == VALIGN_OTHER) {
+ // Text frames do not contribute to the min/max Y values for the
+ // line (instead their parent frame's font-size contributes).
+ // XXXrbs -- relax this restriction because it causes text frames
+ // to jam together when 'font-size-adjust' is enabled
+ // and layout is using dynamic font heights (bug 20394)
+ // -- Note #1: With this code enabled and with the fact that we are not
+ // using Em[Ascent|Descent] as nsDimensions for text metrics in
+ // GFX mean that the discussion in bug 13072 cannot hold.
+ // -- Note #2: We still don't want empty-text frames to interfere.
+ // For example in quirks mode, avoiding empty text frames prevents
+ // "tall" lines around elements like <hr> since the rules of <hr>
+ // in quirks.css have pseudo text contents with LF in them.
+#if 0
+ if (!pfd->mIsTextFrame) {
+#else
+ // Only consider non empty text frames when line-height=normal
+ bool canUpdate = !pfd->mIsTextFrame;
+ if (!canUpdate && pfd->mIsNonWhitespaceTextFrame) {
+ canUpdate =
+ frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal;
+ }
+ if (canUpdate) {
+#endif
+ nscoord blockStart, blockEnd;
+ if (frameSpan) {
+ // For spans that were are now placing, use their position
+ // plus their already computed min-Y and max-Y values for
+ // computing blockStart and blockEnd.
+ blockStart = pfd->mBounds.BStart(lineWM) + frameSpan->mMinBCoord;
+ blockEnd = pfd->mBounds.BStart(lineWM) + frameSpan->mMaxBCoord;
+ }
+ else {
+ blockStart = pfd->mBounds.BStart(lineWM) -
+ pfd->mMargin.BStart(lineWM);
+ blockEnd = blockStart + logicalBSize;
+ }
+ if (!preMode &&
+ mPresContext->CompatibilityMode() != eCompatibility_FullStandards &&
+ !logicalBSize) {
+ // Check if it's a BR frame that is not alone on its line (it
+ // is given a block size of zero to indicate this), and if so reset
+ // blockStart and blockEnd so that BR frames don't influence the line.
+ if (nsGkAtoms::brFrame == frame->GetType()) {
+ blockStart = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM;
+ blockEnd = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM;
+ }
+ }
+ if (blockStart < minBCoord) minBCoord = blockStart;
+ if (blockEnd > maxBCoord) maxBCoord = blockEnd;
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n",
+ pfd->mAscent, pfd->mBounds.BSize(lineWM),
+ pfd->mBorderPadding.Top(lineWM),
+ pfd->mBorderPadding.Bottom(lineWM),
+ logicalBSize,
+ frameSpan ? frameSpan->mBStartLeading : 0,
+ pfd->mBounds.BStart(lineWM), minBCoord, maxBCoord);
+#endif
+ }
+ if (psd != mRootSpan) {
+ frame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd));
+ }
+ }
+ pfd = pfd->mNext;
+ }
+
+ // Factor in the minimum line block-size when handling the root-span for
+ // the block.
+ if (psd == mRootSpan) {
+ // We should factor in the block element's minimum line-height (as
+ // defined in section 10.8.1 of the css2 spec) assuming that
+ // zeroEffectiveSpanBox is not set on the root span. This only happens
+ // in some cases in quirks mode:
+ // (1) if the root span contains non-whitespace text directly (this
+ // is handled by zeroEffectiveSpanBox
+ // (2) if this line has a bullet
+ // (3) if this is the last line of an LI, DT, or DD element
+ // (The last line before a block also counts, but not before a
+ // BR) (NN4/IE5 quirk)
+
+ // (1) and (2) above
+ bool applyMinLH = !zeroEffectiveSpanBox || mHasBullet;
+ bool isLastLine = !mGotLineBox ||
+ (!mLineBox->IsLineWrapped() && !mLineEndsInBR);
+ if (!applyMinLH && isLastLine) {
+ nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent();
+ if (blockContent) {
+ // (3) above, if the last line of LI, DT, or DD
+ if (blockContent->IsAnyOfHTMLElements(nsGkAtoms::li,
+ nsGkAtoms::dt,
+ nsGkAtoms::dd)) {
+ applyMinLH = true;
+ }
+ }
+ }
+ if (applyMinLH) {
+ if (psd->mHasNonemptyContent || preMode || mHasBullet) {
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d", minBCoord, maxBCoord);
+#endif
+ nscoord minimumLineBSize = mMinLineBSize;
+ nscoord blockStart =
+ -nsLayoutUtils::GetCenteredFontBaseline(fm, minimumLineBSize,
+ lineWM.IsLineInverted());
+ nscoord blockEnd = blockStart + minimumLineBSize;
+
+ if (mStyleText->HasTextEmphasis()) {
+ nscoord fontMaxHeight = fm->MaxHeight();
+ nscoord emphasisHeight =
+ GetBSizeOfEmphasisMarks(spanFrame, inflation);
+ nscoord delta = fontMaxHeight + emphasisHeight - minimumLineBSize;
+ if (delta > 0) {
+ if (minimumLineBSize < fontMaxHeight) {
+ // If the leadings are negative, fill them first.
+ nscoord ascent = fm->MaxAscent();
+ nscoord descent = fm->MaxDescent();
+ if (lineWM.IsLineInverted()) {
+ Swap(ascent, descent);
+ }
+ blockStart = -ascent;
+ blockEnd = descent;
+ delta = emphasisHeight;
+ }
+ LogicalSide side = mStyleText->TextEmphasisSide(lineWM);
+ if (side == eLogicalSideBStart) {
+ blockStart -= delta;
+ } else {
+ blockEnd += delta;
+ }
+ }
+ }
+
+ if (blockStart < minBCoord) minBCoord = blockStart;
+ if (blockEnd > maxBCoord) maxBCoord = blockEnd;
+
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" new values: %d,%d\n", minBCoord, maxBCoord);
+#endif
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" Used mMinLineBSize: %d, blockStart: %d, blockEnd: %d\n", mMinLineBSize, blockStart, blockEnd);
+#endif
+ }
+ else {
+ // XXX issues:
+ // [1] BR's on empty lines stop working
+ // [2] May not honor css2's notion of handling empty elements
+ // [3] blank lines in a pre-section ("\n") (handled with preMode)
+
+ // XXX Are there other problems with this?
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [span]==> zapping min/maxBCoord: currentValues: %d,%d newValues: 0,0\n",
+ minBCoord, maxBCoord);
+#endif
+ minBCoord = maxBCoord = 0;
+ }
+ }
+ }
+
+ if ((minBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM) ||
+ (maxBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM)) {
+ minBCoord = maxBCoord = baselineBCoord;
+ }
+
+ if (psd != mRootSpan && zeroEffectiveSpanBox) {
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [span]adjusting for zeroEffectiveSpanBox\n");
+ printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
+ minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM),
+ spanFramePFD->mAscent,
+ psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading);
+#endif
+ nscoord goodMinBCoord =
+ spanFramePFD->mBorderPadding.BStart(lineWM) - psd->mBStartLeading;
+ nscoord goodMaxBCoord = goodMinBCoord + psd->mLogicalBSize;
+
+ // For cases like the one in bug 714519 (text-decoration placement
+ // or making nsLineLayout::IsZeroBSize() handle
+ // vertical-align:top/bottom on a descendant of the line that's not
+ // a child of it), we want to treat elements that are
+ // vertical-align: top or bottom somewhat like children for the
+ // purposes of this quirk. To some extent, this is guessing, since
+ // they might end up being aligned anywhere. However, we'll guess
+ // that they'll be placed aligned with the top or bottom of this
+ // frame (as though this frame is the only thing in the line).
+ // (Guessing isn't crazy, since all we're doing is reducing the
+ // scope of a quirk and making the behavior more standards-like.)
+ if (maxStartBoxBSize > maxBCoord - minBCoord) {
+ // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and
+ // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or
+ // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord.
+ nscoord distribute = maxStartBoxBSize - (maxBCoord - minBCoord);
+ nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0);
+ if (distribute > ascentSpace) {
+ distribute -= ascentSpace;
+ minBCoord -= ascentSpace;
+ nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0);
+ if (distribute > descentSpace) {
+ maxBCoord += descentSpace;
+ } else {
+ maxBCoord += distribute;
+ }
+ } else {
+ minBCoord -= distribute;
+ }
+ }
+ if (maxEndBoxBSize > maxBCoord - minBCoord) {
+ // Likewise, but preferring descent to ascent.
+ nscoord distribute = maxEndBoxBSize - (maxBCoord - minBCoord);
+ nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0);
+ if (distribute > descentSpace) {
+ distribute -= descentSpace;
+ maxBCoord += descentSpace;
+ nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0);
+ if (distribute > ascentSpace) {
+ minBCoord -= ascentSpace;
+ } else {
+ minBCoord -= distribute;
+ }
+ } else {
+ maxBCoord += distribute;
+ }
+ }
+
+ if (minBCoord > goodMinBCoord) {
+ nscoord adjust = minBCoord - goodMinBCoord; // positive
+
+ // shrink the logical extents
+ psd->mLogicalBSize -= adjust;
+ psd->mBStartLeading -= adjust;
+ }
+ if (maxBCoord < goodMaxBCoord) {
+ nscoord adjust = goodMaxBCoord - maxBCoord;
+ psd->mLogicalBSize -= adjust;
+ psd->mBEndLeading -= adjust;
+ }
+ if (minBCoord > 0) {
+
+ // shrink the content by moving its block start down. This is tricky,
+ // since the block start is the 0 for many coordinates, so what we do is
+ // move everything else up.
+ spanFramePFD->mAscent -= minBCoord; // move the baseline up
+ spanFramePFD->mBounds.BSize(lineWM) -= minBCoord; // move the block end up
+ psd->mBStartLeading += minBCoord;
+ *psd->mBaseline -= minBCoord;
+
+ pfd = psd->mFirstFrame;
+ while (nullptr != pfd) {
+ pfd->mBounds.BStart(lineWM) -= minBCoord; // move all the children
+ // back up
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd));
+ pfd = pfd->mNext;
+ }
+ maxBCoord -= minBCoord; // since minBCoord is in the frame's own
+ // coordinate system
+ minBCoord = 0;
+ }
+ if (maxBCoord < spanFramePFD->mBounds.BSize(lineWM)) {
+ nscoord adjust = spanFramePFD->mBounds.BSize(lineWM) - maxBCoord;
+ spanFramePFD->mBounds.BSize(lineWM) -= adjust; // move the bottom up
+ psd->mBEndLeading += adjust;
+ }
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
+ minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM),
+ spanFramePFD->mAscent,
+ psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading);
+#endif
+ }
+
+ psd->mMinBCoord = minBCoord;
+ psd->mMaxBCoord = maxBCoord;
+#ifdef NOISY_BLOCKDIR_ALIGN
+ printf(" [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d maxEndBoxBSize=%d\n",
+ minBCoord, maxBCoord, maxBCoord - minBCoord, maxStartBoxBSize, maxEndBoxBSize);
+#endif
+ if (maxStartBoxBSize > mMaxStartBoxBSize) {
+ mMaxStartBoxBSize = maxStartBoxBSize;
+ }
+ if (maxEndBoxBSize > mMaxEndBoxBSize) {
+ mMaxEndBoxBSize = maxEndBoxBSize;
+ }
+}
+
+static void SlideSpanFrameRect(nsIFrame* aFrame, nscoord aDeltaWidth)
+{
+ // This should not use nsIFrame::MovePositionBy because it happens
+ // prior to relative positioning. In particular, because
+ // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace()
+ // prior to calling aLineLayout.RelativePositionFrames().
+ nsPoint p = aFrame->GetPosition();
+ p.x -= aDeltaWidth;
+ aFrame->SetPosition(p);
+}
+
+bool
+nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
+ nscoord* aDeltaISize)
+{
+ PerFrameData* pfd = psd->mFirstFrame;
+ if (!pfd) {
+ *aDeltaISize = 0;
+ return false;
+ }
+ pfd = pfd->Last();
+ while (nullptr != pfd) {
+#ifdef REALLY_NOISY_TRIM
+ nsFrame::ListTag(stdout, psd->mFrame->mFrame);
+ printf(": attempting trim of ");
+ nsFrame::ListTag(stdout, pfd->mFrame);
+ printf("\n");
+#endif
+ PerSpanData* childSpan = pfd->mSpan;
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ if (childSpan) {
+ // Maybe the child span has the trailing white-space in it?
+ if (TrimTrailingWhiteSpaceIn(childSpan, aDeltaISize)) {
+ nscoord deltaISize = *aDeltaISize;
+ if (deltaISize) {
+ // Adjust the child spans frame size
+ pfd->mBounds.ISize(lineWM) -= deltaISize;
+ if (psd != mRootSpan) {
+ // When the child span is not a direct child of the block
+ // we need to update the child spans frame rectangle
+ // because it most likely will not be done again. Spans
+ // that are direct children of the block will be updated
+ // later, however, because the VerticalAlignFrames method
+ // will be run after this method.
+ nsSize containerSize = ContainerSizeForSpan(childSpan);
+ nsIFrame* f = pfd->mFrame;
+ LogicalRect r(lineWM, f->GetRect(), containerSize);
+ r.ISize(lineWM) -= deltaISize;
+ f->SetRect(lineWM, r, containerSize);
+ }
+
+ // Adjust the inline end edge of the span that contains the child span
+ psd->mICoord -= deltaISize;
+
+ // Slide any frames that follow the child span over by the
+ // correct amount. The only thing that can follow the child
+ // span is empty stuff, so we are just making things
+ // sensible (keeping the combined area honest).
+ while (pfd->mNext) {
+ pfd = pfd->mNext;
+ pfd->mBounds.IStart(lineWM) -= deltaISize;
+ if (psd != mRootSpan) {
+ // When the child span is not a direct child of the block
+ // we need to update the child span's frame rectangle
+ // because it most likely will not be done again. Spans
+ // that are direct children of the block will be updated
+ // later, however, because the VerticalAlignFrames method
+ // will be run after this method.
+ SlideSpanFrameRect(pfd->mFrame, deltaISize);
+ }
+ }
+ }
+ return true;
+ }
+ }
+ else if (!pfd->mIsTextFrame && !pfd->mSkipWhenTrimmingWhitespace) {
+ // If we hit a frame on the end that's not text and not a placeholder,
+ // then there is no trailing whitespace to trim. Stop the search.
+ *aDeltaISize = 0;
+ return true;
+ }
+ else if (pfd->mIsTextFrame) {
+ // Call TrimTrailingWhiteSpace even on empty textframes because they
+ // might have a soft hyphen which should now appear, changing the frame's
+ // width
+ nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)->
+ TrimTrailingWhiteSpace(mBlockReflowInput->mRenderingContext->GetDrawTarget());
+#ifdef NOISY_TRIM
+ nsFrame::ListTag(stdout, psd->mFrame->mFrame);
+ printf(": trim of ");
+ nsFrame::ListTag(stdout, pfd->mFrame);
+ printf(" returned %d\n", trimOutput.mDeltaWidth);
+#endif
+
+ if (trimOutput.mChanged) {
+ pfd->mRecomputeOverflow = true;
+ }
+
+ // Delta width not being zero means that
+ // there is trimmed space in the frame.
+ if (trimOutput.mDeltaWidth) {
+ pfd->mBounds.ISize(lineWM) -= trimOutput.mDeltaWidth;
+
+ // If any trailing space is trimmed, the justification opportunity
+ // generated by the space should be removed as well.
+ pfd->mJustificationInfo.CancelOpportunityForTrimmedSpace();
+
+ // See if the text frame has already been placed in its parent
+ if (psd != mRootSpan) {
+ // The frame was already placed during psd's
+ // reflow. Update the frames rectangle now.
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds,
+ ContainerSizeForSpan(psd));
+ }
+
+ // Adjust containing span's right edge
+ psd->mICoord -= trimOutput.mDeltaWidth;
+
+ // Slide any frames that follow the text frame over by the
+ // right amount. The only thing that can follow the text
+ // frame is empty stuff, so we are just making things
+ // sensible (keeping the combined area honest).
+ while (pfd->mNext) {
+ pfd = pfd->mNext;
+ pfd->mBounds.IStart(lineWM) -= trimOutput.mDeltaWidth;
+ if (psd != mRootSpan) {
+ // When the child span is not a direct child of the block
+ // we need to update the child spans frame rectangle
+ // because it most likely will not be done again. Spans
+ // that are direct children of the block will be updated
+ // later, however, because the VerticalAlignFrames method
+ // will be run after this method.
+ SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth);
+ }
+ }
+ }
+
+ if (pfd->mIsNonEmptyTextFrame || trimOutput.mChanged) {
+ // Pass up to caller so they can shrink their span
+ *aDeltaISize = trimOutput.mDeltaWidth;
+ return true;
+ }
+ }
+ pfd = pfd->mPrev;
+ }
+
+ *aDeltaISize = 0;
+ return false;
+}
+
+bool
+nsLineLayout::TrimTrailingWhiteSpace()
+{
+ PerSpanData* psd = mRootSpan;
+ nscoord deltaISize;
+ TrimTrailingWhiteSpaceIn(psd, &deltaISize);
+ return 0 != deltaISize;
+}
+
+bool
+nsLineLayout::PerFrameData::ParticipatesInJustification() const
+{
+ if (mIsBullet || mIsEmpty || mSkipWhenTrimmingWhitespace) {
+ // Skip bullets, empty frames, and placeholders
+ return false;
+ }
+ if (mIsTextFrame && !mIsNonWhitespaceTextFrame &&
+ static_cast<nsTextFrame*>(mFrame)->IsAtEndOfLine()) {
+ // Skip trimmed whitespaces
+ return false;
+ }
+ return true;
+}
+
+struct nsLineLayout::JustificationComputationState
+{
+ PerFrameData* mFirstParticipant;
+ PerFrameData* mLastParticipant;
+ // When we are going across a boundary of ruby base, i.e. entering
+ // one, leaving one, or both, the following fields will be set to
+ // the corresponding ruby base frame for handling ruby-align.
+ PerFrameData* mLastExitedRubyBase;
+ PerFrameData* mLastEnteredRubyBase;
+
+ JustificationComputationState()
+ : mFirstParticipant(nullptr)
+ , mLastParticipant(nullptr)
+ , mLastExitedRubyBase(nullptr)
+ , mLastEnteredRubyBase(nullptr) { }
+};
+
+static bool
+IsRubyAlignSpaceAround(nsIFrame* aRubyBase)
+{
+ return aRubyBase->StyleText()->mRubyAlign == NS_STYLE_RUBY_ALIGN_SPACE_AROUND;
+}
+
+/**
+ * Assign justification gaps for justification
+ * opportunities across two frames.
+ */
+/* static */ int
+nsLineLayout::AssignInterframeJustificationGaps(
+ PerFrameData* aFrame, JustificationComputationState& aState)
+{
+ PerFrameData* prev = aState.mLastParticipant;
+ MOZ_ASSERT(prev);
+
+ auto& assign = aFrame->mJustificationAssignment;
+ auto& prevAssign = prev->mJustificationAssignment;
+
+ if (aState.mLastExitedRubyBase || aState.mLastEnteredRubyBase) {
+ PerFrameData* exitedRubyBase = aState.mLastExitedRubyBase;
+ if (!exitedRubyBase || IsRubyAlignSpaceAround(exitedRubyBase->mFrame)) {
+ prevAssign.mGapsAtEnd = 1;
+ } else {
+ exitedRubyBase->mJustificationAssignment.mGapsAtEnd = 1;
+ }
+
+ PerFrameData* enteredRubyBase = aState.mLastEnteredRubyBase;
+ if (!enteredRubyBase || IsRubyAlignSpaceAround(enteredRubyBase->mFrame)) {
+ assign.mGapsAtStart = 1;
+ } else {
+ enteredRubyBase->mJustificationAssignment.mGapsAtStart = 1;
+ }
+
+ // We are no longer going across a ruby base boundary.
+ aState.mLastExitedRubyBase = nullptr;
+ aState.mLastEnteredRubyBase = nullptr;
+ return 1;
+ }
+
+ const auto& info = aFrame->mJustificationInfo;
+ const auto& prevInfo = prev->mJustificationInfo;
+ if (!info.mIsStartJustifiable && !prevInfo.mIsEndJustifiable) {
+ return 0;
+ }
+
+ if (!info.mIsStartJustifiable) {
+ prevAssign.mGapsAtEnd = 2;
+ assign.mGapsAtStart = 0;
+ } else if (!prevInfo.mIsEndJustifiable) {
+ prevAssign.mGapsAtEnd = 0;
+ assign.mGapsAtStart = 2;
+ } else {
+ prevAssign.mGapsAtEnd = 1;
+ assign.mGapsAtStart = 1;
+ }
+ return 1;
+}
+
+/**
+ * Compute the justification info of the given span, and store the
+ * number of inner opportunities into the frame's justification info.
+ * It returns the number of non-inner opportunities it detects.
+ */
+int32_t
+nsLineLayout::ComputeFrameJustification(PerSpanData* aPSD,
+ JustificationComputationState& aState)
+{
+ NS_ASSERTION(aPSD, "null arg");
+ NS_ASSERTION(!aState.mLastParticipant || !aState.mLastParticipant->mSpan,
+ "Last participant shall always be a leaf frame");
+ bool firstChild = true;
+ int32_t& innerOpportunities =
+ aPSD->mFrame->mJustificationInfo.mInnerOpportunities;
+ MOZ_ASSERT(innerOpportunities == 0,
+ "Justification info should not have been set yet.");
+ int32_t outerOpportunities = 0;
+
+ for (PerFrameData* pfd = aPSD->mFirstFrame; pfd; pfd = pfd->mNext) {
+ if (!pfd->ParticipatesInJustification()) {
+ continue;
+ }
+
+ bool isRubyBase = pfd->mFrame->GetType() == nsGkAtoms::rubyBaseFrame;
+ PerFrameData* outerRubyBase = aState.mLastEnteredRubyBase;
+ if (isRubyBase) {
+ aState.mLastEnteredRubyBase = pfd;
+ }
+
+ int extraOpportunities = 0;
+ if (pfd->mSpan) {
+ PerSpanData* span = pfd->mSpan;
+ extraOpportunities = ComputeFrameJustification(span, aState);
+ innerOpportunities += pfd->mJustificationInfo.mInnerOpportunities;
+ } else {
+ if (pfd->mIsTextFrame) {
+ innerOpportunities += pfd->mJustificationInfo.mInnerOpportunities;
+ }
+
+ if (!aState.mLastParticipant) {
+ aState.mFirstParticipant = pfd;
+ // It is not an empty ruby base, but we are not assigning gaps
+ // to the content for now. Clear the last entered ruby base so
+ // that we can correctly set the last exited ruby base.
+ aState.mLastEnteredRubyBase = nullptr;
+ } else {
+ extraOpportunities = AssignInterframeJustificationGaps(pfd, aState);
+ }
+
+ aState.mLastParticipant = pfd;
+ }
+
+ if (isRubyBase) {
+ if (aState.mLastEnteredRubyBase == pfd) {
+ // There is no justification participant inside this ruby base.
+ // Ignore this ruby base completely and restore the outer ruby
+ // base here.
+ aState.mLastEnteredRubyBase = outerRubyBase;
+ } else {
+ aState.mLastExitedRubyBase = pfd;
+ }
+ }
+
+ if (firstChild) {
+ outerOpportunities = extraOpportunities;
+ firstChild = false;
+ } else {
+ innerOpportunities += extraOpportunities;
+ }
+ }
+
+ return outerOpportunities;
+}
+
+void
+nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData* aPFD,
+ const nsSize& aContainerSize,
+ nscoord aDeltaICoord,
+ nscoord aDeltaISize)
+{
+ nsIFrame* frame = aPFD->mFrame;
+ nsIAtom* frameType = frame->GetType();
+ MOZ_ASSERT(frameType == nsGkAtoms::rubyTextFrame ||
+ frameType == nsGkAtoms::rubyTextContainerFrame);
+ MOZ_ASSERT(aPFD->mSpan, "rt and rtc should have span.");
+
+ PerSpanData* psd = aPFD->mSpan;
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ aPFD->mBounds.IStart(lineWM) += aDeltaICoord;
+
+ // Check whether this expansion should be counted into the reserved
+ // isize or not. When it is a ruby text container, and it has some
+ // children linked to the base, it must not have reserved isize,
+ // or its children won't align with their bases. Otherwise, this
+ // expansion should be reserved. There are two cases a ruby text
+ // container does not have children linked to the base:
+ // 1. it is a container for span; 2. its children are collapsed.
+ // See bug 1055674 for the second case.
+ if (frameType == nsGkAtoms::rubyTextFrame ||
+ // This ruby text container is a span.
+ (psd->mFirstFrame == psd->mLastFrame && psd->mFirstFrame &&
+ !psd->mFirstFrame->mIsLinkedToBase)) {
+ // For ruby text frames, only increase frames
+ // which are not auto-hidden.
+ if (frameType != nsGkAtoms::rubyTextFrame ||
+ !static_cast<nsRubyTextFrame*>(frame)->IsAutoHidden()) {
+ nscoord reservedISize = RubyUtils::GetReservedISize(frame);
+ RubyUtils::SetReservedISize(frame, reservedISize + aDeltaISize);
+ }
+ } else {
+ // It is a normal ruby text container. Its children will expand
+ // themselves properly. We only need to expand its own size here.
+ aPFD->mBounds.ISize(lineWM) += aDeltaISize;
+ }
+ aPFD->mFrame->SetRect(lineWM, aPFD->mBounds, aContainerSize);
+}
+
+/**
+ * This function applies the changes of icoord and isize caused by
+ * justification to annotations of the given frame.
+ */
+void
+nsLineLayout::ApplyLineJustificationToAnnotations(PerFrameData* aPFD,
+ nscoord aDeltaICoord,
+ nscoord aDeltaISize)
+{
+ PerFrameData* pfd = aPFD->mNextAnnotation;
+ while (pfd) {
+ nsSize containerSize = pfd->mFrame->GetParent()->GetSize();
+ AdvanceAnnotationInlineBounds(pfd, containerSize,
+ aDeltaICoord, aDeltaISize);
+
+ // There are two cases where an annotation frame has siblings which
+ // do not attached to a ruby base-level frame:
+ // 1. there's an intra-annotation whitespace which has no intra-base
+ // white-space to pair with;
+ // 2. there are not enough ruby bases to be paired with annotations.
+ // In these cases, their size should not be affected, but we still
+ // need to move them so that they won't overlap other frames.
+ PerFrameData* sibling = pfd->mNext;
+ while (sibling && !sibling->mIsLinkedToBase) {
+ AdvanceAnnotationInlineBounds(sibling, containerSize,
+ aDeltaICoord + aDeltaISize, 0);
+ sibling = sibling->mNext;
+ }
+
+ pfd = pfd->mNextAnnotation;
+ }
+}
+
+nscoord
+nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD,
+ JustificationApplicationState& aState)
+{
+ NS_ASSERTION(aPSD, "null arg");
+
+ nscoord deltaICoord = 0;
+ for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) {
+ // Don't reposition bullets (and other frames that occur out of X-order?)
+ if (!pfd->mIsBullet) {
+ nscoord dw = 0;
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ const auto& assign = pfd->mJustificationAssignment;
+ bool isInlineText = pfd->mIsTextFrame &&
+ !pfd->mWritingMode.IsOrthogonalTo(lineWM);
+
+ if (isInlineText) {
+ if (aState.IsJustifiable()) {
+ // Set corresponding justification gaps here, so that the
+ // text frame knows how it should add gaps at its sides.
+ const auto& info = pfd->mJustificationInfo;
+ auto textFrame = static_cast<nsTextFrame*>(pfd->mFrame);
+ textFrame->AssignJustificationGaps(assign);
+ dw = aState.Consume(JustificationUtils::CountGaps(info, assign));
+ }
+
+ if (dw) {
+ pfd->mRecomputeOverflow = true;
+ }
+ }
+ else {
+ if (nullptr != pfd->mSpan) {
+ dw = ApplyFrameJustification(pfd->mSpan, aState);
+ }
+ }
+
+ pfd->mBounds.ISize(lineWM) += dw;
+ nscoord gapsAtEnd = 0;
+ if (!isInlineText && assign.TotalGaps()) {
+ // It is possible that we assign gaps to non-text frame or an
+ // orthogonal text frame. Apply the gaps as margin for them.
+ deltaICoord += aState.Consume(assign.mGapsAtStart);
+ gapsAtEnd = aState.Consume(assign.mGapsAtEnd);
+ dw += gapsAtEnd;
+ }
+ pfd->mBounds.IStart(lineWM) += deltaICoord;
+
+ // The gaps added to the end of the frame should also be
+ // excluded from the isize added to the annotation.
+ ApplyLineJustificationToAnnotations(pfd, deltaICoord, dw - gapsAtEnd);
+ deltaICoord += dw;
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(aPSD));
+ }
+ }
+ return deltaICoord;
+}
+
+static nsIFrame*
+FindNearestRubyBaseAncestor(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(aFrame->StyleContext()->ShouldSuppressLineBreak());
+ while (aFrame && aFrame->GetType() != nsGkAtoms::rubyBaseFrame) {
+ aFrame = aFrame->GetParent();
+ }
+ // XXX It is possible that no ruby base ancestor is found because of
+ // some edge cases like form control or canvas inside ruby text.
+ // See bug 1138092 comment 4.
+ NS_WARNING_ASSERTION(aFrame, "no ruby base ancestor?");
+ return aFrame;
+}
+
+/**
+ * This method expands the given frame by the given reserved isize.
+ */
+void
+nsLineLayout::ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize,
+ const nsSize& aContainerSize)
+{
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ auto rubyAlign = aFrame->mFrame->StyleText()->mRubyAlign;
+ switch (rubyAlign) {
+ case NS_STYLE_RUBY_ALIGN_START:
+ // do nothing for start
+ break;
+ case NS_STYLE_RUBY_ALIGN_SPACE_BETWEEN:
+ case NS_STYLE_RUBY_ALIGN_SPACE_AROUND: {
+ int32_t opportunities = aFrame->mJustificationInfo.mInnerOpportunities;
+ int32_t gaps = opportunities * 2;
+ if (rubyAlign == NS_STYLE_RUBY_ALIGN_SPACE_AROUND) {
+ // Each expandable ruby box with ruby-align space-around has a
+ // gap at each of its sides. For rb/rbc, see comment in
+ // AssignInterframeJustificationGaps; for rt/rtc, see comment
+ // in ExpandRubyBoxWithAnnotations.
+ gaps += 2;
+ }
+ if (gaps > 0) {
+ JustificationApplicationState state(gaps, aReservedISize);
+ ApplyFrameJustification(aFrame->mSpan, state);
+ break;
+ }
+ // If there are no justification opportunities for space-between,
+ // fall-through to center per spec.
+ MOZ_FALLTHROUGH;
+ }
+ case NS_STYLE_RUBY_ALIGN_CENTER:
+ // Indent all children by half of the reserved inline size.
+ for (PerFrameData* child = aFrame->mSpan->mFirstFrame;
+ child; child = child->mNext) {
+ child->mBounds.IStart(lineWM) += aReservedISize / 2;
+ child->mFrame->SetRect(lineWM, child->mBounds, aContainerSize);
+ }
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown ruby-align value");
+ }
+
+ aFrame->mBounds.ISize(lineWM) += aReservedISize;
+ aFrame->mFrame->SetRect(lineWM, aFrame->mBounds, aContainerSize);
+}
+
+/**
+ * This method expands the given frame by the reserved inline size.
+ * It also expands its annotations if they are expandable and have
+ * reserved isize larger than zero.
+ */
+void
+nsLineLayout::ExpandRubyBoxWithAnnotations(PerFrameData* aFrame,
+ const nsSize& aContainerSize)
+{
+ nscoord reservedISize = RubyUtils::GetReservedISize(aFrame->mFrame);
+ if (reservedISize) {
+ ExpandRubyBox(aFrame, reservedISize, aContainerSize);
+ }
+
+ WritingMode lineWM = mRootSpan->mWritingMode;
+ bool isLevelContainer =
+ aFrame->mFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame;
+ for (PerFrameData* annotation = aFrame->mNextAnnotation;
+ annotation; annotation = annotation->mNextAnnotation) {
+ if (isLevelContainer) {
+ nsIFrame* rtcFrame = annotation->mFrame;
+ MOZ_ASSERT(rtcFrame->GetType() == nsGkAtoms::rubyTextContainerFrame);
+ // It is necessary to set the rect again because the container
+ // width was unknown, and zero was used instead when we reflow
+ // them. The corresponding base containers were repositioned in
+ // VerticalAlignFrames and PlaceTopBottomFrames.
+ MOZ_ASSERT(
+ rtcFrame->GetLogicalSize(lineWM) == annotation->mBounds.Size(lineWM));
+ rtcFrame->SetPosition(lineWM, annotation->mBounds.Origin(lineWM),
+ aContainerSize);
+ }
+
+ nscoord reservedISize = RubyUtils::GetReservedISize(annotation->mFrame);
+ if (!reservedISize) {
+ continue;
+ }
+
+ MOZ_ASSERT(annotation->mSpan);
+ JustificationComputationState computeState;
+ ComputeFrameJustification(annotation->mSpan, computeState);
+ if (!computeState.mFirstParticipant) {
+ continue;
+ }
+ if (IsRubyAlignSpaceAround(annotation->mFrame)) {
+ // Add one gap at each side of this annotation.
+ computeState.mFirstParticipant->mJustificationAssignment.mGapsAtStart = 1;
+ computeState.mLastParticipant->mJustificationAssignment.mGapsAtEnd = 1;
+ }
+ nsIFrame* parentFrame = annotation->mFrame->GetParent();
+ nsSize containerSize = parentFrame->GetSize();
+ MOZ_ASSERT(containerSize == aContainerSize ||
+ parentFrame->GetType() == nsGkAtoms::rubyTextContainerFrame,
+ "Container width should only be different when the current "
+ "annotation is a ruby text frame, whose parent is not same "
+ "as its base frame.");
+ ExpandRubyBox(annotation, reservedISize, containerSize);
+ ExpandInlineRubyBoxes(annotation->mSpan);
+ }
+}
+
+/**
+ * This method looks for all expandable ruby box in the given span, and
+ * calls ExpandRubyBox to expand them in depth-first preorder.
+ */
+void
+nsLineLayout::ExpandInlineRubyBoxes(PerSpanData* aSpan)
+{
+ nsSize containerSize = ContainerSizeForSpan(aSpan);
+ for (PerFrameData* pfd = aSpan->mFirstFrame; pfd; pfd = pfd->mNext) {
+ if (RubyUtils::IsExpandableRubyBox(pfd->mFrame)) {
+ ExpandRubyBoxWithAnnotations(pfd, containerSize);
+ }
+ if (pfd->mSpan) {
+ ExpandInlineRubyBoxes(pfd->mSpan);
+ }
+ }
+}
+
+// Align inline frames within the line according to the CSS text-align
+// property.
+void
+nsLineLayout::TextAlignLine(nsLineBox* aLine,
+ bool aIsLastLine)
+{
+ /**
+ * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
+ * only in cases where the last line needs special handling.
+ */
+ PerSpanData* psd = mRootSpan;
+ WritingMode lineWM = psd->mWritingMode;
+ LAYOUT_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE,
+ "have unconstrained width; this should only result from "
+ "very large sizes, not attempts at intrinsic width "
+ "calculation");
+ nscoord availISize = psd->mIEnd - psd->mIStart;
+ nscoord remainingISize = availISize - aLine->ISize();
+#ifdef NOISY_INLINEDIR_ALIGN
+ nsFrame::ListTag(stdout, mBlockReflowInput->mFrame);
+ printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n",
+ availISize, aLine->IStart(), aLine->ISize(), remainingISize);
+#endif
+
+ // 'text-align-last: auto' is equivalent to the value of the 'text-align'
+ // property except when 'text-align' is set to 'justify', in which case it
+ // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise.
+ //
+ // XXX: the code below will have to change when we implement text-justify
+ //
+ nscoord dx = 0;
+ uint8_t textAlign = mStyleText->mTextAlign;
+ bool textAlignTrue = mStyleText->mTextAlignTrue;
+ if (aIsLastLine) {
+ textAlignTrue = mStyleText->mTextAlignLastTrue;
+ if (mStyleText->mTextAlignLast == NS_STYLE_TEXT_ALIGN_AUTO) {
+ if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
+ textAlign = NS_STYLE_TEXT_ALIGN_START;
+ }
+ } else {
+ textAlign = mStyleText->mTextAlignLast;
+ }
+ }
+
+ bool isSVG = mBlockReflowInput->mFrame->IsSVGText();
+ bool doTextAlign = remainingISize > 0 || textAlignTrue;
+
+ int32_t additionalGaps = 0;
+ if (!isSVG && (mHasRuby || (doTextAlign &&
+ textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY))) {
+ JustificationComputationState computeState;
+ ComputeFrameJustification(psd, computeState);
+ if (mHasRuby && computeState.mFirstParticipant) {
+ PerFrameData* firstFrame = computeState.mFirstParticipant;
+ if (firstFrame->mFrame->StyleContext()->ShouldSuppressLineBreak()) {
+ MOZ_ASSERT(!firstFrame->mJustificationAssignment.mGapsAtStart);
+ nsIFrame* rubyBase = FindNearestRubyBaseAncestor(firstFrame->mFrame);
+ if (rubyBase && IsRubyAlignSpaceAround(rubyBase)) {
+ firstFrame->mJustificationAssignment.mGapsAtStart = 1;
+ additionalGaps++;
+ }
+ }
+ PerFrameData* lastFrame = computeState.mLastParticipant;
+ if (lastFrame->mFrame->StyleContext()->ShouldSuppressLineBreak()) {
+ MOZ_ASSERT(!lastFrame->mJustificationAssignment.mGapsAtEnd);
+ nsIFrame* rubyBase = FindNearestRubyBaseAncestor(lastFrame->mFrame);
+ if (rubyBase && IsRubyAlignSpaceAround(rubyBase)) {
+ lastFrame->mJustificationAssignment.mGapsAtEnd = 1;
+ additionalGaps++;
+ }
+ }
+ }
+ }
+
+ if (!isSVG && doTextAlign) {
+ switch (textAlign) {
+ case NS_STYLE_TEXT_ALIGN_JUSTIFY: {
+ int32_t opportunities =
+ psd->mFrame->mJustificationInfo.mInnerOpportunities;
+ if (opportunities > 0) {
+ int32_t gaps = opportunities * 2 + additionalGaps;
+ JustificationApplicationState applyState(gaps, remainingISize);
+
+ // Apply the justification, and make sure to update our linebox
+ // width to account for it.
+ aLine->ExpandBy(ApplyFrameJustification(psd, applyState),
+ ContainerSizeForSpan(psd));
+
+ MOZ_ASSERT(applyState.mGaps.mHandled == applyState.mGaps.mCount,
+ "Unprocessed justification gaps");
+ MOZ_ASSERT(applyState.mWidth.mConsumed == applyState.mWidth.mAvailable,
+ "Unprocessed justification width");
+ break;
+ }
+ // Fall through to the default case if we could not justify to fill
+ // the space.
+ MOZ_FALLTHROUGH;
+ }
+
+ case NS_STYLE_TEXT_ALIGN_START:
+ // default alignment is to start edge so do nothing
+ break;
+
+ case NS_STYLE_TEXT_ALIGN_LEFT:
+ case NS_STYLE_TEXT_ALIGN_MOZ_LEFT:
+ if (!lineWM.IsBidiLTR()) {
+ dx = remainingISize;
+ }
+ break;
+
+ case NS_STYLE_TEXT_ALIGN_RIGHT:
+ case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT:
+ if (lineWM.IsBidiLTR()) {
+ dx = remainingISize;
+ }
+ break;
+
+ case NS_STYLE_TEXT_ALIGN_END:
+ dx = remainingISize;
+ break;
+
+ case NS_STYLE_TEXT_ALIGN_CENTER:
+ case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
+ dx = remainingISize / 2;
+ break;
+ }
+ }
+
+ if (mHasRuby) {
+ ExpandInlineRubyBoxes(mRootSpan);
+ }
+
+ if (mPresContext->BidiEnabled() &&
+ (!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) {
+ nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame,
+ aLine->GetChildCount(),
+ lineWM, mContainerSize,
+ psd->mIStart + mTextIndent + dx);
+ if (dx) {
+ aLine->IndentBy(dx, ContainerSize());
+ }
+ } else if (dx) {
+ for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
+ pfd->mBounds.IStart(lineWM) += dx;
+ pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd));
+ }
+ aLine->IndentBy(dx, ContainerSize());
+ }
+}
+
+// This method applies any relative positioning to the given frame.
+void
+nsLineLayout::ApplyRelativePositioning(PerFrameData* aPFD)
+{
+ if (!aPFD->mRelativePos) {
+ return;
+ }
+
+ nsIFrame* frame = aPFD->mFrame;
+ WritingMode frameWM = aPFD->mWritingMode;
+ LogicalPoint origin = frame->GetLogicalPosition(ContainerSize());
+ // right and bottom are handled by
+ // ReflowInput::ComputeRelativeOffsets
+ ReflowInput::ApplyRelativePositioning(frame, frameWM,
+ aPFD->mOffsets, &origin,
+ ContainerSize());
+ frame->SetPosition(frameWM, origin, ContainerSize());
+}
+
+// This method do relative positioning for ruby annotations.
+void
+nsLineLayout::RelativePositionAnnotations(PerSpanData* aRubyPSD,
+ nsOverflowAreas& aOverflowAreas)
+{
+ MOZ_ASSERT(aRubyPSD->mFrame->mFrame->GetType() == nsGkAtoms::rubyFrame);
+ for (PerFrameData* pfd = aRubyPSD->mFirstFrame; pfd; pfd = pfd->mNext) {
+ MOZ_ASSERT(pfd->mFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame);
+ for (PerFrameData* rtc = pfd->mNextAnnotation;
+ rtc; rtc = rtc->mNextAnnotation) {
+ nsIFrame* rtcFrame = rtc->mFrame;
+ MOZ_ASSERT(rtcFrame->GetType() == nsGkAtoms::rubyTextContainerFrame);
+ ApplyRelativePositioning(rtc);
+ nsOverflowAreas rtcOverflowAreas;
+ RelativePositionFrames(rtc->mSpan, rtcOverflowAreas);
+ aOverflowAreas.UnionWith(rtcOverflowAreas + rtcFrame->GetPosition());
+ }
+ }
+}
+
+void
+nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas)
+{
+ nsOverflowAreas overflowAreas;
+ WritingMode wm = psd->mWritingMode;
+ if (psd != mRootSpan) {
+ // The span's overflow areas come in three parts:
+ // -- this frame's width and height
+ // -- pfd->mOverflowAreas, which is the area of a bullet or the union
+ // of a relatively positioned frame's absolute children
+ // -- the bounds of all inline descendants
+ // The former two parts are computed right here, we gather the descendants
+ // below.
+ // At this point psd->mFrame->mBounds might be out of date since
+ // bidi reordering can move and resize the frames. So use the frame's
+ // rect instead of mBounds.
+ nsRect adjustedBounds(nsPoint(0, 0), psd->mFrame->mFrame->GetSize());
+
+ overflowAreas.ScrollableOverflow().UnionRect(
+ psd->mFrame->mOverflowAreas.ScrollableOverflow(), adjustedBounds);
+ overflowAreas.VisualOverflow().UnionRect(
+ psd->mFrame->mOverflowAreas.VisualOverflow(), adjustedBounds);
+ }
+ else {
+ LogicalRect rect(wm, psd->mIStart, mBStartEdge,
+ psd->mICoord - psd->mIStart, mFinalLineBSize);
+ // The minimum combined area for the frames that are direct
+ // children of the block starts at the upper left corner of the
+ // line and is sized to match the size of the line's bounding box
+ // (the same size as the values returned from VerticalAlignFrames)
+ overflowAreas.VisualOverflow() = rect.GetPhysicalRect(wm, ContainerSize());
+ overflowAreas.ScrollableOverflow() = overflowAreas.VisualOverflow();
+ }
+
+ for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
+ nsIFrame* frame = pfd->mFrame;
+
+ // Adjust the origin of the frame
+ ApplyRelativePositioning(pfd);
+
+ // We must position the view correctly before positioning its
+ // descendants so that widgets are positioned properly (since only
+ // some views have widgets).
+ if (frame->HasView())
+ nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
+ frame->GetView(), pfd->mOverflowAreas.VisualOverflow(),
+ NS_FRAME_NO_SIZE_VIEW);
+
+ // Note: the combined area of a child is in its coordinate
+ // system. We adjust the childs combined area into our coordinate
+ // system before computing the aggregated value by adding in
+ // <b>x</b> and <b>y</b> which were computed above.
+ nsOverflowAreas r;
+ if (pfd->mSpan) {
+ // Compute a new combined area for the child span before
+ // aggregating it into our combined area.
+ RelativePositionFrames(pfd->mSpan, r);
+ } else {
+ r = pfd->mOverflowAreas;
+ if (pfd->mIsTextFrame) {
+ // We need to recompute overflow areas in four cases:
+ // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
+ // (2) When there are text decorations, since we can't recompute the
+ // overflow area until Reflow and VerticalAlignLine have finished
+ // (3) When there are text emphasis marks, since the marks may be
+ // put further away if the text is inside ruby.
+ // (4) When there are text strokes
+ if (pfd->mRecomputeOverflow ||
+ frame->StyleContext()->HasTextDecorationLines() ||
+ frame->StyleText()->HasTextEmphasis() ||
+ frame->StyleText()->HasWebkitTextStroke()) {
+ nsTextFrame* f = static_cast<nsTextFrame*>(frame);
+ r = f->RecomputeOverflow(mBlockReflowInput->mFrame);
+ }
+ frame->FinishAndStoreOverflow(r, frame->GetSize());
+ }
+
+ // If we have something that's not an inline but with a complex frame
+ // hierarchy inside that contains views, they need to be
+ // positioned.
+ // All descendant views must be repositioned even if this frame
+ // does have a view in case this frame's view does not have a
+ // widget and some of the descendant views do have widgets --
+ // otherwise the widgets won't be repositioned.
+ nsContainerFrame::PositionChildViews(frame);
+ }
+
+ // Do this here (rather than along with setting the overflow rect
+ // below) so we get leaf frames as well. No need to worry
+ // about the root span, since it doesn't have a frame.
+ if (frame->HasView())
+ nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
+ frame->GetView(),
+ r.VisualOverflow(),
+ NS_FRAME_NO_MOVE_VIEW);
+
+ overflowAreas.UnionWith(r + frame->GetPosition());
+ }
+
+ // Also compute relative position in the annotations.
+ if (psd->mFrame->mFrame->GetType() == nsGkAtoms::rubyFrame) {
+ RelativePositionAnnotations(psd, overflowAreas);
+ }
+
+ // If we just computed a spans combined area, we need to update its
+ // overflow rect...
+ if (psd != mRootSpan) {
+ PerFrameData* spanPFD = psd->mFrame;
+ nsIFrame* frame = spanPFD->mFrame;
+ frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize());
+ }
+ aOverflowAreas = overflowAreas;
+}
diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h
new file mode 100644
index 000000000..a612a9666
--- /dev/null
+++ b/layout/generic/nsLineLayout.h
@@ -0,0 +1,741 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim:cindent:ts=2:et:sw=2:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 VisualAge build.
+ */
+
+/* state and methods used while laying out a single line of a block frame */
+
+#ifndef nsLineLayout_h___
+#define nsLineLayout_h___
+
+#include "gfxTypes.h"
+#include "JustificationUtils.h"
+#include "mozilla/WritingModes.h"
+#include "BlockReflowInput.h"
+#include "nsLineBox.h"
+#include "plarena.h"
+
+class nsFloatManager;
+struct nsStyleText;
+
+class nsLineLayout {
+ using BlockReflowInput = mozilla::BlockReflowInput;
+ using ReflowInput = mozilla::ReflowInput;
+ using ReflowOutput = mozilla::ReflowOutput;
+
+public:
+ /**
+ * @param aBaseLineLayout the nsLineLayout for ruby base,
+ * nullptr if no separate base nsLineLayout is needed.
+ */
+ nsLineLayout(nsPresContext* aPresContext,
+ nsFloatManager* aFloatManager,
+ const ReflowInput* aOuterReflowInput,
+ const nsLineList::iterator* aLine,
+ nsLineLayout* aBaseLineLayout);
+ ~nsLineLayout();
+
+ void Init(BlockReflowInput* aState, nscoord aMinLineBSize,
+ int32_t aLineNumber) {
+ mBlockRI = aState;
+ mMinLineBSize = aMinLineBSize;
+ mLineNumber = aLineNumber;
+ }
+
+ int32_t GetLineNumber() const {
+ return mLineNumber;
+ }
+
+ void BeginLineReflow(nscoord aICoord, nscoord aBCoord,
+ nscoord aISize, nscoord aBSize,
+ bool aImpactedByFloats,
+ bool aIsTopOfPage,
+ mozilla::WritingMode aWritingMode,
+ const nsSize& aContainerSize);
+
+ void EndLineReflow();
+
+ /**
+ * Called when a float has been placed. This method updates the
+ * inline frame and span data to account for any change in positions
+ * due to available space for the line boxes changing.
+ * @param aX/aY/aWidth/aHeight are the new available
+ * space rectangle, relative to the containing block.
+ * @param aFloatFrame the float frame that was placed.
+ */
+ void UpdateBand(mozilla::WritingMode aWM,
+ const mozilla::LogicalRect& aNewAvailableSpace,
+ nsIFrame* aFloatFrame);
+
+ void BeginSpan(nsIFrame* aFrame, const ReflowInput* aSpanReflowInput,
+ nscoord aLeftEdge, nscoord aRightEdge, nscoord* aBaseline);
+
+ // Returns the width of the span
+ nscoord EndSpan(nsIFrame* aFrame);
+
+ // This method attaches the last frame reflowed in this line layout
+ // to that in the base line layout.
+ void AttachLastFrameToBaseLineLayout()
+ {
+ AttachFrameToBaseLineLayout(LastFrame());
+ }
+
+ // This method attaches the root frame of this line layout to the
+ // last reflowed frame in the base line layout.
+ void AttachRootFrameToBaseLineLayout()
+ {
+ AttachFrameToBaseLineLayout(mRootSpan->mFrame);
+ }
+
+ int32_t GetCurrentSpanCount() const;
+
+ void SplitLineTo(int32_t aNewCount);
+
+ bool IsZeroBSize();
+
+ // Reflows the frame and returns the reflow status. aPushedFrame is true
+ // if the frame is pushed to the next line because it doesn't fit.
+ void ReflowFrame(nsIFrame* aFrame,
+ nsReflowStatus& aReflowStatus,
+ ReflowOutput* aMetrics,
+ bool& aPushedFrame);
+
+ void AddBulletFrame(nsIFrame* aFrame, const ReflowOutput& aMetrics);
+
+ void RemoveBulletFrame(nsIFrame* aFrame) {
+ PushFrame(aFrame);
+ }
+
+ /**
+ * Place frames in the block direction (CSS property vertical-align)
+ */
+ void VerticalAlignLine();
+
+ bool TrimTrailingWhiteSpace();
+
+ /**
+ * Place frames in the inline direction (CSS property text-align).
+ */
+ void TextAlignLine(nsLineBox* aLine, bool aIsLastLine);
+
+ /**
+ * Handle all the relative positioning in the line, compute the
+ * combined area (== overflow area) for the line, and handle view
+ * sizing/positioning and the setting of the overflow rect.
+ */
+ void RelativePositionFrames(nsOverflowAreas& aOverflowAreas)
+ {
+ RelativePositionFrames(mRootSpan, aOverflowAreas);
+ }
+
+ // Support methods for word-wrapping during line reflow
+
+ void SetJustificationInfo(const mozilla::JustificationInfo& aInfo)
+ {
+ mJustificationInfo = aInfo;
+ }
+
+ /**
+ * @return true if so far during reflow no non-empty content has been
+ * placed in the line (according to nsIFrame::IsEmpty())
+ */
+ bool LineIsEmpty() const
+ {
+ return mLineIsEmpty;
+ }
+
+ /**
+ * @return true if so far during reflow no non-empty leaf content
+ * (non-collapsed whitespace, replaced element, inline-block, etc) has been
+ * placed in the line
+ */
+ bool LineAtStart() const
+ {
+ return mLineAtStart;
+ }
+
+ bool LineIsBreakable() const;
+
+ bool GetLineEndsInBR() const
+ {
+ return mLineEndsInBR;
+ }
+
+ void SetLineEndsInBR(bool aOn)
+ {
+ mLineEndsInBR = aOn;
+ }
+
+ //----------------------------------------
+ // Inform the line-layout about the presence of a floating frame
+ // XXX get rid of this: use get-frame-type?
+ bool AddFloat(nsIFrame* aFloat, nscoord aAvailableISize)
+ {
+ // When reflowing ruby text frames, no block reflow state is
+ // provided to the line layout. However, floats should never be
+ // associated with ruby text containers, hence this method should
+ // not be called in that case.
+ MOZ_ASSERT(mBlockRI,
+ "Should not call this method if there is no block reflow state "
+ "available");
+ return mBlockRI->AddFloat(this, aFloat, aAvailableISize);
+ }
+
+ void SetTrimmableISize(nscoord aTrimmableISize) {
+ mTrimmableISize = aTrimmableISize;
+ }
+
+ //----------------------------------------
+
+ bool GetFirstLetterStyleOK() const {
+ return mFirstLetterStyleOK;
+ }
+
+ void SetFirstLetterStyleOK(bool aSetting) {
+ mFirstLetterStyleOK = aSetting;
+ }
+
+ bool GetInFirstLetter() const {
+ return mInFirstLetter;
+ }
+
+ void SetInFirstLetter(bool aSetting) {
+ mInFirstLetter = aSetting;
+ }
+
+ bool GetInFirstLine() const {
+ return mInFirstLine;
+ }
+
+ void SetInFirstLine(bool aSetting) {
+ mInFirstLine = aSetting;
+ }
+
+ // Calling this during block reflow ensures that the next line of inlines
+ // will be marked dirty, if there is one.
+ void SetDirtyNextLine() {
+ mDirtyNextLine = true;
+ }
+ bool GetDirtyNextLine() {
+ return mDirtyNextLine;
+ }
+
+ //----------------------------------------
+
+ nsPresContext* mPresContext;
+
+ /**
+ * Record where an optional break could have been placed. During line reflow,
+ * frames containing optional break points (e.g., whitespace in text frames)
+ * can call SetLastOptionalBreakPosition to record where a break could
+ * have been made, but wasn't because we decided to place more content on
+ * the line. For non-text frames, offset 0 means before the frame, offset
+ * INT32_MAX means after the frame.
+ *
+ * Currently this is used to handle cases where a single word comprises
+ * multiple frames, and the first frame fits on the line but the whole word
+ * doesn't. We look back to the last optional break position and
+ * reflow the whole line again, forcing a break at that position. The last
+ * optional break position could be in a text frame or else after a frame
+ * that cannot be part of a text run, so those are the positions we record.
+ *
+ * @param aFrame the frame which contains the optional break position.
+ *
+ * @param aFits set to true if the break position is within the available width.
+ *
+ * @param aPriority the priority of the break opportunity. If we are
+ * prioritizing break opportunities, we will not set a break if we have
+ * already set a break with a higher priority. @see gfxBreakPriority.
+ *
+ * @return true if we are actually reflowing with forced break position and we
+ * should break here
+ */
+ bool NotifyOptionalBreakPosition(nsIFrame* aFrame, int32_t aOffset,
+ bool aFits, gfxBreakPriority aPriority) {
+ NS_ASSERTION(!aFits || !mNeedBackup,
+ "Shouldn't be updating the break position with a break that fits after we've already flagged an overrun");
+ // Remember the last break position that fits; if there was no break that fit,
+ // just remember the first break
+ if ((aFits && aPriority >= mLastOptionalBreakPriority) ||
+ !mLastOptionalBreakFrame) {
+ mLastOptionalBreakFrame = aFrame;
+ mLastOptionalBreakFrameOffset = aOffset;
+ mLastOptionalBreakPriority = aPriority;
+ }
+ return aFrame && mForceBreakFrame == aFrame &&
+ mForceBreakFrameOffset == aOffset;
+ }
+ /**
+ * Like NotifyOptionalBreakPosition, but here it's OK for mNeedBackup
+ * to be set, because the caller is merely pruning some saved break position(s)
+ * that are actually not feasible.
+ */
+ void RestoreSavedBreakPosition(nsIFrame* aFrame, int32_t aOffset,
+ gfxBreakPriority aPriority) {
+ mLastOptionalBreakFrame = aFrame;
+ mLastOptionalBreakFrameOffset = aOffset;
+ mLastOptionalBreakPriority = aPriority;
+ }
+ /**
+ * Signal that no backing up will be required after all.
+ */
+ void ClearOptionalBreakPosition() {
+ mNeedBackup = false;
+ mLastOptionalBreakFrame = nullptr;
+ mLastOptionalBreakFrameOffset = -1;
+ mLastOptionalBreakPriority = gfxBreakPriority::eNoBreak;
+ }
+ // Retrieve last set optional break position. When this returns null, no
+ // optional break has been recorded (which means that the line can't break yet).
+ nsIFrame* GetLastOptionalBreakPosition(int32_t* aOffset,
+ gfxBreakPriority* aPriority) {
+ *aOffset = mLastOptionalBreakFrameOffset;
+ *aPriority = mLastOptionalBreakPriority;
+ return mLastOptionalBreakFrame;
+ }
+ // Whether any optional break position has been recorded.
+ bool HasOptionalBreakPosition() const
+ {
+ return mLastOptionalBreakFrame != nullptr;
+ }
+ // Get the priority of the last optional break position recorded.
+ gfxBreakPriority LastOptionalBreakPriority() const
+ {
+ return mLastOptionalBreakPriority;
+ }
+
+ /**
+ * Check whether frames overflowed the available width and CanPlaceFrame
+ * requested backing up to a saved break position.
+ */
+ bool NeedsBackup() { return mNeedBackup; }
+
+ // Line layout may place too much content on a line, overflowing its available
+ // width. When that happens, if SetLastOptionalBreakPosition has been
+ // used to record an optional break that wasn't taken, we can reflow the line
+ // again and force the break to happen at that point (i.e., backtracking
+ // to the last choice point).
+
+ // Record that we want to break at the given content+offset (which
+ // should have been previously returned by GetLastOptionalBreakPosition
+ // from another nsLineLayout).
+ void ForceBreakAtPosition(nsIFrame* aFrame, int32_t aOffset) {
+ mForceBreakFrame = aFrame;
+ mForceBreakFrameOffset = aOffset;
+ }
+ bool HaveForcedBreakPosition() { return mForceBreakFrame != nullptr; }
+ int32_t GetForcedBreakPosition(nsIFrame* aFrame) {
+ return mForceBreakFrame == aFrame ? mForceBreakFrameOffset : -1;
+ }
+
+ /**
+ * This can't be null. It usually returns a block frame but may return
+ * some other kind of frame when inline frames are reflowed in a non-block
+ * context (e.g. MathML or floating first-letter).
+ */
+ nsIFrame* LineContainerFrame() const { return mBlockReflowInput->mFrame; }
+ const ReflowInput* LineContainerRI() const { return mBlockReflowInput; }
+ const nsLineList::iterator* GetLine() const {
+ return mGotLineBox ? &mLineBox : nullptr;
+ }
+ nsLineList::iterator* GetLine() {
+ return mGotLineBox ? &mLineBox : nullptr;
+ }
+
+ /**
+ * Returns the accumulated advance width of frames before the current frame
+ * on the line, plus the line container's left border+padding.
+ * This is always positive, the advance width is measured from
+ * the right edge for RTL blocks and from the left edge for LTR blocks.
+ * In other words, the current frame's distance from the line container's
+ * start content edge is:
+ * <code>GetCurrentFrameInlineDistanceFromBlock() - lineContainer->GetUsedBorderAndPadding().left</code>
+ * Note the use of <code>.left</code> for both LTR and RTL line containers.
+ */
+ nscoord GetCurrentFrameInlineDistanceFromBlock();
+
+ /**
+ * Move the inline position where the next frame will be reflowed forward by
+ * aAmount.
+ */
+ void AdvanceICoord(nscoord aAmount) { mCurrentSpan->mICoord += aAmount; }
+ /**
+ * Returns the writing mode for the root span.
+ */
+ mozilla::WritingMode GetWritingMode() { return mRootSpan->mWritingMode; }
+ /**
+ * Returns the inline position where the next frame will be reflowed.
+ */
+ nscoord GetCurrentICoord() { return mCurrentSpan->mICoord; }
+
+ void SetSuppressLineWrap(bool aEnabled) { mSuppressLineWrap = aEnabled; }
+
+protected:
+ // This state is constant for a given block frame doing line layout
+ nsFloatManager* mFloatManager;
+ const nsStyleText* mStyleText; // for the block
+ const ReflowInput* mBlockReflowInput;
+
+ // The line layout for the base text. It is usually nullptr.
+ // It becomes not null when the current line layout is for ruby
+ // annotations. When there is nested ruby inside annotation, it
+ // forms a linked list from the inner annotation to the outermost
+ // line layout. The outermost line layout, which has this member
+ // being nullptr, is responsible for managing the life cycle of
+ // per-frame data and per-span data, and handling floats.
+ nsLineLayout* const mBaseLineLayout;
+
+ nsLineLayout* GetOutermostLineLayout() {
+ nsLineLayout* lineLayout = this;
+ while (lineLayout->mBaseLineLayout) {
+ lineLayout = lineLayout->mBaseLineLayout;
+ }
+ return lineLayout;
+ }
+
+ nsIFrame* mLastOptionalBreakFrame;
+ nsIFrame* mForceBreakFrame;
+
+ // XXX remove this when landing bug 154892 (splitting absolute positioned frames)
+ friend class nsInlineFrame;
+
+ // XXX Take care that nsRubyBaseContainer would give nullptr to this
+ // member. It should not be a problem currently, since the only
+ // code use it is handling float, which does not affect ruby.
+ // See comment in nsLineLayout::AddFloat
+ BlockReflowInput* mBlockRI;/* XXX hack! */
+
+ nsLineList::iterator mLineBox;
+
+ // Per-frame data recorded by the line-layout reflow logic. This
+ // state is the state needed to post-process the line after reflow
+ // has completed (block-direction alignment, inline-direction alignment,
+ // justification and relative positioning).
+
+ struct PerSpanData;
+ struct PerFrameData;
+ friend struct PerSpanData;
+ friend struct PerFrameData;
+ struct PerFrameData
+ {
+ // link to next/prev frame in same span
+ PerFrameData* mNext;
+ PerFrameData* mPrev;
+
+ // Link to the frame of next ruby annotation. It is a linked list
+ // through this pointer from ruby base to all its annotations. It
+ // could be nullptr if there is no more annotation.
+ // If PFD_ISLINKEDTOBASE is set, the current PFD is one of the ruby
+ // annotations in the base's list, otherwise it is the ruby base,
+ // and its mNextAnnotation is the start of the linked list.
+ PerFrameData* mNextAnnotation;
+
+ // pointer to child span data if this is an inline container frame
+ PerSpanData* mSpan;
+
+ // The frame
+ nsIFrame* mFrame;
+
+ // From metrics
+ nscoord mAscent;
+ // note that mBounds is a logical rect in the *line*'s writing mode.
+ // When setting frame coordinates, we have to convert to the frame's
+ // writing mode
+ mozilla::LogicalRect mBounds;
+ nsOverflowAreas mOverflowAreas;
+
+ // From reflow-state
+ mozilla::LogicalMargin mMargin; // in *line* writing mode
+ mozilla::LogicalMargin mBorderPadding; // in *line* writing mode
+ mozilla::LogicalMargin mOffsets; // in *frame* writing mode
+
+ // state for text justification
+ // Note that, although all frames would have correct inner
+ // opportunities computed after ComputeFrameJustification, start
+ // and end justifiable info are not reliable for non-text frames.
+ mozilla::JustificationInfo mJustificationInfo;
+ mozilla::JustificationAssignment mJustificationAssignment;
+
+ // PerFrameData flags
+ bool mRelativePos : 1;
+ bool mIsTextFrame : 1;
+ bool mIsNonEmptyTextFrame : 1;
+ bool mIsNonWhitespaceTextFrame : 1;
+ bool mIsLetterFrame : 1;
+ bool mRecomputeOverflow : 1;
+ bool mIsBullet : 1;
+ bool mSkipWhenTrimmingWhitespace : 1;
+ bool mIsEmpty : 1;
+ bool mIsLinkedToBase : 1;
+
+ // Other state we use
+ uint8_t mBlockDirAlign;
+ mozilla::WritingMode mWritingMode;
+
+ PerFrameData* Last() {
+ PerFrameData* pfd = this;
+ while (pfd->mNext) {
+ pfd = pfd->mNext;
+ }
+ return pfd;
+ }
+
+ bool IsStartJustifiable() const
+ {
+ return mJustificationInfo.mIsStartJustifiable;
+ }
+
+ bool IsEndJustifiable() const
+ {
+ return mJustificationInfo.mIsEndJustifiable;
+ }
+
+ bool ParticipatesInJustification() const;
+ };
+ PerFrameData* mFrameFreeList;
+
+ // In nsLineLayout, a "span" is a container inline frame, and a "frame" is one
+ // of its children.
+ //
+ // nsLineLayout::BeginLineReflow() creates the initial PerSpanData which is
+ // called the "root span". nsInlineFrame::ReflowFrames() creates a new
+ // PerSpanData when it calls nsLineLayout::BeginSpan(); at this time, the
+ // nsLineLayout object's mCurrentSpan is switched to the new span. The new
+ // span records the old mCurrentSpan as its parent. After reflowing the child
+ // inline frames, nsInlineFrame::ReflowFrames() calls nsLineLayout::EndSpan(),
+ // which pops the PerSpanData and re-sets mCurrentSpan.
+ struct PerSpanData {
+ union {
+ PerSpanData* mParent;
+ PerSpanData* mNextFreeSpan;
+ };
+
+ // The PerFrameData of the inline frame that "owns" the span, or null if
+ // this is the root span. mFrame is initialized to the containing inline
+ // frame's PerFrameData when a new PerSpanData is pushed in
+ // nsLineLayout::BeginSpan().
+ PerFrameData* mFrame;
+
+ // The first PerFrameData structure in the span.
+ PerFrameData* mFirstFrame;
+
+ // The last PerFrameData structure in the span. PerFrameData structures are
+ // added to the span as they are reflowed. mLastFrame may also be directly
+ // manipulated if a line is split, or if frames are pushed from one line to
+ // the next.
+ PerFrameData* mLastFrame;
+
+ const ReflowInput* mReflowInput;
+ bool mNoWrap;
+ mozilla::WritingMode mWritingMode;
+ bool mContainsFloat;
+ bool mHasNonemptyContent;
+
+ nscoord mIStart;
+ nscoord mICoord;
+ nscoord mIEnd;
+
+ nscoord mBStartLeading, mBEndLeading;
+ nscoord mLogicalBSize;
+ nscoord mMinBCoord, mMaxBCoord;
+ nscoord* mBaseline;
+
+ void AppendFrame(PerFrameData* pfd) {
+ if (nullptr == mLastFrame) {
+ mFirstFrame = pfd;
+ }
+ else {
+ mLastFrame->mNext = pfd;
+ pfd->mPrev = mLastFrame;
+ }
+ mLastFrame = pfd;
+ }
+ };
+ PerSpanData* mSpanFreeList;
+ PerSpanData* mRootSpan;
+ PerSpanData* mCurrentSpan;
+
+ // The container size to use when converting between logical and
+ // physical coordinates for frames in this span. For the root span
+ // this is the size of the block cached in mContainerSize; for
+ // child spans it's the size of the root span.
+ nsSize ContainerSizeForSpan(PerSpanData* aPSD) {
+ return (aPSD == mRootSpan)
+ ? mContainerSize
+ : aPSD->mFrame->mBounds.Size(mRootSpan->mWritingMode).
+ GetPhysicalSize(mRootSpan->mWritingMode);
+ }
+
+ gfxBreakPriority mLastOptionalBreakPriority;
+ int32_t mLastOptionalBreakFrameOffset;
+ int32_t mForceBreakFrameOffset;
+
+ nscoord mMinLineBSize;
+
+ // The amount of text indent that we applied to this line, needed for
+ // max-element-size calculation.
+ nscoord mTextIndent;
+
+ // This state varies during the reflow of a line but is line
+ // "global" state not span "local" state.
+ int32_t mLineNumber;
+ mozilla::JustificationInfo mJustificationInfo;
+
+ int32_t mTotalPlacedFrames;
+
+ nscoord mBStartEdge;
+ nscoord mMaxStartBoxBSize;
+ nscoord mMaxEndBoxBSize;
+
+ nscoord mInflationMinFontSize;
+
+ // Final computed line-bSize value after VerticalAlignFrames for
+ // the block has been called.
+ nscoord mFinalLineBSize;
+
+ // Amount of trimmable whitespace inline size for the trailing text
+ // frame, if any
+ nscoord mTrimmableISize;
+
+ // Physical size. Use only for physical <-> logical coordinate conversion.
+ nsSize mContainerSize;
+ const nsSize& ContainerSize() const { return mContainerSize; }
+
+ bool mFirstLetterStyleOK : 1;
+ bool mIsTopOfPage : 1;
+ bool mImpactedByFloats : 1;
+ bool mLastFloatWasLetterFrame : 1;
+ bool mLineIsEmpty : 1;
+ bool mLineEndsInBR : 1;
+ bool mNeedBackup : 1;
+ bool mInFirstLine : 1;
+ bool mGotLineBox : 1;
+ bool mInFirstLetter : 1;
+ bool mHasBullet : 1;
+ bool mDirtyNextLine : 1;
+ bool mLineAtStart : 1;
+ bool mHasRuby : 1;
+ bool mSuppressLineWrap : 1;
+
+ int32_t mSpanDepth;
+#ifdef DEBUG
+ int32_t mSpansAllocated, mSpansFreed;
+ int32_t mFramesAllocated, mFramesFreed;
+#endif
+ PLArenaPool mArena; // Per span and per frame data, 4 byte aligned
+
+ /**
+ * Allocate a PerFrameData from the mArena pool. The allocation is infallible.
+ */
+ PerFrameData* NewPerFrameData(nsIFrame* aFrame);
+
+ /**
+ * Allocate a PerSpanData from the mArena pool. The allocation is infallible.
+ */
+ PerSpanData* NewPerSpanData();
+
+ PerFrameData* LastFrame() const { return mCurrentSpan->mLastFrame; }
+
+ /**
+ * Unlink the given PerFrameData and all the siblings after it from
+ * the span. The unlinked PFDs are usually freed immediately.
+ * However, if PFD_ISLINKEDTOBASE is set, it won't be freed until
+ * the frame of its base is unlinked.
+ */
+ void UnlinkFrame(PerFrameData* pfd);
+
+ /**
+ * Free the given PerFrameData.
+ */
+ void FreeFrame(PerFrameData* pfd);
+
+ void FreeSpan(PerSpanData* psd);
+
+ bool InBlockContext() const {
+ return mSpanDepth == 0;
+ }
+
+ void PushFrame(nsIFrame* aFrame);
+
+ void AllowForStartMargin(PerFrameData* pfd,
+ ReflowInput& aReflowInput);
+
+ void SyncAnnotationBounds(PerFrameData* aRubyFrame);
+
+ bool CanPlaceFrame(PerFrameData* pfd,
+ bool aNotSafeToBreak,
+ bool aFrameCanContinueTextRun,
+ bool aCanRollBackBeforeFrame,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus,
+ bool* aOptionalBreakAfterFits);
+
+ void PlaceFrame(PerFrameData* pfd,
+ ReflowOutput& aMetrics);
+
+ void AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd,
+ const nsStyleText* aStyleText, float aInflation,
+ bool* aZeroEffectiveSpanBox);
+
+ void VerticalAlignFrames(PerSpanData* psd);
+
+ void PlaceTopBottomFrames(PerSpanData* psd,
+ nscoord aDistanceFromStart,
+ nscoord aLineBSize);
+
+ void ApplyRelativePositioning(PerFrameData* aPFD);
+
+ void RelativePositionAnnotations(PerSpanData* aRubyPSD,
+ nsOverflowAreas& aOverflowAreas);
+
+ void RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas);
+
+ bool TrimTrailingWhiteSpaceIn(PerSpanData* psd, nscoord* aDeltaISize);
+
+ struct JustificationComputationState;
+
+ static int AssignInterframeJustificationGaps(
+ PerFrameData* aFrame, JustificationComputationState& aState);
+
+ int32_t ComputeFrameJustification(PerSpanData* psd,
+ JustificationComputationState& aState);
+
+ void AdvanceAnnotationInlineBounds(PerFrameData* aPFD,
+ const nsSize& aContainerSize,
+ nscoord aDeltaICoord,
+ nscoord aDeltaISize);
+
+ void ApplyLineJustificationToAnnotations(PerFrameData* aPFD,
+ nscoord aDeltaICoord,
+ nscoord aDeltaISize);
+
+ // Apply justification. The return value is the amount by which the width of
+ // the span corresponding to aPSD got increased due to justification.
+ nscoord ApplyFrameJustification(
+ PerSpanData* aPSD, mozilla::JustificationApplicationState& aState);
+
+ void ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize,
+ const nsSize& aContainerSize);
+
+ void ExpandRubyBoxWithAnnotations(PerFrameData* aFrame,
+ const nsSize& aContainerSize);
+
+ void ExpandInlineRubyBoxes(PerSpanData* aSpan);
+
+ void AttachFrameToBaseLineLayout(PerFrameData* aFrame);
+
+#ifdef DEBUG
+ void DumpPerSpanData(PerSpanData* psd, int32_t aIndent);
+#endif
+};
+
+#endif /* nsLineLayout_h___ */
diff --git a/layout/generic/nsPageContentFrame.cpp b/layout/generic/nsPageContentFrame.cpp
new file mode 100644
index 000000000..2c7348afc
--- /dev/null
+++ b/layout/generic/nsPageContentFrame.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsPageContentFrame.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsPresContext.h"
+#include "nsGkAtoms.h"
+#include "nsIPresShell.h"
+#include "nsSimplePageSequenceFrame.h"
+
+using mozilla::LogicalSize;
+using mozilla::WritingMode;
+
+nsPageContentFrame*
+NS_NewPageContentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsPageContentFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsPageContentFrame)
+
+void
+nsPageContentFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ aStatus = NS_FRAME_COMPLETE; // initialize out parameter
+
+ if (GetPrevInFlow() && (GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+ nsresult rv = aPresContext->PresShell()->FrameConstructor()
+ ->ReplicateFixedFrames(this);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+
+ // Set our size up front, since some parts of reflow depend on it
+ // being already set. Note that the computed height may be
+ // unconstrained; that's ok. Consumers should watch out for that.
+ nsSize maxSize(aReflowInput.ComputedWidth(),
+ aReflowInput.ComputedHeight());
+ SetSize(maxSize);
+
+ // A PageContentFrame must always have one child: the canvas frame.
+ // Resize our frame allowing it only to be as big as we are
+ // XXX Pay attention to the page's border and padding...
+ if (mFrames.NotEmpty()) {
+ nsIFrame* frame = mFrames.FirstChild();
+ WritingMode wm = frame->GetWritingMode();
+ LogicalSize logicalSize(wm, maxSize);
+ ReflowInput kidReflowInput(aPresContext, aReflowInput,
+ frame, logicalSize);
+ kidReflowInput.SetComputedBSize(logicalSize.BSize(wm));
+
+ // Reflow the page content area
+ ReflowChild(frame, aPresContext, aDesiredSize, kidReflowInput, 0, 0, 0, aStatus);
+
+ // The document element's background should cover the entire canvas, so
+ // take into account the combined area and any space taken up by
+ // absolutely positioned elements
+ nsMargin padding(0,0,0,0);
+
+ // XXXbz this screws up percentage padding (sets padding to zero
+ // in the percentage padding case)
+ kidReflowInput.mStylePadding->GetPadding(padding);
+
+ // This is for shrink-to-fit, and therefore we want to use the
+ // scrollable overflow, since the purpose of shrink to fit is to
+ // make the content that ought to be reachable (represented by the
+ // scrollable overflow) fit in the page.
+ if (frame->HasOverflowAreas()) {
+ // The background covers the content area and padding area, so check
+ // for children sticking outside the child frame's padding edge
+ nscoord xmost = aDesiredSize.ScrollableOverflow().XMost();
+ if (xmost > aDesiredSize.Width()) {
+ nscoord widthToFit = xmost + padding.right +
+ kidReflowInput.mStyleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT);
+ float ratio = float(maxSize.width) / widthToFit;
+ NS_ASSERTION(ratio >= 0.0 && ratio < 1.0, "invalid shrink-to-fit ratio");
+ mPD->mShrinkToFitRatio = std::min(mPD->mShrinkToFitRatio, ratio);
+ }
+ }
+
+ // Place and size the child
+ FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowInput, 0, 0, 0);
+
+ NS_ASSERTION(aPresContext->IsDynamic() || !NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
+ !frame->GetNextInFlow(), "bad child flow list");
+ }
+
+ // Reflow our fixed frames
+ nsReflowStatus fixedStatus = NS_FRAME_COMPLETE;
+ ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, fixedStatus);
+ NS_ASSERTION(NS_FRAME_IS_COMPLETE(fixedStatus), "fixed frames can be truncated, but not incomplete");
+
+ // Return our desired size
+ WritingMode wm = aReflowInput.GetWritingMode();
+ aDesiredSize.ISize(wm) = aReflowInput.ComputedISize();
+ if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
+ aDesiredSize.BSize(wm) = aReflowInput.ComputedBSize();
+ }
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nsIAtom*
+nsPageContentFrame::GetType() const
+{
+ return nsGkAtoms::pageContentFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsPageContentFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("PageContent"), aResult);
+}
+#endif
diff --git a/layout/generic/nsPageContentFrame.h b/layout/generic/nsPageContentFrame.h
new file mode 100644
index 000000000..2a8459354
--- /dev/null
+++ b/layout/generic/nsPageContentFrame.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsPageContentFrame_h___
+#define nsPageContentFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsViewportFrame.h"
+class nsPageFrame;
+class nsSharedPageData;
+
+// Page frame class used by the simple page sequence frame
+class nsPageContentFrame : public ViewportFrame {
+
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsPageContentFrame* NS_NewPageContentFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ friend class nsPageFrame;
+
+ // nsIFrame
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aMaxSize,
+ nsReflowStatus& aStatus) override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return ViewportFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eCanContainOverflowContainers));
+ }
+
+ virtual void SetSharedPageData(nsSharedPageData* aPD) { mPD = aPD; }
+
+ virtual bool HasTransformGetter() const override { return true; }
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::pageContentFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ // Debugging
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+protected:
+ explicit nsPageContentFrame(nsStyleContext* aContext) : ViewportFrame(aContext) {}
+
+ nsSharedPageData* mPD;
+};
+
+#endif /* nsPageContentFrame_h___ */
+
diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp
new file mode 100644
index 000000000..ae3af6ef7
--- /dev/null
+++ b/layout/generic/nsPageFrame.cpp
@@ -0,0 +1,747 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsPageFrame.h"
+
+#include "mozilla/gfx/2D.h"
+#include "nsDeviceContext.h"
+#include "nsFontMetrics.h"
+#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsGkAtoms.h"
+#include "nsIPresShell.h"
+#include "nsPageContentFrame.h"
+#include "nsDisplayList.h"
+#include "nsLayoutUtils.h" // for function BinarySearchForPosition
+#include "nsSimplePageSequenceFrame.h" // for nsSharedPageData
+#include "nsTextFormatter.h" // for page number localization formatting
+#include "nsBidiUtils.h"
+#include "nsIPrintSettings.h"
+
+#include "mozilla/Logging.h"
+extern mozilla::LazyLogModule gLayoutPrintingLog;
+#define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+nsPageFrame*
+NS_NewPageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsPageFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsPageFrame)
+
+NS_QUERYFRAME_HEAD(nsPageFrame)
+ NS_QUERYFRAME_ENTRY(nsPageFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+nsPageFrame::nsPageFrame(nsStyleContext* aContext)
+: nsContainerFrame(aContext)
+{
+}
+
+nsPageFrame::~nsPageFrame()
+{
+}
+
+void
+nsPageFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsPageFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ aStatus = NS_FRAME_COMPLETE; // initialize out parameter
+
+ NS_ASSERTION(mFrames.FirstChild() &&
+ nsGkAtoms::pageContentFrame == mFrames.FirstChild()->GetType(),
+ "pageFrame must have a pageContentFrame child");
+
+ // Resize our frame allowing it only to be as big as we are
+ // XXX Pay attention to the page's border and padding...
+ if (mFrames.NotEmpty()) {
+ nsIFrame* frame = mFrames.FirstChild();
+ // When the reflow size is NS_UNCONSTRAINEDSIZE it means we are reflowing
+ // a single page to print selection. So this means we want to use
+ // NS_UNCONSTRAINEDSIZE without altering it
+ nscoord avHeight;
+ if (mPD->mReflowSize.height == NS_UNCONSTRAINEDSIZE) {
+ avHeight = NS_UNCONSTRAINEDSIZE;
+ } else {
+ avHeight = mPD->mReflowSize.height;
+ }
+ nsSize maxSize(mPD->mReflowSize.width, avHeight);
+ float scale = aPresContext->GetPageScale();
+ maxSize.width = NSToCoordCeil(maxSize.width / scale);
+ if (maxSize.height != NS_UNCONSTRAINEDSIZE) {
+ maxSize.height = NSToCoordCeil(maxSize.height / scale);
+ }
+ // Get the number of Twips per pixel from the PresContext
+ nscoord onePixelInTwips = nsPresContext::CSSPixelsToAppUnits(1);
+ // insurance against infinite reflow, when reflowing less than a pixel
+ // XXX Shouldn't we do something more friendly when invalid margins
+ // are set?
+ if (maxSize.width < onePixelInTwips || maxSize.height < onePixelInTwips) {
+ aDesiredSize.ClearSize();
+ NS_WARNING("Reflow aborted; no space for content");
+ return;
+ }
+
+ ReflowInput kidReflowInput(aPresContext, aReflowInput, frame,
+ LogicalSize(frame->GetWritingMode(),
+ maxSize));
+ kidReflowInput.mFlags.mIsTopOfPage = true;
+ kidReflowInput.mFlags.mTableIsSplittable = true;
+
+ // Use the margins given in the @page rule.
+ // If a margin is 'auto', use the margin from the print settings for that side.
+ const nsStyleSides& marginStyle = kidReflowInput.mStyleMargin->mMargin;
+ NS_FOR_CSS_SIDES(side) {
+ if (marginStyle.GetUnit(side) == eStyleUnit_Auto) {
+ mPageContentMargin.Side(side) = mPD->mReflowMargin.Side(side);
+ } else {
+ mPageContentMargin.Side(side) = kidReflowInput.ComputedPhysicalMargin().Side(side);
+ }
+ }
+
+
+ nscoord maxWidth = maxSize.width - mPageContentMargin.LeftRight() / scale;
+ nscoord maxHeight;
+ if (maxSize.height == NS_UNCONSTRAINEDSIZE) {
+ maxHeight = NS_UNCONSTRAINEDSIZE;
+ } else {
+ maxHeight = maxSize.height - mPageContentMargin.TopBottom() / scale;
+ }
+
+ // Check the width and height, if they're too small we reset the margins
+ // back to the default.
+ if (maxWidth < onePixelInTwips ||
+ (maxHeight != NS_UNCONSTRAINEDSIZE && maxHeight < onePixelInTwips)) {
+ NS_FOR_CSS_SIDES(side) {
+ mPageContentMargin.Side(side) = mPD->mReflowMargin.Side(side);
+ }
+ maxWidth = maxSize.width - mPageContentMargin.LeftRight() / scale;
+ if (maxHeight != NS_UNCONSTRAINEDSIZE) {
+ maxHeight = maxSize.height - mPageContentMargin.TopBottom() / scale;
+ }
+ }
+
+ kidReflowInput.SetComputedWidth(maxWidth);
+ kidReflowInput.SetComputedHeight(maxHeight);
+
+ // calc location of frame
+ nscoord xc = mPageContentMargin.left;
+ nscoord yc = mPageContentMargin.top;
+
+ // Get the child's desired size
+ ReflowChild(frame, aPresContext, aDesiredSize, kidReflowInput, xc, yc, 0, aStatus);
+
+ // Place and size the child
+ FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowInput, xc, yc, 0);
+
+ NS_ASSERTION(!NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
+ !frame->GetNextInFlow(), "bad child flow list");
+ }
+ PR_PL(("PageFrame::Reflow %p ", this));
+ PR_PL(("[%d,%d][%d,%d]\n", aDesiredSize.Width(), aDesiredSize.Height(),
+ aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ // Return our desired size
+ WritingMode wm = aReflowInput.GetWritingMode();
+ aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
+ if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
+ aDesiredSize.BSize(wm) = aReflowInput.AvailableBSize();
+ }
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ PR_PL(("PageFrame::Reflow %p ", this));
+ PR_PL(("[%d,%d]\n", aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+nsIAtom*
+nsPageFrame::GetType() const
+{
+ return nsGkAtoms::pageFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsPageFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Page"), aResult);
+}
+#endif
+
+void
+nsPageFrame::ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr)
+{
+
+ aNewStr = aStr;
+
+ // Search to see if the &D code is in the string
+ // then subst in the current date/time
+ NS_NAMED_LITERAL_STRING(kDate, "&D");
+ if (aStr.Find(kDate) != kNotFound) {
+ aNewStr.ReplaceSubstring(kDate.get(), mPD->mDateTimeStr.get());
+ }
+
+ // NOTE: Must search for &PT before searching for &P
+ //
+ // Search to see if the "page number and page" total code are in the string
+ // and replace the page number and page total code with the actual
+ // values
+ NS_NAMED_LITERAL_STRING(kPageAndTotal, "&PT");
+ if (aStr.Find(kPageAndTotal) != kNotFound) {
+ char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumAndTotalsFormat.get(), mPageNum, mTotNumPages);
+ aNewStr.ReplaceSubstring(kPageAndTotal.get(), uStr);
+ free(uStr);
+ }
+
+ // Search to see if the page number code is in the string
+ // and replace the page number code with the actual value
+ NS_NAMED_LITERAL_STRING(kPage, "&P");
+ if (aStr.Find(kPage) != kNotFound) {
+ char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumFormat.get(), mPageNum);
+ aNewStr.ReplaceSubstring(kPage.get(), uStr);
+ free(uStr);
+ }
+
+ NS_NAMED_LITERAL_STRING(kTitle, "&T");
+ if (aStr.Find(kTitle) != kNotFound) {
+ aNewStr.ReplaceSubstring(kTitle.get(), mPD->mDocTitle.get());
+ }
+
+ NS_NAMED_LITERAL_STRING(kDocURL, "&U");
+ if (aStr.Find(kDocURL) != kNotFound) {
+ aNewStr.ReplaceSubstring(kDocURL.get(), mPD->mDocURL.get());
+ }
+
+ NS_NAMED_LITERAL_STRING(kPageTotal, "&L");
+ if (aStr.Find(kPageTotal) != kNotFound) {
+ char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumFormat.get(), mTotNumPages);
+ aNewStr.ReplaceSubstring(kPageTotal.get(), uStr);
+ free(uStr);
+ }
+}
+
+
+//------------------------------------------------------------------------------
+nscoord nsPageFrame::GetXPosition(nsRenderingContext& aRenderingContext,
+ nsFontMetrics& aFontMetrics,
+ const nsRect& aRect,
+ int32_t aJust,
+ const nsString& aStr)
+{
+ nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(aStr, this,
+ aFontMetrics,
+ aRenderingContext);
+ nscoord x = aRect.x;
+ switch (aJust) {
+ case nsIPrintSettings::kJustLeft:
+ x += mPD->mEdgePaperMargin.left;
+ break;
+
+ case nsIPrintSettings::kJustCenter:
+ x += (aRect.width - width) / 2;
+ break;
+
+ case nsIPrintSettings::kJustRight:
+ x += aRect.width - width - mPD->mEdgePaperMargin.right;
+ break;
+ } // switch
+
+ return x;
+}
+
+// Draw a header or footer
+// @param aRenderingContext - rendering content ot draw into
+// @param aHeaderFooter - indicates whether it is a header or footer
+// @param aStrLeft - string for the left header or footer; can be empty
+// @param aStrCenter - string for the center header or footer; can be empty
+// @param aStrRight - string for the right header or footer; can be empty
+// @param aRect - the rect of the page
+// @param aAscent - the ascent of the font
+// @param aHeight - the height of the font
+void
+nsPageFrame::DrawHeaderFooter(nsRenderingContext& aRenderingContext,
+ nsFontMetrics& aFontMetrics,
+ nsHeaderFooterEnum aHeaderFooter,
+ const nsString& aStrLeft,
+ const nsString& aStrCenter,
+ const nsString& aStrRight,
+ const nsRect& aRect,
+ nscoord aAscent,
+ nscoord aHeight)
+{
+ int32_t numStrs = 0;
+ if (!aStrLeft.IsEmpty()) numStrs++;
+ if (!aStrCenter.IsEmpty()) numStrs++;
+ if (!aStrRight.IsEmpty()) numStrs++;
+
+ if (numStrs == 0) return;
+ nscoord strSpace = aRect.width / numStrs;
+
+ if (!aStrLeft.IsEmpty()) {
+ DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
+ nsIPrintSettings::kJustLeft, aStrLeft, aRect, aAscent,
+ aHeight, strSpace);
+ }
+ if (!aStrCenter.IsEmpty()) {
+ DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
+ nsIPrintSettings::kJustCenter, aStrCenter, aRect, aAscent,
+ aHeight, strSpace);
+ }
+ if (!aStrRight.IsEmpty()) {
+ DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
+ nsIPrintSettings::kJustRight, aStrRight, aRect, aAscent,
+ aHeight, strSpace);
+ }
+}
+
+// Draw a header or footer string
+// @param aRenderingContext - rendering context to draw into
+// @param aHeaderFooter - indicates whether it is a header or footer
+// @param aJust - indicates where the string is located within the header/footer
+// @param aStr - the string to be drawn
+// @param aRect - the rect of the page
+// @param aHeight - the height of the font
+// @param aAscent - the ascent of the font
+// @param aWidth - available width for the string
+void
+nsPageFrame::DrawHeaderFooter(nsRenderingContext& aRenderingContext,
+ nsFontMetrics& aFontMetrics,
+ nsHeaderFooterEnum aHeaderFooter,
+ int32_t aJust,
+ const nsString& aStr,
+ const nsRect& aRect,
+ nscoord aAscent,
+ nscoord aHeight,
+ nscoord aWidth)
+{
+
+ nscoord contentWidth = aWidth - (mPD->mEdgePaperMargin.left + mPD->mEdgePaperMargin.right);
+
+ gfxContext* gfx = aRenderingContext.ThebesContext();
+ DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
+
+ if ((aHeaderFooter == eHeader && aHeight < mPageContentMargin.top) ||
+ (aHeaderFooter == eFooter && aHeight < mPageContentMargin.bottom)) {
+ nsAutoString str;
+ ProcessSpecialCodes(aStr, str);
+
+ int32_t indx;
+ int32_t textWidth = 0;
+ const char16_t* text = str.get();
+
+ int32_t len = (int32_t)str.Length();
+ if (len == 0) {
+ return; // bail is empty string
+ }
+ // find how much text fits, the "position" is the size of the available area
+ if (nsLayoutUtils::BinarySearchForPosition(drawTarget, aFontMetrics, text,
+ 0, 0, 0, len,
+ int32_t(contentWidth), indx,
+ textWidth)) {
+ if (indx < len-1 ) {
+ // we can't fit in all the text
+ if (indx > 3) {
+ // But we can fit in at least 4 chars. Show all but 3 of them, then
+ // an ellipsis.
+ // XXXbz for non-plane0 text, this may be cutting things in the
+ // middle of a codepoint! Also, we have no guarantees that the three
+ // dots will fit in the space the three chars we removed took up with
+ // these font metrics!
+ str.Truncate(indx-3);
+ str.AppendLiteral("...");
+ } else {
+ // We can only fit 3 or fewer chars. Just show nothing
+ str.Truncate();
+ }
+ }
+ } else {
+ return; // bail if couldn't find the correct length
+ }
+
+ if (HasRTLChars(str)) {
+ PresContext()->SetBidiEnabled();
+ }
+
+ // cacl the x and y positions of the text
+ nscoord x = GetXPosition(aRenderingContext, aFontMetrics, aRect, aJust, str);
+ nscoord y;
+ if (aHeaderFooter == eHeader) {
+ y = aRect.y + mPD->mEdgePaperMargin.top;
+ } else {
+ y = aRect.YMost() - aHeight - mPD->mEdgePaperMargin.bottom;
+ }
+
+ // set up new clip and draw the text
+ gfx->Save();
+ gfx->Clip(NSRectToSnappedRect(aRect, PresContext()->AppUnitsPerDevPixel(),
+ *drawTarget));
+ gfx->SetColor(Color(0.f, 0.f, 0.f));
+ nsLayoutUtils::DrawString(this, aFontMetrics, &aRenderingContext,
+ str.get(), str.Length(),
+ nsPoint(x, y + aAscent));
+ gfx->Restore();
+ }
+}
+
+/**
+ * Remove all leaf display items that are not for descendants of
+ * aBuilder->GetReferenceFrame() from aList.
+ * @param aPage the page we're constructing the display list for
+ * @param aExtraPage the page we constructed aList for
+ * @param aList the list that is modified in-place
+ */
+static void
+PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
+ nsPageFrame* aPage, nsIFrame* aExtraPage,
+ nsDisplayList* aList)
+{
+ nsDisplayList newList;
+
+ while (true) {
+ nsDisplayItem* i = aList->RemoveBottom();
+ if (!i)
+ break;
+ nsDisplayList* subList = i->GetSameCoordinateSystemChildren();
+ if (subList) {
+ PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, subList);
+ i->UpdateBounds(aBuilder);
+ } else {
+ nsIFrame* f = i->Frame();
+ if (!nsLayoutUtils::IsProperAncestorFrameCrossDoc(aPage, f)) {
+ // We're throwing this away so call its destructor now. The memory
+ // is owned by aBuilder which destroys all items at once.
+ i->~nsDisplayItem();
+ continue;
+ }
+ }
+ newList.AppendToTop(i);
+ }
+ aList->AppendToTop(&newList);
+}
+
+static void
+BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
+ nsPageFrame* aPage, nsIFrame* aExtraPage,
+ const nsRect& aDirtyRect, nsDisplayList* aList)
+{
+ // The only content in aExtraPage we care about is out-of-flow content whose
+ // placeholders have occurred in aPage. If
+ // NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO is not set, then aExtraPage has
+ // no such content.
+ if (!aExtraPage->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
+ return;
+ }
+ nsDisplayList list;
+ aExtraPage->BuildDisplayListForStackingContext(aBuilder, aDirtyRect, &list);
+ PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, &list);
+ aList->AppendToTop(&list);
+}
+
+static nsIFrame*
+GetNextPage(nsIFrame* aPageContentFrame)
+{
+ // XXX ugh
+ nsIFrame* pageFrame = aPageContentFrame->GetParent();
+ NS_ASSERTION(pageFrame->GetType() == nsGkAtoms::pageFrame,
+ "pageContentFrame has unexpected parent");
+ nsIFrame* nextPageFrame = pageFrame->GetNextSibling();
+ if (!nextPageFrame)
+ return nullptr;
+ NS_ASSERTION(nextPageFrame->GetType() == nsGkAtoms::pageFrame,
+ "pageFrame's sibling is not a page frame...");
+ nsIFrame* f = nextPageFrame->PrincipalChildList().FirstChild();
+ NS_ASSERTION(f, "pageFrame has no page content frame!");
+ NS_ASSERTION(f->GetType() == nsGkAtoms::pageContentFrame,
+ "pageFrame's child is not page content!");
+ return f;
+}
+
+static gfx::Matrix4x4 ComputePageTransform(nsIFrame* aFrame, float aAppUnitsPerPixel)
+{
+ float scale = aFrame->PresContext()->GetPageScale();
+ return gfx::Matrix4x4::Scaling(scale, scale, 1);
+}
+
+class nsDisplayHeaderFooter : public nsDisplayItem {
+public:
+ nsDisplayHeaderFooter(nsDisplayListBuilder* aBuilder, nsPageFrame *aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ , mDisableSubpixelAA(false)
+ {
+ MOZ_COUNT_CTOR(nsDisplayHeaderFooter);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayHeaderFooter() {
+ MOZ_COUNT_DTOR(nsDisplayHeaderFooter);
+ }
+#endif
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override {
+#ifdef DEBUG
+ nsPageFrame* pageFrame = do_QueryFrame(mFrame);
+ MOZ_ASSERT(pageFrame, "We should have an nsPageFrame");
+#endif
+ static_cast<nsPageFrame*>(mFrame)->
+ PaintHeaderFooter(*aCtx, ToReferenceFrame(), mDisableSubpixelAA);
+ }
+ NS_DISPLAY_DECL_NAME("HeaderFooter", nsDisplayItem::TYPE_HEADER_FOOTER)
+
+ virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override {
+ bool snap;
+ return GetBounds(aBuilder, &snap);
+ }
+
+ virtual void DisableComponentAlpha() override {
+ mDisableSubpixelAA = true;
+ }
+protected:
+ bool mDisableSubpixelAA;
+};
+
+//------------------------------------------------------------------------------
+void
+nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ nsDisplayListCollection set;
+
+ if (PresContext()->IsScreen()) {
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+ }
+
+ nsIFrame *child = mFrames.FirstChild();
+ float scale = PresContext()->GetPageScale();
+ nsRect clipRect(nsPoint(0, 0), child->GetSize());
+ // Note: this computation matches how we compute maxSize.height
+ // in nsPageFrame::Reflow
+ nscoord expectedPageContentHeight = NSToCoordCeil(GetSize().height / scale);
+ if (clipRect.height > expectedPageContentHeight) {
+ // We're doing print-selection, with one long page-content frame.
+ // Clip to the appropriate page-content slice for the current page.
+ NS_ASSERTION(mPageNum > 0, "page num should be positive");
+ // Note: The pageContentFrame's y-position has been set such that a zero
+ // y-value matches the top edge of the current page. So, to clip to the
+ // current page's content (in coordinates *relative* to the page content
+ // frame), we just negate its y-position and add the top margin.
+ clipRect.y = NSToCoordCeil((-child->GetRect().y +
+ mPD->mReflowMargin.top) / scale);
+ clipRect.height = expectedPageContentHeight;
+ NS_ASSERTION(clipRect.y < child->GetSize().height,
+ "Should be clipping to region inside the page content bounds");
+ }
+ clipRect += aBuilder->ToReferenceFrame(child);
+
+ nsDisplayList content;
+ {
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+
+ // Overwrite current clip, since we're going to wrap in a transform
+ // and the current clip is no longer meaningful.
+ clipState.Clear();
+ clipState.ClipContainingBlockDescendants(clipRect, nullptr);
+
+ nsRect dirtyRect = child->GetVisualOverflowRectRelativeToSelf();
+ child->BuildDisplayListForStackingContext(aBuilder, dirtyRect, &content);
+
+ // We may need to paint out-of-flow frames whose placeholders are
+ // on other pages. Add those pages to our display list. Note that
+ // out-of-flow frames can't be placed after their placeholders so
+ // we don't have to process earlier pages. The display lists for
+ // these extra pages are pruned so that only display items for the
+ // page we currently care about (which we would have reached by
+ // following placeholders to their out-of-flows) end up on the list.
+ nsIFrame* page = child;
+ while ((page = GetNextPage(page)) != nullptr) {
+ BuildDisplayListForExtraPage(aBuilder, this, page,
+ dirtyRect + child->GetOffsetTo(page), &content);
+ }
+
+ // Invoke AutoBuildingDisplayList to ensure that the correct dirtyRect
+ // is used to compute the visible rect if AddCanvasBackgroundColorItem
+ // creates a display item.
+ nsDisplayListBuilder::AutoBuildingDisplayList
+ building(aBuilder, child, dirtyRect, true);
+
+ // Add the canvas background color to the bottom of the list. This
+ // happens after we've built the list so that AddCanvasBackgroundColorItem
+ // can monkey with the contents if necessary.
+ nsRect backgroundRect =
+ nsRect(aBuilder->ToReferenceFrame(child), child->GetSize());
+ PresContext()->GetPresShell()->AddCanvasBackgroundColorItem(
+ *aBuilder, content, child, backgroundRect, NS_RGBA(0,0,0,0));
+ }
+
+ content.AppendNewToTop(new (aBuilder) nsDisplayTransform(aBuilder, child,
+ &content, content.GetVisibleRect(), ::ComputePageTransform));
+
+ set.Content()->AppendToTop(&content);
+
+ if (PresContext()->IsRootPaginatedDocument()) {
+ set.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayHeaderFooter(aBuilder, this));
+ }
+
+ set.MoveTo(aLists);
+}
+
+//------------------------------------------------------------------------------
+void
+nsPageFrame::SetPageNumInfo(int32_t aPageNumber, int32_t aTotalPages)
+{
+ mPageNum = aPageNumber;
+ mTotNumPages = aTotalPages;
+}
+
+
+void
+nsPageFrame::PaintHeaderFooter(nsRenderingContext& aRenderingContext,
+ nsPoint aPt, bool aDisableSubpixelAA)
+{
+ nsPresContext* pc = PresContext();
+
+ if (!mPD->mPrintSettings) {
+ if (pc->Type() == nsPresContext::eContext_PrintPreview || pc->IsDynamic())
+ mPD->mPrintSettings = pc->GetPrintSettings();
+ if (!mPD->mPrintSettings)
+ return;
+ }
+
+ nsRect rect(aPt, mRect.Size());
+ aRenderingContext.ThebesContext()->SetColor(Color(0.f, 0.f, 0.f));
+
+ DrawTargetAutoDisableSubpixelAntialiasing
+ disable(aRenderingContext.GetDrawTarget(), aDisableSubpixelAA);
+
+ // Get the FontMetrics to determine width.height of strings
+ nsFontMetrics::Params params;
+ params.userFontSet = pc->GetUserFontSet();
+ params.textPerf = pc->GetTextPerfMetrics();
+ RefPtr<nsFontMetrics> fontMet =
+ pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, params);
+
+ nscoord ascent = 0;
+ nscoord visibleHeight = 0;
+ if (fontMet) {
+ visibleHeight = fontMet->MaxHeight();
+ ascent = fontMet->MaxAscent();
+ }
+
+ // print document headers and footers
+ nsXPIDLString headerLeft, headerCenter, headerRight;
+ mPD->mPrintSettings->GetHeaderStrLeft(getter_Copies(headerLeft));
+ mPD->mPrintSettings->GetHeaderStrCenter(getter_Copies(headerCenter));
+ mPD->mPrintSettings->GetHeaderStrRight(getter_Copies(headerRight));
+ DrawHeaderFooter(aRenderingContext, *fontMet, eHeader,
+ headerLeft, headerCenter, headerRight,
+ rect, ascent, visibleHeight);
+
+ nsXPIDLString footerLeft, footerCenter, footerRight;
+ mPD->mPrintSettings->GetFooterStrLeft(getter_Copies(footerLeft));
+ mPD->mPrintSettings->GetFooterStrCenter(getter_Copies(footerCenter));
+ mPD->mPrintSettings->GetFooterStrRight(getter_Copies(footerRight));
+ DrawHeaderFooter(aRenderingContext, *fontMet, eFooter,
+ footerLeft, footerCenter, footerRight,
+ rect, ascent, visibleHeight);
+}
+
+void
+nsPageFrame::SetSharedPageData(nsSharedPageData* aPD)
+{
+ mPD = aPD;
+ // Set the shared data into the page frame before reflow
+ nsPageContentFrame * pcf = static_cast<nsPageContentFrame*>(mFrames.FirstChild());
+ if (pcf) {
+ pcf->SetSharedPageData(mPD);
+ }
+
+}
+
+nsIFrame*
+NS_NewPageBreakFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ NS_PRECONDITION(aPresShell, "null PresShell");
+ //check that we are only creating page break frames when printing
+ NS_ASSERTION(aPresShell->GetPresContext()->IsPaginated(), "created a page break frame while not printing");
+
+ return new (aPresShell) nsPageBreakFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsPageBreakFrame)
+
+nsPageBreakFrame::nsPageBreakFrame(nsStyleContext* aContext) :
+ nsLeafFrame(aContext), mHaveReflowed(false)
+{
+}
+
+nsPageBreakFrame::~nsPageBreakFrame()
+{
+}
+
+nscoord
+nsPageBreakFrame::GetIntrinsicISize()
+{
+ return nsPresContext::CSSPixelsToAppUnits(1);
+}
+
+nscoord
+nsPageBreakFrame::GetIntrinsicBSize()
+{
+ return 0;
+}
+
+void
+nsPageBreakFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ DO_GLOBAL_REFLOW_COUNT("nsPageBreakFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ // Override reflow, since we don't want to deal with what our
+ // computed values are.
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalSize finalSize(wm, GetIntrinsicISize(),
+ aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE ?
+ 0 : aReflowInput.AvailableBSize());
+ // round the height down to the nearest pixel
+ finalSize.BSize(wm) -=
+ finalSize.BSize(wm) % nsPresContext::CSSPixelsToAppUnits(1);
+ aDesiredSize.SetSize(wm, finalSize);
+
+ // Note: not using NS_FRAME_FIRST_REFLOW here, since it's not clear whether
+ // DidReflow will always get called before the next Reflow() call.
+ mHaveReflowed = true;
+ aStatus = NS_FRAME_COMPLETE;
+}
+
+nsIAtom*
+nsPageBreakFrame::GetType() const
+{
+ return nsGkAtoms::pageBreakFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsPageBreakFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("PageBreak"), aResult);
+}
+#endif
diff --git a/layout/generic/nsPageFrame.h b/layout/generic/nsPageFrame.h
new file mode 100644
index 000000000..aab2ac7b8
--- /dev/null
+++ b/layout/generic/nsPageFrame.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsPageFrame_h___
+#define nsPageFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+#include "nsLeafFrame.h"
+
+class nsFontMetrics;
+class nsSharedPageData;
+
+// Page frame class used by the simple page sequence frame
+class nsPageFrame final : public nsContainerFrame {
+
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsPageFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsPageFrame* NS_NewPageFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aMaxSize,
+ nsReflowStatus& aStatus) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::pageFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ //////////////////
+ // For Printing
+ //////////////////
+
+ // Tell the page which page number it is out of how many
+ virtual void SetPageNumInfo(int32_t aPageNumber, int32_t aTotalPages);
+
+ virtual void SetSharedPageData(nsSharedPageData* aPD);
+
+ // We must allow Print Preview UI to have a background, no matter what the
+ // user's settings
+ virtual bool HonorPrintBackgroundSettings() override { return false; }
+
+ void PaintHeaderFooter(nsRenderingContext& aRenderingContext,
+ nsPoint aPt, bool aSubpixelAA);
+
+protected:
+ explicit nsPageFrame(nsStyleContext* aContext);
+ virtual ~nsPageFrame();
+
+ typedef enum {
+ eHeader,
+ eFooter
+ } nsHeaderFooterEnum;
+
+ nscoord GetXPosition(nsRenderingContext& aRenderingContext,
+ nsFontMetrics& aFontMetrics,
+ const nsRect& aRect,
+ int32_t aJust,
+ const nsString& aStr);
+
+ void DrawHeaderFooter(nsRenderingContext& aRenderingContext,
+ nsFontMetrics& aFontMetrics,
+ nsHeaderFooterEnum aHeaderFooter,
+ int32_t aJust,
+ const nsString& sStr,
+ const nsRect& aRect,
+ nscoord aHeight,
+ nscoord aAscent,
+ nscoord aWidth);
+
+ void DrawHeaderFooter(nsRenderingContext& aRenderingContext,
+ nsFontMetrics& aFontMetrics,
+ nsHeaderFooterEnum aHeaderFooter,
+ const nsString& aStrLeft,
+ const nsString& aStrRight,
+ const nsString& aStrCenter,
+ const nsRect& aRect,
+ nscoord aAscent,
+ nscoord aHeight);
+
+ void ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr);
+
+ int32_t mPageNum;
+ int32_t mTotNumPages;
+
+ nsSharedPageData* mPD;
+ nsMargin mPageContentMargin;
+};
+
+
+class nsPageBreakFrame : public nsLeafFrame
+{
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsPageBreakFrame(nsStyleContext* aContext);
+ ~nsPageBreakFrame();
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+protected:
+
+ virtual nscoord GetIntrinsicISize() override;
+ virtual nscoord GetIntrinsicBSize() override;
+
+ bool mHaveReflowed;
+
+ friend nsIFrame* NS_NewPageBreakFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+};
+
+#endif /* nsPageFrame_h___ */
+
diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp
new file mode 100644
index 000000000..2b6799f48
--- /dev/null
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object for the point that anchors out-of-flow rendering
+ * objects such as floats and absolutely positioned elements
+ */
+
+#include "nsPlaceholderFrame.h"
+
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "nsDisplayList.h"
+#include "nsFrameManager.h"
+#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsIFrameInlines.h"
+#include "nsIContentInlines.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+nsIFrame*
+NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+ nsFrameState aTypeBit)
+{
+ return new (aPresShell) nsPlaceholderFrame(aContext, aTypeBit);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame)
+
+#ifdef DEBUG
+NS_QUERYFRAME_HEAD(nsPlaceholderFrame)
+ NS_QUERYFRAME_ENTRY(nsPlaceholderFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
+#endif
+
+/* virtual */ nsSize
+nsPlaceholderFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState)
+{
+ nsSize size(0, 0);
+ DISPLAY_MIN_SIZE(this, size);
+ return size;
+}
+
+/* virtual */ nsSize
+nsPlaceholderFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
+{
+ nsSize size(0, 0);
+ DISPLAY_PREF_SIZE(this, size);
+ return size;
+}
+
+/* virtual */ nsSize
+nsPlaceholderFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState)
+{
+ nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
+ DISPLAY_MAX_SIZE(this, size);
+ return size;
+}
+
+/* virtual */ void
+nsPlaceholderFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlineMinISizeData* aData)
+{
+ // Override AddInlineMinWith so that *nothing* happens. In
+ // particular, we don't want to zero out |aData->mTrailingWhitespace|,
+ // since nsLineLayout skips placeholders when trimming trailing
+ // whitespace, and we don't want to set aData->mSkipWhitespace to
+ // false.
+
+ // ...but push floats onto the list
+ if (mOutOfFlowFrame->IsFloating()) {
+ nscoord floatWidth =
+ nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ mOutOfFlowFrame,
+ nsLayoutUtils::MIN_ISIZE);
+ aData->mFloats.AppendElement(
+ InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth));
+ }
+}
+
+/* virtual */ void
+nsPlaceholderFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext,
+ nsIFrame::InlinePrefISizeData* aData)
+{
+ // Override AddInlinePrefWith so that *nothing* happens. In
+ // particular, we don't want to zero out |aData->mTrailingWhitespace|,
+ // since nsLineLayout skips placeholders when trimming trailing
+ // whitespace, and we don't want to set aData->mSkipWhitespace to
+ // false.
+
+ // ...but push floats onto the list
+ if (mOutOfFlowFrame->IsFloating()) {
+ nscoord floatWidth =
+ nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+ mOutOfFlowFrame,
+ nsLayoutUtils::PREF_ISIZE);
+ aData->mFloats.AppendElement(
+ InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth));
+ }
+}
+
+void
+nsPlaceholderFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+#ifdef DEBUG
+ // We should be getting reflowed before our out-of-flow.
+ // If this is our first reflow, and our out-of-flow has already received its
+ // first reflow (before us), complain.
+ // XXXdholbert This "look for a previous continuation or IB-split sibling"
+ // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if
+ // we ever add a function like that. (We currently have a "Next" version.)
+ if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
+ !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+
+ // Unfortunately, this can currently happen when the placeholder is in a
+ // later continuation or later IB-split sibling than its out-of-flow (as
+ // is the case in some of our existing unit tests). So for now, in that
+ // case, we'll warn instead of asserting.
+ bool isInContinuationOrIBSplit = false;
+ nsIFrame* ancestor = this;
+ while ((ancestor = ancestor->GetParent())) {
+ if (ancestor->GetPrevContinuation() ||
+ ancestor->Properties().Get(IBSplitPrevSibling())) {
+ isInContinuationOrIBSplit = true;
+ break;
+ }
+ }
+
+ if (isInContinuationOrIBSplit) {
+ NS_WARNING("Out-of-flow frame got reflowed before its placeholder");
+ } else {
+ NS_ERROR("Out-of-flow frame got reflowed before its placeholder");
+ }
+ }
+#endif
+
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ aDesiredSize.ClearSize();
+
+ aStatus = NS_FRAME_COMPLETE;
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+void
+nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ nsIFrame* oof = mOutOfFlowFrame;
+ if (oof) {
+ // Unregister out-of-flow frame
+ nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager();
+ fm->UnregisterPlaceholderFrame(this);
+ mOutOfFlowFrame = nullptr;
+ // If aDestructRoot is not an ancestor of the out-of-flow frame,
+ // then call RemoveFrame on it here.
+ // Also destroy it here if it's a popup frame. (Bug 96291)
+ if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) ||
+ !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) {
+ ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof);
+ fm->RemoveFrame(listId, oof);
+ }
+ // else oof will be destroyed by its parent
+ }
+
+ nsFrame::DestroyFrom(aDestructRoot);
+}
+
+nsIAtom*
+nsPlaceholderFrame::GetType() const
+{
+ return nsGkAtoms::placeholderFrame;
+}
+
+/* virtual */ bool
+nsPlaceholderFrame::CanContinueTextRun() const
+{
+ if (!mOutOfFlowFrame) {
+ return false;
+ }
+ // first-letter frames can continue text runs, and placeholders for floated
+ // first-letter frames can too
+ return mOutOfFlowFrame->CanContinueTextRun();
+}
+
+nsStyleContext*
+nsPlaceholderFrame::GetParentStyleContext(nsIFrame** aProviderFrame) const
+{
+ NS_PRECONDITION(GetParent(), "How can we not have a parent here?");
+
+ nsIContent* parentContent = mContent ? mContent->GetFlattenedTreeParent() : nullptr;
+ if (parentContent) {
+ nsStyleContext* sc =
+ PresContext()->FrameManager()->GetDisplayContentsStyleFor(parentContent);
+ if (sc) {
+ *aProviderFrame = nullptr;
+ return sc;
+ }
+ }
+
+ // Lie about our pseudo so we can step out of all anon boxes and
+ // pseudo-elements. The other option would be to reimplement the
+ // {ib} split gunk here.
+ *aProviderFrame = CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame);
+ return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
+}
+
+
+#ifdef DEBUG
+static void
+PaintDebugPlaceholder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ ColorPattern cyan(ToDeviceColor(Color(0.f, 1.f, 1.f, 1.f)));
+ int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+
+ nscoord x = nsPresContext::CSSPixelsToAppUnits(-5);
+ nsRect r(aPt.x + x, aPt.y,
+ nsPresContext::CSSPixelsToAppUnits(13),
+ nsPresContext::CSSPixelsToAppUnits(3));
+ aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
+
+ nscoord y = nsPresContext::CSSPixelsToAppUnits(-10);
+ r = nsRect(aPt.x, aPt.y + y,
+ nsPresContext::CSSPixelsToAppUnits(3),
+ nsPresContext::CSSPixelsToAppUnits(10));
+ aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
+}
+#endif // DEBUG
+
+#if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF))
+
+void
+nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame");
+
+#ifdef DEBUG
+ if (GetShowFrameBorders()) {
+ aLists.Outlines()->AppendNewToTop(
+ new (aBuilder) nsDisplayGeneric(aBuilder, this, PaintDebugPlaceholder,
+ "DebugPlaceholder",
+ nsDisplayItem::TYPE_DEBUG_PLACEHOLDER));
+ }
+#endif
+}
+#endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF)
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsPlaceholderFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult);
+}
+
+void
+nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+
+ if (mOutOfFlowFrame) {
+ str += " outOfFlowFrame=";
+ nsFrame::ListTag(str, mOutOfFlowFrame);
+ }
+ fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
diff --git a/layout/generic/nsPlaceholderFrame.h b/layout/generic/nsPlaceholderFrame.h
new file mode 100644
index 000000000..0c23a4f75
--- /dev/null
+++ b/layout/generic/nsPlaceholderFrame.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object for the point that anchors out-of-flow rendering
+ * objects such as floats and absolutely positioned elements
+ */
+
+/*
+ * Destruction of a placeholder and its out-of-flow must observe the
+ * following constraints:
+ *
+ * - The mapping from the out-of-flow to the placeholder must be
+ * removed from the frame manager before the placeholder is destroyed.
+ * - The mapping from the out-of-flow to the placeholder must be
+ * removed from the frame manager before the out-of-flow is destroyed.
+ * - The placeholder must be removed from the frame tree, or have the
+ * mapping from it to its out-of-flow cleared, before the out-of-flow
+ * is destroyed (so that the placeholder will not point to a destroyed
+ * frame while it's in the frame tree).
+ *
+ * Furthermore, some code assumes that placeholders point to something
+ * useful, so placeholders without an associated out-of-flow should not
+ * remain in the tree.
+ *
+ * The placeholder's Destroy() implementation handles the destruction of
+ * the placeholder and its out-of-flow. To avoid crashes, frame removal
+ * and destruction code that works with placeholders must not assume
+ * that the placeholder points to its out-of-flow.
+ */
+
+#ifndef nsPlaceholderFrame_h___
+#define nsPlaceholderFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsFrame.h"
+#include "nsGkAtoms.h"
+
+nsIFrame* NS_NewPlaceholderFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext,
+ nsFrameState aTypeBit);
+
+#define PLACEHOLDER_TYPE_MASK (PLACEHOLDER_FOR_FLOAT | \
+ PLACEHOLDER_FOR_ABSPOS | \
+ PLACEHOLDER_FOR_FIXEDPOS | \
+ PLACEHOLDER_FOR_POPUP | \
+ PLACEHOLDER_FOR_TOPLAYER)
+
+/**
+ * Implementation of a frame that's used as a placeholder for a frame that
+ * has been moved out of the flow.
+ */
+class nsPlaceholderFrame final : public nsFrame {
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+#ifdef DEBUG
+ NS_DECL_QUERYFRAME_TARGET(nsPlaceholderFrame)
+ NS_DECL_QUERYFRAME
+#endif
+
+ /**
+ * Create a new placeholder frame. aTypeBit must be one of the
+ * PLACEHOLDER_FOR_* constants above.
+ */
+ friend nsIFrame* NS_NewPlaceholderFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext,
+ nsFrameState aTypeBit);
+ nsPlaceholderFrame(nsStyleContext* aContext, nsFrameState aTypeBit)
+ : nsFrame(aContext)
+ {
+ NS_PRECONDITION(aTypeBit == PLACEHOLDER_FOR_FLOAT ||
+ aTypeBit == PLACEHOLDER_FOR_ABSPOS ||
+ aTypeBit == PLACEHOLDER_FOR_FIXEDPOS ||
+ aTypeBit == PLACEHOLDER_FOR_POPUP ||
+ aTypeBit == PLACEHOLDER_FOR_TOPLAYER,
+ "Unexpected type bit");
+ AddStateBits(aTypeBit);
+ }
+
+ // Get/Set the associated out of flow frame
+ nsIFrame* GetOutOfFlowFrame() const { return mOutOfFlowFrame; }
+ void SetOutOfFlowFrame(nsIFrame* aFrame) {
+ NS_ASSERTION(!aFrame || !aFrame->GetPrevContinuation(),
+ "OOF must be first continuation");
+ mOutOfFlowFrame = aFrame;
+ }
+
+ // nsIFrame overrides
+ // We need to override GetXULMinSize and GetXULPrefSize because XUL uses
+ // placeholders not within lines.
+ virtual void AddInlineMinISize(nsRenderingContext* aRenderingContext,
+ InlineMinISizeData* aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext* aRenderingContext,
+ InlinePrefISizeData* aData) override;
+ virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
+ virtual nsSize GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+#if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF))
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+#endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF)
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif // DEBUG
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::placeholderFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsEmpty() override { return true; }
+ virtual bool IsSelfEmpty() override { return true; }
+
+ virtual bool CanContinueTextRun() const override;
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override
+ {
+ nsIFrame* realFrame = GetRealFrameForPlaceholder(this);
+ return realFrame ? realFrame->AccessibleType() :
+ nsFrame::AccessibleType();
+ }
+#endif
+
+ virtual nsStyleContext* GetParentStyleContext(nsIFrame** aProviderFrame) const override;
+
+ bool RenumberFrameAndDescendants(int32_t* aOrdinal,
+ int32_t aDepth,
+ int32_t aIncrement,
+ bool aForCounting) override
+ {
+ return mOutOfFlowFrame->
+ RenumberFrameAndDescendants(aOrdinal, aDepth, aIncrement, aForCounting);
+ }
+
+ /**
+ * @return the out-of-flow for aFrame if aFrame is a placeholder; otherwise
+ * aFrame
+ */
+ static nsIFrame* GetRealFrameFor(nsIFrame* aFrame) {
+ NS_PRECONDITION(aFrame, "Must have a frame to work with");
+ if (aFrame->GetType() == nsGkAtoms::placeholderFrame) {
+ return GetRealFrameForPlaceholder(aFrame);
+ }
+ return aFrame;
+ }
+
+ /**
+ * @return the out-of-flow for aFrame, which is known to be a placeholder
+ */
+ static nsIFrame* GetRealFrameForPlaceholder(nsIFrame* aFrame) {
+ NS_PRECONDITION(aFrame->GetType() == nsGkAtoms::placeholderFrame,
+ "Must have placeholder frame as input");
+ nsIFrame* outOfFlow =
+ static_cast<nsPlaceholderFrame*>(aFrame)->GetOutOfFlowFrame();
+ NS_ASSERTION(outOfFlow, "Null out-of-flow for placeholder?");
+ return outOfFlow;
+ }
+
+protected:
+ nsIFrame* mOutOfFlowFrame;
+};
+
+#endif /* nsPlaceholderFrame_h___ */
diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp
new file mode 100644
index 000000000..34ed12d44
--- /dev/null
+++ b/layout/generic/nsPluginFrame.cpp
@@ -0,0 +1,1887 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering objects for replaced elements implemented by a plugin */
+
+#include "nsPluginFrame.h"
+
+#include "gfx2DGlue.h"
+#include "gfxMatrix.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/MouseEvents.h"
+#ifdef XP_WIN
+// This is needed for DoublePassRenderingEvent.
+#include "mozilla/plugins/PluginMessageUtils.h"
+#endif
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsWidgetsCID.h"
+#include "nsView.h"
+#include "nsViewManager.h"
+#include "nsString.h"
+#include "nsGkAtoms.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIDOMElement.h"
+#include "nsRenderingContext.h"
+#include "npapi.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsContentUtils.h"
+#include "nsDisplayList.h"
+#include "nsFocusManager.h"
+#include "nsLayoutUtils.h"
+#include "nsFrameManager.h"
+#include "nsIObserverService.h"
+#include "GeckoProfiler.h"
+#include <algorithm>
+
+#include "nsIObjectFrame.h"
+#include "nsPluginNativeWindow.h"
+#include "FrameLayerBuilder.h"
+
+#include "ImageLayers.h"
+#include "nsPluginInstanceOwner.h"
+
+#ifdef XP_WIN
+#include "gfxWindowsNativeDrawing.h"
+#include "gfxWindowsSurface.h"
+#endif
+
+#include "DisplayItemScrollClip.h"
+#include "Layers.h"
+#include "ReadbackLayer.h"
+#include "ImageContainer.h"
+
+// accessibility support
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+#ifdef MOZ_LOGGING
+#define FORCE_PR_LOG 1 /* Allow logging in the release build */
+#endif /* MOZ_LOGGING */
+#include "mozilla/Logging.h"
+
+#ifdef XP_MACOSX
+#include "gfxQuartzNativeDrawing.h"
+#include "mozilla/gfx/QuartzSupport.h"
+#endif
+
+#ifdef MOZ_X11
+#include "mozilla/X11Util.h"
+using mozilla::DefaultXDisplay;
+#endif
+
+#ifdef XP_WIN
+#include <wtypes.h>
+#include <winuser.h>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "GLContext.h"
+#endif
+
+#include "mozilla/dom/TabChild.h"
+#include "ClientLayerManager.h"
+
+#ifdef CreateEvent // Thank you MS.
+#undef CreateEvent
+#endif
+
+static mozilla::LazyLogModule sPluginFrameLog("nsPluginFrame");
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+class PluginBackgroundSink : public ReadbackSink {
+public:
+ PluginBackgroundSink(nsPluginFrame* aFrame, uint64_t aStartSequenceNumber)
+ : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {}
+ ~PluginBackgroundSink() override
+ {
+ if (mFrame) {
+ mFrame->mBackgroundSink = nullptr;
+ }
+ }
+
+ void SetUnknown(uint64_t aSequenceNumber) override
+ {
+ if (!AcceptUpdate(aSequenceNumber))
+ return;
+ mFrame->mInstanceOwner->SetBackgroundUnknown();
+ }
+
+ already_AddRefed<DrawTarget>
+ BeginUpdate(const nsIntRect& aRect, uint64_t aSequenceNumber) override
+ {
+ if (!AcceptUpdate(aSequenceNumber))
+ return nullptr;
+ return mFrame->mInstanceOwner->BeginUpdateBackground(aRect);
+ }
+
+ void EndUpdate(const nsIntRect& aRect) override
+ {
+ return mFrame->mInstanceOwner->EndUpdateBackground(aRect);
+ }
+
+ void Destroy() { mFrame = nullptr; }
+
+protected:
+ bool AcceptUpdate(uint64_t aSequenceNumber) {
+ if (aSequenceNumber > mLastSequenceNumber && mFrame &&
+ mFrame->mInstanceOwner) {
+ mLastSequenceNumber = aSequenceNumber;
+ return true;
+ }
+ return false;
+ }
+
+ uint64_t mLastSequenceNumber;
+ nsPluginFrame* mFrame;
+};
+
+nsPluginFrame::nsPluginFrame(nsStyleContext* aContext)
+ : nsFrame(aContext)
+ , mInstanceOwner(nullptr)
+ , mReflowCallbackPosted(false)
+{
+ MOZ_LOG(sPluginFrameLog, LogLevel::Debug,
+ ("Created new nsPluginFrame %p\n", this));
+}
+
+nsPluginFrame::~nsPluginFrame()
+{
+ MOZ_LOG(sPluginFrameLog, LogLevel::Debug,
+ ("nsPluginFrame %p deleted\n", this));
+}
+
+NS_QUERYFRAME_HEAD(nsPluginFrame)
+ NS_QUERYFRAME_ENTRY(nsPluginFrame)
+ NS_QUERYFRAME_ENTRY(nsIObjectFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsPluginFrame::AccessibleType()
+{
+ return a11y::ePluginType;
+}
+
+#ifdef XP_WIN
+NS_IMETHODIMP nsPluginFrame::GetPluginPort(HWND *aPort)
+{
+ *aPort = (HWND) mInstanceOwner->GetPluginPort();
+ return NS_OK;
+}
+#endif
+#endif
+
+void
+nsPluginFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ MOZ_LOG(sPluginFrameLog, LogLevel::Debug,
+ ("Initializing nsPluginFrame %p for content %p\n", this, aContent));
+
+ nsFrame::Init(aContent, aParent, aPrevInFlow);
+}
+
+void
+nsPluginFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ if (mReflowCallbackPosted) {
+ PresContext()->PresShell()->CancelReflowCallback(this);
+ }
+
+ // Ensure our DidComposite observer is gone.
+ mDidCompositeObserver = nullptr;
+
+ // Tell content owner of the instance to disconnect its frame.
+ nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent));
+ NS_ASSERTION(objContent, "Why not an object loading content?");
+
+ // The content might not have a reference to the instance owner any longer in
+ // the case of re-entry during instantiation or teardown, so make sure we're
+ // dissociated.
+ if (mInstanceOwner) {
+ mInstanceOwner->SetFrame(nullptr);
+ }
+ objContent->HasNewFrame(nullptr);
+
+ if (mBackgroundSink) {
+ mBackgroundSink->Destroy();
+ }
+
+ nsFrame::DestroyFrom(aDestructRoot);
+}
+
+/* virtual */ void
+nsPluginFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+ if (HasView()) {
+ nsView* view = GetView();
+ nsViewManager* vm = view->GetViewManager();
+ if (vm) {
+ nsViewVisibility visibility =
+ IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow;
+ vm->SetViewVisibility(view, visibility);
+ }
+ }
+
+ nsFrame::DidSetStyleContext(aOldStyleContext);
+}
+
+nsIAtom*
+nsPluginFrame::GetType() const
+{
+ return nsGkAtoms::objectFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsPluginFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("PluginFrame"), aResult);
+}
+#endif
+
+nsresult
+nsPluginFrame::PrepForDrawing(nsIWidget *aWidget)
+{
+ mWidget = aWidget;
+
+ nsView* view = GetView();
+ NS_ASSERTION(view, "Object frames must have views");
+ if (!view) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsViewManager* viewMan = view->GetViewManager();
+ // mark the view as hidden since we don't know the (x,y) until Paint
+ // XXX is the above comment correct?
+ viewMan->SetViewVisibility(view, nsViewVisibility_kHide);
+
+ //this is ugly. it was ripped off from didreflow(). MMP
+ // Position and size view relative to its parent, not relative to our
+ // parent frame (our parent frame may not have a view).
+
+ nsView* parentWithView;
+ nsPoint origin;
+ nsRect r(0, 0, mRect.width, mRect.height);
+
+ GetOffsetFromView(origin, &parentWithView);
+ viewMan->ResizeView(view, r);
+ viewMan->MoveViewTo(view, origin.x, origin.y);
+
+ nsPresContext* presContext = PresContext();
+ nsRootPresContext* rpc = presContext->GetRootPresContext();
+ if (!rpc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mWidget) {
+ // Disallow windowed plugins in popups
+ nsIFrame* rootFrame = rpc->PresShell()->FrameManager()->GetRootFrame();
+ nsIWidget* parentWidget = rootFrame->GetNearestWidget();
+ if (!parentWidget || nsLayoutUtils::GetDisplayRootFrame(this) != rootFrame) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mInnerView = viewMan->CreateView(GetContentRectRelativeToSelf(), view);
+ if (!mInnerView) {
+ NS_ERROR("Could not create inner view");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ viewMan->InsertChild(view, mInnerView, nullptr, true);
+
+ mWidget->SetParent(parentWidget);
+ mWidget->Enable(true);
+ mWidget->Show(true);
+
+ // Set the plugin window to have an empty clip region until we know
+ // what our true position, size and clip region are. These
+ // will be reset when nsRootPresContext computes our true
+ // geometry. The plugin window does need to have a good size here, so
+ // set the size explicitly to a reasonable guess.
+ AutoTArray<nsIWidget::Configuration,1> configurations;
+ nsIWidget::Configuration* configuration = configurations.AppendElement();
+ nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+ configuration->mChild = mWidget;
+ configuration->mBounds.width = NSAppUnitsToIntPixels(mRect.width, appUnitsPerDevPixel);
+ configuration->mBounds.height = NSAppUnitsToIntPixels(mRect.height, appUnitsPerDevPixel);
+ parentWidget->ConfigureChildren(configurations);
+
+ mInnerView->AttachWidgetEventHandler(mWidget);
+
+#ifdef XP_MACOSX
+ // On Mac, we need to invalidate ourselves since even windowed
+ // plugins are painted through Thebes and we need to ensure
+ // the PaintedLayer containing the plugin is updated.
+ if (parentWidget == GetNearestWidget()) {
+ InvalidateFrame();
+ }
+#endif
+
+ RegisterPluginForGeometryUpdates();
+
+ // Here we set the background color for this widget because some plugins will use
+ // the child window background color when painting. If it's not set, it may default to gray
+ // Sometimes, a frame doesn't have a background color or is transparent. In this
+ // case, walk up the frame tree until we do find a frame with a background color
+ for (nsIFrame* frame = this; frame; frame = frame->GetParent()) {
+ nscolor bgcolor =
+ frame->GetVisitedDependentColor(eCSSProperty_background_color);
+ if (NS_GET_A(bgcolor) > 0) { // make sure we got an actual color
+ mWidget->SetBackgroundColor(bgcolor);
+ break;
+ }
+ }
+ } else {
+ // Changing to windowless mode changes the NPWindow geometry.
+ FixupWindow(GetContentRectRelativeToSelf().Size());
+ RegisterPluginForGeometryUpdates();
+ }
+
+ if (!IsHidden()) {
+ viewMan->SetViewVisibility(view, nsViewVisibility_kShow);
+ }
+
+#ifdef ACCESSIBILITY
+ nsAccessibilityService* accService = nsIPresShell::AccService();
+ if (accService) {
+ accService->RecreateAccessible(PresContext()->PresShell(), mContent);
+ }
+#endif
+
+ return NS_OK;
+}
+
+#define EMBED_DEF_WIDTH 240
+#define EMBED_DEF_HEIGHT 200
+
+/* virtual */ nscoord
+nsPluginFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result = 0;
+
+ if (!IsHidden(false)) {
+ if (mContent->IsAnyOfHTMLElements(nsGkAtoms::applet,
+ nsGkAtoms::embed)) {
+ bool vertical = GetWritingMode().IsVertical();
+ result = nsPresContext::CSSPixelsToAppUnits(
+ vertical ? EMBED_DEF_HEIGHT : EMBED_DEF_WIDTH);
+ }
+ }
+
+ DISPLAY_MIN_WIDTH(this, result);
+ return result;
+}
+
+/* virtual */ nscoord
+nsPluginFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ return nsPluginFrame::GetMinISize(aRenderingContext);
+}
+
+void
+nsPluginFrame::GetWidgetConfiguration(nsTArray<nsIWidget::Configuration>* aConfigurations)
+{
+ if (!mWidget) {
+ return;
+ }
+
+ if (!mWidget->GetParent()) {
+ // Plugin widgets should not be toplevel except when they're out of the
+ // document, in which case the plugin should not be registered for
+ // geometry updates and this should not be called. But apparently we
+ // have bugs where mWidget sometimes is toplevel here. Bail out.
+ NS_ERROR("Plugin widgets registered for geometry updates should not be toplevel");
+ return;
+ }
+
+ nsIWidget::Configuration* configuration = aConfigurations->AppendElement();
+ configuration->mChild = mWidget;
+ configuration->mBounds = mNextConfigurationBounds;
+ configuration->mClipRegion = mNextConfigurationClipRegion;
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (XRE_IsContentProcess()) {
+ configuration->mWindowID = (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
+ configuration->mVisible = mWidget->IsVisible();
+
+ }
+#endif // defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+}
+
+void
+nsPluginFrame::GetDesiredSize(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ ReflowOutput& aMetrics)
+{
+ // By default, we have no area
+ aMetrics.ClearSize();
+
+ if (IsHidden(false)) {
+ return;
+ }
+
+ aMetrics.Width() = aReflowInput.ComputedWidth();
+ aMetrics.Height() = aReflowInput.ComputedHeight();
+
+ // for EMBED and APPLET, default to 240x200 for compatibility
+ if (mContent->IsAnyOfHTMLElements(nsGkAtoms::applet,
+ nsGkAtoms::embed)) {
+ if (aMetrics.Width() == NS_UNCONSTRAINEDSIZE) {
+ aMetrics.Width() = clamped(nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_WIDTH),
+ aReflowInput.ComputedMinWidth(),
+ aReflowInput.ComputedMaxWidth());
+ }
+ if (aMetrics.Height() == NS_UNCONSTRAINEDSIZE) {
+ aMetrics.Height() = clamped(nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_HEIGHT),
+ aReflowInput.ComputedMinHeight(),
+ aReflowInput.ComputedMaxHeight());
+ }
+
+#if defined(MOZ_WIDGET_GTK)
+ // We need to make sure that the size of the object frame does not
+ // exceed the maximum size of X coordinates. See bug #225357 for
+ // more information. In theory Gtk2 can handle large coordinates,
+ // but underlying plugins can't.
+ aMetrics.Height() = std::min(aPresContext->DevPixelsToAppUnits(INT16_MAX), aMetrics.Height());
+ aMetrics.Width() = std::min(aPresContext->DevPixelsToAppUnits(INT16_MAX), aMetrics.Width());
+#endif
+ }
+
+ // At this point, the width has an unconstrained value only if we have
+ // nothing to go on (no width set, no information from the plugin, nothing).
+ // Make up a number.
+ if (aMetrics.Width() == NS_UNCONSTRAINEDSIZE) {
+ aMetrics.Width() =
+ (aReflowInput.ComputedMinWidth() != NS_UNCONSTRAINEDSIZE) ?
+ aReflowInput.ComputedMinWidth() : 0;
+ }
+
+ // At this point, the height has an unconstrained value only in two cases:
+ // a) We are in standards mode with percent heights and parent is auto-height
+ // b) We have no height information at all.
+ // In either case, we have to make up a number.
+ if (aMetrics.Height() == NS_UNCONSTRAINEDSIZE) {
+ aMetrics.Height() =
+ (aReflowInput.ComputedMinHeight() != NS_UNCONSTRAINEDSIZE) ?
+ aReflowInput.ComputedMinHeight() : 0;
+ }
+
+ // XXXbz don't add in the border and padding, because we screw up our
+ // plugin's size and positioning if we do... Eventually we _do_ want to
+ // paint borders, though! At that point, we will need to adjust the desired
+ // size either here or in Reflow.... Further, we will need to fix Paint() to
+ // call the superclass in all cases.
+}
+
+void
+nsPluginFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsPluginFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+
+ // Get our desired size
+ GetDesiredSize(aPresContext, aReflowInput, aMetrics);
+ aMetrics.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aMetrics);
+
+ // delay plugin instantiation until all children have
+ // arrived. Otherwise there may be PARAMs or other stuff that the
+ // plugin needs to see that haven't arrived yet.
+ if (!GetContent()->IsDoneAddingChildren()) {
+ aStatus = NS_FRAME_COMPLETE;
+ return;
+ }
+
+ // if we are printing or print previewing, bail for now
+ if (aPresContext->Medium() == nsGkAtoms::print) {
+ aStatus = NS_FRAME_COMPLETE;
+ return;
+ }
+
+ nsRect r(0, 0, aMetrics.Width(), aMetrics.Height());
+ r.Deflate(aReflowInput.ComputedPhysicalBorderPadding());
+
+ if (mInnerView) {
+ nsViewManager* vm = mInnerView->GetViewManager();
+ vm->MoveViewTo(mInnerView, r.x, r.y);
+ vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), r.Size()), true);
+ }
+
+ FixupWindow(r.Size());
+ if (!mReflowCallbackPosted) {
+ mReflowCallbackPosted = true;
+ aPresContext->PresShell()->PostReflowCallback(this);
+ }
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+///////////// nsIReflowCallback ///////////////
+
+bool
+nsPluginFrame::ReflowFinished()
+{
+ mReflowCallbackPosted = false;
+ CallSetWindow();
+ return true;
+}
+
+void
+nsPluginFrame::ReflowCallbackCanceled()
+{
+ mReflowCallbackPosted = false;
+}
+
+void
+nsPluginFrame::FixupWindow(const nsSize& aSize)
+{
+ nsPresContext* presContext = PresContext();
+
+ if (!mInstanceOwner)
+ return;
+
+ NPWindow *window;
+ mInstanceOwner->GetWindow(window);
+
+ NS_ENSURE_TRUE_VOID(window);
+
+ bool windowless = (window->type == NPWindowTypeDrawable);
+
+ nsIntPoint origin = GetWindowOriginInPixels(windowless);
+
+ // window must be in "display pixels"
+#if defined(XP_MACOSX)
+ // window must be in "display pixels"
+ double scaleFactor = 1.0;
+ if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) {
+ scaleFactor = 1.0;
+ }
+ int intScaleFactor = ceil(scaleFactor);
+ window->x = origin.x / intScaleFactor;
+ window->y = origin.y / intScaleFactor;
+ window->width = presContext->AppUnitsToDevPixels(aSize.width) / intScaleFactor;
+ window->height = presContext->AppUnitsToDevPixels(aSize.height) / intScaleFactor;
+#else
+ window->x = origin.x;
+ window->y = origin.y;
+ window->width = presContext->AppUnitsToDevPixels(aSize.width);
+ window->height = presContext->AppUnitsToDevPixels(aSize.height);
+#endif
+
+#ifndef XP_MACOSX
+ mInstanceOwner->UpdateWindowPositionAndClipRect(false);
+#endif
+
+ NotifyPluginReflowObservers();
+}
+
+nsresult
+nsPluginFrame::CallSetWindow(bool aCheckIsHidden)
+{
+ NPWindow *win = nullptr;
+
+ nsresult rv = NS_ERROR_FAILURE;
+ RefPtr<nsNPAPIPluginInstance> pi;
+ if (!mInstanceOwner ||
+ NS_FAILED(rv = mInstanceOwner->GetInstance(getter_AddRefs(pi))) ||
+ !pi ||
+ NS_FAILED(rv = mInstanceOwner->GetWindow(win)) ||
+ !win)
+ return rv;
+
+ nsPluginNativeWindow *window = (nsPluginNativeWindow *)win;
+
+ if (aCheckIsHidden && IsHidden())
+ return NS_ERROR_FAILURE;
+
+ // Calling either nsPluginInstanceOwner::FixUpPluginWindow() (here,
+ // on OS X) or SetWindow() (below, on all platforms) can destroy this
+ // frame. (FixUpPluginWindow() calls SetWindow()). So grab a safe
+ // reference to mInstanceOwner which we can use below, if needed.
+ RefPtr<nsPluginInstanceOwner> instanceOwnerRef(mInstanceOwner);
+
+ // refresh the plugin port as well
+#ifdef XP_MACOSX
+ mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable);
+ // Bail now if our frame has been destroyed.
+ if (!instanceOwnerRef->GetFrame()) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+ window->window = mInstanceOwner->GetPluginPort();
+
+ // Adjust plugin dimensions according to pixel snap results
+ // and reduce amount of SetWindow calls
+ nsPresContext* presContext = PresContext();
+ nsRootPresContext* rootPC = presContext->GetRootPresContext();
+ if (!rootPC)
+ return NS_ERROR_FAILURE;
+ int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+ nsIFrame* rootFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
+ nsRect bounds = GetContentRectRelativeToSelf() + GetOffsetToCrossDoc(rootFrame);
+ nsIntRect intBounds = bounds.ToNearestPixels(appUnitsPerDevPixel);
+
+ // In e10s, this returns the offset to the top level window, in non-e10s
+ // it return 0,0.
+ LayoutDeviceIntPoint intOffset = GetRemoteTabChromeOffset();
+ intBounds.x += intOffset.x;
+ intBounds.y += intOffset.y;
+
+#if defined(XP_MACOSX)
+ // window must be in "display pixels"
+ double scaleFactor = 1.0;
+ if (NS_FAILED(instanceOwnerRef->GetContentsScaleFactor(&scaleFactor))) {
+ scaleFactor = 1.0;
+ }
+
+ size_t intScaleFactor = ceil(scaleFactor);
+ window->x = intBounds.x / intScaleFactor;
+ window->y = intBounds.y / intScaleFactor;
+ window->width = intBounds.width / intScaleFactor;
+ window->height = intBounds.height / intScaleFactor;
+#else
+ window->x = intBounds.x;
+ window->y = intBounds.y;
+ window->width = intBounds.width;
+ window->height = intBounds.height;
+#endif
+ // BE CAREFUL: By the time we get here the PluginFrame is sometimes destroyed
+ // and poisoned. If we reference local fields (implicit this deref),
+ // we will crash.
+ instanceOwnerRef->ResolutionMayHaveChanged();
+
+ // This will call pi->SetWindow and take care of window subclassing
+ // if needed, see bug 132759. Calling SetWindow can destroy this frame
+ // so check for that before doing anything else with this frame's memory.
+ if (instanceOwnerRef->UseAsyncRendering()) {
+ rv = pi->AsyncSetWindow(window);
+ }
+ else {
+ rv = window->CallSetWindow(pi);
+ }
+
+ instanceOwnerRef->ReleasePluginPort(window->window);
+
+ return rv;
+}
+
+void
+nsPluginFrame::RegisterPluginForGeometryUpdates()
+{
+ nsRootPresContext* rpc = PresContext()->GetRootPresContext();
+ NS_ASSERTION(rpc, "We should have a root pres context!");
+ if (mRootPresContextRegisteredWith == rpc || !rpc) {
+ // Already registered with current root pres context,
+ // or null root pres context...
+ return;
+ }
+ if (mRootPresContextRegisteredWith && mRootPresContextRegisteredWith != rpc) {
+ // Registered to some other root pres context. Unregister, and
+ // re-register with our current one...
+ UnregisterPluginForGeometryUpdates();
+ }
+ mRootPresContextRegisteredWith = rpc;
+ mRootPresContextRegisteredWith->RegisterPluginForGeometryUpdates(mContent);
+}
+
+void
+nsPluginFrame::UnregisterPluginForGeometryUpdates()
+{
+ if (!mRootPresContextRegisteredWith) {
+ // Not registered...
+ return;
+ }
+ mRootPresContextRegisteredWith->UnregisterPluginForGeometryUpdates(mContent);
+ mRootPresContextRegisteredWith = nullptr;
+}
+
+void
+nsPluginFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner)
+{
+ // The ownership model here is historically fuzzy. This should only be called
+ // by nsPluginInstanceOwner when it is given a new frame, and
+ // nsObjectLoadingContent should be arbitrating frame-ownership via its
+ // HasNewFrame callback.
+ mInstanceOwner = aOwner;
+
+ // Reset the DidCompositeObserver since the owner changed.
+ mDidCompositeObserver = nullptr;
+
+ if (mInstanceOwner) {
+ return;
+ }
+
+ UnregisterPluginForGeometryUpdates();
+ if (mWidget && mInnerView) {
+ mInnerView->DetachWidgetEventHandler(mWidget);
+ // Make sure the plugin is hidden in case an update of plugin geometry
+ // hasn't happened since this plugin became hidden.
+ nsIWidget* parent = mWidget->GetParent();
+ if (parent) {
+ nsTArray<nsIWidget::Configuration> configurations;
+ nsIWidget::Configuration* configuration = configurations.AppendElement();
+ configuration->mChild = mWidget;
+ parent->ConfigureChildren(configurations);
+
+ mWidget->Show(false);
+ mWidget->Enable(false);
+ mWidget->SetParent(nullptr);
+ }
+ }
+}
+
+bool
+nsPluginFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
+{
+ if (aTabIndex)
+ *aTabIndex = -1;
+ return nsFrame::IsFocusable(aTabIndex, aWithMouse);
+}
+
+bool
+nsPluginFrame::IsHidden(bool aCheckVisibilityStyle) const
+{
+ if (aCheckVisibilityStyle) {
+ if (!StyleVisibility()->IsVisibleOrCollapsed())
+ return true;
+ }
+
+ // only <embed> tags support the HIDDEN attribute
+ if (mContent->IsHTMLElement(nsGkAtoms::embed)) {
+ // Yes, these are really the kooky ways that you could tell 4.x
+ // not to hide the <embed> once you'd put the 'hidden' attribute
+ // on the tag...
+
+ // HIDDEN w/ no attributes gets translated as we are hidden for
+ // compatibility w/ 4.x and IE so we don't create a non-painting
+ // widget in layout. See bug 188959.
+ nsAutoString hidden;
+ if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::hidden, hidden) &&
+ (hidden.IsEmpty() ||
+ (!hidden.LowerCaseEqualsLiteral("false") &&
+ !hidden.LowerCaseEqualsLiteral("no") &&
+ !hidden.LowerCaseEqualsLiteral("off")))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+mozilla::LayoutDeviceIntPoint
+nsPluginFrame::GetRemoteTabChromeOffset()
+{
+ LayoutDeviceIntPoint offset;
+ if (XRE_IsContentProcess()) {
+ if (nsPIDOMWindowOuter* window = GetContent()->OwnerDoc()->GetWindow()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetTop()) {
+ dom::TabChild* tc = dom::TabChild::GetFrom(topWindow);
+ if (tc) {
+ offset += tc->GetChromeDisplacement();
+ }
+ }
+ }
+ }
+ return offset;
+}
+
+nsIntPoint
+nsPluginFrame::GetWindowOriginInPixels(bool aWindowless)
+{
+ nsView * parentWithView;
+ nsPoint origin(0,0);
+
+ GetOffsetFromView(origin, &parentWithView);
+
+ // if it's windowless, let's make sure we have our origin set right
+ // it may need to be corrected, like after scrolling
+ if (aWindowless && parentWithView) {
+ nsPoint offsetToWidget;
+ parentWithView->GetNearestWidget(&offsetToWidget);
+ origin += offsetToWidget;
+ }
+ origin += GetContentRectRelativeToSelf().TopLeft();
+
+ nsIntPoint pt(PresContext()->AppUnitsToDevPixels(origin.x),
+ PresContext()->AppUnitsToDevPixels(origin.y));
+
+ // If we're in the content process offsetToWidget is tied to the top level
+ // widget we can access in the child process, which is the tab. We need the
+ // offset all the way up to the top level native window here. (If this is
+ // non-e10s this routine will return 0,0.)
+ if (aWindowless) {
+ mozilla::LayoutDeviceIntPoint lpt = GetRemoteTabChromeOffset();
+ pt += nsIntPoint(lpt.x, lpt.y);
+ }
+
+ return pt;
+}
+
+void
+nsPluginFrame::DidReflow(nsPresContext* aPresContext,
+ const ReflowInput* aReflowInput,
+ nsDidReflowStatus aStatus)
+{
+ // Do this check before calling the superclass, as that clears
+ // NS_FRAME_FIRST_REFLOW
+ if (aStatus == nsDidReflowStatus::FINISHED &&
+ (GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+ nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent));
+ NS_ASSERTION(objContent, "Why not an object loading content?");
+ objContent->HasNewFrame(this);
+ }
+
+ nsFrame::DidReflow(aPresContext, aReflowInput, aStatus);
+
+ // The view is created hidden; once we have reflowed it and it has been
+ // positioned then we show it.
+ if (aStatus != nsDidReflowStatus::FINISHED)
+ return;
+
+ if (HasView()) {
+ nsView* view = GetView();
+ nsViewManager* vm = view->GetViewManager();
+ if (vm)
+ vm->SetViewVisibility(view, IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow);
+ }
+}
+
+/* static */ void
+nsPluginFrame::PaintPrintPlugin(nsIFrame* aFrame, nsRenderingContext* aCtx,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ gfxContext* ctx = aCtx->ThebesContext();
+
+ // Translate the context:
+ nsPoint pt = aPt + aFrame->GetContentRectRelativeToSelf().TopLeft();
+ gfxPoint devPixelPt =
+ nsLayoutUtils::PointToGfxPoint(pt, aFrame->PresContext()->AppUnitsPerDevPixel());
+
+ gfxContextMatrixAutoSaveRestore autoSR(ctx);
+ ctx->SetMatrix(ctx->CurrentMatrix().Translate(devPixelPt));
+
+ // FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
+
+ static_cast<nsPluginFrame*>(aFrame)->PrintPlugin(*aCtx, aDirtyRect);
+}
+
+/**
+ * nsDisplayPluginReadback creates an active ReadbackLayer. The ReadbackLayer
+ * obtains from the compositor the contents of the window underneath
+ * the ReadbackLayer, which we then use as an opaque buffer for plugins to
+ * asynchronously draw onto.
+ */
+class nsDisplayPluginReadback : public nsDisplayItem {
+public:
+ nsDisplayPluginReadback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ {
+ MOZ_COUNT_CTOR(nsDisplayPluginReadback);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ ~nsDisplayPluginReadback() override {
+ MOZ_COUNT_DTOR(nsDisplayPluginReadback);
+ }
+#endif
+
+ nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override;
+
+ NS_DISPLAY_DECL_NAME("PluginReadback", TYPE_PLUGIN_READBACK)
+
+ already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) override
+ {
+ return static_cast<nsPluginFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
+ }
+
+ LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) override
+ {
+ return LAYER_ACTIVE;
+ }
+};
+
+static nsRect
+GetDisplayItemBounds(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, nsIFrame* aFrame)
+{
+ // XXX For slightly more accurate region computations we should pixel-snap this
+ return aFrame->GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
+}
+
+nsRect
+nsDisplayPluginReadback::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
+{
+ *aSnap = false;
+ return GetDisplayItemBounds(aBuilder, this, mFrame);
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+
+class nsDisplayPluginVideo : public nsDisplayItem {
+public:
+ nsDisplayPluginVideo(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsNPAPIPluginInstance::VideoInfo* aVideoInfo)
+ : nsDisplayItem(aBuilder, aFrame), mVideoInfo(aVideoInfo)
+ {
+ MOZ_COUNT_CTOR(nsDisplayPluginVideo);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayPluginVideo() {
+ MOZ_COUNT_DTOR(nsDisplayPluginVideo);
+ }
+#endif
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override;
+
+ NS_DISPLAY_DECL_NAME("PluginVideo", TYPE_PLUGIN_VIDEO)
+
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) override
+ {
+ return static_cast<nsPluginFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
+ }
+
+ virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) override
+ {
+ return LAYER_ACTIVE;
+ }
+
+ nsNPAPIPluginInstance::VideoInfo* VideoInfo() { return mVideoInfo; }
+
+private:
+ nsNPAPIPluginInstance::VideoInfo* mVideoInfo;
+};
+
+nsRect
+nsDisplayPluginVideo::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
+{
+ *aSnap = false;
+ return GetDisplayItemBounds(aBuilder, this, mFrame);
+}
+
+#endif
+
+nsRect
+nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
+{
+ *aSnap = true;
+ return GetDisplayItemBounds(aBuilder, this, mFrame);
+}
+
+void
+nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame);
+ bool snap;
+ f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder, &snap));
+}
+
+static nsRect
+GetClippedBoundsIncludingAllScrollClips(nsDisplayItem* aItem,
+ nsDisplayListBuilder* aBuilder)
+{
+ nsRect r = aItem->GetClippedBounds(aBuilder);
+ for (auto* sc = aItem->ScrollClip(); sc; sc = sc->mParent) {
+ if (sc->mClip) {
+ r = sc->mClip->ApplyNonRoundedIntersection(r);
+ }
+ }
+ return r;
+}
+
+bool
+nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder,
+ nsRegion* aVisibleRegion)
+{
+ if (aBuilder->IsForPluginGeometry()) {
+ nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame);
+ if (!aBuilder->IsInTransform() || f->IsPaintedByGecko()) {
+ // Since transforms induce reference frames, we don't need to worry
+ // about this method fluffing out due to non-rectilinear transforms.
+ nsRect rAncestor = nsLayoutUtils::TransformFrameRectToAncestor(f,
+ f->GetContentRectRelativeToSelf(), ReferenceFrame());
+ nscoord appUnitsPerDevPixel =
+ ReferenceFrame()->PresContext()->AppUnitsPerDevPixel();
+ f->mNextConfigurationBounds = LayoutDeviceIntRect::FromUnknownRect(
+ rAncestor.ToNearestPixels(appUnitsPerDevPixel));
+
+ nsRegion visibleRegion;
+ // Apply all scroll clips when computing the clipped bounds of this item.
+ // We hide windowed plugins during APZ scrolling, so there never is an
+ // async transform that we need to take into account when clipping.
+ visibleRegion.And(*aVisibleRegion, GetClippedBoundsIncludingAllScrollClips(this, aBuilder));
+ // Make visibleRegion relative to f
+ visibleRegion.MoveBy(-ToReferenceFrame());
+
+ f->mNextConfigurationClipRegion.Clear();
+ for (auto iter = visibleRegion.RectIter(); !iter.Done(); iter.Next()) {
+ nsRect rAncestor =
+ nsLayoutUtils::TransformFrameRectToAncestor(f, iter.Get(), ReferenceFrame());
+ LayoutDeviceIntRect rPixels =
+ LayoutDeviceIntRect::FromUnknownRect(rAncestor.ToNearestPixels(appUnitsPerDevPixel)) -
+ f->mNextConfigurationBounds.TopLeft();
+ if (!rPixels.IsEmpty()) {
+ f->mNextConfigurationClipRegion.AppendElement(rPixels);
+ }
+ }
+ }
+
+ if (f->mInnerView) {
+ // This should produce basically the same rectangle (but not relative
+ // to the root frame). We only call this here for the side-effect of
+ // setting mViewToWidgetOffset on the view.
+ f->mInnerView->CalcWidgetBounds(eWindowType_plugin);
+ }
+ }
+
+ return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion);
+}
+
+nsRegion
+nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+ bool* aSnap)
+{
+ *aSnap = false;
+ nsRegion result;
+ nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame);
+ if (!aBuilder->IsForPluginGeometry()) {
+ nsIWidget* widget = f->GetWidget();
+ if (widget) {
+ // Be conservative and treat plugins with widgets as not opaque,
+ // because that's simple and we might need the content under the widget
+ // if the widget is unexpectedly clipped away. (As can happen when
+ // chrome content over a plugin forces us to clip out the plugin for
+ // security reasons.)
+ // We shouldn't be repainting the content under plugins much anyway
+ // since there generally shouldn't be anything to invalidate or paint
+ // in PaintedLayers there.
+ return result;
+ }
+ }
+
+ if (f->IsOpaque()) {
+ nsRect bounds = GetBounds(aBuilder, aSnap);
+ if (aBuilder->IsForPluginGeometry() ||
+ (f->GetPaintedRect(this) + ToReferenceFrame()).Contains(bounds)) {
+ // We can treat this as opaque
+ result = bounds;
+ }
+ }
+
+ return result;
+}
+
+nsresult
+nsPluginFrame::PluginEventNotifier::Run() {
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ obsSvc->NotifyObservers(nullptr, "plugin-changed-event", mEventType.get());
+ return NS_OK;
+}
+
+void
+nsPluginFrame::NotifyPluginReflowObservers()
+{
+ nsContentUtils::AddScriptRunner(new PluginEventNotifier(NS_LITERAL_STRING("reflow")));
+}
+
+void
+nsPluginFrame::DidSetWidgetGeometry()
+{
+#if defined(XP_MACOSX)
+ if (mInstanceOwner && !IsHidden()) {
+ mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable);
+ }
+#else
+ if (!mWidget && mInstanceOwner) {
+ // UpdateWindowVisibility will notify the plugin of position changes
+ // by updating the NPWindow and calling NPP_SetWindow/AsyncSetWindow.
+ // We treat windowless plugins inside popups as always visible, since
+ // plugins inside popups don't get valid mNextConfigurationBounds
+ // set up.
+ mInstanceOwner->UpdateWindowVisibility(
+ nsLayoutUtils::IsPopup(nsLayoutUtils::GetDisplayRootFrame(this)) ||
+ !mNextConfigurationBounds.IsEmpty());
+ }
+#endif
+}
+
+bool
+nsPluginFrame::IsOpaque() const
+{
+#if defined(XP_MACOSX)
+ return false;
+#elif defined(MOZ_WIDGET_ANDROID)
+ // We don't know, so just assume transparent
+ return false;
+#else
+
+ if (mInstanceOwner && mInstanceOwner->UseAsyncRendering()) {
+ return false;
+ }
+ return !IsTransparentMode();
+#endif
+}
+
+bool
+nsPluginFrame::IsTransparentMode() const
+{
+#if defined(XP_MACOSX)
+ return false;
+#else
+ if (!mInstanceOwner)
+ return false;
+
+ NPWindow *window = nullptr;
+ mInstanceOwner->GetWindow(window);
+ if (!window) {
+ return false;
+ }
+
+ if (window->type != NPWindowTypeDrawable)
+ return false;
+
+ nsresult rv;
+ RefPtr<nsNPAPIPluginInstance> pi;
+ rv = mInstanceOwner->GetInstance(getter_AddRefs(pi));
+ if (NS_FAILED(rv) || !pi)
+ return false;
+
+ bool transparent = false;
+ pi->IsTransparent(&transparent);
+ return transparent;
+#endif
+}
+
+void
+nsPluginFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ // XXX why are we painting collapsed object frames?
+ if (!IsVisibleOrCollapsedForPainting(aBuilder))
+ return;
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ nsPresContext::nsPresContextType type = PresContext()->Type();
+
+ // If we are painting in Print Preview do nothing....
+ if (type == nsPresContext::eContext_PrintPreview)
+ return;
+
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsPluginFrame");
+
+#ifndef XP_MACOSX
+ if (mWidget && aBuilder->IsInTransform()) {
+ // Windowed plugins should not be rendered inside a transform.
+ return;
+ }
+#endif
+
+ if (aBuilder->IsForPainting() && mInstanceOwner) {
+ // Update plugin frame for both content scaling and full zoom changes.
+ mInstanceOwner->ResolutionMayHaveChanged();
+#ifdef XP_MACOSX
+ mInstanceOwner->WindowFocusMayHaveChanged();
+#endif
+ if (mInstanceOwner->UseAsyncRendering()) {
+ NPWindow* window = nullptr;
+ mInstanceOwner->GetWindow(window);
+ bool isVisible = window && window->width > 0 && window->height > 0;
+ if (isVisible && aBuilder->ShouldSyncDecodeImages()) {
+#ifndef XP_MACOSX
+ mInstanceOwner->UpdateWindowVisibility(true);
+#endif
+ }
+
+ mInstanceOwner->NotifyPaintWaiter(aBuilder);
+ }
+ }
+
+ DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
+ clip(aBuilder, this);
+
+ // determine if we are printing
+ if (type == nsPresContext::eContext_Print) {
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayGeneric(aBuilder, this, PaintPrintPlugin, "PrintPlugin",
+ nsDisplayItem::TYPE_PRINT_PLUGIN));
+ } else {
+ LayerState state = GetLayerState(aBuilder, nullptr);
+ if (state == LAYER_INACTIVE &&
+ nsDisplayItem::ForceActiveLayers()) {
+ state = LAYER_ACTIVE;
+ }
+ // We don't need this on Android, and it just confuses things
+#if !MOZ_WIDGET_ANDROID
+ if (aBuilder->IsPaintingToWindow() &&
+ state == LAYER_ACTIVE &&
+ IsTransparentMode()) {
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayPluginReadback(aBuilder, this));
+ }
+#endif
+
+#if MOZ_WIDGET_ANDROID
+ if (aBuilder->IsPaintingToWindow() &&
+ state == LAYER_ACTIVE) {
+
+ nsTArray<nsNPAPIPluginInstance::VideoInfo*> videos;
+ mInstanceOwner->GetVideos(videos);
+
+ for (uint32_t i = 0; i < videos.Length(); i++) {
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayPluginVideo(aBuilder, this, videos[i]));
+ }
+ }
+#endif
+
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayPlugin(aBuilder, this));
+ }
+}
+
+void
+nsPluginFrame::PrintPlugin(nsRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect)
+{
+ nsCOMPtr<nsIObjectLoadingContent> obj(do_QueryInterface(mContent));
+ if (!obj)
+ return;
+
+ nsIFrame* frame = nullptr;
+ obj->GetPrintFrame(&frame);
+ if (!frame)
+ return;
+
+ nsPresContext* presContext = PresContext();
+ // make sure this is REALLY an nsIObjectFrame
+ // we may need to go through the children to get it
+ nsIObjectFrame* objectFrame = do_QueryFrame(frame);
+ if (!objectFrame)
+ objectFrame = GetNextObjectFrame(presContext,frame);
+ if (!objectFrame)
+ return;
+
+ // finally we can get our plugin instance
+ RefPtr<nsNPAPIPluginInstance> pi;
+ if (NS_FAILED(objectFrame->GetPluginInstance(getter_AddRefs(pi))) || !pi)
+ return;
+
+ // now we need to setup the correct location for printing
+ NPWindow window;
+ window.window = nullptr;
+
+ // prepare embedded mode printing struct
+ NPPrint npprint;
+ npprint.mode = NP_EMBED;
+
+ // we need to find out if we are windowless or not
+ bool windowless = false;
+ pi->IsWindowless(&windowless);
+ window.type = windowless ? NPWindowTypeDrawable : NPWindowTypeWindow;
+
+ window.clipRect.bottom = 0; window.clipRect.top = 0;
+ window.clipRect.left = 0; window.clipRect.right = 0;
+
+// platform specific printing code
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+ // Doesn't work in a thebes world, or on OS X.
+ (void)window;
+ (void)npprint;
+#elif defined(XP_WIN)
+
+ /* On Windows, we use the win32 printing surface to print. This, in
+ * turn, uses the Cairo paginated surface, which in turn uses the
+ * meta surface to record all operations and then play them back.
+ * This doesn't work too well for plugins, because if plugins render
+ * directly into the DC, the meta surface won't have any knowledge
+ * of them, and so at the end when it actually does the replay step,
+ * it'll fill the background with white and draw over whatever was
+ * rendered before.
+ *
+ * So, to avoid this, we use PushGroup, which creates a new windows
+ * surface, the plugin renders to that, and then we use normal
+ * cairo methods to composite that in such that it's recorded using the
+ * meta surface.
+ */
+
+ /* we'll already be translated into the right spot by gfxWindowsNativeDrawing */
+ nsSize contentSize = GetContentRectRelativeToSelf().Size();
+ window.x = 0;
+ window.y = 0;
+ window.width = presContext->AppUnitsToDevPixels(contentSize.width);
+ window.height = presContext->AppUnitsToDevPixels(contentSize.height);
+
+ gfxContext *ctx = aRenderingContext.ThebesContext();
+
+ ctx->Save();
+
+ /* Make sure plugins don't do any damage outside of where they're supposed to */
+ ctx->NewPath();
+ gfxRect r(window.x, window.y, window.width, window.height);
+ ctx->Rectangle(r);
+ ctx->Clip();
+
+ gfxWindowsNativeDrawing nativeDraw(ctx, r);
+ do {
+ HDC dc = nativeDraw.BeginNativeDrawing();
+ if (!dc)
+ return;
+
+ // XXX don't we need to call nativeDraw.TransformToNativeRect here?
+ npprint.print.embedPrint.platformPrint = dc;
+ npprint.print.embedPrint.window = window;
+ // send off print info to plugin
+ pi->Print(&npprint);
+
+ nativeDraw.EndNativeDrawing();
+ } while (nativeDraw.ShouldRenderAgain());
+ nativeDraw.PaintToContext();
+
+ ctx->Restore();
+#endif
+
+ // XXX Nav 4.x always sent a SetWindow call after print. Should we do the same?
+ // XXX Calling DidReflow here makes no sense!!!
+ nsDidReflowStatus status = nsDidReflowStatus::FINISHED; // should we use a special status?
+ frame->DidReflow(presContext,
+ nullptr, status); // DidReflow will take care of it
+}
+
+nsRect
+nsPluginFrame::GetPaintedRect(nsDisplayPlugin* aItem)
+{
+ if (!mInstanceOwner)
+ return nsRect();
+ nsRect r = GetContentRectRelativeToSelf();
+ if (!mInstanceOwner->UseAsyncRendering())
+ return r;
+
+ nsIntSize size = mInstanceOwner->GetCurrentImageSize();
+ nsPresContext* pc = PresContext();
+ r.IntersectRect(r, nsRect(0, 0, pc->DevPixelsToAppUnits(size.width),
+ pc->DevPixelsToAppUnits(size.height)));
+ return r;
+}
+
+LayerState
+nsPluginFrame::GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager)
+{
+ if (!mInstanceOwner)
+ return LAYER_NONE;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // We always want a layer on Honeycomb and later
+ return LAYER_ACTIVE;
+#else
+ if (mInstanceOwner->NeedsScrollImageLayer()) {
+ return LAYER_ACTIVE;
+ }
+
+ if (!mInstanceOwner->UseAsyncRendering()) {
+ return LAYER_NONE;
+ }
+
+ return LAYER_ACTIVE_FORCE;
+#endif
+}
+
+class PluginFrameDidCompositeObserver final : public ClientLayerManager::
+ DidCompositeObserver
+{
+public:
+ PluginFrameDidCompositeObserver(nsPluginInstanceOwner* aOwner, ClientLayerManager* aLayerManager)
+ : mInstanceOwner(aOwner),
+ mLayerManager(aLayerManager)
+ {
+ }
+ ~PluginFrameDidCompositeObserver() {
+ mLayerManager->RemoveDidCompositeObserver(this);
+ }
+ void DidComposite() override {
+ mInstanceOwner->DidComposite();
+ }
+ bool IsValid(ClientLayerManager* aLayerManager) {
+ return aLayerManager == mLayerManager;
+ }
+
+private:
+ nsPluginInstanceOwner* mInstanceOwner;
+ RefPtr<ClientLayerManager> mLayerManager;
+};
+
+already_AddRefed<Layer>
+nsPluginFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ const ContainerLayerParameters& aContainerParameters)
+{
+ if (!mInstanceOwner)
+ return nullptr;
+
+ NPWindow* window = nullptr;
+ mInstanceOwner->GetWindow(window);
+ if (!window)
+ return nullptr;
+
+ if (window->width <= 0 || window->height <= 0)
+ return nullptr;
+
+#if defined(XP_MACOSX)
+ // window is in "display pixels", but size needs to be in device pixels
+ // window must be in "display pixels"
+ double scaleFactor = 1.0;
+ if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) {
+ scaleFactor = 1.0;
+ }
+
+ size_t intScaleFactor = ceil(scaleFactor);
+#else
+ size_t intScaleFactor = 1;
+#endif
+
+ IntSize size(window->width * intScaleFactor, window->height * intScaleFactor);
+
+ nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
+ gfxRect r = nsLayoutUtils::RectToGfxRect(area, PresContext()->AppUnitsPerDevPixel());
+ // to provide crisper and faster drawing.
+ r.Round();
+ RefPtr<Layer> layer =
+ (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
+
+ if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN) {
+ RefPtr<ImageContainer> container;
+ // Image for Windowed plugins that support window capturing for scroll
+ // operations or async windowless rendering.
+ container = mInstanceOwner->GetImageContainer();
+ if (!container) {
+ // This can occur if our instance is gone or if the current plugin
+ // configuration does not require a backing image layer.
+ return nullptr;
+ }
+
+ if (!layer) {
+ mInstanceOwner->NotifyPaintWaiter(aBuilder);
+ // Initialize ImageLayer
+ layer = aManager->CreateImageLayer();
+ if (!layer)
+ return nullptr;
+ }
+
+ NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "Bad layer type");
+ ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get());
+#ifdef XP_MACOSX
+ if (!mInstanceOwner->UseAsyncRendering()) {
+ mInstanceOwner->DoCocoaEventDrawRect(r, nullptr);
+ }
+#endif
+
+ imglayer->SetScaleToSize(size, ScaleMode::STRETCH);
+ imglayer->SetContainer(container);
+ SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(this);
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ if (!aManager->IsCompositingCheap()) {
+ // Pixman just horrible with bilinear filter scaling
+ samplingFilter = SamplingFilter::POINT;
+ }
+#endif
+ imglayer->SetSamplingFilter(samplingFilter);
+
+ layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0);
+
+ if (aBuilder->IsPaintingToWindow() &&
+ aBuilder->GetWidgetLayerManager() &&
+ aBuilder->GetWidgetLayerManager()->AsClientLayerManager() &&
+ mInstanceOwner->UseAsyncRendering())
+ {
+ RefPtr<ClientLayerManager> lm = aBuilder->GetWidgetLayerManager()->AsClientLayerManager();
+ if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) {
+ mDidCompositeObserver = MakeUnique<PluginFrameDidCompositeObserver>(mInstanceOwner, lm);
+ }
+ lm->AddDidCompositeObserver(mDidCompositeObserver.get());
+ }
+#ifdef MOZ_WIDGET_ANDROID
+ } else if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_VIDEO) {
+ nsDisplayPluginVideo* videoItem = reinterpret_cast<nsDisplayPluginVideo*>(aItem);
+ nsNPAPIPluginInstance::VideoInfo* videoInfo = videoItem->VideoInfo();
+
+ RefPtr<ImageContainer> container = mInstanceOwner->GetImageContainerForVideo(videoInfo);
+ if (!container)
+ return nullptr;
+
+ if (!layer) {
+ // Initialize ImageLayer
+ layer = aManager->CreateImageLayer();
+ if (!layer)
+ return nullptr;
+ }
+
+ ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get());
+ imglayer->SetContainer(container);
+
+ layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0);
+
+ // Set the offset and size according to the video dimensions
+ r.MoveBy(videoInfo->mDimensions.TopLeft());
+ size.width = videoInfo->mDimensions.width;
+ size.height = videoInfo->mDimensions.height;
+#endif
+ } else {
+ NS_ASSERTION(aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_READBACK,
+ "Unknown item type");
+ MOZ_ASSERT(!IsOpaque(), "Opaque plugins don't use backgrounds");
+
+ if (!layer) {
+ layer = aManager->CreateReadbackLayer();
+ if (!layer)
+ return nullptr;
+ }
+ NS_ASSERTION(layer->GetType() == Layer::TYPE_READBACK, "Bad layer type");
+
+ ReadbackLayer* readback = static_cast<ReadbackLayer*>(layer.get());
+ if (readback->GetSize() != size) {
+ // This will destroy any old background sink and notify us that the
+ // background is now unknown
+ readback->SetSink(nullptr);
+ readback->SetSize(size);
+
+ if (mBackgroundSink) {
+ // Maybe we still have a background sink associated with another
+ // readback layer that wasn't recycled for some reason? Unhook it
+ // now so that if this frame goes away, it doesn't have a dangling
+ // reference to us.
+ mBackgroundSink->Destroy();
+ }
+ mBackgroundSink =
+ new PluginBackgroundSink(this,
+ readback->AllocateSequenceNumber());
+ readback->SetSink(mBackgroundSink);
+ // The layer has taken ownership of our sink. When either the sink dies
+ // or the frame dies, the connection from the surviving object is nulled out.
+ }
+ }
+
+ // Set a transform on the layer to draw the plugin in the right place
+ gfxPoint p = r.TopLeft() + aContainerParameters.mOffset;
+ Matrix transform = Matrix::Translation(p.x, p.y);
+
+ layer->SetBaseTransform(Matrix4x4::From2D(transform));
+ return layer.forget();
+}
+
+void
+nsPluginFrame::PaintPlugin(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect, const nsRect& aPluginRect)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mInstanceOwner) {
+ gfxRect frameGfxRect =
+ PresContext()->AppUnitsToGfxUnits(aPluginRect);
+ gfxRect dirtyGfxRect =
+ PresContext()->AppUnitsToGfxUnits(aDirtyRect);
+
+ gfxContext* ctx = aRenderingContext.ThebesContext();
+
+ mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect);
+ return;
+ }
+#else
+# if defined(DEBUG)
+ // On Desktop, we should have built a layer as we no longer support in-process
+ // plugins or synchronous painting. We can only get here for windowed plugins
+ // (which draw themselves), or via some error/unload state.
+ if (mInstanceOwner) {
+ NPWindow *window = nullptr;
+ mInstanceOwner->GetWindow(window);
+ MOZ_ASSERT(!window || window->type == NPWindowTypeWindow);
+ }
+# endif
+#endif
+}
+
+nsresult
+nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* anEvent,
+ nsEventStatus* anEventStatus)
+{
+ NS_ENSURE_ARG_POINTER(anEvent);
+ NS_ENSURE_ARG_POINTER(anEventStatus);
+ nsresult rv = NS_OK;
+
+ if (!mInstanceOwner)
+ return NS_ERROR_NULL_POINTER;
+
+ mInstanceOwner->ConsiderNewEventloopNestingLevel();
+
+ if (anEvent->mMessage == ePluginActivate) {
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(GetContent());
+ if (fm && elem)
+ return fm->SetFocus(elem, 0);
+ }
+ else if (anEvent->mMessage == ePluginFocus) {
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ nsCOMPtr<nsIContent> content = GetContent();
+ return fm->FocusPlugin(content);
+ }
+ }
+
+ if (mInstanceOwner->SendNativeEvents() &&
+ anEvent->IsNativeEventDelivererForPlugin()) {
+ *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
+ // Due to plugin code reentering Gecko, this frame may be dead at this
+ // point.
+ return rv;
+ }
+
+#ifdef XP_WIN
+ rv = nsFrame::HandleEvent(aPresContext, anEvent, anEventStatus);
+ return rv;
+#endif
+
+#ifdef XP_MACOSX
+ // we want to process some native mouse events in the cocoa event model
+ if ((anEvent->mMessage == eMouseEnterIntoWidget ||
+ anEvent->mMessage == eWheel) &&
+ mInstanceOwner->GetEventModel() == NPEventModelCocoa) {
+ *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
+ // Due to plugin code reentering Gecko, this frame may be dead at this
+ // point.
+ return rv;
+ }
+
+ // These two calls to nsIPresShell::SetCapturingContext() (on mouse-down
+ // and mouse-up) are needed to make the routing of mouse events while
+ // dragging conform to standard OS X practice, and to the Cocoa NPAPI spec.
+ // See bug 525078 and bug 909678.
+ if (anEvent->mMessage == eMouseDown) {
+ nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
+ }
+#endif
+
+ rv = nsFrame::HandleEvent(aPresContext, anEvent, anEventStatus);
+
+ // We need to be careful from this point because the call to
+ // nsFrame::HandleEvent() might have killed us.
+
+#ifdef XP_MACOSX
+ if (anEvent->mMessage == eMouseUp) {
+ nsIPresShell::SetCapturingContent(nullptr, 0);
+ }
+#endif
+
+ return rv;
+}
+
+void
+nsPluginFrame::HandleWheelEventAsDefaultAction(WidgetWheelEvent* aWheelEvent)
+{
+ MOZ_ASSERT(WantsToHandleWheelEventAsDefaultAction());
+ MOZ_ASSERT(!aWheelEvent->DefaultPrevented());
+
+ if (NS_WARN_IF(!mInstanceOwner) ||
+ NS_WARN_IF(aWheelEvent->mMessage != eWheel)) {
+ return;
+ }
+
+ // If the wheel event has native message, it should may be handled by
+ // HandleEvent() in the future. In such case, we should do nothing here.
+ if (NS_WARN_IF(!!aWheelEvent->mPluginEvent)) {
+ return;
+ }
+
+ mInstanceOwner->ProcessEvent(*aWheelEvent);
+ // We need to assume that the event is always consumed/handled by the
+ // plugin. There is no way to know if it's actually consumed/handled.
+ aWheelEvent->mViewPortIsOverscrolled = false;
+ aWheelEvent->mOverflowDeltaX = 0;
+ aWheelEvent->mOverflowDeltaY = 0;
+ // Consume the event explicitly.
+ aWheelEvent->PreventDefault();
+}
+
+bool
+nsPluginFrame::WantsToHandleWheelEventAsDefaultAction() const
+{
+#ifdef XP_WIN
+ if (!mInstanceOwner) {
+ return false;
+ }
+ NPWindow* window = nullptr;
+ mInstanceOwner->GetWindow(window);
+ // On Windows, only when the plugin is windowless, we need to send wheel
+ // events as default action.
+ return window->type == NPWindowTypeDrawable;
+#else
+ return false;
+#endif
+}
+
+nsresult
+nsPluginFrame::GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance)
+{
+ *aPluginInstance = nullptr;
+
+ if (!mInstanceOwner) {
+ return NS_OK;
+ }
+
+ return mInstanceOwner->GetInstance(aPluginInstance);
+}
+
+nsresult
+nsPluginFrame::GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor)
+{
+ if (!mInstanceOwner) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsNPAPIPluginInstance> inst;
+ mInstanceOwner->GetInstance(getter_AddRefs(inst));
+ if (!inst) {
+ return NS_ERROR_FAILURE;
+ }
+
+ bool useDOMCursor = static_cast<nsNPAPIPluginInstance*>(inst.get())->UsesDOMForCursor();
+ if (!useDOMCursor) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return nsFrame::GetCursor(aPoint, aCursor);
+}
+
+void
+nsPluginFrame::SetIsDocumentActive(bool aIsActive)
+{
+ if (mInstanceOwner) {
+ mInstanceOwner->UpdateDocumentActiveState(aIsActive);
+ }
+}
+
+// static
+nsIObjectFrame *
+nsPluginFrame::GetNextObjectFrame(nsPresContext* aPresContext, nsIFrame* aRoot)
+{
+ for (nsIFrame* child : aRoot->PrincipalChildList()) {
+ nsIObjectFrame* outFrame = do_QueryFrame(child);
+ if (outFrame) {
+ RefPtr<nsNPAPIPluginInstance> pi;
+ outFrame->GetPluginInstance(getter_AddRefs(pi)); // make sure we have a REAL plugin
+ if (pi)
+ return outFrame;
+ }
+
+ outFrame = GetNextObjectFrame(aPresContext, child);
+ if (outFrame)
+ return outFrame;
+ }
+
+ return nullptr;
+}
+
+/*static*/ void
+nsPluginFrame::BeginSwapDocShells(nsISupports* aSupports, void*)
+{
+ NS_PRECONDITION(aSupports, "");
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
+ if (!content) {
+ return;
+ }
+
+ // This function is called from a document content enumerator so we need
+ // to filter out the nsPluginFrames and ignore the rest.
+ nsIObjectFrame* obj = do_QueryFrame(content->GetPrimaryFrame());
+ if (!obj)
+ return;
+
+ nsPluginFrame* objectFrame = static_cast<nsPluginFrame*>(obj);
+ NS_ASSERTION(!objectFrame->mWidget || objectFrame->mWidget->GetParent(),
+ "Plugin windows must not be toplevel");
+ objectFrame->UnregisterPluginForGeometryUpdates();
+}
+
+/*static*/ void
+nsPluginFrame::EndSwapDocShells(nsISupports* aSupports, void*)
+{
+ NS_PRECONDITION(aSupports, "");
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
+ if (!content) {
+ return;
+ }
+
+ // This function is called from a document content enumerator so we need
+ // to filter out the nsPluginFrames and ignore the rest.
+ nsIObjectFrame* obj = do_QueryFrame(content->GetPrimaryFrame());
+ if (!obj)
+ return;
+
+ nsPluginFrame* objectFrame = static_cast<nsPluginFrame*>(obj);
+ nsRootPresContext* rootPC = objectFrame->PresContext()->GetRootPresContext();
+ NS_ASSERTION(rootPC, "unable to register the plugin frame");
+ nsIWidget* widget = objectFrame->mWidget;
+ if (widget) {
+ // Reparent the widget.
+ nsIWidget* parent =
+ rootPC->PresShell()->GetRootFrame()->GetNearestWidget();
+ widget->SetParent(parent);
+ nsWeakFrame weakFrame(objectFrame);
+ objectFrame->CallSetWindow();
+ if (!weakFrame.IsAlive()) {
+ return;
+ }
+ }
+
+ if (objectFrame->mInstanceOwner) {
+ objectFrame->RegisterPluginForGeometryUpdates();
+ }
+}
+
+nsIFrame*
+NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsPluginFrame(aContext);
+}
+
+bool
+nsPluginFrame::IsPaintedByGecko() const
+{
+#ifdef XP_MACOSX
+ return true;
+#else
+ return !mWidget;
+#endif
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsPluginFrame)
diff --git a/layout/generic/nsPluginFrame.h b/layout/generic/nsPluginFrame.h
new file mode 100644
index 000000000..5d9f9f475
--- /dev/null
+++ b/layout/generic/nsPluginFrame.h
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering objects for replaced elements implemented by a plugin */
+
+#ifndef nsPluginFrame_h___
+#define nsPluginFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIObjectFrame.h"
+#include "nsFrame.h"
+#include "nsRegion.h"
+#include "nsDisplayList.h"
+#include "nsIReflowCallback.h"
+#include "Units.h"
+
+#ifdef XP_WIN
+#include <windows.h> // For HWND :(
+// Undo the windows.h damage
+#undef GetMessage
+#undef CreateEvent
+#undef GetClassName
+#undef GetBinaryType
+#undef RemoveDirectory
+#undef LoadIcon
+#undef LoadImage
+#undef GetObject
+#endif
+
+class nsPresContext;
+class nsRootPresContext;
+class nsDisplayPlugin;
+class PluginBackgroundSink;
+class nsPluginInstanceOwner;
+
+namespace mozilla {
+namespace layers {
+class ImageContainer;
+class Layer;
+class LayerManager;
+} // namespace layers
+} // namespace mozilla
+
+class PluginFrameDidCompositeObserver;
+
+class nsPluginFrame : public nsFrame
+ , public nsIObjectFrame
+ , public nsIReflowCallback
+{
+public:
+ typedef mozilla::LayerState LayerState;
+ typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint;
+ typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
+ typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion;
+ typedef mozilla::layers::Layer Layer;
+ typedef mozilla::layers::LayerManager LayerManager;
+ typedef mozilla::layers::ImageContainer ImageContainer;
+ typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
+
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsIFrame* NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+ NS_DECL_QUERYFRAME
+ NS_DECL_QUERYFRAME_TARGET(nsPluginFrame)
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ virtual void DidReflow(nsPresContext* aPresContext,
+ const ReflowInput* aReflowInput,
+ nsDidReflowStatus aStatus) override;
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual nsresult HandleEvent(nsPresContext* aPresContext,
+ mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) override;
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
+ }
+
+ virtual bool NeedsView() override { return true; }
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
+
+ NS_IMETHOD GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) override;
+
+ virtual void SetIsDocumentActive(bool aIsActive) override;
+
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor) override;
+
+ // APIs used by nsRootPresContext to set up the widget position/size/clip
+ // region.
+ /**
+ * Set the next widget configuration for the plugin to the desired
+ * position of the plugin's widget, on the assumption that it is not visible
+ * (clipped out or covered by opaque content).
+ * This will only be called for plugins which have been registered
+ * with the root pres context for geometry updates.
+ * If there is no widget associated with the plugin, this will have no effect.
+ */
+ void SetEmptyWidgetConfiguration()
+ {
+ mNextConfigurationBounds = LayoutDeviceIntRect(0,0,0,0);
+ mNextConfigurationClipRegion.Clear();
+ }
+ /**
+ * Append the desired widget configuration to aConfigurations.
+ */
+ void GetWidgetConfiguration(nsTArray<nsIWidget::Configuration>* aConfigurations);
+
+ LayoutDeviceIntRect GetWidgetlessClipRect() {
+ return RegionFromArray(mNextConfigurationClipRegion).GetBounds();
+ }
+
+ /**
+ * Called after all widget position/size/clip regions have been changed
+ * (even if there isn't a widget for this plugin).
+ */
+ void DidSetWidgetGeometry();
+
+ // accessibility support
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#ifdef XP_WIN
+ NS_IMETHOD GetPluginPort(HWND *aPort);
+#endif
+#endif
+
+ //local methods
+ nsresult PrepForDrawing(nsIWidget *aWidget);
+
+ // for a given aRoot, this walks the frame tree looking for the next outFrame
+ static nsIObjectFrame* GetNextObjectFrame(nsPresContext* aPresContext,
+ nsIFrame* aRoot);
+
+ // nsIReflowCallback
+ virtual bool ReflowFinished() override;
+ virtual void ReflowCallbackCanceled() override;
+
+ /**
+ * Builds either an ImageLayer or a ReadbackLayer, depending on the type
+ * of aItem (TYPE_PLUGIN or TYPE_PLUGIN_READBACK respectively).
+ */
+ already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ const ContainerLayerParameters& aContainerParameters);
+
+ LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager);
+
+ /**
+ * Get the rectangle (relative to this frame) which it will paint. Normally
+ * the frame's content-box but may be smaller if the plugin is rendering
+ * asynchronously and has a different-sized image temporarily.
+ */
+ nsRect GetPaintedRect(nsDisplayPlugin* aItem);
+
+ /**
+ * If aSupports has a nsPluginFrame, then prepare it for a DocShell swap.
+ * @see nsSubDocumentFrame::BeginSwapDocShells.
+ * There will be a call to EndSwapDocShells after we were moved to the
+ * new view tree.
+ */
+ static void BeginSwapDocShells(nsISupports* aSupports, void*);
+ /**
+ * If aSupports has a nsPluginFrame, then set it up after a DocShell swap.
+ * @see nsSubDocumentFrame::EndSwapDocShells.
+ */
+ static void EndSwapDocShells(nsISupports* aSupports, void*);
+
+ nsIWidget* GetWidget() override {
+ if (!mInnerView) {
+ return nullptr;
+ }
+ return mWidget;
+ }
+
+ /**
+ * Adjust the plugin's idea of its size, using aSize as its new size.
+ * (aSize must be in twips)
+ */
+ void FixupWindow(const nsSize& aSize);
+
+ /*
+ * Sets up the plugin window and calls SetWindow on the plugin.
+ */
+ nsresult CallSetWindow(bool aCheckIsHidden = true);
+
+ void SetInstanceOwner(nsPluginInstanceOwner* aOwner);
+
+ /**
+ * HandleWheelEventAsDefaultAction() handles eWheel event as default action.
+ * This should be called only when WantsToHandleWheelEventAsDefaultAction()
+ * returns true.
+ */
+ void HandleWheelEventAsDefaultAction(mozilla::WidgetWheelEvent* aEvent);
+
+ /**
+ * WantsToHandleWheelEventAsDefaultAction() returns true if the plugin
+ * may want to handle wheel events as default action.
+ */
+ bool WantsToHandleWheelEventAsDefaultAction() const;
+
+protected:
+ explicit nsPluginFrame(nsStyleContext* aContext);
+ virtual ~nsPluginFrame();
+
+ // NOTE: This frame class does not inherit from |nsLeafFrame|, so
+ // this is not a virtual method implementation.
+ void GetDesiredSize(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ ReflowOutput& aDesiredSize);
+
+ bool IsFocusable(int32_t *aTabIndex = nullptr,
+ bool aWithMouse = false) override;
+
+ // check attributes and optionally CSS to see if we should display anything
+ bool IsHidden(bool aCheckVisibilityStyle = true) const;
+
+ bool IsOpaque() const;
+ bool IsTransparentMode() const;
+ bool IsPaintedByGecko() const;
+
+ nsIntPoint GetWindowOriginInPixels(bool aWindowless);
+
+ /*
+ * If this frame is in a remote tab, return the tab offset to
+ * the origin of the chrome window. In non-e10s, this return 0,0.
+ * This api sends a sync ipc request so be careful about use.
+ */
+ LayoutDeviceIntPoint GetRemoteTabChromeOffset();
+
+ static void PaintPrintPlugin(nsIFrame* aFrame,
+ nsRenderingContext* aRenderingContext,
+ const nsRect& aDirtyRect, nsPoint aPt);
+ void PrintPlugin(nsRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect);
+ void PaintPlugin(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect, const nsRect& aPluginRect);
+
+ void NotifyPluginReflowObservers();
+
+ friend class nsPluginInstanceOwner;
+ friend class nsDisplayPlugin;
+ friend class PluginBackgroundSink;
+
+private:
+ // Registers the plugin for a geometry update, and requests a geometry
+ // update. This caches the root pres context in
+ // mRootPresContextRegisteredWith, so that we can be sure we unregister
+ // from the right root prest context in UnregisterPluginForGeometryUpdates.
+ void RegisterPluginForGeometryUpdates();
+
+ // Unregisters the plugin for geometry updated with the root pres context
+ // stored in mRootPresContextRegisteredWith.
+ void UnregisterPluginForGeometryUpdates();
+
+ static const LayoutDeviceIntRegion
+ RegionFromArray(const nsTArray<LayoutDeviceIntRect>& aRects)
+ {
+ LayoutDeviceIntRegion region;
+ for (uint32_t i = 0; i < aRects.Length(); ++i) {
+ region.Or(region, aRects[i]);
+ }
+ return region;
+ }
+
+ class PluginEventNotifier : public mozilla::Runnable {
+ public:
+ explicit PluginEventNotifier(const nsString &aEventType) :
+ mEventType(aEventType) {}
+
+ NS_IMETHOD Run() override;
+ private:
+ nsString mEventType;
+ };
+
+ nsPluginInstanceOwner* mInstanceOwner; // WEAK
+ nsView* mInnerView;
+ nsCOMPtr<nsIWidget> mWidget;
+ nsIntRect mWindowlessRect;
+ /**
+ * This is owned by the ReadbackLayer for this nsPluginFrame. It is
+ * automatically cleared if the PluginBackgroundSink is destroyed.
+ */
+ PluginBackgroundSink* mBackgroundSink;
+
+ /**
+ * Bounds that we should set the plugin's widget to in the next composite,
+ * for plugins with widgets. For plugins without widgets, bounds in device
+ * pixels relative to the nearest frame that's a display list reference frame.
+ */
+ LayoutDeviceIntRect mNextConfigurationBounds;
+ /**
+ * Clip region that we should set the plugin's widget to
+ * in the next composite. Only meaningful for plugins with widgets.
+ */
+ nsTArray<LayoutDeviceIntRect> mNextConfigurationClipRegion;
+
+ bool mReflowCallbackPosted;
+
+ // We keep this reference to ensure we can always unregister the
+ // plugins we register on the root PresContext.
+ // This is only non-null while we have a plugin registered for geometry
+ // updates.
+ RefPtr<nsRootPresContext> mRootPresContextRegisteredWith;
+
+ mozilla::UniquePtr<PluginFrameDidCompositeObserver> mDidCompositeObserver;
+};
+
+class nsDisplayPlugin : public nsDisplayItem {
+public:
+ nsDisplayPlugin(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ {
+ MOZ_COUNT_CTOR(nsDisplayPlugin);
+ aBuilder->SetContainsPluginItem();
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayPlugin() {
+ MOZ_COUNT_DTOR(nsDisplayPlugin);
+ }
+#endif
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
+ virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override;
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
+ nsRegion* aVisibleRegion) override;
+
+ NS_DISPLAY_DECL_NAME("Plugin", TYPE_PLUGIN)
+
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) override
+ {
+ return static_cast<nsPluginFrame*>(mFrame)->BuildLayer(aBuilder,
+ aManager,
+ this,
+ aContainerParameters);
+ }
+
+ virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) override
+ {
+ return static_cast<nsPluginFrame*>(mFrame)->GetLayerState(aBuilder,
+ aManager);
+ }
+};
+
+#endif /* nsPluginFrame_h___ */
diff --git a/layout/generic/nsQueryFrame.h b/layout/generic/nsQueryFrame.h
new file mode 100644
index 000000000..7f7ea2fc1
--- /dev/null
+++ b/layout/generic/nsQueryFrame.h
@@ -0,0 +1,102 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsQueryFrame_h
+#define nsQueryFrame_h
+
+#include "nscore.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/TypeTraits.h"
+
+// NOTE: the long lines in this file are intentional to make compiler error
+// messages more readable.
+
+#define NS_DECL_QUERYFRAME_TARGET(classname) \
+ static const nsQueryFrame::FrameIID kFrameIID = nsQueryFrame::classname##_id; \
+ typedef classname Has_NS_DECL_QUERYFRAME_TARGET;
+
+#define NS_DECL_QUERYFRAME \
+ virtual void* QueryFrame(FrameIID id) override;
+
+#define NS_QUERYFRAME_HEAD(class) \
+ void* class::QueryFrame(FrameIID id) { switch (id) {
+
+#define NS_QUERYFRAME_ENTRY(class) \
+ case class::kFrameIID: { \
+ static_assert(mozilla::IsSame<class, class::Has_NS_DECL_QUERYFRAME_TARGET>::value, \
+ #class " must declare itself as a queryframe target"); \
+ return static_cast<class*>(this); \
+ }
+
+#define NS_QUERYFRAME_ENTRY_CONDITIONAL(class, condition) \
+ case class::kFrameIID: \
+ if (condition) { \
+ static_assert(mozilla::IsSame<class, class::Has_NS_DECL_QUERYFRAME_TARGET>::value, \
+ #class " must declare itself as a queryframe target"); \
+ return static_cast<class*>(this); \
+ } \
+ break;
+
+#define NS_QUERYFRAME_TAIL_INHERITING(class) \
+ default: break; \
+ } \
+ return class::QueryFrame(id); \
+}
+
+#define NS_QUERYFRAME_TAIL_INHERITANCE_ROOT \
+ default: break; \
+ } \
+ MOZ_ASSERT(id != GetFrameId(), \
+ "A frame failed to QueryFrame to its *own type*. " \
+ "It may be missing NS_DECL_QUERYFRAME, or a " \
+ "NS_QUERYFRAME_ENTRY() line with its own type name"); \
+ return nullptr; \
+}
+
+class nsQueryFrame
+{
+public:
+ enum FrameIID {
+#define FRAME_ID(classname) classname##_id,
+#include "nsFrameIdList.h"
+#undef FRAME_ID
+
+ // The PresArena implementation uses this bit to distinguish objects
+ // allocated by size from objects allocated by type ID (that is, frames
+ // using AllocateByFrameID, and other objects using AllocateByObjectID).
+ // It should not collide with any frame ID (above) or Object ID (in
+ // nsPresArena.h). It is not 0x80000000 to avoid the question of
+ // whether enumeration constants are signed.
+ NON_FRAME_MARKER = 0x20000000
+ };
+
+ virtual void* QueryFrame(FrameIID id) = 0;
+};
+
+class do_QueryFrame
+{
+public:
+ explicit do_QueryFrame(nsQueryFrame *s) : mRawPtr(s) { }
+
+ // The return and argument types here are arbitrarily selected so no
+ // corresponding member function exists.
+ typedef void (do_QueryFrame::* MatchNullptr)(double, float);
+ // Implicit constructor for nullptr, trick borrowed from already_AddRefed.
+ MOZ_IMPLICIT do_QueryFrame(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
+
+ template<class Dest>
+ operator Dest*() {
+ static_assert(mozilla::IsSame<Dest, typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>::value,
+ "Dest must declare itself as a queryframe target");
+ if (!mRawPtr)
+ return nullptr;
+
+ return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID));
+ }
+
+private:
+ nsQueryFrame *mRawPtr;
+};
+
+#endif // nsQueryFrame_h
diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp
new file mode 100644
index 000000000..401b7d576
--- /dev/null
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -0,0 +1,836 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-base-container" */
+
+#include "nsRubyBaseContainerFrame.h"
+#include "nsRubyTextContainerFrame.h"
+#include "nsRubyBaseFrame.h"
+#include "nsRubyTextFrame.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/WritingModes.h"
+#include "nsLayoutUtils.h"
+#include "nsLineLayout.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+#include "nsStyleStructInlines.h"
+#include "nsTextFrame.h"
+#include "RubyUtils.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsRubyBaseContainerFrame)
+ NS_QUERYFRAME_ENTRY(nsRubyBaseContainerFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsRubyBaseContainerFrame)
+
+nsContainerFrame*
+NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsRubyBaseContainerFrame(aContext);
+}
+
+
+//----------------------------------------------------------------------
+
+// nsRubyBaseContainerFrame Method Implementations
+// ===============================================
+
+nsIAtom*
+nsRubyBaseContainerFrame::GetType() const
+{
+ return nsGkAtoms::rubyBaseContainerFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsRubyBaseContainerFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("RubyBaseContainer"), aResult);
+}
+#endif
+
+static gfxBreakPriority
+LineBreakBefore(nsIFrame* aFrame,
+ DrawTarget* aDrawTarget,
+ nsIFrame* aLineContainerFrame,
+ const nsLineList::iterator* aLine)
+{
+ for (nsIFrame* child = aFrame; child;
+ child = child->PrincipalChildList().FirstChild()) {
+ if (!child->CanContinueTextRun()) {
+ // It is not an inline element. We can break before it.
+ return gfxBreakPriority::eNormalBreak;
+ }
+ if (child->GetType() != nsGkAtoms::textFrame) {
+ continue;
+ }
+
+ auto textFrame = static_cast<nsTextFrame*>(child);
+ gfxSkipCharsIterator iter =
+ textFrame->EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
+ aLineContainerFrame, aLine);
+ iter.SetOriginalOffset(textFrame->GetContentOffset());
+ uint32_t pos = iter.GetSkippedOffset();
+ gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated);
+ if (pos >= textRun->GetLength()) {
+ // The text frame contains no character at all.
+ return gfxBreakPriority::eNoBreak;
+ }
+ // Return whether we can break before the first character.
+ if (textRun->CanBreakLineBefore(pos)) {
+ return gfxBreakPriority::eNormalBreak;
+ }
+ // Check whether we can wrap word here.
+ const nsStyleText* textStyle = textFrame->StyleText();
+ if (textStyle->WordCanWrap(textFrame) && textRun->IsClusterStart(pos)) {
+ return gfxBreakPriority::eWordWrapBreak;
+ }
+ // We cannot break before.
+ return gfxBreakPriority::eNoBreak;
+ }
+ // Neither block, nor text frame is found as a leaf. We won't break
+ // before this base frame. It is the behavior of empty spans.
+ return gfxBreakPriority::eNoBreak;
+}
+
+static void
+GetIsLineBreakAllowed(nsIFrame* aFrame, bool aIsLineBreakable,
+ bool* aAllowInitialLineBreak, bool* aAllowLineBreak)
+{
+ nsIFrame* parent = aFrame->GetParent();
+ bool lineBreakSuppressed = parent->StyleContext()->ShouldSuppressLineBreak();
+ // Allow line break between ruby bases when white-space allows,
+ // we are not inside a nested ruby, and there is no span.
+ bool allowLineBreak = !lineBreakSuppressed &&
+ aFrame->StyleText()->WhiteSpaceCanWrap(aFrame);
+ bool allowInitialLineBreak = allowLineBreak;
+ if (!aFrame->GetPrevInFlow()) {
+ allowInitialLineBreak = !lineBreakSuppressed &&
+ parent->StyleText()->WhiteSpaceCanWrap(parent);
+ }
+ if (!aIsLineBreakable) {
+ allowInitialLineBreak = false;
+ }
+ *aAllowInitialLineBreak = allowInitialLineBreak;
+ *aAllowLineBreak = allowLineBreak;
+}
+
+/**
+ * @param aBaseISizeData is an in/out param. This method updates the
+ * `skipWhitespace` and `trailingWhitespace` fields of the struct with
+ * the base level frame. Note that we don't need to do the same thing
+ * for ruby text frames, because they are text run container themselves
+ * (see nsTextFrame.cpp:BuildTextRuns), and thus no whitespace collapse
+ * happens across the boundary of those frames.
+ */
+static nscoord
+CalculateColumnPrefISize(nsRenderingContext* aRenderingContext,
+ const RubyColumnEnumerator& aEnumerator,
+ nsIFrame::InlineIntrinsicISizeData* aBaseISizeData)
+{
+ nscoord max = 0;
+ uint32_t levelCount = aEnumerator.GetLevelCount();
+ for (uint32_t i = 0; i < levelCount; i++) {
+ nsIFrame* frame = aEnumerator.GetFrameAtLevel(i);
+ if (frame) {
+ nsIFrame::InlinePrefISizeData data;
+ if (i == 0) {
+ data.SetLineContainer(aBaseISizeData->LineContainer());
+ data.mSkipWhitespace = aBaseISizeData->mSkipWhitespace;
+ data.mTrailingWhitespace = aBaseISizeData->mTrailingWhitespace;
+ } else {
+ // The line container of ruby text frames is their parent,
+ // ruby text container frame.
+ data.SetLineContainer(frame->GetParent());
+ }
+ frame->AddInlinePrefISize(aRenderingContext, &data);
+ MOZ_ASSERT(data.mPrevLines == 0, "Shouldn't have prev lines");
+ max = std::max(max, data.mCurrentLine);
+ if (i == 0) {
+ aBaseISizeData->mSkipWhitespace = data.mSkipWhitespace;
+ aBaseISizeData->mTrailingWhitespace = data.mTrailingWhitespace;
+ }
+ }
+ }
+ return max;
+}
+
+// FIXME Currently we use pref isize of ruby content frames for
+// computing min isize of ruby frame, which may cause problem.
+// See bug 1134945.
+/* virtual */ void
+nsRubyBaseContainerFrame::AddInlineMinISize(
+ nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData)
+{
+ AutoRubyTextContainerArray textContainers(this);
+
+ for (uint32_t i = 0, iend = textContainers.Length(); i < iend; i++) {
+ if (textContainers[i]->IsSpanContainer()) {
+ // Since spans are not breakable internally, use our pref isize
+ // directly if there is any span.
+ nsIFrame::InlinePrefISizeData data;
+ data.SetLineContainer(aData->LineContainer());
+ data.mSkipWhitespace = aData->mSkipWhitespace;
+ data.mTrailingWhitespace = aData->mTrailingWhitespace;
+ AddInlinePrefISize(aRenderingContext, &data);
+ aData->mCurrentLine += data.mCurrentLine;
+ if (data.mCurrentLine > 0) {
+ aData->mAtStartOfLine = false;
+ }
+ aData->mSkipWhitespace = data.mSkipWhitespace;
+ aData->mTrailingWhitespace = data.mTrailingWhitespace;
+ return;
+ }
+ }
+
+ bool firstFrame = true;
+ bool allowInitialLineBreak, allowLineBreak;
+ GetIsLineBreakAllowed(this, !aData->mAtStartOfLine,
+ &allowInitialLineBreak, &allowLineBreak);
+ for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
+ RubyColumnEnumerator enumerator(
+ static_cast<nsRubyBaseContainerFrame*>(frame), textContainers);
+ for (; !enumerator.AtEnd(); enumerator.Next()) {
+ if (firstFrame ? allowInitialLineBreak : allowLineBreak) {
+ nsIFrame* baseFrame = enumerator.GetFrameAtLevel(0);
+ if (baseFrame) {
+ gfxBreakPriority breakPriority =
+ LineBreakBefore(baseFrame, aRenderingContext->GetDrawTarget(),
+ nullptr, nullptr);
+ if (breakPriority != gfxBreakPriority::eNoBreak) {
+ aData->OptionallyBreak();
+ }
+ }
+ }
+ firstFrame = false;
+ nscoord isize = CalculateColumnPrefISize(aRenderingContext,
+ enumerator, aData);
+ aData->mCurrentLine += isize;
+ if (isize > 0) {
+ aData->mAtStartOfLine = false;
+ }
+ }
+ }
+}
+
+/* virtual */ void
+nsRubyBaseContainerFrame::AddInlinePrefISize(
+ nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData)
+{
+ AutoRubyTextContainerArray textContainers(this);
+
+ nscoord sum = 0;
+ for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
+ RubyColumnEnumerator enumerator(
+ static_cast<nsRubyBaseContainerFrame*>(frame), textContainers);
+ for (; !enumerator.AtEnd(); enumerator.Next()) {
+ sum += CalculateColumnPrefISize(aRenderingContext, enumerator, aData);
+ }
+ }
+ for (uint32_t i = 0, iend = textContainers.Length(); i < iend; i++) {
+ if (textContainers[i]->IsSpanContainer()) {
+ nsIFrame* frame = textContainers[i]->PrincipalChildList().FirstChild();
+ nsIFrame::InlinePrefISizeData data;
+ frame->AddInlinePrefISize(aRenderingContext, &data);
+ MOZ_ASSERT(data.mPrevLines == 0, "Shouldn't have prev lines");
+ sum = std::max(sum, data.mCurrentLine);
+ }
+ }
+ aData->mCurrentLine += sum;
+}
+
+/* virtual */ bool
+nsRubyBaseContainerFrame::IsFrameOfType(uint32_t aFlags) const
+{
+ if (aFlags & eSupportsCSSTransforms) {
+ return false;
+ }
+ return nsContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eLineParticipant));
+}
+
+/* virtual */ bool
+nsRubyBaseContainerFrame::CanContinueTextRun() const
+{
+ return true;
+}
+
+/* virtual */ LogicalSize
+nsRubyBaseContainerFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ // Ruby base container frame is inline,
+ // hence don't compute size before reflow.
+ return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+}
+
+/* virtual */ nscoord
+nsRubyBaseContainerFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+ return mBaseline;
+}
+
+struct nsRubyBaseContainerFrame::RubyReflowInput
+{
+ bool mAllowInitialLineBreak;
+ bool mAllowLineBreak;
+ const AutoRubyTextContainerArray& mTextContainers;
+ const ReflowInput& mBaseReflowInput;
+ const nsTArray<UniquePtr<ReflowInput>>& mTextReflowInputs;
+};
+
+/* virtual */ void
+nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ aStatus = NS_FRAME_COMPLETE;
+
+ if (!aReflowInput.mLineLayout) {
+ NS_ASSERTION(
+ aReflowInput.mLineLayout,
+ "No line layout provided to RubyBaseContainerFrame reflow method.");
+ return;
+ }
+
+ mDescendantLeadings.Reset();
+
+ MoveOverflowToChildList();
+ // Ask text containers to drain overflows
+ AutoRubyTextContainerArray textContainers(this);
+ const uint32_t rtcCount = textContainers.Length();
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ textContainers[i]->MoveOverflowToChildList();
+ }
+
+ WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
+ LogicalSize availSize(lineWM, aReflowInput.AvailableISize(),
+ aReflowInput.AvailableBSize());
+
+ // We have a reflow state and a line layout for each RTC.
+ // They are conceptually the state of the RTCs, but we don't actually
+ // reflow those RTCs in this code. These two arrays are holders of
+ // the reflow states and line layouts.
+ // Since there are pointers refer to reflow states and line layouts,
+ // it is necessary to guarantee that they won't be moved. For this
+ // reason, they are wrapped in UniquePtr here.
+ AutoTArray<UniquePtr<ReflowInput>, RTC_ARRAY_SIZE> reflowInputs;
+ AutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts;
+ reflowInputs.SetCapacity(rtcCount);
+ lineLayouts.SetCapacity(rtcCount);
+
+ // Begin the line layout for each ruby text container in advance.
+ bool hasSpan = false;
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsRubyTextContainerFrame* textContainer = textContainers[i];
+ if (textContainer->IsSpanContainer()) {
+ hasSpan = true;
+ }
+
+ ReflowInput* reflowInput = new ReflowInput(
+ aPresContext, *aReflowInput.mParentReflowInput, textContainer,
+ availSize.ConvertTo(textContainer->GetWritingMode(), lineWM));
+ reflowInputs.AppendElement(reflowInput);
+ nsLineLayout* lineLayout = new nsLineLayout(aPresContext,
+ reflowInput->mFloatManager,
+ reflowInput, nullptr,
+ aReflowInput.mLineLayout);
+ lineLayout->SetSuppressLineWrap(true);
+ lineLayouts.AppendElement(lineLayout);
+
+ // Line number is useless for ruby text
+ // XXX nullptr here may cause problem, see comments for
+ // nsLineLayout::mBlockRI and nsLineLayout::AddFloat
+ lineLayout->Init(nullptr, reflowInput->CalcLineHeight(), -1);
+ reflowInput->mLineLayout = lineLayout;
+
+ // Border and padding are suppressed on ruby text containers.
+ // If the writing mode is vertical-rl, the horizontal position of
+ // rt frames will be updated when reflowing this text container,
+ // hence leave container size 0 here for now.
+ lineLayout->BeginLineReflow(0, 0, reflowInput->ComputedISize(),
+ NS_UNCONSTRAINEDSIZE,
+ false, false, lineWM, nsSize(0, 0));
+ lineLayout->AttachRootFrameToBaseLineLayout();
+ }
+
+ aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
+ 0, aReflowInput.AvailableISize(),
+ &mBaseline);
+
+ bool allowInitialLineBreak, allowLineBreak;
+ GetIsLineBreakAllowed(this, aReflowInput.mLineLayout->LineIsBreakable(),
+ &allowInitialLineBreak, &allowLineBreak);
+
+ nscoord isize = 0;
+ // Reflow columns excluding any span
+ RubyReflowInput reflowInput = {
+ allowInitialLineBreak, allowLineBreak && !hasSpan,
+ textContainers, aReflowInput, reflowInputs
+ };
+ isize = ReflowColumns(reflowInput, aStatus);
+ DebugOnly<nscoord> lineSpanSize = aReflowInput.mLineLayout->EndSpan(this);
+ aDesiredSize.ISize(lineWM) = isize;
+ // When there are no frames inside the ruby base container, EndSpan
+ // will return 0. However, in this case, the actual width of the
+ // container could be non-zero because of non-empty ruby annotations.
+ // XXX When bug 765861 gets fixed, this warning should be upgraded.
+ NS_WARNING_ASSERTION(
+ NS_INLINE_IS_BREAK(aStatus) || isize == lineSpanSize || mFrames.IsEmpty(),
+ "bad isize");
+
+ // If there exists any span, the columns must either be completely
+ // reflowed, or be not reflowed at all.
+ MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
+ NS_FRAME_IS_COMPLETE(aStatus) || !hasSpan);
+ if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
+ NS_FRAME_IS_COMPLETE(aStatus) && hasSpan) {
+ // Reflow spans
+ RubyReflowInput reflowInput = {
+ false, false, textContainers, aReflowInput, reflowInputs
+ };
+ nscoord spanISize = ReflowSpans(reflowInput);
+ isize = std::max(isize, spanISize);
+ }
+
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ // It happens before the ruby text container is reflowed, and that
+ // when it is reflowed, it will just use this size.
+ nsRubyTextContainerFrame* textContainer = textContainers[i];
+ nsLineLayout* lineLayout = lineLayouts[i].get();
+
+ RubyUtils::ClearReservedISize(textContainer);
+ nscoord rtcISize = lineLayout->GetCurrentICoord();
+ // Only span containers and containers with collapsed annotations
+ // need reserving isize. For normal ruby text containers, their
+ // children will be expanded properly. We only need to expand their
+ // own size.
+ if (!textContainer->IsSpanContainer()) {
+ rtcISize = isize;
+ } else if (isize > rtcISize) {
+ RubyUtils::SetReservedISize(textContainer, isize - rtcISize);
+ }
+
+ lineLayout->VerticalAlignLine();
+ textContainer->SetISize(rtcISize);
+ lineLayout->EndLineReflow();
+ }
+
+ // Border and padding are suppressed on ruby base container,
+ // create a fake borderPadding for setting BSize.
+ WritingMode frameWM = aReflowInput.GetWritingMode();
+ LogicalMargin borderPadding(frameWM);
+ nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
+ borderPadding, lineWM, frameWM);
+}
+
+/**
+ * This struct stores the continuations after this frame and
+ * corresponding text containers. It is used to speed up looking
+ * ahead for nonempty continuations.
+ */
+struct MOZ_STACK_CLASS nsRubyBaseContainerFrame::PullFrameState
+{
+ ContinuationTraversingState mBase;
+ AutoTArray<ContinuationTraversingState, RTC_ARRAY_SIZE> mTexts;
+ const AutoRubyTextContainerArray& mTextContainers;
+
+ PullFrameState(nsRubyBaseContainerFrame* aBaseContainer,
+ const AutoRubyTextContainerArray& aTextContainers);
+};
+
+nscoord
+nsRubyBaseContainerFrame::ReflowColumns(const RubyReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ nsLineLayout* lineLayout = aReflowInput.mBaseReflowInput.mLineLayout;
+ const uint32_t rtcCount = aReflowInput.mTextContainers.Length();
+ nscoord icoord = lineLayout->GetCurrentICoord();
+ MOZ_ASSERT(icoord == 0, "border/padding of rbc should have been suppressed");
+ nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
+ aStatus = NS_FRAME_COMPLETE;
+
+ uint32_t columnIndex = 0;
+ RubyColumn column;
+ column.mTextFrames.SetCapacity(rtcCount);
+ RubyColumnEnumerator e(this, aReflowInput.mTextContainers);
+ for (; !e.AtEnd(); e.Next()) {
+ e.GetColumn(column);
+ icoord += ReflowOneColumn(aReflowInput, columnIndex, column, reflowStatus);
+ if (!NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) {
+ columnIndex++;
+ }
+ if (NS_INLINE_IS_BREAK(reflowStatus)) {
+ break;
+ }
+ // We are not handling overflow here.
+ MOZ_ASSERT(reflowStatus == NS_FRAME_COMPLETE);
+ }
+
+ bool isComplete = false;
+ PullFrameState pullFrameState(this, aReflowInput.mTextContainers);
+ while (!NS_INLINE_IS_BREAK(reflowStatus)) {
+ // We are not handling overflow here.
+ MOZ_ASSERT(reflowStatus == NS_FRAME_COMPLETE);
+
+ // Try pull some frames from next continuations. This call replaces
+ // frames in |column| with the frame pulled in each level.
+ PullOneColumn(lineLayout, pullFrameState, column, isComplete);
+ if (isComplete) {
+ // No more frames can be pulled.
+ break;
+ }
+ icoord += ReflowOneColumn(aReflowInput, columnIndex, column, reflowStatus);
+ if (!NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) {
+ columnIndex++;
+ }
+ }
+
+ if (!e.AtEnd() && NS_INLINE_IS_BREAK_AFTER(reflowStatus)) {
+ // The current column has been successfully placed.
+ // Skip to the next column and mark break before.
+ e.Next();
+ e.GetColumn(column);
+ reflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ }
+ if (!e.AtEnd() || (GetNextInFlow() && !isComplete)) {
+ NS_FRAME_SET_INCOMPLETE(aStatus);
+ }
+
+ if (NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) {
+ if (!columnIndex || !aReflowInput.mAllowLineBreak) {
+ // If no column has been placed yet, or we have any span,
+ // the whole container should be in the next line.
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return 0;
+ }
+ aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(aStatus) || aReflowInput.mAllowLineBreak);
+
+ // If we are on an intra-level whitespace column, null values in
+ // column.mBaseFrame and column.mTextFrames don't represent the
+ // end of the frame-sibling-chain at that level -- instead, they
+ // represent an anonymous empty intra-level whitespace box. It is
+ // likely that there are frames in the next column (which can't be
+ // intra-level whitespace). Those frames should be pushed as well.
+ Maybe<RubyColumn> nextColumn;
+ if (column.mIsIntraLevelWhitespace && !e.AtEnd()) {
+ e.Next();
+ nextColumn.emplace();
+ e.GetColumn(nextColumn.ref());
+ }
+ nsIFrame* baseFrame = column.mBaseFrame;
+ if (!baseFrame & nextColumn.isSome()) {
+ baseFrame = nextColumn->mBaseFrame;
+ }
+ if (baseFrame) {
+ PushChildren(baseFrame, baseFrame->GetPrevSibling());
+ }
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsRubyTextFrame* textFrame = column.mTextFrames[i];
+ if (!textFrame && nextColumn.isSome()) {
+ textFrame = nextColumn->mTextFrames[i];
+ }
+ if (textFrame) {
+ aReflowInput.mTextContainers[i]->PushChildren(
+ textFrame, textFrame->GetPrevSibling());
+ }
+ }
+ } else if (NS_INLINE_IS_BREAK_AFTER(reflowStatus)) {
+ // |reflowStatus| being break after here may only happen when
+ // there is a break after the column just pulled, or the whole
+ // segment has been completely reflowed. In those cases, we do
+ // not need to push anything.
+ MOZ_ASSERT(e.AtEnd());
+ aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
+ }
+
+ return icoord;
+}
+
+nscoord
+nsRubyBaseContainerFrame::ReflowOneColumn(const RubyReflowInput& aReflowInput,
+ uint32_t aColumnIndex,
+ const RubyColumn& aColumn,
+ nsReflowStatus& aStatus)
+{
+ const ReflowInput& baseReflowInput = aReflowInput.mBaseReflowInput;
+ const auto& textReflowInputs = aReflowInput.mTextReflowInputs;
+ nscoord istart = baseReflowInput.mLineLayout->GetCurrentICoord();
+
+ if (aColumn.mBaseFrame) {
+ bool allowBreakBefore = aColumnIndex ?
+ aReflowInput.mAllowLineBreak : aReflowInput.mAllowInitialLineBreak;
+ if (allowBreakBefore) {
+ gfxBreakPriority breakPriority = LineBreakBefore(
+ aColumn.mBaseFrame, baseReflowInput.mRenderingContext->GetDrawTarget(),
+ baseReflowInput.mLineLayout->LineContainerFrame(),
+ baseReflowInput.mLineLayout->GetLine());
+ if (breakPriority != gfxBreakPriority::eNoBreak) {
+ gfxBreakPriority lastBreakPriority =
+ baseReflowInput.mLineLayout->LastOptionalBreakPriority();
+ if (breakPriority >= lastBreakPriority) {
+ // Either we have been overflow, or we are forced
+ // to break here, do break before.
+ if (istart > baseReflowInput.AvailableISize() ||
+ baseReflowInput.mLineLayout->NotifyOptionalBreakPosition(
+ aColumn.mBaseFrame, 0, true, breakPriority)) {
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ const uint32_t rtcCount = aReflowInput.mTextContainers.Length();
+ MOZ_ASSERT(aColumn.mTextFrames.Length() == rtcCount);
+ MOZ_ASSERT(textReflowInputs.Length() == rtcCount);
+ nscoord columnISize = 0;
+
+ nsAutoString baseText;
+ if (aColumn.mBaseFrame) {
+ nsLayoutUtils::GetFrameTextContent(aColumn.mBaseFrame, baseText);
+ }
+
+ // Reflow text frames
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsRubyTextFrame* textFrame = aColumn.mTextFrames[i];
+ if (textFrame) {
+ nsAutoString annotationText;
+ nsLayoutUtils::GetFrameTextContent(textFrame, annotationText);
+
+ // Per CSS Ruby spec, the content comparison for auto-hiding
+ // takes place prior to white spaces collapsing (white-space)
+ // and text transformation (text-transform), and ignores elements
+ // (considers only the textContent of the boxes). Which means
+ // using the content tree text comparison is correct.
+ if (annotationText.Equals(baseText)) {
+ textFrame->AddStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE);
+ } else {
+ textFrame->RemoveStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE);
+ }
+ RubyUtils::ClearReservedISize(textFrame);
+
+ bool pushedFrame;
+ nsReflowStatus reflowStatus;
+ nsLineLayout* lineLayout = textReflowInputs[i]->mLineLayout;
+ nscoord textIStart = lineLayout->GetCurrentICoord();
+ lineLayout->ReflowFrame(textFrame, reflowStatus, nullptr, pushedFrame);
+ if (MOZ_UNLIKELY(NS_INLINE_IS_BREAK(reflowStatus) || pushedFrame)) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Any line break inside ruby box should have been suppressed");
+ // For safety, always drain the overflow list, so that
+ // no frames are left there after reflow.
+ textFrame->DrainSelfOverflowList();
+ }
+ nscoord textISize = lineLayout->GetCurrentICoord() - textIStart;
+ columnISize = std::max(columnISize, textISize);
+ }
+ }
+
+ // Reflow the base frame
+ if (aColumn.mBaseFrame) {
+ RubyUtils::ClearReservedISize(aColumn.mBaseFrame);
+
+ bool pushedFrame;
+ nsReflowStatus reflowStatus;
+ nsLineLayout* lineLayout = baseReflowInput.mLineLayout;
+ nscoord baseIStart = lineLayout->GetCurrentICoord();
+ lineLayout->ReflowFrame(aColumn.mBaseFrame, reflowStatus,
+ nullptr, pushedFrame);
+ if (MOZ_UNLIKELY(NS_INLINE_IS_BREAK(reflowStatus) || pushedFrame)) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Any line break inside ruby box should have been suppressed");
+ // For safety, always drain the overflow list, so that
+ // no frames are left there after reflow.
+ aColumn.mBaseFrame->DrainSelfOverflowList();
+ }
+ nscoord baseISize = lineLayout->GetCurrentICoord() - baseIStart;
+ columnISize = std::max(columnISize, baseISize);
+ }
+
+ // Align all the line layout to the new coordinate.
+ nscoord icoord = istart + columnISize;
+ nscoord deltaISize = icoord - baseReflowInput.mLineLayout->GetCurrentICoord();
+ if (deltaISize > 0) {
+ baseReflowInput.mLineLayout->AdvanceICoord(deltaISize);
+ if (aColumn.mBaseFrame) {
+ RubyUtils::SetReservedISize(aColumn.mBaseFrame, deltaISize);
+ }
+ }
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ if (aReflowInput.mTextContainers[i]->IsSpanContainer()) {
+ continue;
+ }
+ nsLineLayout* lineLayout = textReflowInputs[i]->mLineLayout;
+ nsRubyTextFrame* textFrame = aColumn.mTextFrames[i];
+ nscoord deltaISize = icoord - lineLayout->GetCurrentICoord();
+ if (deltaISize > 0) {
+ lineLayout->AdvanceICoord(deltaISize);
+ if (textFrame && !textFrame->IsAutoHidden()) {
+ RubyUtils::SetReservedISize(textFrame, deltaISize);
+ }
+ }
+ if (aColumn.mBaseFrame && textFrame) {
+ lineLayout->AttachLastFrameToBaseLineLayout();
+ }
+ }
+
+ return columnISize;
+}
+
+nsRubyBaseContainerFrame::PullFrameState::PullFrameState(
+ nsRubyBaseContainerFrame* aBaseContainer,
+ const AutoRubyTextContainerArray& aTextContainers)
+ : mBase(aBaseContainer)
+ , mTextContainers(aTextContainers)
+{
+ const uint32_t rtcCount = aTextContainers.Length();
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ mTexts.AppendElement(aTextContainers[i]);
+ }
+}
+
+void
+nsRubyBaseContainerFrame::PullOneColumn(nsLineLayout* aLineLayout,
+ PullFrameState& aPullFrameState,
+ RubyColumn& aColumn,
+ bool& aIsComplete)
+{
+ const AutoRubyTextContainerArray& textContainers =
+ aPullFrameState.mTextContainers;
+ const uint32_t rtcCount = textContainers.Length();
+
+ nsIFrame* nextBase = GetNextInFlowChild(aPullFrameState.mBase);
+ MOZ_ASSERT(!nextBase || nextBase->GetType() == nsGkAtoms::rubyBaseFrame);
+ aColumn.mBaseFrame = static_cast<nsRubyBaseFrame*>(nextBase);
+ bool foundFrame = !!aColumn.mBaseFrame;
+ bool pullingIntraLevelWhitespace =
+ aColumn.mBaseFrame && aColumn.mBaseFrame->IsIntraLevelWhitespace();
+
+ aColumn.mTextFrames.ClearAndRetainStorage();
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsIFrame* nextText =
+ textContainers[i]->GetNextInFlowChild(aPullFrameState.mTexts[i]);
+ MOZ_ASSERT(!nextText || nextText->GetType() == nsGkAtoms::rubyTextFrame);
+ nsRubyTextFrame* textFrame = static_cast<nsRubyTextFrame*>(nextText);
+ aColumn.mTextFrames.AppendElement(textFrame);
+ foundFrame = foundFrame || nextText;
+ if (nextText && !pullingIntraLevelWhitespace) {
+ pullingIntraLevelWhitespace = textFrame->IsIntraLevelWhitespace();
+ }
+ }
+ // If there exists any frame in continations, we haven't
+ // completed the reflow process.
+ aIsComplete = !foundFrame;
+ if (!foundFrame) {
+ return;
+ }
+
+ aColumn.mIsIntraLevelWhitespace = pullingIntraLevelWhitespace;
+ if (pullingIntraLevelWhitespace) {
+ // We are pulling an intra-level whitespace. Drop all frames which
+ // are not part of this intra-level whitespace column. (Those frames
+ // are really part of the *next* column, after the pulled one.)
+ if (aColumn.mBaseFrame && !aColumn.mBaseFrame->IsIntraLevelWhitespace()) {
+ aColumn.mBaseFrame = nullptr;
+ }
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsRubyTextFrame*& textFrame = aColumn.mTextFrames[i];
+ if (textFrame && !textFrame->IsIntraLevelWhitespace()) {
+ textFrame = nullptr;
+ }
+ }
+ } else {
+ // We are not pulling an intra-level whitespace, which means all
+ // elements we are going to pull can have non-whitespace content,
+ // which may contain float which we need to reparent.
+ nsBlockFrame* oldFloatCB = nullptr;
+ for (nsIFrame* frame : aColumn) {
+ oldFloatCB = nsLayoutUtils::GetFloatContainingBlock(frame);
+ break;
+ }
+#ifdef DEBUG
+ MOZ_ASSERT(oldFloatCB, "Must have found a float containing block");
+ for (nsIFrame* frame : aColumn) {
+ MOZ_ASSERT(nsLayoutUtils::GetFloatContainingBlock(frame) == oldFloatCB,
+ "All frames in the same ruby column should share "
+ "the same old float containing block");
+ }
+#endif
+ nsBlockFrame* newFloatCB =
+ nsLayoutUtils::GetAsBlock(aLineLayout->LineContainerFrame());
+ MOZ_ASSERT(newFloatCB, "Must have a float containing block");
+ if (oldFloatCB != newFloatCB) {
+ for (nsIFrame* frame : aColumn) {
+ newFloatCB->ReparentFloats(frame, oldFloatCB, false);
+ }
+ }
+ }
+
+ // Pull the frames of this column.
+ if (aColumn.mBaseFrame) {
+ DebugOnly<nsIFrame*> pulled = PullNextInFlowChild(aPullFrameState.mBase);
+ MOZ_ASSERT(pulled == aColumn.mBaseFrame, "pulled a wrong frame?");
+ }
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ if (aColumn.mTextFrames[i]) {
+ DebugOnly<nsIFrame*> pulled =
+ textContainers[i]->PullNextInFlowChild(aPullFrameState.mTexts[i]);
+ MOZ_ASSERT(pulled == aColumn.mTextFrames[i], "pulled a wrong frame?");
+ }
+ }
+
+ if (!aIsComplete) {
+ // We pulled frames from the next line, hence mark it dirty.
+ aLineLayout->SetDirtyNextLine();
+ }
+}
+
+nscoord
+nsRubyBaseContainerFrame::ReflowSpans(const RubyReflowInput& aReflowInput)
+{
+ nscoord spanISize = 0;
+ for (uint32_t i = 0, iend = aReflowInput.mTextContainers.Length();
+ i < iend; i++) {
+ nsRubyTextContainerFrame* container = aReflowInput.mTextContainers[i];
+ if (!container->IsSpanContainer()) {
+ continue;
+ }
+
+ nsIFrame* rtFrame = container->PrincipalChildList().FirstChild();
+ nsReflowStatus reflowStatus;
+ bool pushedFrame;
+ nsLineLayout* lineLayout = aReflowInput.mTextReflowInputs[i]->mLineLayout;
+ MOZ_ASSERT(lineLayout->GetCurrentICoord() == 0,
+ "border/padding of rtc should have been suppressed");
+ lineLayout->ReflowFrame(rtFrame, reflowStatus, nullptr, pushedFrame);
+ MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame,
+ "Any line break inside ruby box should has been suppressed");
+ spanISize = std::max(spanISize, lineLayout->GetCurrentICoord());
+ }
+ return spanISize;
+}
diff --git a/layout/generic/nsRubyBaseContainerFrame.h b/layout/generic/nsRubyBaseContainerFrame.h
new file mode 100644
index 000000000..7e8c449d3
--- /dev/null
+++ b/layout/generic/nsRubyBaseContainerFrame.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-base-container" */
+
+#ifndef nsRubyBaseContainerFrame_h___
+#define nsRubyBaseContainerFrame_h___
+
+#include "nsContainerFrame.h"
+#include "RubyUtils.h"
+
+/**
+ * Factory function.
+ * @return a newly allocated nsRubyBaseContainerFrame (infallible)
+ */
+nsContainerFrame* NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+class nsRubyBaseContainerFrame final : public nsContainerFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsRubyBaseContainerFrame)
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame overrides
+ virtual nsIAtom* GetType() const override;
+ virtual bool IsFrameOfType(uint32_t aFlags) const override;
+ virtual bool CanContinueTextRun() const override;
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual nscoord
+ GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ void UpdateDescendantLeadings(const mozilla::RubyBlockLeadings& aLeadings) {
+ mDescendantLeadings.Update(aLeadings);
+ }
+ mozilla::RubyBlockLeadings GetDescendantLeadings() const {
+ return mDescendantLeadings;
+ }
+
+protected:
+ friend nsContainerFrame*
+ NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ explicit nsRubyBaseContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+
+ struct RubyReflowInput;
+ nscoord ReflowColumns(const RubyReflowInput& aReflowInput,
+ nsReflowStatus& aStatus);
+ nscoord ReflowOneColumn(const RubyReflowInput& aReflowInput,
+ uint32_t aColumnIndex,
+ const mozilla::RubyColumn& aColumn,
+ nsReflowStatus& aStatus);
+ nscoord ReflowSpans(const RubyReflowInput& aReflowInput);
+
+ struct PullFrameState;
+
+ // Pull ruby base and corresponding ruby text frames from
+ // continuations after them.
+ void PullOneColumn(nsLineLayout* aLineLayout,
+ PullFrameState& aPullFrameState,
+ mozilla::RubyColumn& aColumn,
+ bool& aIsComplete);
+
+ nscoord mBaseline;
+
+ // Leading produced by descendant ruby annotations.
+ mozilla::RubyBlockLeadings mDescendantLeadings;
+};
+
+#endif /* nsRubyBaseContainerFrame_h___ */
diff --git a/layout/generic/nsRubyBaseFrame.cpp b/layout/generic/nsRubyBaseFrame.cpp
new file mode 100644
index 000000000..668bf77fd
--- /dev/null
+++ b/layout/generic/nsRubyBaseFrame.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-base" */
+
+#include "nsRubyBaseFrame.h"
+
+#include "mozilla/WritingModes.h"
+#include "nsLineLayout.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsRubyBaseFrame)
+ NS_QUERYFRAME_ENTRY(nsRubyBaseFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsRubyContentFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsRubyBaseFrame)
+
+nsContainerFrame*
+NS_NewRubyBaseFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsRubyBaseFrame(aContext);
+}
+
+
+//----------------------------------------------------------------------
+
+// nsRubyBaseFrame Method Implementations
+// ======================================
+
+nsIAtom*
+nsRubyBaseFrame::GetType() const
+{
+ return nsGkAtoms::rubyBaseFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsRubyBaseFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("RubyBase"), aResult);
+}
+#endif
diff --git a/layout/generic/nsRubyBaseFrame.h b/layout/generic/nsRubyBaseFrame.h
new file mode 100644
index 000000000..b6dde1029
--- /dev/null
+++ b/layout/generic/nsRubyBaseFrame.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-base" */
+
+#ifndef nsRubyBaseFrame_h___
+#define nsRubyBaseFrame_h___
+
+#include "nsRubyContentFrame.h"
+
+/**
+ * Factory function.
+ * @return a newly allocated nsRubyBaseFrame (infallible)
+ */
+nsContainerFrame* NS_NewRubyBaseFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+class nsRubyBaseFrame final : public nsRubyContentFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsRubyBaseFrame)
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame overrides
+ virtual nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+protected:
+ friend nsContainerFrame* NS_NewRubyBaseFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ explicit nsRubyBaseFrame(nsStyleContext* aContext)
+ : nsRubyContentFrame(aContext) {}
+};
+
+#endif /* nsRubyBaseFrame_h___ */
diff --git a/layout/generic/nsRubyContentFrame.cpp b/layout/generic/nsRubyContentFrame.cpp
new file mode 100644
index 000000000..c7f0774bb
--- /dev/null
+++ b/layout/generic/nsRubyContentFrame.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* base class for ruby rendering objects that directly contain content */
+
+#include "nsRubyContentFrame.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+#include "nsCSSAnonBoxes.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+// nsRubyContentFrame Method Implementations
+// ======================================
+
+/* virtual */ bool
+nsRubyContentFrame::IsFrameOfType(uint32_t aFlags) const
+{
+ if (aFlags & eBidiInlineContainer) {
+ return false;
+ }
+ return nsInlineFrame::IsFrameOfType(aFlags);
+}
+
+bool
+nsRubyContentFrame::IsIntraLevelWhitespace() const
+{
+ nsIAtom* pseudoType = StyleContext()->GetPseudo();
+ if (pseudoType != nsCSSAnonBoxes::rubyBase &&
+ pseudoType != nsCSSAnonBoxes::rubyText) {
+ return false;
+ }
+
+ nsIFrame* child = mFrames.OnlyChild();
+ return child && child->GetContent()->TextIsOnlyWhitespace();
+}
diff --git a/layout/generic/nsRubyContentFrame.h b/layout/generic/nsRubyContentFrame.h
new file mode 100644
index 000000000..05f8a566d
--- /dev/null
+++ b/layout/generic/nsRubyContentFrame.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* base class for ruby rendering objects that directly contain content */
+
+#ifndef nsRubyContentFrame_h___
+#define nsRubyContentFrame_h___
+
+#include "nsInlineFrame.h"
+
+class nsRubyContentFrame : public nsInlineFrame
+{
+public:
+ NS_DECL_ABSTRACT_FRAME(nsRubyContentFrame)
+
+ // nsIFrame overrides
+ virtual bool IsFrameOfType(uint32_t aFlags) const override;
+
+ // Indicates whether this is an "intra-level whitespace" frame, i.e.
+ // an anonymous frame that was created to contain non-droppable
+ // whitespaces directly inside a ruby level container. This impacts
+ // ruby pairing behavior.
+ // See http://dev.w3.org/csswg/css-ruby/#anon-gen-interpret-space
+ bool IsIntraLevelWhitespace() const;
+
+protected:
+ explicit nsRubyContentFrame(nsStyleContext* aContext)
+ : nsInlineFrame(aContext) {}
+};
+
+#endif /* nsRubyContentFrame_h___ */
diff --git a/layout/generic/nsRubyFrame.cpp b/layout/generic/nsRubyFrame.cpp
new file mode 100644
index 000000000..31e0e2277
--- /dev/null
+++ b/layout/generic/nsRubyFrame.cpp
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby" */
+
+#include "nsRubyFrame.h"
+
+#include "RubyUtils.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/WritingModes.h"
+#include "nsLineLayout.h"
+#include "nsPresContext.h"
+#include "nsRubyBaseContainerFrame.h"
+#include "nsRubyTextContainerFrame.h"
+#include "nsStyleContext.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsRubyFrame)
+ NS_QUERYFRAME_ENTRY(nsRubyFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsRubyFrame)
+
+nsContainerFrame*
+NS_NewRubyFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsRubyFrame(aContext);
+}
+
+//----------------------------------------------------------------------
+
+// nsRubyFrame Method Implementations
+// ==================================
+
+nsIAtom*
+nsRubyFrame::GetType() const
+{
+ return nsGkAtoms::rubyFrame;
+}
+
+/* virtual */ bool
+nsRubyFrame::IsFrameOfType(uint32_t aFlags) const
+{
+ if (aFlags & eBidiInlineContainer) {
+ return false;
+ }
+ return nsInlineFrame::IsFrameOfType(aFlags);
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsRubyFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Ruby"), aResult);
+}
+#endif
+
+/* virtual */ void
+nsRubyFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData)
+{
+ for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
+ for (RubySegmentEnumerator e(static_cast<nsRubyFrame*>(frame));
+ !e.AtEnd(); e.Next()) {
+ e.GetBaseContainer()->AddInlineMinISize(aRenderingContext, aData);
+ }
+ }
+}
+
+/* virtual */ void
+nsRubyFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlinePrefISizeData *aData)
+{
+ for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
+ for (RubySegmentEnumerator e(static_cast<nsRubyFrame*>(frame));
+ !e.AtEnd(); e.Next()) {
+ e.GetBaseContainer()->AddInlinePrefISize(aRenderingContext, aData);
+ }
+ }
+}
+
+static nsRubyBaseContainerFrame*
+FindRubyBaseContainerAncestor(nsIFrame* aFrame)
+{
+ for (nsIFrame* ancestor = aFrame->GetParent();
+ ancestor && ancestor->IsFrameOfType(nsIFrame::eLineParticipant);
+ ancestor = ancestor->GetParent()) {
+ if (ancestor->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
+ return static_cast<nsRubyBaseContainerFrame*>(ancestor);
+ }
+ }
+ return nullptr;
+}
+
+/* virtual */ void
+nsRubyFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ if (!aReflowInput.mLineLayout) {
+ NS_ASSERTION(aReflowInput.mLineLayout,
+ "No line layout provided to RubyFrame reflow method.");
+ aStatus = NS_FRAME_COMPLETE;
+ return;
+ }
+
+ // Grab overflow frames from prev-in-flow and its own.
+ MoveOverflowToChildList();
+
+ // Clear leadings
+ mLeadings.Reset();
+
+ // Begin the span for the ruby frame
+ WritingMode frameWM = aReflowInput.GetWritingMode();
+ WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
+ LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
+ nscoord startEdge = 0;
+ const bool boxDecorationBreakClone =
+ StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone;
+ if (boxDecorationBreakClone || !GetPrevContinuation()) {
+ startEdge = borderPadding.IStart(frameWM);
+ }
+ NS_ASSERTION(aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE,
+ "should no longer use available widths");
+ nscoord availableISize = aReflowInput.AvailableISize();
+ availableISize -= startEdge + borderPadding.IEnd(frameWM);
+ aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
+ startEdge, availableISize, &mBaseline);
+
+ aStatus = NS_FRAME_COMPLETE;
+ for (RubySegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
+ ReflowSegment(aPresContext, aReflowInput, e.GetBaseContainer(), aStatus);
+
+ if (NS_INLINE_IS_BREAK(aStatus)) {
+ // A break occurs when reflowing the segment.
+ // Don't continue reflowing more segments.
+ break;
+ }
+ }
+
+ ContinuationTraversingState pullState(this);
+ while (aStatus == NS_FRAME_COMPLETE) {
+ nsRubyBaseContainerFrame* baseContainer =
+ PullOneSegment(aReflowInput.mLineLayout, pullState);
+ if (!baseContainer) {
+ // No more continuations after, finish now.
+ break;
+ }
+ ReflowSegment(aPresContext, aReflowInput, baseContainer, aStatus);
+ }
+ // We never handle overflow in ruby.
+ MOZ_ASSERT(!NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus));
+
+ aDesiredSize.ISize(lineWM) = aReflowInput.mLineLayout->EndSpan(this);
+ if (boxDecorationBreakClone || !GetPrevContinuation()) {
+ aDesiredSize.ISize(lineWM) += borderPadding.IStart(frameWM);
+ }
+ if (boxDecorationBreakClone || NS_FRAME_IS_COMPLETE(aStatus)) {
+ aDesiredSize.ISize(lineWM) += borderPadding.IEnd(frameWM);
+ }
+
+ // Update descendant leadings of ancestor ruby base container.
+ if (nsRubyBaseContainerFrame* rbc = FindRubyBaseContainerAncestor(this)) {
+ rbc->UpdateDescendantLeadings(mLeadings);
+ }
+
+ nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
+ borderPadding, lineWM, frameWM);
+}
+
+void
+nsRubyFrame::ReflowSegment(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsRubyBaseContainerFrame* aBaseContainer,
+ nsReflowStatus& aStatus)
+{
+ WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
+ LogicalSize availSize(lineWM, aReflowInput.AvailableISize(),
+ aReflowInput.AvailableBSize());
+ WritingMode rubyWM = GetWritingMode();
+ NS_ASSERTION(!rubyWM.IsOrthogonalTo(lineWM),
+ "Ruby frame writing-mode shouldn't be orthogonal to its line");
+
+ AutoRubyTextContainerArray textContainers(aBaseContainer);
+ const uint32_t rtcCount = textContainers.Length();
+
+ ReflowOutput baseMetrics(aReflowInput);
+ bool pushedFrame;
+ aReflowInput.mLineLayout->ReflowFrame(aBaseContainer, aStatus,
+ &baseMetrics, pushedFrame);
+
+ if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) {
+ if (aBaseContainer != mFrames.FirstChild()) {
+ // Some segments may have been reflowed before, hence it is not
+ // a break-before for the ruby container.
+ aStatus = NS_INLINE_LINE_BREAK_AFTER(NS_FRAME_NOT_COMPLETE);
+ PushChildren(aBaseContainer, aBaseContainer->GetPrevSibling());
+ aReflowInput.mLineLayout->SetDirtyNextLine();
+ }
+ // This base container is not placed at all, we can skip all
+ // text containers paired with it.
+ return;
+ }
+ if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
+ // It always promise that if the status is incomplete, there is a
+ // break occurs. Break before has been processed above. However,
+ // it is possible that break after happens with the frame reflow
+ // completed. It happens if there is a force break at the end.
+ MOZ_ASSERT(NS_INLINE_IS_BREAK_AFTER(aStatus));
+ // Find the previous sibling which we will
+ // insert new continuations after.
+ nsIFrame* lastChild;
+ if (rtcCount > 0) {
+ lastChild = textContainers.LastElement();
+ } else {
+ lastChild = aBaseContainer;
+ }
+
+ // Create continuations for the base container
+ nsIFrame* newBaseContainer = CreateNextInFlow(aBaseContainer);
+ // newBaseContainer is null if there are existing next-in-flows.
+ // We only need to move and push if there were not.
+ if (newBaseContainer) {
+ // Move the new frame after all the text containers
+ mFrames.RemoveFrame(newBaseContainer);
+ mFrames.InsertFrame(nullptr, lastChild, newBaseContainer);
+
+ // Create continuations for text containers
+ nsIFrame* newLastChild = newBaseContainer;
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsIFrame* newTextContainer = CreateNextInFlow(textContainers[i]);
+ MOZ_ASSERT(newTextContainer, "Next-in-flow of rtc should not exist "
+ "if the corresponding rbc does not");
+ mFrames.RemoveFrame(newTextContainer);
+ mFrames.InsertFrame(nullptr, newLastChild, newTextContainer);
+ newLastChild = newTextContainer;
+ }
+ }
+ if (lastChild != mFrames.LastChild()) {
+ // Always push the next frame after the last child in this segment.
+ // It is possible that we pulled it back before our next-in-flow
+ // drain our overflow.
+ PushChildren(lastChild->GetNextSibling(), lastChild);
+ aReflowInput.mLineLayout->SetDirtyNextLine();
+ }
+ } else {
+ // If the ruby base container is reflowed completely, the line
+ // layout will remove the next-in-flows of that frame. But the
+ // line layout is not aware of the ruby text containers, hence
+ // it is necessary to remove them here.
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsIFrame* nextRTC = textContainers[i]->GetNextInFlow();
+ if (nextRTC) {
+ nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true);
+ }
+ }
+ }
+
+ nscoord segmentISize = baseMetrics.ISize(lineWM);
+ const nsSize dummyContainerSize;
+ LogicalRect baseRect =
+ aBaseContainer->GetLogicalRect(lineWM, dummyContainerSize);
+ // We need to position our rtc frames on one side or the other of the
+ // base container's rect, using a coordinate space that's relative to
+ // the ruby frame. Right now, the base container's rect's block-axis
+ // position is relative to the block container frame containing the
+ // lines, so we use 0 instead. (i.e. we assume that the base container
+ // is adjacent to the ruby frame's block-start edge.)
+ // XXX We may need to add border/padding here. See bug 1055667.
+ baseRect.BStart(lineWM) = 0;
+ // The rect for offsets of text containers.
+ LogicalRect offsetRect = baseRect;
+ RubyBlockLeadings descLeadings = aBaseContainer->GetDescendantLeadings();
+ offsetRect.BStart(lineWM) -= descLeadings.mStart;
+ offsetRect.BSize(lineWM) += descLeadings.mStart + descLeadings.mEnd;
+ for (uint32_t i = 0; i < rtcCount; i++) {
+ nsRubyTextContainerFrame* textContainer = textContainers[i];
+ WritingMode rtcWM = textContainer->GetWritingMode();
+ nsReflowStatus textReflowStatus;
+ ReflowOutput textMetrics(aReflowInput);
+ ReflowInput textReflowInput(aPresContext, aReflowInput, textContainer,
+ availSize.ConvertTo(rtcWM, lineWM));
+ // FIXME We probably shouldn't be using the same nsLineLayout for
+ // the text containers. But it should be fine now as we are
+ // not actually using this line layout to reflow something,
+ // but just read the writing mode from it.
+ textReflowInput.mLineLayout = aReflowInput.mLineLayout;
+ textContainer->Reflow(aPresContext, textMetrics,
+ textReflowInput, textReflowStatus);
+ // Ruby text containers always return NS_FRAME_COMPLETE even when
+ // they have continuations, because the breaking has already been
+ // handled when reflowing the base containers.
+ NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE,
+ "Ruby text container must not break itself inside");
+ // The metrics is initialized with reflow state of this ruby frame,
+ // hence the writing-mode is tied to rubyWM instead of rtcWM.
+ LogicalSize size = textMetrics.Size(rubyWM).ConvertTo(lineWM, rubyWM);
+ textContainer->SetSize(lineWM, size);
+
+ nscoord reservedISize = RubyUtils::GetReservedISize(textContainer);
+ segmentISize = std::max(segmentISize, size.ISize(lineWM) + reservedISize);
+
+ uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition;
+ MOZ_ASSERT(rubyPosition == NS_STYLE_RUBY_POSITION_OVER ||
+ rubyPosition == NS_STYLE_RUBY_POSITION_UNDER);
+ Maybe<LogicalSide> side;
+ if (rubyPosition == NS_STYLE_RUBY_POSITION_OVER) {
+ side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirOver));
+ } else if (rubyPosition == NS_STYLE_RUBY_POSITION_UNDER) {
+ side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder));
+ } else {
+ // XXX inter-character support in bug 1055672
+ MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position");
+ }
+
+ LogicalPoint position(lineWM);
+ if (side.isSome()) {
+ if (side.value() == eLogicalSideBStart) {
+ offsetRect.BStart(lineWM) -= size.BSize(lineWM);
+ offsetRect.BSize(lineWM) += size.BSize(lineWM);
+ position = offsetRect.Origin(lineWM);
+ } else if (side.value() == eLogicalSideBEnd) {
+ position = offsetRect.Origin(lineWM) +
+ LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM));
+ offsetRect.BSize(lineWM) += size.BSize(lineWM);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("???");
+ }
+ }
+ // Using a dummy container-size here, so child positioning may not be
+ // correct. We will fix it in nsLineLayout after the whole line is
+ // reflowed.
+ FinishReflowChild(textContainer, aPresContext, textMetrics,
+ &textReflowInput, lineWM, position, dummyContainerSize, 0);
+ }
+ MOZ_ASSERT(baseRect.ISize(lineWM) == offsetRect.ISize(lineWM),
+ "Annotations should only be placed on the block directions");
+
+ nscoord deltaISize = segmentISize - baseMetrics.ISize(lineWM);
+ if (deltaISize <= 0) {
+ RubyUtils::ClearReservedISize(aBaseContainer);
+ } else {
+ RubyUtils::SetReservedISize(aBaseContainer, deltaISize);
+ aReflowInput.mLineLayout->AdvanceICoord(deltaISize);
+ }
+
+ // Set block leadings of the base container
+ nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM);
+ nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM);
+ // XXX When bug 765861 gets fixed, this warning should be upgraded.
+ NS_WARNING_ASSERTION(startLeading >= 0 && endLeading >= 0,
+ "Leadings should be non-negative (because adding "
+ "ruby annotation can only increase the size)");
+ mLeadings.Update(startLeading, endLeading);
+}
+
+nsRubyBaseContainerFrame*
+nsRubyFrame::PullOneSegment(const nsLineLayout* aLineLayout,
+ ContinuationTraversingState& aState)
+{
+ // Pull a ruby base container
+ nsIFrame* baseFrame = GetNextInFlowChild(aState);
+ if (!baseFrame) {
+ return nullptr;
+ }
+ MOZ_ASSERT(baseFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame);
+
+ // Get the float containing block of the base frame before we pull it.
+ nsBlockFrame* oldFloatCB =
+ nsLayoutUtils::GetFloatContainingBlock(baseFrame);
+ PullNextInFlowChild(aState);
+
+ // Pull all ruby text containers following the base container
+ nsIFrame* nextFrame;
+ while ((nextFrame = GetNextInFlowChild(aState)) != nullptr &&
+ nextFrame->GetType() == nsGkAtoms::rubyTextContainerFrame) {
+ PullNextInFlowChild(aState);
+ }
+
+ if (nsBlockFrame* newFloatCB =
+ nsLayoutUtils::GetAsBlock(aLineLayout->LineContainerFrame())) {
+ if (oldFloatCB && oldFloatCB != newFloatCB) {
+ newFloatCB->ReparentFloats(baseFrame, oldFloatCB, true);
+ }
+ }
+
+ return static_cast<nsRubyBaseContainerFrame*>(baseFrame);
+}
diff --git a/layout/generic/nsRubyFrame.h b/layout/generic/nsRubyFrame.h
new file mode 100644
index 000000000..c1fc7a303
--- /dev/null
+++ b/layout/generic/nsRubyFrame.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby" */
+
+#ifndef nsRubyFrame_h___
+#define nsRubyFrame_h___
+
+#include "nsInlineFrame.h"
+#include "RubyUtils.h"
+
+/**
+ * Factory function.
+ * @return a newly allocated nsRubyFrame (infallible)
+ */
+nsContainerFrame* NS_NewRubyFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+class nsRubyFrame final : public nsInlineFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsRubyFrame)
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame overrides
+ virtual nsIAtom* GetType() const override;
+ virtual bool IsFrameOfType(uint32_t aFlags) const override;
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ mozilla::RubyBlockLeadings GetBlockLeadings() const {
+ return mLeadings;
+ }
+
+protected:
+ friend nsContainerFrame* NS_NewRubyFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ explicit nsRubyFrame(nsStyleContext* aContext)
+ : nsInlineFrame(aContext) {}
+
+ void ReflowSegment(nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ nsRubyBaseContainerFrame* aBaseContainer,
+ nsReflowStatus& aStatus);
+
+ nsRubyBaseContainerFrame* PullOneSegment(const nsLineLayout* aLineLayout,
+ ContinuationTraversingState& aState);
+
+ // The leadings required to put the annotations. They are dummy-
+ // initialized to 0, and get meaningful values at first reflow.
+ mozilla::RubyBlockLeadings mLeadings;
+};
+
+#endif /* nsRubyFrame_h___ */
diff --git a/layout/generic/nsRubyTextContainerFrame.cpp b/layout/generic/nsRubyTextContainerFrame.cpp
new file mode 100644
index 000000000..e91f1151f
--- /dev/null
+++ b/layout/generic/nsRubyTextContainerFrame.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-text-container" */
+
+#include "nsRubyTextContainerFrame.h"
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WritingModes.h"
+#include "nsLineLayout.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsRubyTextContainerFrame)
+ NS_QUERYFRAME_ENTRY(nsRubyTextContainerFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsRubyTextContainerFrame)
+
+nsContainerFrame*
+NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsRubyTextContainerFrame(aContext);
+}
+
+
+//----------------------------------------------------------------------
+
+// nsRubyTextContainerFrame Method Implementations
+// ===============================================
+
+nsIAtom*
+nsRubyTextContainerFrame::GetType() const
+{
+ return nsGkAtoms::rubyTextContainerFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsRubyTextContainerFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("RubyTextContainer"), aResult);
+}
+#endif
+
+/* virtual */ bool
+nsRubyTextContainerFrame::IsFrameOfType(uint32_t aFlags) const
+{
+ if (aFlags & eSupportsCSSTransforms) {
+ return false;
+ }
+ return nsContainerFrame::IsFrameOfType(aFlags);
+}
+
+/* virtual */ void
+nsRubyTextContainerFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ nsContainerFrame::SetInitialChildList(aListID, aChildList);
+ if (aListID == kPrincipalList) {
+ UpdateSpanFlag();
+ }
+}
+
+/* virtual */ void
+nsRubyTextContainerFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ nsContainerFrame::AppendFrames(aListID, aFrameList);
+ UpdateSpanFlag();
+}
+
+/* virtual */ void
+nsRubyTextContainerFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
+ UpdateSpanFlag();
+}
+
+/* virtual */ void
+nsRubyTextContainerFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ nsContainerFrame::RemoveFrame(aListID, aOldFrame);
+ UpdateSpanFlag();
+}
+
+void
+nsRubyTextContainerFrame::UpdateSpanFlag()
+{
+ bool isSpan = false;
+ // The continuation checks are safe here because spans never break.
+ if (!GetPrevContinuation() && !GetNextContinuation()) {
+ nsIFrame* onlyChild = mFrames.OnlyChild();
+ if (onlyChild && onlyChild->IsPseudoFrame(GetContent())) {
+ // Per CSS Ruby spec, if the only child of an rtc frame is
+ // a pseudo rt frame, it spans all bases in the segment.
+ isSpan = true;
+ }
+ }
+
+ if (isSpan) {
+ AddStateBits(NS_RUBY_TEXT_CONTAINER_IS_SPAN);
+ } else {
+ RemoveStateBits(NS_RUBY_TEXT_CONTAINER_IS_SPAN);
+ }
+}
+
+/* virtual */ void
+nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ // Although a ruby text container may have continuations, returning
+ // NS_FRAME_COMPLETE here is still safe, since its parent, ruby frame,
+ // ignores the status, and continuations of the ruby base container
+ // will take care of our continuations.
+ aStatus = NS_FRAME_COMPLETE;
+ WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
+
+ nscoord minBCoord = nscoord_MAX;
+ nscoord maxBCoord = nscoord_MIN;
+ // The container size is not yet known, so we use a dummy (0, 0) size.
+ // The block-dir position will be corrected below after containerSize
+ // is finalized.
+ const nsSize dummyContainerSize;
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ nsIFrame* child = e.get();
+ MOZ_ASSERT(child->GetType() == nsGkAtoms::rubyTextFrame);
+ LogicalRect rect = child->GetLogicalRect(lineWM, dummyContainerSize);
+ LogicalMargin margin = child->GetLogicalUsedMargin(lineWM);
+ nscoord blockStart = rect.BStart(lineWM) - margin.BStart(lineWM);
+ minBCoord = std::min(minBCoord, blockStart);
+ nscoord blockEnd = rect.BEnd(lineWM) + margin.BEnd(lineWM);
+ maxBCoord = std::max(maxBCoord, blockEnd);
+ }
+
+ LogicalSize size(lineWM, mISize, 0);
+ if (!mFrames.IsEmpty()) {
+ if (MOZ_UNLIKELY(minBCoord > maxBCoord)) {
+ // XXX When bug 765861 gets fixed, this warning should be upgraded.
+ NS_WARNING("bad block coord");
+ minBCoord = maxBCoord = 0;
+ }
+ size.BSize(lineWM) = maxBCoord - minBCoord;
+ nsSize containerSize = size.GetPhysicalSize(lineWM);
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ nsIFrame* child = e.get();
+ // We reflowed the child with a dummy container size, as the true size
+ // was not yet known at that time.
+ LogicalPoint pos = child->GetLogicalPosition(lineWM, dummyContainerSize);
+ // Adjust block position to account for minBCoord,
+ // then reposition child based on the true container width.
+ pos.B(lineWM) -= minBCoord;
+ // Relative positioning hasn't happened yet.
+ // So MovePositionBy should not be used here.
+ child->SetPosition(lineWM, pos, containerSize);
+ nsContainerFrame::PlaceFrameView(child);
+ }
+ }
+
+ aDesiredSize.SetSize(lineWM, size);
+}
diff --git a/layout/generic/nsRubyTextContainerFrame.h b/layout/generic/nsRubyTextContainerFrame.h
new file mode 100644
index 000000000..1ce93db9b
--- /dev/null
+++ b/layout/generic/nsRubyTextContainerFrame.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-text-container" */
+
+#ifndef nsRubyTextContainerFrame_h___
+#define nsRubyTextContainerFrame_h___
+
+#include "nsBlockFrame.h"
+
+/**
+ * Factory function.
+ * @return a newly allocated nsRubyTextContainerFrame (infallible)
+ */
+nsContainerFrame* NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+class nsRubyTextContainerFrame final : public nsContainerFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsRubyTextContainerFrame)
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame overrides
+ virtual nsIAtom* GetType() const override;
+ virtual bool IsFrameOfType(uint32_t aFlags) const override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ // nsContainerFrame overrides
+ virtual void SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList) override;
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+
+ bool IsSpanContainer() const
+ {
+ return GetStateBits() & NS_RUBY_TEXT_CONTAINER_IS_SPAN;
+ }
+
+protected:
+ friend nsContainerFrame*
+ NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ explicit nsRubyTextContainerFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+ , mISize(0) {}
+
+ void UpdateSpanFlag();
+
+ friend class nsRubyBaseContainerFrame;
+ void SetISize(nscoord aISize) { mISize = aISize; }
+
+ // The intended inline size of the ruby text container. It is set by
+ // the corresponding ruby base container when the segment is reflowed,
+ // and used when the ruby text container is reflowed by its parent.
+ nscoord mISize;
+};
+
+#endif /* nsRubyTextContainerFrame_h___ */
diff --git a/layout/generic/nsRubyTextFrame.cpp b/layout/generic/nsRubyTextFrame.cpp
new file mode 100644
index 000000000..b4a26ff33
--- /dev/null
+++ b/layout/generic/nsRubyTextFrame.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-text" */
+
+#include "nsRubyTextFrame.h"
+
+#include "mozilla/WritingModes.h"
+#include "nsLineLayout.h"
+#include "nsPresContext.h"
+#include "nsStyleContext.h"
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+// Frame class boilerplate
+// =======================
+
+NS_QUERYFRAME_HEAD(nsRubyTextFrame)
+ NS_QUERYFRAME_ENTRY(nsRubyTextFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsRubyContentFrame)
+
+NS_IMPL_FRAMEARENA_HELPERS(nsRubyTextFrame)
+
+nsContainerFrame*
+NS_NewRubyTextFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext)
+{
+ return new (aPresShell) nsRubyTextFrame(aContext);
+}
+
+
+//----------------------------------------------------------------------
+
+// nsRubyTextFrame Method Implementations
+// ======================================
+
+nsIAtom*
+nsRubyTextFrame::GetType() const
+{
+ return nsGkAtoms::rubyTextFrame;
+}
+
+/* virtual */ bool
+nsRubyTextFrame::CanContinueTextRun() const
+{
+ return false;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsRubyTextFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("RubyText"), aResult);
+}
+#endif
+
+
+
+/* virtual */ void
+nsRubyTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (IsAutoHidden()) {
+ return;
+ }
+
+ nsRubyContentFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
+}
+
+/* virtual */ void
+nsRubyTextFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ // Even if we want to hide this frame, we have to reflow it first.
+ // If we leave it dirty, changes to its content will never be
+ // propagated to the ancestors, then it won't be displayed even if
+ // the content is no longer the same, until next reflow triggered by
+ // some other change. In general, we always reflow all the frames we
+ // created. There might be other problems if we don't do that.
+ nsRubyContentFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
+
+ if (IsAutoHidden()) {
+ // Reset the ISize. The BSize is not changed so that it won't
+ // affect vertical positioning in unexpected way.
+ WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
+ aDesiredSize.ISize(lineWM) = 0;
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ }
+}
diff --git a/layout/generic/nsRubyTextFrame.h b/layout/generic/nsRubyTextFrame.h
new file mode 100644
index 000000000..841b5081f
--- /dev/null
+++ b/layout/generic/nsRubyTextFrame.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for CSS "display: ruby-text" */
+
+#ifndef nsRubyTextFrame_h___
+#define nsRubyTextFrame_h___
+
+#include "nsRubyContentFrame.h"
+
+/**
+ * Factory function.
+ * @return a newly allocated nsRubyTextFrame (infallible)
+ */
+nsContainerFrame* NS_NewRubyTextFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+class nsRubyTextFrame final : public nsRubyContentFrame
+{
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+ NS_DECL_QUERYFRAME_TARGET(nsRubyTextFrame)
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame overrides
+ virtual nsIAtom* GetType() const override;
+ virtual bool CanContinueTextRun() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ bool IsAutoHidden() const
+ {
+ return GetStateBits() & NS_RUBY_TEXT_FRAME_AUTOHIDE;
+ }
+
+protected:
+ friend nsContainerFrame* NS_NewRubyTextFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+ explicit nsRubyTextFrame(nsStyleContext* aContext)
+ : nsRubyContentFrame(aContext) {}
+};
+
+#endif /* nsRubyTextFrame_h___ */
diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp
new file mode 100644
index 000000000..e0d65632e
--- /dev/null
+++ b/layout/generic/nsSelection.cpp
@@ -0,0 +1,6837 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of selection: nsISelection,nsISelectionPrivate and nsFrameSelection
+ */
+
+#include "mozilla/dom/Selection.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventStates.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsFrameSelection.h"
+#include "nsISelectionListener.h"
+#include "nsContentCID.h"
+#include "nsDeviceContext.h"
+#include "nsIContent.h"
+#include "nsIDOMNode.h"
+#include "nsRange.h"
+#include "nsCOMArray.h"
+#include "nsITableCellLayout.h"
+#include "nsTArray.h"
+#include "nsTableWrapperFrame.h"
+#include "nsTableCellFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsCCUncollectableMarker.h"
+#include "nsIContentIterator.h"
+#include "nsIDocumentEncoder.h"
+#include "nsTextFragment.h"
+#include <algorithm>
+#include "nsContentUtils.h"
+
+#include "nsGkAtoms.h"
+#include "nsIFrameTraversal.h"
+#include "nsLayoutUtils.h"
+#include "nsLayoutCID.h"
+#include "nsBidiPresUtils.h"
+static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
+#include "nsTextFrame.h"
+
+#include "nsIDOMText.h"
+
+#include "nsContentUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsDOMClassInfoID.h"
+
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsCaret.h"
+#include "AccessibleCaretEventHub.h"
+
+#include "mozilla/MouseEvents.h"
+#include "mozilla/TextEvents.h"
+
+#include "nsITimer.h"
+#include "nsFrameManager.h"
+// notifications
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+
+#include "nsISelectionController.h"//for the enums
+#include "nsAutoCopyListener.h"
+#include "SelectionChangeListener.h"
+#include "nsCopySupport.h"
+#include "nsIClipboard.h"
+#include "nsIFrameInlines.h"
+
+#include "nsIBidiKeyboard.h"
+
+#include "nsError.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/SelectionBinding.h"
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/layers/ScrollInputMethods.h"
+#include "nsViewManager.h"
+
+#include "nsIEditor.h"
+#include "nsIHTMLEditor.h"
+#include "nsFocusManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using mozilla::layers::ScrollInputMethod;
+
+//#define DEBUG_TABLE 1
+
+static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
+
+static nsIAtom *GetTag(nsINode *aNode);
+// returns the parent
+static nsINode* ParentOffset(nsINode *aNode, int32_t *aChildOffset);
+static nsINode* GetCellParent(nsINode *aDomNode);
+
+#ifdef PRINT_RANGE
+static void printRange(nsRange *aDomRange);
+#define DEBUG_OUT_RANGE(x) printRange(x)
+#else
+#define DEBUG_OUT_RANGE(x)
+#endif // PRINT_RANGE
+
+/******************************************************************************
+ * Utility methods defined in nsISelectionController.idl
+ ******************************************************************************/
+
+namespace mozilla {
+
+const char*
+ToChar(SelectionType aSelectionType)
+{
+ switch (aSelectionType) {
+ case SelectionType::eInvalid:
+ return "SelectionType::eInvalid";
+ case SelectionType::eNone:
+ return "SelectionType::eNone";
+ case SelectionType::eNormal:
+ return "SelectionType::eNormal";
+ case SelectionType::eSpellCheck:
+ return "SelectionType::eSpellCheck";
+ case SelectionType::eIMERawClause:
+ return "SelectionType::eIMERawClause";
+ case SelectionType::eIMESelectedRawClause:
+ return "SelectionType::eIMESelectedRawClause";
+ case SelectionType::eIMEConvertedClause:
+ return "SelectionType::eIMEConvertedClause";
+ case SelectionType::eIMESelectedClause:
+ return "SelectionType::eIMESelectedClause";
+ case SelectionType::eAccessibility:
+ return "SelectionType::eAccessibility";
+ case SelectionType::eFind:
+ return "SelectionType::eFind";
+ case SelectionType::eURLSecondary:
+ return "SelectionType::eURLSecondary";
+ case SelectionType::eURLStrikeout:
+ return "SelectionType::eURLStrikeout";
+ default:
+ return "Invalid SelectionType";
+ }
+}
+
+static bool
+IsValidSelectionType(RawSelectionType aRawSelectionType)
+{
+ switch (static_cast<SelectionType>(aRawSelectionType)) {
+ case SelectionType::eNone:
+ case SelectionType::eNormal:
+ case SelectionType::eSpellCheck:
+ case SelectionType::eIMERawClause:
+ case SelectionType::eIMESelectedRawClause:
+ case SelectionType::eIMEConvertedClause:
+ case SelectionType::eIMESelectedClause:
+ case SelectionType::eAccessibility:
+ case SelectionType::eFind:
+ case SelectionType::eURLSecondary:
+ case SelectionType::eURLStrikeout:
+ return true;
+ default:
+ return false;
+ }
+}
+
+SelectionType
+ToSelectionType(RawSelectionType aRawSelectionType)
+{
+ if (!IsValidSelectionType(aRawSelectionType)) {
+ return SelectionType::eInvalid;
+ }
+ return static_cast<SelectionType>(aRawSelectionType);
+}
+
+RawSelectionType
+ToRawSelectionType(SelectionType aSelectionType)
+{
+ return static_cast<RawSelectionType>(aSelectionType);
+}
+
+bool operator &(SelectionType aSelectionType,
+ RawSelectionType aRawSelectionTypes)
+{
+ return (ToRawSelectionType(aSelectionType) & aRawSelectionTypes) != 0;
+}
+
+} // namespace mozilla
+
+/******************************************************************************
+ * nsPeekOffsetStruct
+ ******************************************************************************/
+
+//#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
+//#define DEBUG_NAVIGATION
+
+
+//#define DEBUG_TABLE_SELECTION 1
+
+nsPeekOffsetStruct::nsPeekOffsetStruct(nsSelectionAmount aAmount,
+ nsDirection aDirection,
+ int32_t aStartOffset,
+ nsPoint aDesiredPos,
+ bool aJumpLines,
+ bool aScrollViewStop,
+ bool aIsKeyboardSelect,
+ bool aVisual,
+ bool aExtend,
+ EWordMovementType aWordMovementType)
+ : mAmount(aAmount)
+ , mDirection(aDirection)
+ , mStartOffset(aStartOffset)
+ , mDesiredPos(aDesiredPos)
+ , mWordMovementType(aWordMovementType)
+ , mJumpLines(aJumpLines)
+ , mScrollViewStop(aScrollViewStop)
+ , mIsKeyboardSelect(aIsKeyboardSelect)
+ , mVisual(aVisual)
+ , mExtend(aExtend)
+ , mResultContent()
+ , mResultFrame(nullptr)
+ , mContentOffset(0)
+ , mAttach(CARET_ASSOCIATE_BEFORE)
+{
+}
+
+struct CachedOffsetForFrame {
+ CachedOffsetForFrame()
+ : mCachedFrameOffset(0, 0) // nsPoint ctor
+ , mLastCaretFrame(nullptr)
+ , mLastContentOffset(0)
+ , mCanCacheFrameOffset(false)
+ {}
+
+ nsPoint mCachedFrameOffset; // cached frame offset
+ nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in.
+ int32_t mLastContentOffset; // store last content offset
+ bool mCanCacheFrameOffset; // cached frame offset is valid?
+};
+
+class nsAutoScrollTimer final : public nsITimerCallback
+{
+public:
+
+ NS_DECL_ISUPPORTS
+
+ nsAutoScrollTimer()
+ : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
+ {
+ }
+
+ // aPoint is relative to aPresContext's root frame
+ nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
+ {
+ mPoint = aPoint;
+
+ // Store the presentation context. The timer will be
+ // stopped by the selection if the prescontext is destroyed.
+ mPresContext = aPresContext;
+
+ mContent = nsIPresShell::GetCapturingContent();
+
+ if (!mTimer)
+ {
+ nsresult result;
+ mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
+
+ if (NS_FAILED(result))
+ return result;
+ }
+
+ return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
+ }
+
+ nsresult Stop()
+ {
+ if (mTimer)
+ {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ mContent = nullptr;
+ return NS_OK;
+ }
+
+ nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection)
+ {
+ mFrameSelection = aFrameSelection;
+ mSelection = aSelection;
+ return NS_OK;
+ }
+
+ nsresult SetDelay(uint32_t aDelay)
+ {
+ mDelay = aDelay;
+ return NS_OK;
+ }
+
+ NS_IMETHOD Notify(nsITimer *timer) override
+ {
+ if (mSelection && mPresContext)
+ {
+ nsWeakFrame frame =
+ mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
+ if (!frame)
+ return NS_OK;
+ mContent = nullptr;
+
+ nsPoint pt = mPoint -
+ frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ frameSelection->HandleDrag(frame, pt);
+ if (!frame.IsAlive())
+ return NS_OK;
+
+ NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
+ mSelection->DoAutoScroll(frame, pt);
+ }
+ return NS_OK;
+ }
+
+protected:
+ virtual ~nsAutoScrollTimer()
+ {
+ if (mTimer) {
+ mTimer->Cancel();
+ }
+ }
+
+private:
+ nsFrameSelection *mFrameSelection;
+ Selection* mSelection;
+ nsPresContext *mPresContext;
+ // relative to mPresContext's root frame
+ nsPoint mPoint;
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<nsIContent> mContent;
+ uint32_t mDelay;
+};
+
+NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback)
+
+nsresult NS_NewDomSelection(nsISelection **aDomSelection)
+{
+ Selection* rlist = new Selection;
+ *aDomSelection = (nsISelection *)rlist;
+ NS_ADDREF(rlist);
+ return NS_OK;
+}
+
+static int8_t
+GetIndexFromSelectionType(SelectionType aSelectionType)
+{
+ switch (aSelectionType) {
+ case SelectionType::eNormal:
+ return 0;
+ case SelectionType::eSpellCheck:
+ return 1;
+ case SelectionType::eIMERawClause:
+ return 2;
+ case SelectionType::eIMESelectedRawClause:
+ return 3;
+ case SelectionType::eIMEConvertedClause:
+ return 4;
+ case SelectionType::eIMESelectedClause:
+ return 5;
+ case SelectionType::eAccessibility:
+ return 6;
+ case SelectionType::eFind:
+ return 7;
+ case SelectionType::eURLSecondary:
+ return 8;
+ case SelectionType::eURLStrikeout:
+ return 9;
+ default:
+ return -1;
+ }
+ /* NOTREACHED */
+}
+
+static SelectionType
+GetSelectionTypeFromIndex(int8_t aIndex)
+{
+ static const SelectionType kSelectionTypes[] = {
+ SelectionType::eNormal,
+ SelectionType::eSpellCheck,
+ SelectionType::eIMERawClause,
+ SelectionType::eIMESelectedRawClause,
+ SelectionType::eIMEConvertedClause,
+ SelectionType::eIMESelectedClause,
+ SelectionType::eAccessibility,
+ SelectionType::eFind,
+ SelectionType::eURLSecondary,
+ SelectionType::eURLStrikeout
+ };
+ if (NS_WARN_IF(aIndex < 0) ||
+ NS_WARN_IF(static_cast<size_t>(aIndex) >= ArrayLength(kSelectionTypes))) {
+ return SelectionType::eNormal;
+ }
+ return kSelectionTypes[aIndex];
+}
+
+/*
+The limiter is used specifically for the text areas and textfields
+In that case it is the DIV tag that is anonymously created for the text
+areas/fields. Text nodes and BR nodes fall beneath it. In the case of a
+BR node the limiter will be the parent and the offset will point before or
+after the BR node. In the case of the text node the parent content is
+the text node itself and the offset will be the exact character position.
+The offset is not important to check for validity. Simply look at the
+passed in content. If it equals the limiter then the selection point is valid.
+If its parent it the limiter then the point is also valid. In the case of
+NO limiter all points are valid since you are in a topmost iframe. (browser
+or composer)
+*/
+bool
+IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
+{
+ if (!aFrameSel || !aNode)
+ return false;
+
+ nsIContent *limiter = aFrameSel->GetLimiter();
+ if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
+ //if newfocus == the limiter. that's ok. but if not there and not parent bad
+ return false; //not in the right content. tLimiter said so
+ }
+
+ limiter = aFrameSel->GetAncestorLimiter();
+ return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
+}
+
+namespace mozilla {
+struct MOZ_RAII AutoPrepareFocusRange
+{
+ AutoPrepareFocusRange(Selection* aSelection,
+ bool aContinueSelection,
+ bool aMultipleSelection
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+ if (aSelection->mRanges.Length() <= 1) {
+ return;
+ }
+
+ if (aSelection->mFrameSelection->IsUserSelectionReason()) {
+ mUserSelect.emplace(aSelection);
+ }
+ bool userSelection = aSelection->mUserInitiated;
+
+ nsTArray<RangeData>& ranges = aSelection->mRanges;
+ if (!userSelection ||
+ (!aContinueSelection && aMultipleSelection)) {
+ // Scripted command or the user is starting a new explicit multi-range
+ // selection.
+ for (RangeData& entry : ranges) {
+ entry.mRange->SetIsGenerated(false);
+ }
+ return;
+ }
+
+ int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
+ bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
+ nsISelectionListener::MOUSEDOWN_REASON |
+ nsISelectionListener::MOUSEUP_REASON |
+ nsISelectionListener::COLLAPSETOSTART_REASON));
+ if (!isAnchorRelativeOp) {
+ return;
+ }
+
+ // This operation is against the anchor but our current mAnchorFocusRange
+ // represents the focus in a multi-range selection. The anchor from a user
+ // perspective is the most distant generated range on the opposite side.
+ // Find that range and make it the mAnchorFocusRange.
+ const size_t len = ranges.Length();
+ size_t newAnchorFocusIndex = size_t(-1);
+ if (aSelection->GetDirection() == eDirNext) {
+ for (size_t i = 0; i < len; ++i) {
+ if (ranges[i].mRange->IsGenerated()) {
+ newAnchorFocusIndex = i;
+ break;
+ }
+ }
+ } else {
+ size_t i = len;
+ while (i--) {
+ if (ranges[i].mRange->IsGenerated()) {
+ newAnchorFocusIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (newAnchorFocusIndex == size_t(-1)) {
+ // There are no generated ranges - that's fine.
+ return;
+ }
+
+ // Setup the new mAnchorFocusRange and mark the old one as generated.
+ if (aSelection->mAnchorFocusRange) {
+ aSelection->mAnchorFocusRange->SetIsGenerated(true);
+ }
+ nsRange* range = ranges[newAnchorFocusIndex].mRange;
+ range->SetIsGenerated(false);
+ aSelection->mAnchorFocusRange = range;
+
+ // Remove all generated ranges (including the old mAnchorFocusRange).
+ RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
+ size_t i = len;
+ while (i--) {
+ range = aSelection->mRanges[i].mRange;
+ if (range->IsGenerated()) {
+ range->SetSelection(nullptr);
+ aSelection->selectFrames(presContext, range, false);
+ aSelection->mRanges.RemoveElementAt(i);
+ }
+ }
+ if (aSelection->mFrameSelection) {
+ aSelection->mFrameSelection->InvalidateDesiredPos();
+ }
+ }
+
+ Maybe<Selection::AutoUserInitiated> mUserSelect;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+} // namespace mozilla
+
+////////////BEGIN nsFrameSelection methods
+
+nsFrameSelection::nsFrameSelection()
+{
+ for (size_t i = 0; i < kPresentSelectionTypeCount; i++){
+ mDomSelections[i] = new Selection(this);
+ mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
+ }
+ mBatching = 0;
+ mChangesDuringBatching = false;
+ mNotifyFrames = true;
+
+ mMouseDoubleDownState = false;
+
+ mHint = CARET_ASSOCIATE_BEFORE;
+ mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
+ mKbdBidiLevel = NSBIDI_LTR;
+
+ mDragSelectingCells = false;
+ mSelectingTableCellMode = 0;
+ mSelectedCellIndex = 0;
+
+ nsAutoCopyListener *autoCopy = nullptr;
+ // On macOS, cache the current selection to send to osx service menu.
+#ifdef XP_MACOSX
+ autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionCache);
+#endif
+
+ // Check to see if the autocopy pref is enabled
+ // and add the autocopy listener if it is
+ if (Preferences::GetBool("clipboard.autocopy")) {
+ autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionClipboard);
+ }
+
+ if (autoCopy) {
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (mDomSelections[index]) {
+ autoCopy->Listen(mDomSelections[index]);
+ }
+ }
+
+ mDisplaySelection = nsISelectionController::SELECTION_OFF;
+ mSelectionChangeReason = nsISelectionListener::NO_REASON;
+
+ mDelayedMouseEventValid = false;
+ // These values are not used since they are only valid when
+ // mDelayedMouseEventValid is true, and setting mDelayedMouseEventValid
+ //alwaysoverrides these values.
+ mDelayedMouseEventIsShift = false;
+ mDelayedMouseEventClickCount = 0;
+}
+
+nsFrameSelection::~nsFrameSelection()
+{
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
+ for (size_t i = 0; i < kPresentSelectionTypeCount; ++i) {
+ tmp->mDomSelections[i] = nullptr;
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCellParent)
+ tmp->mSelectingTableCellMode = 0;
+ tmp->mDragSelectingCells = false;
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mAppendStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnselectCellOnMouseUp)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaintainRange)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiter)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mAncestorLimiter)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
+ if (tmp->mShell && tmp->mShell->GetDocument() &&
+ nsCCUncollectableMarker::InGeneration(cb,
+ tmp->mShell->GetDocument()->
+ GetMarkedCCGeneration())) {
+ return NS_SUCCESS_INTERRUPTED_TRAVERSE;
+ }
+ for (size_t i = 0; i < kPresentSelectionTypeCount; ++i) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDomSelections[i])
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellParent)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAppendStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnselectCellOnMouseUp)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMaintainRange)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiter)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAncestorLimiter)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsFrameSelection, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsFrameSelection, Release)
+
+// Get the x (or y, in vertical writing mode) position requested
+// by the Key Handling for line-up/down
+nsresult
+nsFrameSelection::FetchDesiredPos(nsPoint &aDesiredPos)
+{
+ if (!mShell) {
+ NS_ERROR("fetch desired position failed");
+ return NS_ERROR_FAILURE;
+ }
+ if (mDesiredPosSet) {
+ aDesiredPos = mDesiredPos;
+ return NS_OK;
+ }
+
+ RefPtr<nsCaret> caret = mShell->GetCaret();
+ if (!caret) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ caret->SetSelection(mDomSelections[index]);
+
+ nsRect coord;
+ nsIFrame* caretFrame = caret->GetGeometry(&coord);
+ if (!caretFrame) {
+ return NS_ERROR_FAILURE;
+ }
+ nsPoint viewOffset(0, 0);
+ nsView* view = nullptr;
+ caretFrame->GetOffsetFromView(viewOffset, &view);
+ if (view) {
+ coord += viewOffset;
+ }
+ aDesiredPos = coord.TopLeft();
+ return NS_OK;
+}
+
+void
+nsFrameSelection::InvalidateDesiredPos() // do not listen to mDesiredPos;
+ // you must get another.
+{
+ mDesiredPosSet = false;
+}
+
+void
+nsFrameSelection::SetDesiredPos(nsPoint aPos)
+{
+ mDesiredPos = aPos;
+ mDesiredPosSet = true;
+}
+
+nsresult
+nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame,
+ nsPoint& aPoint,
+ nsIFrame **aRetFrame,
+ nsPoint& aRetPoint)
+{
+ //
+ // The whole point of this method is to return a frame and point that
+ // that lie within the same valid subtree as the anchor node's frame,
+ // for use with the method GetContentAndOffsetsFromPoint().
+ //
+ // A valid subtree is defined to be one where all the content nodes in
+ // the tree have a valid parent-child relationship.
+ //
+ // If the anchor frame and aFrame are in the same subtree, aFrame will
+ // be returned in aRetFrame. If they are in different subtrees, we
+ // return the frame for the root of the subtree.
+ //
+
+ if (!aFrame || !aRetFrame)
+ return NS_ERROR_NULL_POINTER;
+
+ *aRetFrame = aFrame;
+ aRetPoint = aPoint;
+
+ //
+ // Get the frame and content for the selection's anchor point!
+ //
+
+ nsresult result;
+ nsCOMPtr<nsIDOMNode> anchorNode;
+ int32_t anchorOffset = 0;
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
+
+ if (NS_FAILED(result))
+ return result;
+
+ if (!anchorNode)
+ return NS_OK;
+
+ result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
+
+ if (NS_FAILED(result))
+ return result;
+
+ nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
+
+ if (!anchorContent)
+ return NS_ERROR_FAILURE;
+
+ //
+ // Now find the root of the subtree containing the anchor's content.
+ //
+
+ NS_ENSURE_STATE(mShell);
+ nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell);
+ NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
+
+ //
+ // Now find the root of the subtree containing aFrame's content.
+ //
+
+ nsIContent* content = aFrame->GetContent();
+
+ if (content)
+ {
+ nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
+ NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
+
+ if (anchorRoot == contentRoot)
+ {
+ // If the aFrame's content isn't the capturing content, it should be
+ // a descendant. At this time, we can return simply.
+ nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
+ if (capturedContent != content)
+ {
+ return NS_OK;
+ }
+
+ // Find the frame under the mouse cursor with the root frame.
+ // At this time, don't use the anchor's frame because it may not have
+ // fixed positioned frames.
+ nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
+ nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
+ nsIFrame* cursorFrame =
+ nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
+
+ // If the mouse cursor in on a frame which is descendant of same
+ // selection root, we can expand the selection to the frame.
+ if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
+ {
+ nsIContent* cursorContent = cursorFrame->GetContent();
+ NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
+ nsIContent* cursorContentRoot =
+ cursorContent->GetSelectionRootContent(mShell);
+ NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
+ if (cursorContentRoot == anchorRoot)
+ {
+ *aRetFrame = cursorFrame;
+ aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
+ return NS_OK;
+ }
+ }
+ // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
+ // cursor is out of the window), we should use the frame of the anchor
+ // root.
+ }
+ }
+
+ //
+ // When we can't find a frame which is under the mouse cursor and has a same
+ // selection root as the anchor node's, we should return the selection root
+ // frame.
+ //
+
+ *aRetFrame = anchorRoot->GetPrimaryFrame();
+
+ if (!*aRetFrame)
+ return NS_ERROR_FAILURE;
+
+ //
+ // Now make sure that aRetPoint is converted to the same coordinate
+ // system used by aRetFrame.
+ //
+
+ aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
+
+ return NS_OK;
+}
+
+void
+nsFrameSelection::SetCaretBidiLevel(nsBidiLevel aLevel)
+{
+ // If the current level is undefined, we have just inserted new text.
+ // In this case, we don't want to reset the keyboard language
+ mCaretBidiLevel = aLevel;
+
+ RefPtr<nsCaret> caret;
+ if (mShell && (caret = mShell->GetCaret())) {
+ caret->SchedulePaint();
+ }
+
+ return;
+}
+
+nsBidiLevel
+nsFrameSelection::GetCaretBidiLevel() const
+{
+ return mCaretBidiLevel;
+}
+
+void
+nsFrameSelection::UndefineCaretBidiLevel()
+{
+ mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
+}
+
+#ifdef PRINT_RANGE
+void printRange(nsRange *aDomRange)
+{
+ if (!aDomRange)
+ {
+ printf("NULL nsIDOMRange\n");
+ }
+ nsINode* startNode = aDomRange->GetStartParent();
+ nsINode* endNode = aDomRange->GetEndParent();
+ int32_t startOffset = aDomRange->StartOffset();
+ int32_t endOffset = aDomRange->EndOffset();
+
+ printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
+ (unsigned long)aDomRange,
+ (unsigned long)startNode, (long)startOffset,
+ (unsigned long)endNode, (long)endOffset);
+
+}
+#endif /* PRINT_RANGE */
+
+static
+nsIAtom *GetTag(nsINode *aNode)
+{
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+ if (!content)
+ {
+ NS_NOTREACHED("bad node passed to GetTag()");
+ return nullptr;
+ }
+
+ return content->NodeInfo()->NameAtom();
+}
+
+// Returns the parent
+nsINode*
+ParentOffset(nsINode *aNode, int32_t *aChildOffset)
+{
+ if (!aNode || !aChildOffset)
+ return nullptr;
+
+ nsIContent* parent = aNode->GetParent();
+ if (parent)
+ {
+ *aChildOffset = parent->IndexOf(aNode);
+
+ return parent;
+ }
+
+ return nullptr;
+}
+
+static nsINode*
+GetCellParent(nsINode *aDomNode)
+{
+ if (!aDomNode)
+ return nullptr;
+ nsINode* current = aDomNode;
+ // Start with current node and look for a table cell
+ while (current)
+ {
+ nsIAtom* tag = GetTag(current);
+ if (tag == nsGkAtoms::td || tag == nsGkAtoms::th)
+ return current;
+ current = current->GetParent();
+ }
+ return nullptr;
+}
+
+void
+nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
+{
+ mShell = aShell;
+ mDragState = false;
+ mDesiredPosSet = false;
+ mLimiter = aLimiter;
+ mCaretMovementStyle =
+ Preferences::GetInt("bidi.edit.caret_movement_style", 2);
+
+ // This should only ever be initialized on the main thread, so we are OK here.
+ static bool prefCachesInitialized = false;
+ if (!prefCachesInitialized) {
+ prefCachesInitialized = true;
+
+ Preferences::AddBoolVarCache(&sSelectionEventsEnabled,
+ "dom.select_events.enabled", false);
+ Preferences::AddBoolVarCache(&sSelectionEventsOnTextControlsEnabled,
+ "dom.select_events.textcontrols.enabled", false);
+ }
+
+ RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
+ if (eventHub) {
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (mDomSelections[index]) {
+ mDomSelections[index]->AddSelectionListener(eventHub);
+ }
+ }
+
+ nsIDocument* doc = aShell->GetDocument();
+ if (sSelectionEventsEnabled ||
+ (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (mDomSelections[index]) {
+ // The Selection instance will hold a strong reference to its selectionchangelistener
+ // so we don't have to worry about that!
+ RefPtr<SelectionChangeListener> listener = new SelectionChangeListener;
+ mDomSelections[index]->AddSelectionListener(listener);
+ }
+ }
+}
+
+bool nsFrameSelection::sSelectionEventsEnabled = false;
+bool nsFrameSelection::sSelectionEventsOnTextControlsEnabled = false;
+
+nsresult
+nsFrameSelection::MoveCaret(nsDirection aDirection,
+ bool aContinueSelection,
+ nsSelectionAmount aAmount,
+ CaretMovementStyle aMovementStyle)
+{
+ bool visualMovement = aMovementStyle == eVisual ||
+ (aMovementStyle == eUsePrefStyle &&
+ (mCaretMovementStyle == 1 ||
+ (mCaretMovementStyle == 2 && !aContinueSelection)));
+
+ NS_ENSURE_STATE(mShell);
+ // Flush out layout, since we need it to be up to date to do caret
+ // positioning.
+ mShell->FlushPendingNotifications(Flush_Layout);
+
+ if (!mShell) {
+ return NS_OK;
+ }
+
+ nsPresContext *context = mShell->GetPresContext();
+ if (!context)
+ return NS_ERROR_FAILURE;
+
+ bool isCollapsed;
+ nsPoint desiredPos(0, 0); //we must keep this around and revalidate it when its just UP/DOWN
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ RefPtr<Selection> sel = mDomSelections[index];
+ if (!sel)
+ return NS_ERROR_NULL_POINTER;
+
+ int32_t scrollFlags = Selection::SCROLL_FOR_CARET_MOVE;
+ nsINode* focusNode = sel->GetFocusNode();
+ if (focusNode &&
+ (focusNode->IsEditable() ||
+ (focusNode->IsElement() &&
+ focusNode->AsElement()->State().
+ HasState(NS_EVENT_STATE_MOZ_READWRITE)))) {
+ // If caret moves in editor, it should cause scrolling even if it's in
+ // overflow: hidden;.
+ scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
+ }
+
+ nsresult result = sel->GetIsCollapsed(&isCollapsed);
+ if (NS_FAILED(result)) {
+ return result;
+ }
+
+ int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0);
+ if (caretStyle == 0
+#ifdef XP_WIN
+ && aAmount != eSelectLine
+#endif
+ ) {
+ // Put caret at the selection edge in the |aDirection| direction.
+ caretStyle = 2;
+ }
+
+ bool doCollapse = !isCollapsed && !aContinueSelection && caretStyle == 2 &&
+ aAmount <= eSelectLine;
+ if (doCollapse) {
+ if (aDirection == eDirPrevious) {
+ PostReason(nsISelectionListener::COLLAPSETOSTART_REASON);
+ mHint = CARET_ASSOCIATE_AFTER;
+ } else {
+ PostReason(nsISelectionListener::COLLAPSETOEND_REASON);
+ mHint = CARET_ASSOCIATE_BEFORE;
+ }
+ } else {
+ PostReason(nsISelectionListener::KEYPRESS_REASON);
+ }
+
+ AutoPrepareFocusRange prep(sel, aContinueSelection, false);
+
+ if (aAmount == eSelectLine) {
+ result = FetchDesiredPos(desiredPos);
+ if (NS_FAILED(result)) {
+ return result;
+ }
+ SetDesiredPos(desiredPos);
+ }
+
+ if (doCollapse) {
+ const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
+ if (anchorFocusRange) {
+ nsINode* node;
+ int32_t offset;
+ if (aDirection == eDirPrevious) {
+ node = anchorFocusRange->GetStartParent();
+ offset = anchorFocusRange->StartOffset();
+ } else {
+ node = anchorFocusRange->GetEndParent();
+ offset = anchorFocusRange->EndOffset();
+ }
+ sel->Collapse(node, offset);
+ }
+ sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
+ nsIPresShell::ScrollAxis(),
+ nsIPresShell::ScrollAxis(), scrollFlags);
+ return NS_OK;
+ }
+
+ nsIFrame *frame;
+ int32_t offsetused = 0;
+ result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
+ visualMovement);
+
+ if (NS_FAILED(result) || !frame)
+ return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
+
+ //set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking
+ //when we hit scrollable views. If no limiter then just let it go ahead
+ nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredPos,
+ true, mLimiter != nullptr, true, visualMovement,
+ aContinueSelection);
+
+ nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
+
+ CaretAssociateHint tHint(mHint); //temporary variable so we dont set mHint until it is necessary
+ switch (aAmount){
+ case eSelectCharacter:
+ case eSelectCluster:
+ case eSelectWord:
+ case eSelectWordNoSpace:
+ InvalidateDesiredPos();
+ pos.mAmount = aAmount;
+ pos.mDirection = (visualMovement && paraDir == NSBIDI_RTL)
+ ? nsDirection(1 - aDirection) : aDirection;
+ break;
+ case eSelectLine:
+ pos.mAmount = aAmount;
+ pos.mDirection = aDirection;
+ break;
+ case eSelectBeginLine:
+ case eSelectEndLine:
+ InvalidateDesiredPos();
+ pos.mAmount = aAmount;
+ pos.mDirection = (visualMovement && paraDir == NSBIDI_RTL)
+ ? nsDirection(1 - aDirection) : aDirection;
+ break;
+ default:
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent)
+ {
+ nsIFrame *theFrame;
+ int32_t currentOffset, frameStart, frameEnd;
+
+ if (aAmount <= eSelectWordNoSpace)
+ {
+ // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward,
+ // so determine the hint here based on the result frame and offset:
+ // If we're at the end of a text frame, set the hint to ASSOCIATE_BEFORE to indicate that we
+ // want the caret displayed at the end of this frame, not at the beginning of the next one.
+ theFrame = pos.mResultFrame;
+ theFrame->GetOffsets(frameStart, frameEnd);
+ currentOffset = pos.mContentOffset;
+ if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0))
+ tHint = CARET_ASSOCIATE_BEFORE;
+ else
+ tHint = CARET_ASSOCIATE_AFTER;
+ } else {
+ // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all.
+ // In these cases, get the frame based on the content and hint returned by PeekOffset().
+ tHint = pos.mAttach;
+ theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset,
+ tHint, &currentOffset);
+ if (!theFrame)
+ return NS_ERROR_FAILURE;
+
+ theFrame->GetOffsets(frameStart, frameEnd);
+ }
+
+ if (context->BidiEnabled())
+ {
+ switch (aAmount) {
+ case eSelectBeginLine:
+ case eSelectEndLine: {
+ // In Bidi contexts, PeekOffset calculates pos.mContentOffset
+ // differently depending on whether the movement is visual or logical.
+ // For visual movement, pos.mContentOffset depends on the direction-
+ // ality of the first/last frame on the line (theFrame), and the caret
+ // directionality must correspond.
+ FrameBidiData bidiData = theFrame->GetBidiData();
+ SetCaretBidiLevel(visualMovement ? bidiData.embeddingLevel
+ : bidiData.baseLevel);
+ break;
+ }
+ default:
+ // If the current position is not a frame boundary, it's enough just
+ // to take the Bidi level of the current frame
+ if ((pos.mContentOffset != frameStart &&
+ pos.mContentOffset != frameEnd) ||
+ eSelectLine == aAmount) {
+ SetCaretBidiLevel(theFrame->GetEmbeddingLevel());
+ }
+ else {
+ BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset,
+ aAmount, tHint);
+ }
+ }
+ }
+ result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
+ tHint, aContinueSelection, false);
+ } else if (aAmount <= eSelectWordNoSpace && aDirection == eDirNext &&
+ !aContinueSelection) {
+ // Collapse selection if PeekOffset failed, we either
+ // 1. bumped into the BRFrame, bug 207623
+ // 2. had select-all in a text input (DIV range), bug 352759.
+ bool isBRFrame = frame->GetType() == nsGkAtoms::brFrame;
+ sel->Collapse(sel->GetFocusNode(), sel->FocusOffset());
+ // Note: 'frame' might be dead here.
+ if (!isBRFrame) {
+ mHint = CARET_ASSOCIATE_BEFORE; // We're now at the end of the frame to the left.
+ }
+ result = NS_OK;
+ }
+ if (NS_SUCCEEDED(result))
+ {
+ result = mDomSelections[index]->
+ ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
+ nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
+ scrollFlags);
+ }
+
+ return result;
+}
+
+//END nsFrameSelection methods
+
+
+//BEGIN nsFrameSelection methods
+
+NS_IMETHODIMP
+Selection::ToString(nsAString& aReturn)
+{
+ // We need Flush_Style here to make sure frames have been created for
+ // the selected content. Use mFrameSelection->GetShell() which returns
+ // null if the Selection has been disconnected (the shell is Destroyed).
+ nsCOMPtr<nsIPresShell> shell =
+ mFrameSelection ? mFrameSelection->GetShell() : nullptr;
+ if (!shell) {
+ aReturn.Truncate();
+ return NS_OK;
+ }
+ shell->FlushPendingNotifications(Flush_Style);
+
+ return ToStringWithFormat("text/plain",
+ nsIDocumentEncoder::SkipInvisibleContent,
+ 0, aReturn);
+}
+
+void
+Selection::Stringify(nsAString& aResult)
+{
+ // Eat the error code
+ ToString(aResult);
+}
+
+NS_IMETHODIMP
+Selection::ToStringWithFormat(const char* aFormatType, uint32_t aFlags,
+ int32_t aWrapCol, nsAString& aReturn)
+{
+ ErrorResult result;
+ NS_ConvertUTF8toUTF16 format(aFormatType);
+ ToStringWithFormat(format, aFlags, aWrapCol, aReturn, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+void
+Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
+ int32_t aWrapCol, nsAString& aReturn,
+ ErrorResult& aRv)
+{
+ nsresult rv = NS_OK;
+ NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
+ formatType.Append(aFormatType);
+ nsCOMPtr<nsIDocumentEncoder> encoder =
+ do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+
+ nsIPresShell* shell = GetPresShell();
+ if (!shell) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ nsIDocument *doc = shell->GetDocument();
+
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
+ NS_ASSERTION(domDoc, "Need a document");
+
+ // Flags should always include OutputSelectionOnly if we're coming from here:
+ aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
+ nsAutoString readstring;
+ readstring.Assign(aFormatType);
+ rv = encoder->Init(domDoc, readstring, aFlags);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+
+ encoder->SetSelection(this);
+ if (aWrapCol != 0)
+ encoder->SetWrapColumn(aWrapCol);
+
+ rv = encoder->EncodeToString(aReturn);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+}
+
+NS_IMETHODIMP
+Selection::SetInterlinePosition(bool aHintRight)
+{
+ ErrorResult result;
+ SetInterlinePosition(aHintRight, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+void
+Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv)
+{
+ if (!mFrameSelection) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
+ return;
+ }
+ mFrameSelection->SetHint(aHintRight ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
+}
+
+NS_IMETHODIMP
+Selection::GetInterlinePosition(bool* aHintRight)
+{
+ ErrorResult result;
+ *aHintRight = GetInterlinePosition(result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+bool
+Selection::GetInterlinePosition(ErrorResult& aRv)
+{
+ if (!mFrameSelection) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
+ return false;
+ }
+ return mFrameSelection->GetHint() == CARET_ASSOCIATE_AFTER;
+}
+
+Nullable<int16_t>
+Selection::GetCaretBidiLevel(mozilla::ErrorResult& aRv) const
+{
+ if (!mFrameSelection) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED);
+ return Nullable<int16_t>();
+ }
+ nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
+ return (caretBidiLevel & BIDI_LEVEL_UNDEFINED) ?
+ Nullable<int16_t>() : Nullable<int16_t>(caretBidiLevel);
+}
+
+void
+Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv)
+{
+ if (!mFrameSelection) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED);
+ return;
+ }
+ if (aCaretBidiLevel.IsNull()) {
+ mFrameSelection->UndefineCaretBidiLevel();
+ } else {
+ mFrameSelection->SetCaretBidiLevel(aCaretBidiLevel.Value());
+ }
+}
+
+nsPrevNextBidiLevels
+nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
+ uint32_t aContentOffset,
+ bool aJumpLines) const
+{
+ return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
+}
+
+nsPrevNextBidiLevels
+nsFrameSelection::GetPrevNextBidiLevels(nsIContent* aNode,
+ uint32_t aContentOffset,
+ CaretAssociateHint aHint,
+ bool aJumpLines) const
+{
+ // Get the level of the frames on each side
+ nsIFrame *currentFrame;
+ int32_t currentOffset;
+ int32_t frameStart, frameEnd;
+ nsDirection direction;
+
+ nsPrevNextBidiLevels levels;
+ levels.SetData(nullptr, nullptr, 0, 0);
+
+ currentFrame = GetFrameForNodeOffset(aNode, aContentOffset,
+ aHint, &currentOffset);
+ if (!currentFrame)
+ return levels;
+
+ currentFrame->GetOffsets(frameStart, frameEnd);
+
+ if (0 == frameStart && 0 == frameEnd)
+ direction = eDirPrevious;
+ else if (frameStart == currentOffset)
+ direction = eDirPrevious;
+ else if (frameEnd == currentOffset)
+ direction = eDirNext;
+ else {
+ // we are neither at the beginning nor at the end of the frame, so we have no worries
+ nsBidiLevel currentLevel = currentFrame->GetEmbeddingLevel();
+ levels.SetData(currentFrame, currentFrame, currentLevel, currentLevel);
+ return levels;
+ }
+
+ nsIFrame *newFrame;
+ int32_t offset;
+ bool jumpedLine, movedOverNonSelectableText;
+ nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
+ aJumpLines, true,
+ &newFrame, &offset, &jumpedLine,
+ &movedOverNonSelectableText);
+ if (NS_FAILED(rv))
+ newFrame = nullptr;
+
+ FrameBidiData currentBidi = currentFrame->GetBidiData();
+ nsBidiLevel currentLevel = currentBidi.embeddingLevel;
+ nsBidiLevel newLevel = newFrame ? newFrame->GetEmbeddingLevel()
+ : currentBidi.baseLevel;
+
+ // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
+ // XXX This could be removed once bug 339786 is fixed.
+ if (!aJumpLines) {
+ if (currentFrame->GetType() == nsGkAtoms::brFrame) {
+ currentFrame = nullptr;
+ currentLevel = currentBidi.baseLevel;
+ }
+ if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) {
+ newFrame = nullptr;
+ newLevel = currentBidi.baseLevel;
+ }
+ }
+
+ if (direction == eDirNext)
+ levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
+ else
+ levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
+
+ return levels;
+}
+
+nsresult
+nsFrameSelection::GetFrameFromLevel(nsIFrame *aFrameIn,
+ nsDirection aDirection,
+ nsBidiLevel aBidiLevel,
+ nsIFrame **aFrameOut) const
+{
+ NS_ENSURE_STATE(mShell);
+ nsBidiLevel foundLevel = 0;
+ nsIFrame *foundFrame = aFrameIn;
+
+ nsCOMPtr<nsIFrameEnumerator> frameTraversal;
+ nsresult result;
+ nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
+ if (NS_FAILED(result))
+ return result;
+
+ result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
+ mShell->GetPresContext(), aFrameIn,
+ eLeaf,
+ false, // aVisual
+ false, // aLockInScrollView
+ false, // aFollowOOFs
+ false // aSkipPopupChecks
+ );
+ if (NS_FAILED(result))
+ return result;
+
+ do {
+ *aFrameOut = foundFrame;
+ if (aDirection == eDirNext)
+ frameTraversal->Next();
+ else
+ frameTraversal->Prev();
+
+ foundFrame = frameTraversal->CurrentItem();
+ if (!foundFrame)
+ return NS_ERROR_FAILURE;
+ foundLevel = foundFrame->GetEmbeddingLevel();
+
+ } while (foundLevel > aBidiLevel);
+
+ return NS_OK;
+}
+
+
+nsresult
+nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ mMaintainedAmount = aAmount;
+
+ const nsRange* anchorFocusRange =
+ mDomSelections[index]->GetAnchorFocusRange();
+ if (anchorFocusRange && aAmount != eSelectNoAmount) {
+ mMaintainRange = anchorFocusRange->CloneRange();
+ return NS_OK;
+ }
+
+ mMaintainRange = nullptr;
+ return NS_OK;
+}
+
+
+/** After moving the caret, its Bidi level is set according to the following rules:
+ *
+ * After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
+ * After Home and End, set to the paragraph embedding level.
+ * After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
+ * After mouse click, set to the level of the current frame.
+ *
+ * The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
+ * BidiLevelFromMove is called when the caret is moved in response to a keyboard event
+ *
+ * @param aPresShell is the presentation shell
+ * @param aNode is the content node
+ * @param aContentOffset is the new caret position, as an offset into aNode
+ * @param aAmount is the amount of the move that gave the caret its new position
+ * @param aHint is the hint indicating in what logical direction the caret moved
+ */
+void nsFrameSelection::BidiLevelFromMove(nsIPresShell* aPresShell,
+ nsIContent* aNode,
+ uint32_t aContentOffset,
+ nsSelectionAmount aAmount,
+ CaretAssociateHint aHint)
+{
+ switch (aAmount) {
+
+ // Movement within the line: the new cursor Bidi level is the level of the
+ // last character moved over
+ case eSelectCharacter:
+ case eSelectCluster:
+ case eSelectWord:
+ case eSelectWordNoSpace:
+ case eSelectBeginLine:
+ case eSelectEndLine:
+ case eSelectNoAmount:
+ {
+ nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset,
+ aHint, false);
+
+ SetCaretBidiLevel(aHint == CARET_ASSOCIATE_BEFORE ?
+ levels.mLevelBefore : levels.mLevelAfter);
+ break;
+ }
+ /*
+ // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters
+ case eSelectLine:
+ case eSelectParagraph:
+ GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
+ aPresShell->SetCaretBidiLevel(std::min(firstLevel, secondLevel));
+ break;
+ */
+
+ default:
+ UndefineCaretBidiLevel();
+ }
+}
+
+/**
+ * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
+ *
+ * @param aNode is the content node
+ * @param aContentOffset is the new caret position, as an offset into aNode
+ */
+void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
+ uint32_t aContentOffset)
+{
+ nsIFrame* clickInFrame=nullptr;
+ int32_t OffsetNotUsed;
+
+ clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
+ if (!clickInFrame)
+ return;
+
+ SetCaretBidiLevel(clickInFrame->GetEmbeddingLevel());
+}
+
+
+bool
+nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
+ int32_t aOffset)
+{
+ if (!mMaintainRange)
+ return false;
+
+ if (!aContent) {
+ return false;
+ }
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return false;
+
+ nsINode* rangeStartNode = mMaintainRange->GetStartParent();
+ nsINode* rangeEndNode = mMaintainRange->GetEndParent();
+ int32_t rangeStartOffset = mMaintainRange->StartOffset();
+ int32_t rangeEndOffset = mMaintainRange->EndOffset();
+
+ int32_t relToStart =
+ nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset,
+ aContent, aOffset);
+ int32_t relToEnd =
+ nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset,
+ aContent, aOffset);
+
+ // If aContent/aOffset is inside the maintained selection, or if it is on the
+ // "anchor" side of the maintained selection, we need to do something.
+ if ((relToStart < 0 && relToEnd > 0) ||
+ (relToStart > 0 &&
+ mDomSelections[index]->GetDirection() == eDirNext) ||
+ (relToEnd < 0 &&
+ mDomSelections[index]->GetDirection() == eDirPrevious)) {
+ // Set the current range to the maintained range.
+ mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange);
+ if (relToStart < 0 && relToEnd > 0) {
+ // We're inside the maintained selection, just keep it selected.
+ return true;
+ }
+ // Reverse the direction of the selection so that the anchor will be on the
+ // far side of the maintained selection, relative to aContent/aOffset.
+ mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext);
+ }
+ return false;
+}
+
+
+nsresult
+nsFrameSelection::HandleClick(nsIContent* aNewFocus,
+ uint32_t aContentOffset,
+ uint32_t aContentEndOffset,
+ bool aContinueSelection,
+ bool aMultipleSelection,
+ CaretAssociateHint aHint)
+{
+ if (!aNewFocus)
+ return NS_ERROR_INVALID_ARG;
+
+ InvalidateDesiredPos();
+
+ if (!aContinueSelection) {
+ mMaintainRange = nullptr;
+ if (!IsValidSelectionPoint(this, aNewFocus)) {
+ mAncestorLimiter = nullptr;
+ }
+ }
+
+ // Don't take focus when dragging off of a table
+ if (!mDragSelectingCells)
+ {
+ BidiLevelFromClick(aNewFocus, aContentOffset);
+ PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
+ if (aContinueSelection &&
+ AdjustForMaintainedSelection(aNewFocus, aContentOffset))
+ return NS_OK; //shift clicked to maintained selection. rejected.
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ AutoPrepareFocusRange prep(mDomSelections[index], aContinueSelection, aMultipleSelection);
+ return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aHint,
+ aContinueSelection, aMultipleSelection);
+ }
+
+ return NS_OK;
+}
+
+void
+nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
+{
+ if (!aFrame || !mShell)
+ return;
+
+ nsresult result;
+ nsIFrame *newFrame = 0;
+ nsPoint newPoint;
+
+ result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
+ if (NS_FAILED(result))
+ return;
+ if (!newFrame)
+ return;
+
+ nsIFrame::ContentOffsets offsets =
+ newFrame->GetContentOffsetsFromPoint(newPoint);
+ if (!offsets.content)
+ return;
+
+ if (newFrame->IsSelected() &&
+ AdjustForMaintainedSelection(offsets.content, offsets.offset))
+ return;
+
+ // Adjust offsets according to maintained amount
+ if (mMaintainRange &&
+ mMaintainedAmount != eSelectNoAmount) {
+
+ nsINode* rangenode = mMaintainRange->GetStartParent();
+ int32_t rangeOffset = mMaintainRange->StartOffset();
+ int32_t relativePosition =
+ nsContentUtils::ComparePoints(rangenode, rangeOffset,
+ offsets.content, offsets.offset);
+
+ nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext;
+ nsSelectionAmount amount = mMaintainedAmount;
+ if (amount == eSelectBeginLine && direction == eDirNext)
+ amount = eSelectEndLine;
+
+ int32_t offset;
+ nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset,
+ CARET_ASSOCIATE_AFTER, &offset);
+
+ if (frame && amount == eSelectWord && direction == eDirPrevious) {
+ // To avoid selecting the previous word when at start of word,
+ // first move one character forward.
+ nsPeekOffsetStruct charPos(eSelectCharacter, eDirNext, offset,
+ nsPoint(0, 0), false, mLimiter != nullptr,
+ false, false, false);
+ if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) {
+ frame = charPos.mResultFrame;
+ offset = charPos.mContentOffset;
+ }
+ }
+
+ nsPeekOffsetStruct pos(amount, direction, offset, nsPoint(0, 0),
+ false, mLimiter != nullptr, false, false, false);
+
+ if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) {
+ offsets.content = pos.mResultContent;
+ offsets.offset = pos.mContentOffset;
+ }
+ }
+
+ HandleClick(offsets.content, offsets.offset, offsets.offset,
+ true, false, offsets.associate);
+}
+
+nsresult
+nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
+ nsPoint aPoint,
+ uint32_t aDelay)
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
+}
+
+void
+nsFrameSelection::StopAutoScrollTimer()
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return;
+
+ mDomSelections[index]->StopAutoScrollTimer();
+}
+
+/**
+hard to go from nodes to frames, easy the other way!
+ */
+nsresult
+nsFrameSelection::TakeFocus(nsIContent* aNewFocus,
+ uint32_t aContentOffset,
+ uint32_t aContentEndOffset,
+ CaretAssociateHint aHint,
+ bool aContinueSelection,
+ bool aMultipleSelection)
+{
+ if (!aNewFocus)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_ENSURE_STATE(mShell);
+
+ if (!IsValidSelectionPoint(this,aNewFocus))
+ return NS_ERROR_FAILURE;
+
+ // Clear all table selection data
+ mSelectingTableCellMode = 0;
+ mDragSelectingCells = false;
+ mStartSelectedCell = nullptr;
+ mEndSelectedCell = nullptr;
+ mAppendStartSelectedCell = nullptr;
+ mHint = aHint;
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ Maybe<Selection::AutoUserInitiated> userSelect;
+ if (IsUserSelectionReason()) {
+ userSelect.emplace(mDomSelections[index]);
+ }
+
+ //traverse through document and unselect crap here
+ if (!aContinueSelection) {//single click? setting cursor down
+ uint32_t batching = mBatching;//hack to use the collapse code.
+ bool changes = mChangesDuringBatching;
+ mBatching = 1;
+
+ if (aMultipleSelection) {
+ // Remove existing collapsed ranges as there's no point in having
+ // non-anchor/focus collapsed ranges.
+ mDomSelections[index]->RemoveCollapsedRanges();
+
+ RefPtr<nsRange> newRange = new nsRange(aNewFocus);
+
+ newRange->SetStart(aNewFocus, aContentOffset);
+ newRange->SetEnd(aNewFocus, aContentOffset);
+ mDomSelections[index]->AddRange(newRange);
+ mBatching = batching;
+ mChangesDuringBatching = changes;
+ } else {
+ bool oldDesiredPosSet = mDesiredPosSet; //need to keep old desired position if it was set.
+ mDomSelections[index]->Collapse(aNewFocus, aContentOffset);
+ mDesiredPosSet = oldDesiredPosSet; //now reset desired pos back.
+ mBatching = batching;
+ mChangesDuringBatching = changes;
+ }
+ if (aContentEndOffset != aContentOffset) {
+ mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);
+ }
+
+ //find out if we are inside a table. if so, find out which one and which cell
+ //once we do that, the next time we get a takefocus, check the parent tree.
+ //if we are no longer inside same table ,cell then switch to table selection mode.
+ // BUT only do this in an editor
+
+ NS_ENSURE_STATE(mShell);
+ bool editableCell = false;
+ RefPtr<nsPresContext> context = mShell->GetPresContext();
+ if (context) {
+ nsCOMPtr<nsIHTMLEditor> editor = do_QueryInterface(nsContentUtils::GetHTMLEditor(context));
+ if (editor) {
+ nsINode* cellparent = GetCellParent(aNewFocus);
+ nsCOMPtr<nsINode> editorHostNode = editor->GetActiveEditingHost();
+ editableCell = cellparent && editorHostNode &&
+ nsContentUtils::ContentIsDescendantOf(cellparent, editorHostNode);
+ if (editableCell) {
+ mCellParent = cellparent;
+#ifdef DEBUG_TABLE_SELECTION
+ printf(" * TakeFocus - Collapsing into new cell\n");
+#endif
+ }
+ }
+ }
+ }
+ else {
+ // Now update the range list:
+ if (aContinueSelection && aNewFocus)
+ {
+ int32_t offset;
+ nsINode *cellparent = GetCellParent(aNewFocus);
+ if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
+ {
+#ifdef DEBUG_TABLE_SELECTION
+printf(" * TakeFocus - moving into new cell\n");
+#endif
+ WidgetMouseEvent event(false, eVoidEvent, nullptr,
+ WidgetMouseEvent::eReal);
+
+ // Start selecting in the cell we were in before
+ nsINode* parent = ParentOffset(mCellParent, &offset);
+ if (parent)
+ HandleTableSelection(parent, offset,
+ nsISelectionPrivate::TABLESELECTION_CELL, &event);
+
+ // Find the parent of this new cell and extend selection to it
+ parent = ParentOffset(cellparent, &offset);
+
+ // XXXX We need to REALLY get the current key shift state
+ // (we'd need to add event listener -- let's not bother for now)
+ event.mModifiers &= ~MODIFIER_SHIFT; //aContinueSelection;
+ if (parent)
+ {
+ mCellParent = cellparent;
+ // Continue selection into next cell
+ HandleTableSelection(parent, offset,
+ nsISelectionPrivate::TABLESELECTION_CELL, &event);
+ }
+ }
+ else
+ {
+ // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
+ // is this the place to erase seleced cells ?????
+ if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough
+ {
+ mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff
+ }
+ else
+ mDomSelections[index]->Extend(aNewFocus, aContentOffset);
+ }
+ }
+ }
+
+ // Don't notify selection listeners if batching is on:
+ if (GetBatching())
+ return NS_OK;
+ return NotifySelectionListeners(SelectionType::eNormal);
+}
+
+
+SelectionDetails*
+nsFrameSelection::LookUpSelection(nsIContent *aContent,
+ int32_t aContentOffset,
+ int32_t aContentLength,
+ bool aSlowCheck) const
+{
+ if (!aContent || !mShell)
+ return nullptr;
+
+ SelectionDetails* details = nullptr;
+
+ for (size_t j = 0; j < kPresentSelectionTypeCount; j++) {
+ if (mDomSelections[j]) {
+ mDomSelections[j]->LookUpSelection(aContent, aContentOffset,
+ aContentLength, &details,
+ ToSelectionType(1 << j),
+ aSlowCheck);
+ }
+ }
+
+ return details;
+}
+
+void
+nsFrameSelection::SetDragState(bool aState)
+{
+ if (mDragState == aState)
+ return;
+
+ mDragState = aState;
+
+ if (!mDragState)
+ {
+ mDragSelectingCells = false;
+ // Notify that reason is mouse up.
+ PostReason(nsISelectionListener::MOUSEUP_REASON);
+ NotifySelectionListeners(SelectionType::eNormal);
+ }
+}
+
+Selection*
+nsFrameSelection::GetSelection(SelectionType aSelectionType) const
+{
+ int8_t index = GetIndexFromSelectionType(aSelectionType);
+ if (index < 0)
+ return nullptr;
+
+ return mDomSelections[index];
+}
+
+nsresult
+nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,
+ SelectionRegion aRegion,
+ int16_t aFlags) const
+{
+ int8_t index = GetIndexFromSelectionType(aSelectionType);
+ if (index < 0)
+ return NS_ERROR_INVALID_ARG;
+
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ nsIPresShell::ScrollAxis verticalScroll = nsIPresShell::ScrollAxis();
+ int32_t flags = Selection::SCROLL_DO_FLUSH;
+ if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
+ flags |= Selection::SCROLL_SYNCHRONOUS;
+ } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
+ flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY;
+ }
+ if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) {
+ flags |= Selection::SCROLL_OVERFLOW_HIDDEN;
+ }
+ if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) {
+ verticalScroll = nsIPresShell::ScrollAxis(
+ nsIPresShell::SCROLL_CENTER, nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE);
+ }
+ if (aFlags & nsISelectionController::SCROLL_FOR_CARET_MOVE) {
+ flags |= Selection::SCROLL_FOR_CARET_MOVE;
+ }
+
+ // After ScrollSelectionIntoView(), the pending notifications might be
+ // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
+ RefPtr<Selection> sel = mDomSelections[index];
+ return sel->ScrollIntoView(aRegion, verticalScroll,
+ nsIPresShell::ScrollAxis(), flags);
+}
+
+nsresult
+nsFrameSelection::RepaintSelection(SelectionType aSelectionType)
+{
+ int8_t index = GetIndexFromSelectionType(aSelectionType);
+ if (index < 0)
+ return NS_ERROR_INVALID_ARG;
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+ NS_ENSURE_STATE(mShell);
+
+// On macOS, update the selection cache to the new active selection
+// aka the current selection.
+#ifdef XP_MACOSX
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ // Check an active window exists otherwise there cannot be a current selection
+ // and that it's a normal selection.
+ if (fm->GetActiveWindow() && aSelectionType == SelectionType::eNormal) {
+ UpdateSelectionCacheOnRepaintSelection(mDomSelections[index]);
+ }
+#endif
+ return mDomSelections[index]->Repaint(mShell->GetPresContext());
+}
+
+nsIFrame*
+nsFrameSelection::GetFrameForNodeOffset(nsIContent* aNode,
+ int32_t aOffset,
+ CaretAssociateHint aHint,
+ int32_t* aReturnOffset) const
+{
+ if (!aNode || !aReturnOffset || !mShell)
+ return nullptr;
+
+ if (aOffset < 0)
+ return nullptr;
+
+ if (!aNode->GetPrimaryFrame() &&
+ !mShell->FrameManager()->GetDisplayContentsStyleFor(aNode)) {
+ return nullptr;
+ }
+
+ nsIFrame* returnFrame = nullptr;
+ nsCOMPtr<nsIContent> theNode;
+
+ while (true) {
+ *aReturnOffset = aOffset;
+
+ theNode = aNode;
+
+ if (aNode->IsElement()) {
+ int32_t childIndex = 0;
+ int32_t numChildren = theNode->GetChildCount();
+
+ if (aHint == CARET_ASSOCIATE_BEFORE) {
+ if (aOffset > 0) {
+ childIndex = aOffset - 1;
+ } else {
+ childIndex = aOffset;
+ }
+ } else {
+ NS_ASSERTION(aHint == CARET_ASSOCIATE_AFTER, "unknown direction");
+ if (aOffset >= numChildren) {
+ if (numChildren > 0) {
+ childIndex = numChildren - 1;
+ } else {
+ childIndex = 0;
+ }
+ } else {
+ childIndex = aOffset;
+ }
+ }
+
+ if (childIndex > 0 || numChildren > 0) {
+ nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
+
+ if (!childNode) {
+ break;
+ }
+
+ theNode = childNode;
+ }
+
+ // Now that we have the child node, check if it too
+ // can contain children. If so, descend into child.
+ if (theNode->IsElement() &&
+ theNode->GetChildCount() &&
+ !theNode->HasIndependentSelection()) {
+ aNode = theNode;
+ aOffset = aOffset > childIndex ? theNode->GetChildCount() : 0;
+ continue;
+ } else {
+ // Check to see if theNode is a text node. If it is, translate
+ // aOffset into an offset into the text node.
+
+ nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
+ if (textNode) {
+ if (theNode->GetPrimaryFrame()) {
+ if (aOffset > childIndex) {
+ uint32_t textLength = 0;
+ nsresult rv = textNode->GetLength(&textLength);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+
+ *aReturnOffset = (int32_t)textLength;
+ } else {
+ *aReturnOffset = 0;
+ }
+ } else {
+ int32_t numChildren = aNode->GetChildCount();
+ int32_t newChildIndex =
+ aHint == CARET_ASSOCIATE_BEFORE ? childIndex - 1 : childIndex + 1;
+
+ if (newChildIndex >= 0 && newChildIndex < numChildren) {
+ nsCOMPtr<nsIContent> newChildNode = aNode->GetChildAt(newChildIndex);
+ if (!newChildNode) {
+ return nullptr;
+ }
+
+ aNode = newChildNode;
+ aOffset = aHint == CARET_ASSOCIATE_BEFORE ? aNode->GetChildCount() : 0;
+ continue;
+ } else {
+ // newChildIndex is illegal which means we're at first or last
+ // child. Just use original node to get the frame.
+ theNode = aNode;
+ }
+ }
+ }
+ }
+ }
+
+ // If the node is a ShadowRoot, the frame needs to be adjusted,
+ // because a ShadowRoot does not get a frame. Its children are rendered
+ // as children of the host.
+ mozilla::dom::ShadowRoot* shadowRoot =
+ mozilla::dom::ShadowRoot::FromNode(theNode);
+ if (shadowRoot) {
+ theNode = shadowRoot->GetHost();
+ }
+
+ returnFrame = theNode->GetPrimaryFrame();
+ if (!returnFrame) {
+ if (aHint == CARET_ASSOCIATE_BEFORE) {
+ if (aOffset > 0) {
+ --aOffset;
+ continue;
+ } else {
+ break;
+ }
+ } else {
+ int32_t end = theNode->GetChildCount();
+ if (aOffset < end) {
+ ++aOffset;
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+
+ break;
+ } // end while
+
+ if (!returnFrame)
+ return nullptr;
+
+ // If we ended up here and were asked to position the caret after a visible
+ // break, let's return the frame on the next line instead if it exists.
+ if (aOffset > 0 && (uint32_t) aOffset >= aNode->Length() &&
+ theNode == aNode->GetLastChild()) {
+ nsIFrame* newFrame;
+ nsLayoutUtils::IsInvisibleBreak(theNode, &newFrame);
+ if (newFrame) {
+ returnFrame = newFrame;
+ *aReturnOffset = 0;
+ }
+ }
+
+ // find the child frame containing the offset we want
+ returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint == CARET_ASSOCIATE_AFTER,
+ &aOffset, &returnFrame);
+ return returnFrame;
+}
+
+void
+nsFrameSelection::CommonPageMove(bool aForward,
+ bool aExtend,
+ nsIScrollableFrame* aScrollableFrame)
+{
+ // expected behavior for PageMove is to scroll AND move the caret
+ // and remain relative position of the caret in view. see Bug 4302.
+
+ //get the frame from the scrollable view
+
+ nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
+ if (!scrolledFrame)
+ return;
+
+ // find out where the caret is.
+ // we should know mDesiredPos value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
+ nsISelection* domSel = GetSelection(SelectionType::eNormal);
+ if (!domSel) {
+ return;
+ }
+
+ nsRect caretPos;
+ nsIFrame* caretFrame = nsCaret::GetGeometry(domSel, &caretPos);
+ if (!caretFrame)
+ return;
+
+ //need to adjust caret jump by percentage scroll
+ nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount();
+
+ if (aForward)
+ caretPos.y += scrollDelta.height;
+ else
+ caretPos.y -= scrollDelta.height;
+
+ caretPos += caretFrame->GetOffsetTo(scrolledFrame);
+
+ // get a content at desired location
+ nsPoint desiredPoint;
+ desiredPoint.x = caretPos.x;
+ desiredPoint.y = caretPos.y + caretPos.height/2;
+ nsIFrame::ContentOffsets offsets =
+ scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
+
+ if (!offsets.content)
+ return;
+
+ // scroll one page
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
+ (uint32_t) ScrollInputMethod::MainThreadScrollPage);
+ aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
+ nsIScrollableFrame::PAGES,
+ nsIScrollableFrame::SMOOTH);
+
+ // place the caret
+ HandleClick(offsets.content, offsets.offset,
+ offsets.offset, aExtend, false, CARET_ASSOCIATE_AFTER);
+}
+
+nsresult
+nsFrameSelection::PhysicalMove(int16_t aDirection, int16_t aAmount,
+ bool aExtend)
+{
+ NS_ENSURE_STATE(mShell);
+ // Flush out layout, since we need it to be up to date to do caret
+ // positioning.
+ mShell->FlushPendingNotifications(Flush_Layout);
+
+ if (!mShell) {
+ return NS_OK;
+ }
+
+ // Check that parameters are safe
+ if (aDirection < 0 || aDirection > 3 || aAmount < 0 || aAmount > 1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPresContext *context = mShell->GetPresContext();
+ if (!context) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ RefPtr<Selection> sel = mDomSelections[index];
+ if (!sel) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // Map the abstract movement amounts (0-1) to direction-specific
+ // selection units.
+ static const nsSelectionAmount inlineAmount[] =
+ { eSelectCluster, eSelectWord };
+ static const nsSelectionAmount blockPrevAmount[] =
+ { eSelectLine, eSelectBeginLine };
+ static const nsSelectionAmount blockNextAmount[] =
+ { eSelectLine, eSelectEndLine };
+
+ struct PhysicalToLogicalMapping {
+ nsDirection direction;
+ const nsSelectionAmount *amounts;
+ };
+ static const PhysicalToLogicalMapping verticalLR[4] = {
+ { eDirPrevious, blockPrevAmount }, // left
+ { eDirNext, blockNextAmount }, // right
+ { eDirPrevious, inlineAmount }, // up
+ { eDirNext, inlineAmount } // down
+ };
+ static const PhysicalToLogicalMapping verticalRL[4] = {
+ { eDirNext, blockNextAmount },
+ { eDirPrevious, blockPrevAmount },
+ { eDirPrevious, inlineAmount },
+ { eDirNext, inlineAmount }
+ };
+ static const PhysicalToLogicalMapping horizontal[4] = {
+ { eDirPrevious, inlineAmount },
+ { eDirNext, inlineAmount },
+ { eDirPrevious, blockPrevAmount },
+ { eDirNext, blockNextAmount }
+ };
+
+ WritingMode wm;
+ nsIFrame *frame = nullptr;
+ int32_t offsetused = 0;
+ if (NS_SUCCEEDED(sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
+ true))) {
+ if (frame) {
+ if (!frame->StyleContext()->IsTextCombined()) {
+ wm = frame->GetWritingMode();
+ } else {
+ // Using different direction for horizontal-in-vertical would
+ // make it hard to navigate via keyboard. Inherit the moving
+ // direction from its parent.
+ MOZ_ASSERT(frame->GetType() == nsGkAtoms::textFrame);
+ wm = frame->GetParent()->GetWritingMode();
+ MOZ_ASSERT(wm.IsVertical(), "Text combined "
+ "can only appear in vertical text");
+ }
+ }
+ }
+
+ const PhysicalToLogicalMapping& mapping =
+ wm.IsVertical()
+ ? wm.IsVerticalLR() ? verticalLR[aDirection] : verticalRL[aDirection]
+ : horizontal[aDirection];
+
+ nsresult rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount],
+ eVisual);
+ if (NS_FAILED(rv)) {
+ // If we tried to do a line move, but couldn't move in the given direction,
+ // then we'll "promote" this to a line-edge move instead.
+ if (mapping.amounts[aAmount] == eSelectLine) {
+ rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount + 1],
+ eVisual);
+ }
+ // And if it was a next-word move that failed (which can happen when
+ // eat_space_to_next_word is true, see bug 1153237), then just move forward
+ // to the line-edge.
+ else if (mapping.amounts[aAmount] == eSelectWord &&
+ mapping.direction == eDirNext) {
+ rv = MoveCaret(eDirNext, aExtend, eSelectEndLine, eVisual);
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
+{
+ return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectCluster,
+ eUsePrefStyle);
+}
+
+nsresult
+nsFrameSelection::CharacterExtendForDelete()
+{
+ return MoveCaret(eDirNext, true, eSelectCluster, eLogical);
+}
+
+nsresult
+nsFrameSelection::CharacterExtendForBackspace()
+{
+ return MoveCaret(eDirPrevious, true, eSelectCharacter, eLogical);
+}
+
+nsresult
+nsFrameSelection::WordMove(bool aForward, bool aExtend)
+{
+ return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectWord,
+ eUsePrefStyle);
+}
+
+nsresult
+nsFrameSelection::WordExtendForDelete(bool aForward)
+{
+ return MoveCaret(aForward ? eDirNext : eDirPrevious, true, eSelectWord,
+ eLogical);
+}
+
+nsresult
+nsFrameSelection::LineMove(bool aForward, bool aExtend)
+{
+ return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectLine,
+ eUsePrefStyle);
+}
+
+nsresult
+nsFrameSelection::IntraLineMove(bool aForward, bool aExtend)
+{
+ if (aForward) {
+ return MoveCaret(eDirNext, aExtend, eSelectEndLine, eLogical);
+ } else {
+ return MoveCaret(eDirPrevious, aExtend, eSelectBeginLine, eLogical);
+ }
+}
+
+nsresult
+nsFrameSelection::SelectAll()
+{
+ nsCOMPtr<nsIContent> rootContent;
+ if (mLimiter)
+ {
+ rootContent = mLimiter;//addrefit
+ }
+ else if (mAncestorLimiter) {
+ rootContent = mAncestorLimiter;
+ }
+ else
+ {
+ NS_ENSURE_STATE(mShell);
+ nsIDocument *doc = mShell->GetDocument();
+ if (!doc)
+ return NS_ERROR_FAILURE;
+ rootContent = doc->GetRootElement();
+ if (!rootContent)
+ return NS_ERROR_FAILURE;
+ }
+ int32_t numChildren = rootContent->GetChildCount();
+ PostReason(nsISelectionListener::NO_REASON);
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ AutoPrepareFocusRange prep(mDomSelections[index], false, false);
+ return TakeFocus(rootContent, 0, numChildren, CARET_ASSOCIATE_BEFORE, false, false);
+}
+
+//////////END FRAMESELECTION
+
+void
+nsFrameSelection::StartBatchChanges()
+{
+ mBatching++;
+}
+
+void
+nsFrameSelection::EndBatchChanges(int16_t aReason)
+{
+ mBatching--;
+ NS_ASSERTION(mBatching >=0,"Bad mBatching");
+
+ if (mBatching == 0 && mChangesDuringBatching) {
+ int16_t postReason = PopReason() | aReason;
+ PostReason(postReason);
+ mChangesDuringBatching = false;
+ NotifySelectionListeners(SelectionType::eNormal);
+ }
+}
+
+
+nsresult
+nsFrameSelection::NotifySelectionListeners(SelectionType aSelectionType)
+{
+ int8_t index = GetIndexFromSelectionType(aSelectionType);
+ if (index >=0 && mDomSelections[index])
+ {
+ return mDomSelections[index]->NotifySelectionListeners();
+ }
+ return NS_ERROR_FAILURE;
+}
+
+// Start of Table Selection methods
+
+static bool IsCell(nsIContent *aContent)
+{
+ return aContent->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
+}
+
+nsITableCellLayout*
+nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
+{
+ NS_ENSURE_TRUE(mShell, nullptr);
+ nsITableCellLayout *cellLayoutObject =
+ do_QueryFrame(aCellContent->GetPrimaryFrame());
+ return cellLayoutObject;
+}
+
+nsresult
+nsFrameSelection::ClearNormalSelection()
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ return mDomSelections[index]->RemoveAllRanges();
+}
+
+static nsIContent*
+GetFirstSelectedContent(nsRange* aRange)
+{
+ if (!aRange) {
+ return nullptr;
+ }
+
+ NS_PRECONDITION(aRange->GetStartParent(), "Must have start parent!");
+ NS_PRECONDITION(aRange->GetStartParent()->IsElement(),
+ "Unexpected parent");
+
+ return aRange->GetStartParent()->GetChildAt(aRange->StartOffset());
+}
+
+// Table selection support.
+// TODO: Separate table methods into a separate nsITableSelection interface
+nsresult
+nsFrameSelection::HandleTableSelection(nsINode* aParentContent,
+ int32_t aContentOffset,
+ int32_t aTarget,
+ WidgetMouseEvent* aMouseEvent)
+{
+ NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
+ NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
+
+ if (mDragState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
+ {
+ // We were selecting cells and user drags mouse in table border or inbetween cells,
+ // just do nothing
+ return NS_OK;
+ }
+
+ nsresult result = NS_OK;
+
+ nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
+
+ // When doing table selection, always set the direction to next so
+ // we can be sure that anchorNode's offset always points to the
+ // selected cell
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ mDomSelections[index]->SetDirection(eDirNext);
+
+ // Stack-class to wrap all table selection changes in
+ // BeginBatchChanges() / EndBatchChanges()
+ SelectionBatcher selectionBatcher(mDomSelections[index]);
+
+ int32_t startRowIndex, startColIndex, curRowIndex, curColIndex;
+ if (mDragState && mDragSelectingCells)
+ {
+ // We are drag-selecting
+ if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
+ {
+ // If dragging in the same cell as last event, do nothing
+ if (mEndSelectedCell == childContent)
+ return NS_OK;
+
+#ifdef DEBUG_TABLE_SELECTION
+ printf(" mStartSelectedCell = %p, mEndSelectedCell = %p, childContent = %p \n",
+ mStartSelectedCell.get(), mEndSelectedCell.get(), childContent);
+#endif
+ // aTarget can be any "cell mode",
+ // so we can easily drag-select rows and columns
+ // Once we are in row or column mode,
+ // we can drift into any cell to stay in that mode
+ // even if aTarget = TABLESELECTION_CELL
+
+ if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
+ mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
+ {
+ if (mEndSelectedCell)
+ {
+ // Also check if cell is in same row/col
+ result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
+ if (NS_FAILED(result)) return result;
+ result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+ if (NS_FAILED(result)) return result;
+
+#ifdef DEBUG_TABLE_SELECTION
+printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
+#endif
+ if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
+ (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex))
+ return NS_OK;
+ }
+#ifdef DEBUG_TABLE_SELECTION
+printf(" Dragged into a new column or row\n");
+#endif
+ // Continue dragging row or column selection
+ return SelectRowOrColumn(childContent, mSelectingTableCellMode);
+ }
+ else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
+ {
+#ifdef DEBUG_TABLE_SELECTION
+printf("HandleTableSelection: Dragged into a new cell\n");
+#endif
+ // Trick for quick selection of rows and columns
+ // Hold down shift, then start selecting in one direction
+ // If next cell dragged into is in same row, select entire row,
+ // if next cell is in same column, select entire column
+ if (mStartSelectedCell && aMouseEvent->IsShift())
+ {
+ result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+ if (NS_FAILED(result)) return result;
+ result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+ if (NS_FAILED(result)) return result;
+
+ if (startRowIndex == curRowIndex ||
+ startColIndex == curColIndex)
+ {
+ // Force new selection block
+ mStartSelectedCell = nullptr;
+ mDomSelections[index]->RemoveAllRanges();
+
+ if (startRowIndex == curRowIndex)
+ mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
+ else
+ mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
+
+ return SelectRowOrColumn(childContent, mSelectingTableCellMode);
+ }
+ }
+
+ // Reselect block of cells to new end location
+ return SelectBlockOfCells(mStartSelectedCell, childContent);
+ }
+ }
+ // Do nothing if dragging in table, but outside a cell
+ return NS_OK;
+ }
+ else
+ {
+ // Not dragging -- mouse event is down or up
+ if (mDragState)
+ {
+#ifdef DEBUG_TABLE_SELECTION
+printf("HandleTableSelection: Mouse down event\n");
+#endif
+ // Clear cell we stored in mouse-down
+ mUnselectCellOnMouseUp = nullptr;
+
+ if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
+ {
+ bool isSelected = false;
+
+ // Check if we have other selected cells
+ nsIContent* previousCellNode =
+ GetFirstSelectedContent(GetFirstCellRange());
+ if (previousCellNode)
+ {
+ // We have at least 1 other selected cell
+
+ // Check if new cell is already selected
+ nsIFrame *cellFrame = childContent->GetPrimaryFrame();
+ if (!cellFrame) return NS_ERROR_NULL_POINTER;
+ isSelected = cellFrame->IsSelected();
+ }
+ else
+ {
+ // No cells selected -- remove non-cell selection
+ mDomSelections[index]->RemoveAllRanges();
+ }
+ mDragSelectingCells = true; // Signal to start drag-cell-selection
+ mSelectingTableCellMode = aTarget;
+ // Set start for new drag-selection block (not appended)
+ mStartSelectedCell = childContent;
+ // The initial block end is same as the start
+ mEndSelectedCell = childContent;
+
+ if (isSelected)
+ {
+ // Remember this cell to (possibly) unselect it on mouseup
+ mUnselectCellOnMouseUp = childContent;
+#ifdef DEBUG_TABLE_SELECTION
+printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
+#endif
+ }
+ else
+ {
+ // Select an unselected cell
+ // but first remove existing selection if not in same table
+ if (previousCellNode &&
+ !IsInSameTable(previousCellNode, childContent))
+ {
+ mDomSelections[index]->RemoveAllRanges();
+ // Reset selection mode that is cleared in RemoveAllRanges
+ mSelectingTableCellMode = aTarget;
+ }
+
+ return SelectCellElement(childContent);
+ }
+
+ return NS_OK;
+ }
+ else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
+ {
+ //TODO: We currently select entire table when clicked between cells,
+ // should we restrict to only around border?
+ // *** How do we get location data for cell and click?
+ mDragSelectingCells = false;
+ mStartSelectedCell = nullptr;
+ mEndSelectedCell = nullptr;
+
+ // Remove existing selection and select the table
+ mDomSelections[index]->RemoveAllRanges();
+ return CreateAndAddRange(aParentContent, aContentOffset);
+ }
+ else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
+ {
+#ifdef DEBUG_TABLE_SELECTION
+printf("aTarget == %d\n", aTarget);
+#endif
+
+ // Start drag-selecting mode so multiple rows/cols can be selected
+ // Note: Currently, nsFrame::GetDataForTableSelection
+ // will never call us for row or column selection on mouse down
+ mDragSelectingCells = true;
+
+ // Force new selection block
+ mStartSelectedCell = nullptr;
+ mDomSelections[index]->RemoveAllRanges();
+ // Always do this AFTER RemoveAllRanges
+ mSelectingTableCellMode = aTarget;
+ return SelectRowOrColumn(childContent, aTarget);
+ }
+ }
+ else
+ {
+#ifdef DEBUG_TABLE_SELECTION
+ printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%p\n",
+ mDragSelectingCells, mStartSelectedCell.get());
+#endif
+ // First check if we are extending a block selection
+ int32_t rangeCount;
+ result = mDomSelections[index]->GetRangeCount(&rangeCount);
+ if (NS_FAILED(result))
+ return result;
+
+ if (rangeCount > 0 && aMouseEvent->IsShift() &&
+ mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
+ {
+ // Shift key is down: append a block selection
+ mDragSelectingCells = false;
+ return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
+ }
+
+ if (mDragSelectingCells)
+ mAppendStartSelectedCell = mStartSelectedCell;
+
+ mDragSelectingCells = false;
+ mStartSelectedCell = nullptr;
+ mEndSelectedCell = nullptr;
+
+ // Any other mouseup actions require that Ctrl or Cmd key is pressed
+ // else stop table selection mode
+ bool doMouseUpAction = false;
+#ifdef XP_MACOSX
+ doMouseUpAction = aMouseEvent->IsMeta();
+#else
+ doMouseUpAction = aMouseEvent->IsControl();
+#endif
+ if (!doMouseUpAction)
+ {
+#ifdef DEBUG_TABLE_SELECTION
+ printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%p\n",
+ mAppendStartSelectedCell.get());
+#endif
+ return NS_OK;
+ }
+ // Unselect a cell only if it wasn't
+ // just selected on mousedown
+ if( childContent == mUnselectCellOnMouseUp)
+ {
+ // Scan ranges to find the cell to unselect (the selection range to remove)
+ // XXXbz it's really weird that this lives outside the loop, so once we
+ // find one we keep looking at it even if we find no more cells...
+ nsINode* previousCellParent = nullptr;
+#ifdef DEBUG_TABLE_SELECTION
+printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
+#endif
+ for( int32_t i = 0; i < rangeCount; i++)
+ {
+ // Strong reference, because sometimes we want to remove
+ // this range, and then we might be the only owner.
+ RefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i);
+ if (!range) return NS_ERROR_NULL_POINTER;
+
+ nsINode* parent = range->GetStartParent();
+ if (!parent) return NS_ERROR_NULL_POINTER;
+
+ int32_t offset = range->StartOffset();
+ // Be sure previous selection is a table cell
+ nsIContent* child = parent->GetChildAt(offset);
+ if (child && IsCell(child))
+ previousCellParent = parent;
+
+ // We're done if we didn't find parent of a previously-selected cell
+ if (!previousCellParent) break;
+
+ if (previousCellParent == aParentContent && offset == aContentOffset)
+ {
+ // Cell is already selected
+ if (rangeCount == 1)
+ {
+#ifdef DEBUG_TABLE_SELECTION
+printf("HandleTableSelection: Unselecting single selected cell\n");
+#endif
+ // This was the only cell selected.
+ // Collapse to "normal" selection inside the cell
+ mStartSelectedCell = nullptr;
+ mEndSelectedCell = nullptr;
+ mAppendStartSelectedCell = nullptr;
+ //TODO: We need a "Collapse to just before deepest child" routine
+ // Even better, should we collapse to just after the LAST deepest child
+ // (i.e., at the end of the cell's contents)?
+ return mDomSelections[index]->Collapse(childContent, 0);
+ }
+#ifdef DEBUG_TABLE_SELECTION
+printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+#endif
+ // Unselecting the start of previous block
+ // XXX What do we use now!
+ if (childContent == mAppendStartSelectedCell)
+ mAppendStartSelectedCell = nullptr;
+
+ // Deselect cell by removing its range from selection
+ return mDomSelections[index]->RemoveRange(range);
+ }
+ }
+ mUnselectCellOnMouseUp = nullptr;
+ }
+ }
+ }
+ return result;
+}
+
+nsresult
+nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
+{
+ NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
+ NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
+ mEndSelectedCell = aEndCell;
+
+ nsresult result = NS_OK;
+
+ // If new end cell is in a different table, do nothing
+ nsIContent* table = IsInSameTable(aStartCell, aEndCell);
+ if (!table) {
+ return NS_OK;
+ }
+
+ // Get starting and ending cells' location in the cellmap
+ int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
+ result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
+ if(NS_FAILED(result)) return result;
+ result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
+ if(NS_FAILED(result)) return result;
+
+ if (mDragSelectingCells)
+ {
+ // Drag selecting: remove selected cells outside of new block limits
+ UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
+ true);
+ }
+
+ // Note that we select block in the direction of user's mouse dragging,
+ // which means start cell may be after the end cell in either row or column
+ return AddCellsToSelection(table, startRowIndex, startColIndex,
+ endRowIndex, endColIndex);
+}
+
+nsresult
+nsFrameSelection::UnselectCells(nsIContent *aTableContent,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex,
+ bool aRemoveOutsideOfCellRange)
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return NS_ERROR_FAILURE;
+
+ int32_t minRowIndex = std::min(aStartRowIndex, aEndRowIndex);
+ int32_t maxRowIndex = std::max(aStartRowIndex, aEndRowIndex);
+ int32_t minColIndex = std::min(aStartColumnIndex, aEndColumnIndex);
+ int32_t maxColIndex = std::max(aStartColumnIndex, aEndColumnIndex);
+
+ // Strong reference because we sometimes remove the range
+ RefPtr<nsRange> range = GetFirstCellRange();
+ nsIContent* cellNode = GetFirstSelectedContent(range);
+ NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
+
+ int32_t curRowIndex, curColIndex;
+ while (cellNode)
+ {
+ nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
+ if (NS_FAILED(result))
+ return result;
+
+#ifdef DEBUG_TABLE_SELECTION
+ if (!range)
+ printf("RemoveCellsToSelection -- range is null\n");
+#endif
+
+ if (range) {
+ if (aRemoveOutsideOfCellRange) {
+ if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
+ curColIndex < minColIndex || curColIndex > maxColIndex) {
+
+ mDomSelections[index]->RemoveRange(range);
+ // Since we've removed the range, decrement pointer to next range
+ mSelectedCellIndex--;
+ }
+
+ } else {
+ // Remove cell from selection if it belongs to the given cells range or
+ // it is spanned onto the cells range.
+ nsTableCellFrame* cellFrame =
+ tableFrame->GetCellFrameAt(curRowIndex, curColIndex);
+
+ int32_t origRowIndex, origColIndex;
+ cellFrame->GetRowIndex(origRowIndex);
+ cellFrame->GetColIndex(origColIndex);
+ uint32_t actualRowSpan =
+ tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
+ uint32_t actualColSpan =
+ tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
+ if (origRowIndex <= maxRowIndex && maxRowIndex >= 0 &&
+ origRowIndex + actualRowSpan - 1 >= static_cast<uint32_t>(minRowIndex) &&
+ origColIndex <= maxColIndex && maxColIndex >= 0 &&
+ origColIndex + actualColSpan - 1 >= static_cast<uint32_t>(minColIndex)) {
+
+ mDomSelections[index]->RemoveRange(range);
+ // Since we've removed the range, decrement pointer to next range
+ mSelectedCellIndex--;
+ }
+ }
+ }
+
+ range = GetNextCellRange();
+ cellNode = GetFirstSelectedContent(range);
+ NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex)
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
+ if (!tableFrame) // Check that |table| is a table.
+ return NS_ERROR_FAILURE;
+
+ nsresult result = NS_OK;
+ int32_t row = aStartRowIndex;
+ while(true)
+ {
+ int32_t col = aStartColumnIndex;
+ while(true)
+ {
+ nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col);
+
+ // Skip cells that are spanned from previous locations or are already selected
+ if (cellFrame) {
+ int32_t origRow, origCol;
+ cellFrame->GetRowIndex(origRow);
+ cellFrame->GetColIndex(origCol);
+ if (origRow == row && origCol == col && !cellFrame->IsSelected()) {
+ result = SelectCellElement(cellFrame->GetContent());
+ if (NS_FAILED(result)) return result;
+ }
+ }
+ // Done when we reach end column
+ if (col == aEndColumnIndex) break;
+
+ if (aStartColumnIndex < aEndColumnIndex)
+ col ++;
+ else
+ col--;
+ }
+ if (row == aEndRowIndex) break;
+
+ if (aStartRowIndex < aEndRowIndex)
+ row++;
+ else
+ row--;
+ }
+ return result;
+}
+
+nsresult
+nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex)
+{
+ return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
+ aEndRowIndex, aEndColumnIndex, false);
+}
+
+nsresult
+nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
+ int32_t aStartRowIndex,
+ int32_t aStartColumnIndex,
+ int32_t aEndRowIndex,
+ int32_t aEndColumnIndex)
+{
+ return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
+ aEndRowIndex, aEndColumnIndex, true);
+}
+
+nsresult
+nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, uint32_t aTarget)
+{
+ if (!aCellContent) return NS_ERROR_NULL_POINTER;
+
+ nsIContent* table = GetParentTable(aCellContent);
+ if (!table) return NS_ERROR_NULL_POINTER;
+
+ // Get table and cell layout interfaces to access
+ // cell data based on cellmap location
+ // Frames are not ref counted, so don't use an nsCOMPtr
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame());
+ if (!tableFrame) return NS_ERROR_FAILURE;
+ nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
+ if (!cellLayout) return NS_ERROR_FAILURE;
+
+ // Get location of target cell:
+ int32_t rowIndex, colIndex;
+ nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
+ if (NS_FAILED(result)) return result;
+
+ // Be sure we start at proper beginning
+ // (This allows us to select row or col given ANY cell!)
+ if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
+ colIndex = 0;
+ if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
+ rowIndex = 0;
+
+ nsCOMPtr<nsIContent> firstCell, lastCell;
+ while (true) {
+ // Loop through all cells in column or row to find first and last
+ nsCOMPtr<nsIContent> curCellContent =
+ tableFrame->GetCellAt(rowIndex, colIndex);
+ if (!curCellContent)
+ break;
+
+ if (!firstCell)
+ firstCell = curCellContent;
+
+ lastCell = curCellContent.forget();
+
+ // Move to next cell in cellmap, skipping spanned locations
+ if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
+ colIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
+ else
+ rowIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
+ }
+
+ // Use SelectBlockOfCells:
+ // This will replace existing selection,
+ // but allow unselecting by dragging out of selected region
+ if (firstCell && lastCell)
+ {
+ if (!mStartSelectedCell)
+ {
+ // We are starting a new block, so select the first cell
+ result = SelectCellElement(firstCell);
+ if (NS_FAILED(result)) return result;
+ mStartSelectedCell = firstCell;
+ }
+ nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
+ result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
+
+ // This gets set to the cell at end of row/col,
+ // but we need it to be the cell under cursor
+ mEndSelectedCell = aCellContent;
+ return result;
+ }
+
+#if 0
+// This is a more efficient strategy that appends row to current selection,
+// but doesn't allow dragging OFF of an existing selection to unselect!
+ do {
+ // Loop through all cells in column or row
+ result = tableLayout->GetCellDataAt(rowIndex, colIndex,
+ getter_AddRefs(cellElement),
+ curRowIndex, curColIndex,
+ rowSpan, colSpan,
+ actualRowSpan, actualColSpan,
+ isSelected);
+ if (NS_FAILED(result)) return result;
+ // We're done when cell is not found
+ if (!cellElement) break;
+
+
+ // Check spans else we infinitely loop
+ NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
+ NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
+
+ // Skip cells that are already selected or span from outside our region
+ if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
+ {
+ result = SelectCellElement(cellElement);
+ if (NS_FAILED(result)) return result;
+ }
+ // Move to next row or column in cellmap, skipping spanned locations
+ if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
+ colIndex += actualColSpan;
+ else
+ rowIndex += actualRowSpan;
+ }
+ while (cellElement);
+#endif
+
+ return NS_OK;
+}
+
+nsIContent*
+nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const
+{
+ if (!aRange) return nullptr;
+
+ nsINode* startParent = aRange->GetStartParent();
+ if (!startParent)
+ return nullptr;
+
+ int32_t offset = aRange->StartOffset();
+
+ nsIContent* childContent = startParent->GetChildAt(offset);
+ if (!childContent)
+ return nullptr;
+ // Don't return node if not a cell
+ if (!IsCell(childContent))
+ return nullptr;
+
+ return childContent;
+}
+
+nsRange*
+nsFrameSelection::GetFirstCellRange()
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return nullptr;
+
+ nsRange* firstRange = mDomSelections[index]->GetRangeAt(0);
+ if (!GetFirstCellNodeInRange(firstRange)) {
+ return nullptr;
+ }
+
+ // Setup for next cell
+ mSelectedCellIndex = 1;
+
+ return firstRange;
+}
+
+nsRange*
+nsFrameSelection::GetNextCellRange()
+{
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return nullptr;
+
+ nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex);
+
+ // Get first node in next range of selection - test if it's a cell
+ if (!GetFirstCellNodeInRange(range)) {
+ return nullptr;
+ }
+
+ // Setup for next cell
+ mSelectedCellIndex++;
+
+ return range;
+}
+
+nsresult
+nsFrameSelection::GetCellIndexes(nsIContent *aCell,
+ int32_t &aRowIndex,
+ int32_t &aColIndex)
+{
+ if (!aCell) return NS_ERROR_NULL_POINTER;
+
+ aColIndex=0; // initialize out params
+ aRowIndex=0;
+
+ nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
+ if (!cellLayoutObject) return NS_ERROR_FAILURE;
+ return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
+}
+
+nsIContent*
+nsFrameSelection::IsInSameTable(nsIContent *aContent1,
+ nsIContent *aContent2) const
+{
+ if (!aContent1 || !aContent2) return nullptr;
+
+ nsIContent* tableNode1 = GetParentTable(aContent1);
+ nsIContent* tableNode2 = GetParentTable(aContent2);
+
+ // Must be in the same table. Note that we want to return false for
+ // the test if both tables are null.
+ return (tableNode1 == tableNode2) ? tableNode1 : nullptr;
+}
+
+nsIContent*
+nsFrameSelection::GetParentTable(nsIContent *aCell) const
+{
+ if (!aCell) {
+ return nullptr;
+ }
+
+ for (nsIContent* parent = aCell->GetParent(); parent;
+ parent = parent->GetParent()) {
+ if (parent->IsHTMLElement(nsGkAtoms::table)) {
+ return parent;
+ }
+ }
+
+ return nullptr;
+}
+
+nsresult
+nsFrameSelection::SelectCellElement(nsIContent *aCellElement)
+{
+ nsIContent *parent = aCellElement->GetParent();
+
+ // Get child offset
+ int32_t offset = parent->IndexOf(aCellElement);
+
+ return CreateAndAddRange(parent, offset);
+}
+
+nsresult
+Selection::getTableCellLocationFromRange(nsRange* aRange,
+ int32_t* aSelectionType,
+ int32_t* aRow, int32_t* aCol)
+{
+ if (!aRange || !aSelectionType || !aRow || !aCol)
+ return NS_ERROR_NULL_POINTER;
+
+ *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
+ *aRow = 0;
+ *aCol = 0;
+
+ // Must have access to frame selection to get cell info
+ if (!mFrameSelection) return NS_OK;
+
+ nsresult result = GetTableSelectionType(aRange, aSelectionType);
+ if (NS_FAILED(result)) return result;
+
+ // Don't fail if range does not point to a single table cell,
+ // let aSelectionType tell user if we don't have a cell
+ if (*aSelectionType != nsISelectionPrivate::TABLESELECTION_CELL)
+ return NS_OK;
+
+ // Get the child content (the cell) pointed to by starting node of range
+ // We do minimal checking since GetTableSelectionType assures
+ // us that this really is a table cell
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
+ if (!content)
+ return NS_ERROR_FAILURE;
+
+ nsIContent *child = content->GetChildAt(aRange->StartOffset());
+ if (!child)
+ return NS_ERROR_FAILURE;
+
+ //Note: This is a non-ref-counted pointer to the frame
+ nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
+ if (NS_FAILED(result))
+ return result;
+ if (!cellLayout)
+ return NS_ERROR_FAILURE;
+
+ return cellLayout->GetCellIndexes(*aRow, *aCol);
+}
+
+nsresult
+Selection::addTableCellRange(nsRange* aRange, bool* aDidAddRange,
+ int32_t* aOutIndex)
+{
+ if (!aDidAddRange || !aOutIndex)
+ return NS_ERROR_NULL_POINTER;
+
+ *aDidAddRange = false;
+ *aOutIndex = -1;
+
+ if (!mFrameSelection)
+ return NS_OK;
+
+ if (!aRange)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult result;
+
+ // Get if we are adding a cell selection and the row, col of cell if we are
+ int32_t newRow, newCol, tableMode;
+ result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
+ if (NS_FAILED(result)) return result;
+
+ // If not adding a cell range, we are done here
+ if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
+ {
+ mFrameSelection->mSelectingTableCellMode = tableMode;
+ // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
+ return NS_OK;
+ }
+
+ // Set frame selection mode only if not already set to a table mode
+ // so we don't lose the select row and column flags (not detected by getTableCellLocation)
+ if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
+ mFrameSelection->mSelectingTableCellMode = tableMode;
+
+ *aDidAddRange = true;
+ return AddItem(aRange, aOutIndex);
+}
+
+//TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
+nsresult
+Selection::GetTableSelectionType(nsIDOMRange* aDOMRange,
+ int32_t* aTableSelectionType)
+{
+ if (!aDOMRange || !aTableSelectionType)
+ return NS_ERROR_NULL_POINTER;
+ nsRange* range = static_cast<nsRange*>(aDOMRange);
+
+ *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
+
+ // Must have access to frame selection to get cell info
+ if(!mFrameSelection) return NS_OK;
+
+ nsINode* startNode = range->GetStartParent();
+ if (!startNode) return NS_ERROR_FAILURE;
+
+ nsINode* endNode = range->GetEndParent();
+ if (!endNode) return NS_ERROR_FAILURE;
+
+ // Not a single selected node
+ if (startNode != endNode) return NS_OK;
+
+ int32_t startOffset = range->StartOffset();
+ int32_t endOffset = range->EndOffset();
+
+ // Not a single selected node
+ if ((endOffset - startOffset) != 1)
+ return NS_OK;
+
+ nsIContent* startContent = static_cast<nsIContent*>(startNode);
+ if (!(startNode->IsElement() && startContent->IsHTMLElement())) {
+ // Implies a check for being an element; if we ever make this work
+ // for non-HTML, need to keep checking for elements.
+ return NS_OK;
+ }
+
+ if (startContent->IsHTMLElement(nsGkAtoms::tr))
+ {
+ *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
+ }
+ else //check to see if we are selecting a table or row (column and all cells not done yet)
+ {
+ nsIContent *child = startNode->GetChildAt(startOffset);
+ if (!child)
+ return NS_ERROR_FAILURE;
+
+ if (child->IsHTMLElement(nsGkAtoms::table))
+ *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
+ else if (child->IsHTMLElement(nsGkAtoms::tr))
+ *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, int32_t aOffset)
+{
+ if (!aParentNode) return NS_ERROR_NULL_POINTER;
+
+ RefPtr<nsRange> range = new nsRange(aParentNode);
+
+ // Set range around child at given offset
+ nsresult result = range->SetStart(aParentNode, aOffset);
+ if (NS_FAILED(result)) return result;
+ result = range->SetEnd(aParentNode, aOffset+1);
+ if (NS_FAILED(result)) return result;
+
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ return mDomSelections[index]->AddRange(range);
+}
+
+// End of Table Selection
+
+void
+nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
+{
+ if (mAncestorLimiter != aLimiter) {
+ mAncestorLimiter = aLimiter;
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return;
+
+ if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
+ ClearNormalSelection();
+ if (mAncestorLimiter) {
+ PostReason(nsISelectionListener::NO_REASON);
+ TakeFocus(mAncestorLimiter, 0, 0, CARET_ASSOCIATE_BEFORE, false, false);
+ }
+ }
+ }
+}
+
+//END nsFrameSelection methods
+
+
+//BEGIN nsISelection interface implementations
+
+
+
+nsresult
+nsFrameSelection::DeleteFromDocument()
+{
+ nsresult res;
+
+ // If we're already collapsed, then we do nothing (bug 719503).
+ bool isCollapsed;
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
+ mDomSelections[index]->GetIsCollapsed( &isCollapsed);
+ if (isCollapsed)
+ {
+ return NS_OK;
+ }
+
+ RefPtr<Selection> selection = mDomSelections[index];
+ for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
+ RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
+ res = range->DeleteContents();
+ if (NS_FAILED(res))
+ return res;
+ }
+
+ // Collapse to the new location.
+ // If we deleted one character, then we move back one element.
+ // FIXME We don't know how to do this past frame boundaries yet.
+ if (isCollapsed)
+ mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset()-1);
+ else if (mDomSelections[index]->AnchorOffset() > 0)
+ mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset());
+#ifdef DEBUG
+ else
+ printf("Don't know how to set selection back past frame boundary\n");
+#endif
+
+ return NS_OK;
+}
+
+void
+nsFrameSelection::SetDelayedCaretData(WidgetMouseEvent* aMouseEvent)
+{
+ if (aMouseEvent) {
+ mDelayedMouseEventValid = true;
+ mDelayedMouseEventIsShift = aMouseEvent->IsShift();
+ mDelayedMouseEventClickCount = aMouseEvent->mClickCount;
+ } else {
+ mDelayedMouseEventValid = false;
+ }
+}
+
+void
+nsFrameSelection::DisconnectFromPresShell()
+{
+ RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
+ if (eventHub) {
+ int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+ mDomSelections[index]->RemoveSelectionListener(eventHub);
+ }
+
+ StopAutoScrollTimer();
+ for (size_t i = 0; i < kPresentSelectionTypeCount; i++) {
+ mDomSelections[i]->Clear(nullptr);
+ }
+ mShell = nullptr;
+}
+
+//END nsISelection interface implementations
+
+#if 0
+#pragma mark -
+#endif
+
+// mozilla::dom::Selection implementation
+
+// note: this can return a nil anchor node
+
+Selection::Selection()
+ : mCachedOffsetForFrame(nullptr)
+ , mDirection(eDirNext)
+ , mSelectionType(SelectionType::eNormal)
+ , mUserInitiated(false)
+ , mSelectionChangeBlockerCount(0)
+{
+}
+
+Selection::Selection(nsFrameSelection* aList)
+ : mFrameSelection(aList)
+ , mCachedOffsetForFrame(nullptr)
+ , mDirection(eDirNext)
+ , mSelectionType(SelectionType::eNormal)
+ , mUserInitiated(false)
+ , mSelectionChangeBlockerCount(0)
+{
+}
+
+Selection::~Selection()
+{
+ setAnchorFocusRange(-1);
+
+ uint32_t count = mRanges.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ mRanges[i].mRange->SetSelection(nullptr);
+ }
+
+ if (mAutoScrollTimer) {
+ mAutoScrollTimer->Stop();
+ mAutoScrollTimer = nullptr;
+ }
+
+ mScrollEvent.Revoke();
+
+ if (mCachedOffsetForFrame) {
+ delete mCachedOffsetForFrame;
+ mCachedOffsetForFrame = nullptr;
+ }
+}
+
+nsIDocument*
+Selection::GetParentObject() const
+{
+ nsIPresShell* shell = GetPresShell();
+ if (shell) {
+ return shell->GetDocument();
+ }
+ return nullptr;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
+ // Unlink the selection listeners *before* we do RemoveAllRanges since
+ // we don't want to notify the listeners during JS GC (they could be
+ // in JS!).
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
+ tmp->RemoveAllRanges();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
+ {
+ uint32_t i, count = tmp->mRanges.Length();
+ for (i = 0; i < count; ++i) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange)
+ }
+ }
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection)
+
+// QueryInterface implementation for Selection
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISelection)
+ NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
+
+
+NS_IMETHODIMP
+Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
+{
+ nsINode* anchorNode = GetAnchorNode();
+ if (anchorNode) {
+ return CallQueryInterface(anchorNode, aAnchorNode);
+ }
+
+ *aAnchorNode = nullptr;
+ return NS_OK;
+}
+
+nsINode*
+Selection::GetAnchorNode()
+{
+ if (!mAnchorFocusRange)
+ return nullptr;
+
+ if (GetDirection() == eDirNext) {
+ return mAnchorFocusRange->GetStartParent();
+ }
+
+ return mAnchorFocusRange->GetEndParent();
+}
+
+NS_IMETHODIMP
+Selection::GetAnchorOffset(int32_t* aAnchorOffset)
+{
+ *aAnchorOffset = static_cast<int32_t>(AnchorOffset());
+ return NS_OK;
+}
+
+// note: this can return a nil focus node
+NS_IMETHODIMP
+Selection::GetFocusNode(nsIDOMNode** aFocusNode)
+{
+ nsINode* focusNode = GetFocusNode();
+ if (focusNode) {
+ return CallQueryInterface(focusNode, aFocusNode);
+ }
+
+ *aFocusNode = nullptr;
+ return NS_OK;
+}
+
+nsINode*
+Selection::GetFocusNode()
+{
+ if (!mAnchorFocusRange)
+ return nullptr;
+
+ if (GetDirection() == eDirNext){
+ return mAnchorFocusRange->GetEndParent();
+ }
+
+ return mAnchorFocusRange->GetStartParent();
+}
+
+NS_IMETHODIMP
+Selection::GetFocusOffset(int32_t* aFocusOffset)
+{
+ *aFocusOffset = static_cast<int32_t>(FocusOffset());
+ return NS_OK;
+}
+
+void
+Selection::setAnchorFocusRange(int32_t indx)
+{
+ if (indx >= (int32_t)mRanges.Length())
+ return;
+ if (indx < 0) //release all
+ {
+ mAnchorFocusRange = nullptr;
+ }
+ else{
+ mAnchorFocusRange = mRanges[indx].mRange;
+ }
+}
+
+uint32_t
+Selection::AnchorOffset()
+{
+ if (!mAnchorFocusRange)
+ return 0;
+
+ if (GetDirection() == eDirNext){
+ return mAnchorFocusRange->StartOffset();
+ }
+
+ return mAnchorFocusRange->EndOffset();
+}
+
+uint32_t
+Selection::FocusOffset()
+{
+ if (!mAnchorFocusRange)
+ return 0;
+
+ if (GetDirection() == eDirNext){
+ return mAnchorFocusRange->EndOffset();
+ }
+
+ return mAnchorFocusRange->StartOffset();
+}
+
+static nsresult
+CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
+ nsRange* aRange, int32_t* aCmp)
+{
+ nsINode* start = aRange->GetStartParent();
+ NS_ENSURE_STATE(aCompareNode && start);
+ // If the nodes that we're comparing are not in the same document,
+ // assume that aCompareNode will fall at the end of the ranges.
+ if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
+ !start->GetComposedDoc()) {
+ *aCmp = 1;
+ } else {
+ *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
+ start, aRange->StartOffset());
+ }
+ return NS_OK;
+}
+
+static nsresult
+CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
+ nsRange* aRange, int32_t* aCmp)
+{
+ nsINode* end = aRange->GetEndParent();
+ NS_ENSURE_STATE(aCompareNode && end);
+ // If the nodes that we're comparing are not in the same document,
+ // assume that aCompareNode will fall at the end of the ranges.
+ if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
+ !end->GetComposedDoc()) {
+ *aCmp = 1;
+ } else {
+ *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
+ end, aRange->EndOffset());
+ }
+ return NS_OK;
+}
+
+// Selection::FindInsertionPoint
+//
+// Binary searches the given sorted array of ranges for the insertion point
+// for the given node/offset. The given comparator is used, and the index
+// where the point should appear in the array is placed in *aInsertionPoint.
+//
+// If there is an item in the array equal to the input point, we will return
+// the index of this item.
+
+nsresult
+Selection::FindInsertionPoint(
+ nsTArray<RangeData>* aElementArray,
+ nsINode* aPointNode, int32_t aPointOffset,
+ nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
+ int32_t* aPoint)
+{
+ *aPoint = 0;
+ int32_t beginSearch = 0;
+ int32_t endSearch = aElementArray->Length(); // one beyond what to check
+
+ if (endSearch) {
+ int32_t center = endSearch - 1; // Check last index, then binary search
+ do {
+ nsRange* range = (*aElementArray)[center].mRange;
+
+ int32_t cmp;
+ nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (cmp < 0) { // point < cur
+ endSearch = center;
+ } else if (cmp > 0) { // point > cur
+ beginSearch = center + 1;
+ } else { // found match, done
+ beginSearch = center;
+ break;
+ }
+ center = (endSearch - beginSearch) / 2 + beginSearch;
+ } while (endSearch - beginSearch > 0);
+ }
+
+ *aPoint = beginSearch;
+ return NS_OK;
+}
+
+// Selection::SubtractRange
+//
+// A helper function that subtracts aSubtract from aRange, and adds
+// 1 or 2 RangeData objects representing the remaining non-overlapping
+// difference to aOutput. It is assumed that the caller has checked that
+// aRange and aSubtract do indeed overlap
+
+nsresult
+Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
+ nsTArray<RangeData>* aOutput)
+{
+ nsRange* range = aRange->mRange;
+
+ // First we want to compare to the range start
+ int32_t cmp;
+ nsresult rv = CompareToRangeStart(range->GetStartParent(),
+ range->StartOffset(),
+ aSubtract, &cmp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Also, make a comparison to the range end
+ int32_t cmp2;
+ rv = CompareToRangeEnd(range->GetEndParent(),
+ range->EndOffset(),
+ aSubtract, &cmp2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If the existing range left overlaps the new range (aSubtract) then
+ // cmp < 0, and cmp2 < 0
+ // If it right overlaps the new range then cmp > 0 and cmp2 > 0
+ // If it fully contains the new range, then cmp < 0 and cmp2 > 0
+
+ if (cmp2 > 0) {
+ // We need to add a new RangeData to the output, running from
+ // the end of aSubtract to the end of range
+ RefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndParent());
+
+ rv =
+ postOverlap->SetStart(aSubtract->GetEndParent(), aSubtract->EndOffset());
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv =
+ postOverlap->SetEnd(range->GetEndParent(), range->EndOffset());
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!postOverlap->Collapsed()) {
+ if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
+ }
+ }
+
+ if (cmp < 0) {
+ // We need to add a new RangeData to the output, running from
+ // the start of the range to the start of aSubtract
+ RefPtr<nsRange> preOverlap = new nsRange(range->GetStartParent());
+
+ nsresult rv =
+ preOverlap->SetStart(range->GetStartParent(), range->StartOffset());
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv =
+ preOverlap->SetEnd(aSubtract->GetStartParent(), aSubtract->StartOffset());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!preOverlap->Collapsed()) {
+ if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+Selection::UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange>>& aRangesToAdd)
+{
+ aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
+ if (aRangesToAdd.IsEmpty()) {
+ ErrorResult err;
+ nsINode* node = aItem->GetStartContainer(err);
+ if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
+ // A contenteditable node with user-select:none, for example.
+ // Allow it to have a collapsed selection (for the caret).
+ aItem->Collapse(GetDirection() == eDirPrevious);
+ aRangesToAdd.AppendElement(aItem);
+ }
+ }
+}
+
+nsresult
+Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
+{
+ if (!aItem)
+ return NS_ERROR_NULL_POINTER;
+ if (!aItem->IsPositioned())
+ return NS_ERROR_UNEXPECTED;
+
+ NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
+
+ if (mUserInitiated) {
+ AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
+ *aOutIndex = -1;
+
+ nsIDocument* doc = GetParentObject();
+ bool selectEventsEnabled =
+ nsFrameSelection::sSelectionEventsEnabled ||
+ (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
+
+ if (!aNoStartSelect &&
+ mSelectionType == SelectionType::eNormal &&
+ selectEventsEnabled && Collapsed() &&
+ !IsBlockingSelectionChangeEvents()) {
+ // First, we generate the ranges to add with a scratch range, which is a
+ // clone of the original range passed in. We do this seperately, because the
+ // selectstart event could have caused the world to change, and required
+ // ranges to be re-generated
+ RefPtr<nsRange> scratchRange = aItem->CloneRange();
+ UserSelectRangesToAdd(scratchRange, rangesToAdd);
+ bool newRangesNonEmpty = rangesToAdd.Length() > 1 ||
+ (rangesToAdd.Length() == 1 && !rangesToAdd[0]->Collapsed());
+
+ MOZ_ASSERT(!newRangesNonEmpty || nsContentUtils::IsSafeToRunScript());
+ if (newRangesNonEmpty && nsContentUtils::IsSafeToRunScript()) {
+ // We consider a selection to be starting if we are currently collapsed,
+ // and the selection is becoming uncollapsed, and this is caused by a user
+ // initiated event.
+ bool defaultAction = true;
+
+ // The spec currently doesn't say that we should dispatch this event
+ // on text controls, so for now we only support doing that under a
+ // pref, disabled by default.
+ // See https://github.com/w3c/selection-api/issues/53.
+ bool dispatchEvent = true;
+ nsCOMPtr<nsINode> target = aItem->GetStartParent();
+ if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
+ // Get the first element which isn't in a native anonymous subtree
+ while (target && target->IsInNativeAnonymousSubtree()) {
+ target = target->GetParent();
+ }
+ } else {
+ if (target->IsInNativeAnonymousSubtree()) {
+ // This is a selection under a text control, so don't dispatch the
+ // event.
+ dispatchEvent = false;
+ }
+ }
+
+ if (dispatchEvent) {
+ nsContentUtils::DispatchTrustedEvent(GetParentObject(), target,
+ NS_LITERAL_STRING("selectstart"),
+ true, true, &defaultAction);
+
+ if (!defaultAction) {
+ return NS_OK;
+ }
+
+ // As we just dispatched an event to the DOM, something could have
+ // changed under our feet. Re-generate the rangesToAdd array, and ensure
+ // that the range we are about to add is still valid.
+ if (!aItem->IsPositioned()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ }
+
+ // The scratch ranges we generated may be invalid now, throw them out
+ rangesToAdd.ClearAndRetainStorage();
+ }
+
+ // Generate the ranges to add
+ UserSelectRangesToAdd(aItem, rangesToAdd);
+ size_t newAnchorFocusIndex =
+ GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
+ for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
+ int32_t index;
+ nsresult rv = AddItemInternal(rangesToAdd[i], &index);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (i == newAnchorFocusIndex) {
+ *aOutIndex = index;
+ rangesToAdd[i]->SetIsGenerated(false);
+ } else {
+ rangesToAdd[i]->SetIsGenerated(true);
+ }
+ }
+ return NS_OK;
+ }
+ return AddItemInternal(aItem, aOutIndex);
+}
+
+nsresult
+Selection::AddItemInternal(nsRange* aItem, int32_t* aOutIndex)
+{
+ MOZ_ASSERT(aItem);
+ MOZ_ASSERT(aItem->IsPositioned());
+ MOZ_ASSERT(aOutIndex);
+
+ *aOutIndex = -1;
+
+ // a common case is that we have no ranges yet
+ if (mRanges.Length() == 0) {
+ if (!mRanges.AppendElement(RangeData(aItem)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ aItem->SetSelection(this);
+
+ *aOutIndex = 0;
+ return NS_OK;
+ }
+
+ int32_t startIndex, endIndex;
+ nsresult rv = GetIndicesForInterval(aItem->GetStartParent(),
+ aItem->StartOffset(),
+ aItem->GetEndParent(),
+ aItem->EndOffset(), false,
+ &startIndex, &endIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (endIndex == -1) {
+ // All ranges start after the given range. We can insert our range at
+ // position 0, knowing there are no overlaps (handled below)
+ startIndex = 0;
+ endIndex = 0;
+ } else if (startIndex == -1) {
+ // All ranges end before the given range. We can insert our range at
+ // the end of the array, knowing there are no overlaps (handled below)
+ startIndex = mRanges.Length();
+ endIndex = startIndex;
+ }
+
+ // If the range is already contained in mRanges, silently succeed
+ bool sameRange = EqualsRangeAtPoint(aItem->GetStartParent(),
+ aItem->StartOffset(),
+ aItem->GetEndParent(),
+ aItem->EndOffset(), startIndex);
+ if (sameRange) {
+ *aOutIndex = startIndex;
+ return NS_OK;
+ }
+
+ if (startIndex == endIndex) {
+ // The new range doesn't overlap any existing ranges
+ if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ aItem->SetSelection(this);
+ *aOutIndex = startIndex;
+ return NS_OK;
+ }
+
+ // We now know that at least 1 existing range overlaps with the range that
+ // we are trying to add. In fact, the only ranges of interest are those at
+ // the two end points, startIndex and endIndex - 1 (which may point to the
+ // same range) as these may partially overlap the new range. Any ranges
+ // between these indices are fully overlapped by the new range, and so can be
+ // removed
+ nsTArray<RangeData> overlaps;
+ if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (endIndex - 1 != startIndex) {
+ if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Remove all the overlapping ranges
+ for (int32_t i = startIndex; i < endIndex; ++i) {
+ mRanges[i].mRange->SetSelection(nullptr);
+ }
+ mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
+
+ nsTArray<RangeData> temp;
+ for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
+ nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Insert the new element into our "leftovers" array
+ int32_t insertionPoint;
+ rv = FindInsertionPoint(&temp, aItem->GetStartParent(),
+ aItem->StartOffset(), CompareToRangeStart,
+ &insertionPoint);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Merge the leftovers back in to mRanges
+ if (!mRanges.InsertElementsAt(startIndex, temp))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (uint32_t i = 0; i < temp.Length(); ++i) {
+ temp[i].mRange->SetSelection(this);
+ }
+
+ *aOutIndex = startIndex + insertionPoint;
+ return NS_OK;
+}
+
+nsresult
+Selection::RemoveItem(nsRange* aItem)
+{
+ if (!aItem)
+ return NS_ERROR_NULL_POINTER;
+
+ // Find the range's index & remove it. We could use FindInsertionPoint to
+ // get O(log n) time, but that requires many expensive DOM comparisons.
+ // For even several thousand items, this is probably faster because the
+ // comparisons are so fast.
+ int32_t idx = -1;
+ uint32_t i;
+ for (i = 0; i < mRanges.Length(); i ++) {
+ if (mRanges[i].mRange == aItem) {
+ idx = (int32_t)i;
+ break;
+ }
+ }
+ if (idx < 0)
+ return NS_ERROR_INVALID_ARG;
+
+ mRanges.RemoveElementAt(idx);
+ aItem->SetSelection(nullptr);
+ return NS_OK;
+}
+
+nsresult
+Selection::RemoveCollapsedRanges()
+{
+ uint32_t i = 0;
+ while (i < mRanges.Length()) {
+ if (mRanges[i].mRange->Collapsed()) {
+ nsresult rv = RemoveItem(mRanges[i].mRange);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ ++i;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+Selection::Clear(nsPresContext* aPresContext)
+{
+ setAnchorFocusRange(-1);
+
+ for (uint32_t i = 0; i < mRanges.Length(); ++i) {
+ mRanges[i].mRange->SetSelection(nullptr);
+ selectFrames(aPresContext, mRanges[i].mRange, false);
+ }
+ mRanges.Clear();
+
+ // Reset direction so for more dependable table selection range handling
+ SetDirection(eDirNext);
+
+ // If this was an ATTENTION selection, change it back to normal now
+ if (mFrameSelection &&
+ mFrameSelection->GetDisplaySelection() ==
+ nsISelectionController::SELECTION_ATTENTION) {
+ mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::GetType(int16_t* aType)
+{
+ NS_ENSURE_ARG_POINTER(aType);
+ *aType = ToRawSelectionType(Type());
+ return NS_OK;
+}
+
+// RangeMatches*Point
+//
+// Compares the range beginning or ending point, and returns true if it
+// exactly matches the given DOM point.
+
+static inline bool
+RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
+{
+ return aRange->GetStartParent() == aNode && aRange->StartOffset() == aOffset;
+}
+
+static inline bool
+RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
+{
+ return aRange->GetEndParent() == aNode && aRange->EndOffset() == aOffset;
+}
+
+// Selection::EqualsRangeAtPoint
+//
+// Utility method for checking equivalence of two ranges.
+
+bool
+Selection::EqualsRangeAtPoint(
+ nsINode* aBeginNode, int32_t aBeginOffset,
+ nsINode* aEndNode, int32_t aEndOffset,
+ int32_t aRangeIndex)
+{
+ if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) {
+ nsRange* range = mRanges[aRangeIndex].mRange;
+ if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
+ RangeMatchesEndPoint(range, aEndNode, aEndOffset))
+ return true;
+ }
+ return false;
+}
+
+// Selection::GetRangesForInterval
+//
+// XPCOM wrapper for the nsTArray version
+
+NS_IMETHODIMP
+Selection::GetRangesForInterval(nsIDOMNode* aBeginNode, int32_t aBeginOffset,
+ nsIDOMNode* aEndNode, int32_t aEndOffset,
+ bool aAllowAdjacent,
+ uint32_t* aResultCount,
+ nsIDOMRange*** aResults)
+{
+ if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
+ return NS_ERROR_NULL_POINTER;
+
+ *aResultCount = 0;
+ *aResults = nullptr;
+
+ nsTArray<RefPtr<nsRange>> results;
+ ErrorResult result;
+ nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
+ nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
+ NS_ENSURE_TRUE(beginNode && endNode, NS_ERROR_NULL_POINTER);
+ GetRangesForInterval(*beginNode, aBeginOffset, *endNode, aEndOffset,
+ aAllowAdjacent, results, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ *aResultCount = results.Length();
+ if (*aResultCount == 0) {
+ return NS_OK;
+ }
+
+ *aResults = static_cast<nsIDOMRange**>
+ (moz_xmalloc(sizeof(nsIDOMRange*) * *aResultCount));
+ NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
+
+ for (uint32_t i = 0; i < *aResultCount; i++) {
+ (*aResults)[i] = results[i].forget().take();
+ }
+ return NS_OK;
+}
+
+
+void
+Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
+ nsINode& aEndNode, int32_t aEndOffset,
+ bool aAllowAdjacent,
+ nsTArray<RefPtr<nsRange>>& aReturn,
+ mozilla::ErrorResult& aRv)
+{
+ nsTArray<nsRange*> results;
+ nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset,
+ &aEndNode, aEndOffset,
+ aAllowAdjacent, &results);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+
+ aReturn.SetLength(results.Length());
+ for (uint32_t i = 0; i < results.Length(); ++i) {
+ aReturn[i] = results[i]; // AddRefs
+ }
+}
+
+// Selection::GetRangesForIntervalArray
+//
+// Fills a nsTArray with the ranges overlapping the range specified by
+// the given endpoints. Ranges in the selection exactly adjacent to the
+// input range are not returned unless aAllowAdjacent is set.
+//
+// For example, if the following ranges were in the selection
+// (assume everything is within the same node)
+//
+// Start Offset: 0 2 7 9
+// End Offset: 2 5 9 10
+//
+// and passed aBeginOffset of 2 and aEndOffset of 9, then with
+// aAllowAdjacent set, all the ranges should be returned. If
+// aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
+// should be returned
+//
+// Now that overlapping ranges are disallowed, there can be a maximum of
+// 2 adjacent ranges
+
+nsresult
+Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
+ nsINode* aEndNode, int32_t aEndOffset,
+ bool aAllowAdjacent,
+ nsTArray<nsRange*>* aRanges)
+{
+ aRanges->Clear();
+ int32_t startIndex, endIndex;
+ nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset,
+ aEndNode, aEndOffset, aAllowAdjacent,
+ &startIndex, &endIndex);
+ NS_ENSURE_SUCCESS(res, res);
+
+ if (startIndex == -1 || endIndex == -1)
+ return NS_OK;
+
+ for (int32_t i = startIndex; i < endIndex; i++) {
+ if (!aRanges->AppendElement(mRanges[i].mRange))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+// Selection::GetIndicesForInterval
+//
+// Works on the same principle as GetRangesForIntervalArray above, however
+// instead this returns the indices into mRanges between which the
+// overlapping ranges lie.
+
+nsresult
+Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
+ nsINode* aEndNode, int32_t aEndOffset,
+ bool aAllowAdjacent,
+ int32_t* aStartIndex, int32_t* aEndIndex)
+{
+ int32_t startIndex;
+ int32_t endIndex;
+
+ if (!aStartIndex)
+ aStartIndex = &startIndex;
+ if (!aEndIndex)
+ aEndIndex = &endIndex;
+
+ *aStartIndex = -1;
+ *aEndIndex = -1;
+
+ if (mRanges.Length() == 0)
+ return NS_OK;
+
+ bool intervalIsCollapsed = aBeginNode == aEndNode &&
+ aBeginOffset == aEndOffset;
+
+ // Ranges that end before the given interval and begin after the given
+ // interval can be discarded
+ int32_t endsBeforeIndex;
+ if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
+ &CompareToRangeStart,
+ &endsBeforeIndex))) {
+ return NS_OK;
+ }
+
+ if (endsBeforeIndex == 0) {
+ nsRange* endRange = mRanges[endsBeforeIndex].mRange;
+
+ // If the interval is strictly before the range at index 0, we can optimize
+ // by returning now - all ranges start after the given interval
+ if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
+ return NS_OK;
+
+ // We now know that the start point of mRanges[0].mRange equals the end of
+ // the interval. Thus, when aAllowadjacent is true, the caller is always
+ // interested in this range. However, when excluding adjacencies, we must
+ // remember to include the range when both it and the given interval are
+ // collapsed to the same point
+ if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
+ return NS_OK;
+ }
+ *aEndIndex = endsBeforeIndex;
+
+ int32_t beginsAfterIndex;
+ if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
+ &CompareToRangeEnd,
+ &beginsAfterIndex))) {
+ return NS_OK;
+ }
+ if (beginsAfterIndex == (int32_t) mRanges.Length())
+ return NS_OK; // optimization: all ranges are strictly before us
+
+ if (aAllowAdjacent) {
+ // At this point, one of the following holds:
+ // endsBeforeIndex == mRanges.Length(),
+ // endsBeforeIndex points to a range whose start point does not equal the
+ // given interval's start point
+ // endsBeforeIndex points to a range whose start point equals the given
+ // interval's start point
+ // In the final case, there can be two such ranges, a collapsed range, and
+ // an adjacent range (they will appear in mRanges in that order). For this
+ // final case, we need to increment endsBeforeIndex, until one of the
+ // first two possibilites hold
+ while (endsBeforeIndex < (int32_t) mRanges.Length()) {
+ nsRange* endRange = mRanges[endsBeforeIndex].mRange;
+ if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
+ break;
+ endsBeforeIndex++;
+ }
+
+ // Likewise, one of the following holds:
+ // beginsAfterIndex == 0,
+ // beginsAfterIndex points to a range whose end point does not equal
+ // the given interval's end point
+ // beginsOnOrAfter points to a range whose end point equals the given
+ // interval's end point
+ // In the final case, there can be two such ranges, an adjacent range, and
+ // a collapsed range (they will appear in mRanges in that order). For this
+ // final case, we only need to take action if both those ranges exist, and
+ // we are pointing to the collapsed range - we need to point to the
+ // adjacent range
+ nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
+ if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
+ RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
+ beginRange = mRanges[beginsAfterIndex - 1].mRange;
+ if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
+ beginsAfterIndex--;
+ }
+ } else {
+ // See above for the possibilities at this point. The only case where we
+ // need to take action is when the range at beginsAfterIndex ends on
+ // the given interval's start point, but that range isn't collapsed (a
+ // collapsed range should be included in the returned results).
+ nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
+ if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
+ !beginRange->Collapsed())
+ beginsAfterIndex++;
+
+ // Again, see above for the meaning of endsBeforeIndex at this point.
+ // In particular, endsBeforeIndex may point to a collaped range which
+ // represents the point at the end of the interval - this range should be
+ // included
+ if (endsBeforeIndex < (int32_t) mRanges.Length()) {
+ nsRange* endRange = mRanges[endsBeforeIndex].mRange;
+ if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
+ endRange->Collapsed())
+ endsBeforeIndex++;
+ }
+ }
+
+ NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex,
+ "Is mRanges not ordered?");
+ NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
+
+ *aStartIndex = beginsAfterIndex;
+ *aEndIndex = endsBeforeIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame)
+{
+ if (!aReturnFrame)
+ return NS_ERROR_NULL_POINTER;
+
+ int32_t frameOffset = 0;
+ *aReturnFrame = 0;
+ nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
+ if (content && mFrameSelection)
+ {
+ *aReturnFrame = mFrameSelection->
+ GetFrameForNodeOffset(content, AnchorOffset(),
+ mFrameSelection->GetHint(), &frameOffset);
+ if (*aReturnFrame)
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
+ int32_t* aOffsetUsed,
+ bool aVisual)
+{
+ if (!aReturnFrame)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
+ if (!content || !mFrameSelection)
+ return NS_ERROR_FAILURE;
+
+ int32_t frameOffset = 0;
+ *aReturnFrame = 0;
+ if (!aOffsetUsed)
+ aOffsetUsed = &frameOffset;
+
+ CaretAssociationHint hint = mFrameSelection->GetHint();
+
+ if (aVisual) {
+ nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
+
+ return nsCaret::GetCaretFrameForNodeOffset(mFrameSelection,
+ content, FocusOffset(), hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
+ }
+
+ *aReturnFrame = mFrameSelection->
+ GetFrameForNodeOffset(content, FocusOffset(),
+ hint, aOffsetUsed);
+ if (!*aReturnFrame)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+//select all content children of aContent
+nsresult
+Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter,
+ nsIContent* aContent,
+ bool aSelected)
+{
+ nsresult result = aInnerIter->Init(aContent);
+ nsIFrame *frame;
+ if (NS_SUCCEEDED(result))
+ {
+ // First select frame of content passed in
+ frame = aContent->GetPrimaryFrame();
+ if (frame && frame->GetType() == nsGkAtoms::textFrame) {
+ nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+ textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(),
+ aSelected, mSelectionType);
+ }
+ // Now iterated through the child frames and set them
+ while (!aInnerIter->IsDone()) {
+ nsCOMPtr<nsIContent> innercontent =
+ do_QueryInterface(aInnerIter->GetCurrentNode());
+
+ frame = innercontent->GetPrimaryFrame();
+ if (frame) {
+ if (frame->GetType() == nsGkAtoms::textFrame) {
+ nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+ textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(),
+ aSelected, mSelectionType);
+ } else {
+ frame->InvalidateFrameSubtree(); // frame continuations?
+ }
+ }
+
+ aInnerIter->Next();
+ }
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+/**
+ * The idea of this helper method is to select or deselect "top to bottom",
+ * traversing through the frames
+ */
+nsresult
+Selection::selectFrames(nsPresContext* aPresContext, nsRange* aRange,
+ bool aSelect)
+{
+ if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
+ // nothing to do
+ return NS_OK;
+ }
+ MOZ_ASSERT(aRange);
+
+ if (mFrameSelection->GetTableCellSelection()) {
+ nsINode* node = aRange->GetCommonAncestor();
+ nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
+ : aPresContext->FrameManager()->GetRootFrame();
+ if (frame) {
+ frame->InvalidateFrameSubtree();
+ }
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
+ iter->Init(aRange);
+
+ // Loop through the content iterator for each content node; for each text
+ // node, call SetSelected on it:
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
+ if (!content) {
+ // Don't warn, bug 1055722
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // We must call first one explicitly
+ if (content->IsNodeOfType(nsINode::eTEXT)) {
+ nsIFrame* frame = content->GetPrimaryFrame();
+ // The frame could be an SVG text frame, in which case we'll ignore it.
+ if (frame && frame->GetType() == nsGkAtoms::textFrame) {
+ nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+ uint32_t startOffset = aRange->StartOffset();
+ uint32_t endOffset;
+ if (aRange->GetEndParent() == content) {
+ endOffset = aRange->EndOffset();
+ } else {
+ endOffset = content->Length();
+ }
+ textFrame->SetSelectedRange(startOffset, endOffset, aSelect,
+ mSelectionType);
+ }
+ }
+
+ iter->First();
+ nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
+ for (iter->First(); !iter->IsDone(); iter->Next()) {
+ content = do_QueryInterface(iter->GetCurrentNode());
+ SelectAllFramesForContent(inneriter, content, aSelect);
+ }
+
+ // We must now do the last one if it is not the same as the first
+ if (aRange->GetEndParent() != aRange->GetStartParent()) {
+ nsresult res;
+ content = do_QueryInterface(aRange->GetEndParent(), &res);
+ NS_ENSURE_SUCCESS(res, res);
+ NS_ENSURE_TRUE(content, res);
+
+ if (content->IsNodeOfType(nsINode::eTEXT)) {
+ nsIFrame* frame = content->GetPrimaryFrame();
+ // The frame could be an SVG text frame, in which case we'll ignore it.
+ if (frame && frame->GetType() == nsGkAtoms::textFrame) {
+ nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+ textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect,
+ mSelectionType);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+
+// Selection::LookUpSelection
+//
+// This function is called when a node wants to know where the selection is
+// over itself.
+//
+// Usually, this is called when we already know there is a selection over
+// the node in question, and we only need to find the boundaries of it on
+// that node. This is when slowCheck is false--a strict test is not needed.
+// Other times, the caller has no idea, and wants us to test everything,
+// so we are supposed to determine whether there is a selection over the
+// node at all.
+//
+// A previous version of this code used this flag to do less work when
+// inclusion was already known (slowCheck=false). However, our tree
+// structure allows us to quickly determine ranges overlapping the node,
+// so we just ignore the slowCheck flag and do the full test every time.
+//
+// PERFORMANCE: a common case is that we are doing a fast check with exactly
+// one range in the selection. In this case, this function is slower than
+// brute force because of the overhead of checking the tree. We can optimize
+// this case to make it faster by doing the same thing the previous version
+// of this function did in the case of 1 range. This would also mean that
+// the aSlowCheck flag would have meaning again.
+
+NS_IMETHODIMP
+Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset,
+ int32_t aContentLength,
+ SelectionDetails** aReturnDetails,
+ SelectionType aSelectionType,
+ bool aSlowCheck)
+{
+ nsresult rv;
+ if (!aContent || ! aReturnDetails)
+ return NS_ERROR_NULL_POINTER;
+
+ // it is common to have no ranges, to optimize that
+ if (mRanges.Length() == 0)
+ return NS_OK;
+
+ nsTArray<nsRange*> overlappingRanges;
+ rv = GetRangesForIntervalArray(aContent, aContentOffset,
+ aContent, aContentOffset + aContentLength,
+ false,
+ &overlappingRanges);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (overlappingRanges.Length() == 0)
+ return NS_OK;
+
+ for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
+ nsRange* range = overlappingRanges[i];
+ nsINode* startNode = range->GetStartParent();
+ nsINode* endNode = range->GetEndParent();
+ int32_t startOffset = range->StartOffset();
+ int32_t endOffset = range->EndOffset();
+
+ int32_t start = -1, end = -1;
+ if (startNode == aContent && endNode == aContent) {
+ if (startOffset < (aContentOffset + aContentLength) &&
+ endOffset > aContentOffset) {
+ // this range is totally inside the requested content range
+ start = std::max(0, startOffset - aContentOffset);
+ end = std::min(aContentLength, endOffset - aContentOffset);
+ }
+ // otherwise, range is inside the requested node, but does not intersect
+ // the requested content range, so ignore it
+ } else if (startNode == aContent) {
+ if (startOffset < (aContentOffset + aContentLength)) {
+ // the beginning of the range is inside the requested node, but the
+ // end is outside, select everything from there to the end
+ start = std::max(0, startOffset - aContentOffset);
+ end = aContentLength;
+ }
+ } else if (endNode == aContent) {
+ if (endOffset > aContentOffset) {
+ // the end of the range is inside the requested node, but the beginning
+ // is outside, select everything from the beginning to there
+ start = 0;
+ end = std::min(aContentLength, endOffset - aContentOffset);
+ }
+ } else {
+ // this range does not begin or end in the requested node, but since
+ // GetRangesForInterval returned this range, we know it overlaps.
+ // Therefore, this node is enclosed in the range, and we select all
+ // of it.
+ start = 0;
+ end = aContentLength;
+ }
+ if (start < 0)
+ continue; // the ranges do not overlap the input range
+
+ SelectionDetails* details = new SelectionDetails;
+
+ details->mNext = *aReturnDetails;
+ details->mStart = start;
+ details->mEnd = end;
+ details->mSelectionType = aSelectionType;
+ RangeData *rd = FindRangeData(range);
+ if (rd) {
+ details->mTextRangeStyle = rd->mTextRangeStyle;
+ }
+ *aReturnDetails = details;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::Repaint(nsPresContext* aPresContext)
+{
+ int32_t arrCount = (int32_t)mRanges.Length();
+
+ if (arrCount < 1)
+ return NS_OK;
+
+ int32_t i;
+
+ for (i = 0; i < arrCount; i++)
+ {
+ nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::GetCanCacheFrameOffset(bool* aCanCacheFrameOffset)
+{
+ NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
+
+ if (mCachedOffsetForFrame)
+ *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
+ else
+ *aCanCacheFrameOffset = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
+{
+ if (!mCachedOffsetForFrame) {
+ mCachedOffsetForFrame = new CachedOffsetForFrame;
+ }
+
+ mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
+
+ // clean up cached frame when turn off cache
+ // fix bug 207936
+ if (!aCanCacheFrameOffset) {
+ mCachedOffsetForFrame->mLastCaretFrame = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
+ nsPoint& aPoint)
+{
+ if (!mCachedOffsetForFrame) {
+ mCachedOffsetForFrame = new CachedOffsetForFrame;
+ }
+
+ nsresult rv = NS_OK;
+ if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
+ mCachedOffsetForFrame->mLastCaretFrame &&
+ (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
+ (inOffset == mCachedOffsetForFrame->mLastContentOffset))
+ {
+ // get cached frame offset
+ aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
+ }
+ else
+ {
+ // Recalculate frame offset and cache it. Don't cache a frame offset if
+ // GetPointFromOffset fails, though.
+ rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
+ if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
+ mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
+ mCachedOffsetForFrame->mLastCaretFrame = aFrame;
+ mCachedOffsetForFrame->mLastContentOffset = inOffset;
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+Selection::GetAncestorLimiter(nsIContent** aContent)
+{
+ if (mFrameSelection) {
+ nsCOMPtr<nsIContent> c = mFrameSelection->GetAncestorLimiter();
+ c.forget(aContent);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::SetAncestorLimiter(nsIContent* aContent)
+{
+ if (mFrameSelection) {
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ frameSelection->SetAncestorLimiter(aContent);
+ }
+ return NS_OK;
+}
+
+RangeData*
+Selection::FindRangeData(nsIDOMRange* aRange)
+{
+ NS_ENSURE_TRUE(aRange, nullptr);
+ for (uint32_t i = 0; i < mRanges.Length(); i++) {
+ if (mRanges[i].mRange == aRange)
+ return &mRanges[i];
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+Selection::SetTextRangeStyle(nsIDOMRange* aRange,
+ const TextRangeStyle& aTextRangeStyle)
+{
+ NS_ENSURE_ARG_POINTER(aRange);
+ RangeData *rd = FindRangeData(aRange);
+ if (rd) {
+ rd->mTextRangeStyle = aTextRangeStyle;
+ }
+ return NS_OK;
+}
+
+nsresult
+Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint,
+ uint32_t aDelay)
+{
+ NS_PRECONDITION(aFrame, "Need a frame");
+
+ nsresult result;
+ if (!mFrameSelection)
+ return NS_OK;//nothing to do
+
+ if (!mAutoScrollTimer)
+ {
+ mAutoScrollTimer = new nsAutoScrollTimer();
+
+ result = mAutoScrollTimer->Init(mFrameSelection, this);
+
+ if (NS_FAILED(result))
+ return result;
+ }
+
+ result = mAutoScrollTimer->SetDelay(aDelay);
+
+ if (NS_FAILED(result))
+ return result;
+
+ return DoAutoScroll(aFrame, aPoint);
+}
+
+nsresult
+Selection::StopAutoScrollTimer()
+{
+ if (mAutoScrollTimer) {
+ return mAutoScrollTimer->Stop();
+ }
+ return NS_OK;
+}
+
+nsresult
+Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint)
+{
+ NS_PRECONDITION(aFrame, "Need a frame");
+
+ if (mAutoScrollTimer)
+ (void)mAutoScrollTimer->Stop();
+
+ nsPresContext* presContext = aFrame->PresContext();
+ nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
+ nsRootPresContext* rootPC = presContext->GetRootPresContext();
+ if (!rootPC)
+ return NS_OK;
+ nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
+ nsWeakFrame weakRootFrame(rootmostFrame);
+ nsWeakFrame weakFrame(aFrame);
+ // Get the point relative to the root most frame because the scroll we are
+ // about to do will change the coordinates of aFrame.
+ nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
+
+ bool done = false;
+ bool didScroll;
+ while (true) {
+ didScroll = shell->ScrollFrameRectIntoView(
+ aFrame, nsRect(aPoint, nsSize(0, 0)),
+ nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
+ 0);
+ if (!weakFrame || !weakRootFrame) {
+ return NS_OK;
+ }
+ if (!didScroll && !done) {
+ // If aPoint is at the screen edge then try to scroll anyway, once.
+ RefPtr<nsDeviceContext> dx = shell->GetViewManager()->GetDeviceContext();
+ nsRect screen;
+ dx->GetRect(screen);
+ nsPoint screenPoint = globalPoint +
+ rootmostFrame->GetScreenRectInAppUnits().TopLeft();
+ nscoord onePx = nsPresContext::AppUnitsPerCSSPixel();
+ nscoord scrollAmount = 10 * onePx;
+ if (std::abs(screen.x - screenPoint.x) <= onePx) {
+ aPoint.x -= scrollAmount;
+ } else if (std::abs(screen.XMost() - screenPoint.x) <= onePx) {
+ aPoint.x += scrollAmount;
+ } else if (std::abs(screen.y - screenPoint.y) <= onePx) {
+ aPoint.y -= scrollAmount;
+ } else if (std::abs(screen.YMost() - screenPoint.y) <= onePx) {
+ aPoint.y += scrollAmount;
+ } else {
+ break;
+ }
+ done = true;
+ continue;
+ }
+ break;
+ }
+
+ // Start the AutoScroll timer if necessary.
+ if (didScroll && mAutoScrollTimer) {
+ nsPoint presContextPoint = globalPoint -
+ shell->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
+ mAutoScrollTimer->Start(presContext, presContextPoint);
+ }
+
+ return NS_OK;
+}
+
+
+/** RemoveAllRanges zeroes the selection
+ */
+NS_IMETHODIMP
+Selection::RemoveAllRanges()
+{
+ ErrorResult result;
+ RemoveAllRanges(result);
+ return result.StealNSResult();
+}
+
+void
+Selection::RemoveAllRanges(ErrorResult& aRv)
+{
+ if (!mFrameSelection)
+ return; // nothing to do
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ nsresult result = Clear(presContext);
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ return;
+ }
+
+ // Turn off signal for table selection
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ frameSelection->ClearTableCellSelection();
+
+ result = frameSelection->NotifySelectionListeners(GetType());
+ // Also need to notify the frames!
+ // PresShell::CharacterDataChanged should do that on DocumentChanged
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ }
+}
+
+/** AddRange adds the specified range to the selection
+ * @param aRange is the range to be added
+ */
+NS_IMETHODIMP
+Selection::AddRange(nsIDOMRange* aDOMRange)
+{
+ if (!aDOMRange) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ nsRange* range = static_cast<nsRange*>(aDOMRange);
+ ErrorResult result;
+ AddRange(*range, result);
+ return result.StealNSResult();
+}
+
+void
+Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
+{
+ return AddRangeInternal(aRange, GetParentObject(), aRv);
+}
+
+void
+Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
+ ErrorResult& aRv)
+{
+ nsINode* rangeRoot = aRange.GetRoot();
+ if (aDocument != rangeRoot && (!rangeRoot ||
+ aDocument != rangeRoot->GetComposedDoc())) {
+ // http://w3c.github.io/selection-api/#dom-selection-addrange
+ // "... if the root of the range's boundary points are the document
+ // associated with context object. Otherwise, this method must do nothing."
+ return;
+ }
+
+ // This inserts a table cell range in proper document order
+ // and returns NS_OK if range doesn't contain just one table cell
+ bool didAddRange;
+ int32_t rangeIndex;
+ nsresult result = addTableCellRange(&aRange, &didAddRange, &rangeIndex);
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ return;
+ }
+
+ if (!didAddRange) {
+ result = AddItem(&aRange, &rangeIndex);
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ return;
+ }
+ }
+
+ if (rangeIndex < 0) {
+ return;
+ }
+
+ setAnchorFocusRange(rangeIndex);
+
+ // Make sure the caret appears on the next line, if at a newline
+ if (mSelectionType == SelectionType::eNormal) {
+ SetInterlinePosition(true);
+ }
+
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ selectFrames(presContext, &aRange, true);
+
+ if (!mFrameSelection)
+ return;//nothing to do
+
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ result = frameSelection->NotifySelectionListeners(GetType());
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ }
+}
+
+// Selection::RemoveRange
+//
+// Removes the given range from the selection. The tricky part is updating
+// the flags on the frames that indicate whether they have a selection or
+// not. There could be several selection ranges on the frame, and clearing
+// the bit would cause the selection to not be drawn, even when there is
+// another range on the frame (bug 346185).
+//
+// We therefore find any ranges that intersect the same nodes as the range
+// being removed, and cause them to set the selected bits back on their
+// selected frames after we've cleared the bit from ours.
+
+nsresult
+Selection::RemoveRange(nsIDOMRange* aDOMRange)
+{
+ if (!aDOMRange) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsRange* range = static_cast<nsRange*>(aDOMRange);
+ ErrorResult result;
+ RemoveRange(*range, result);
+ return result.StealNSResult();
+}
+
+void
+Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
+{
+ nsresult rv = RemoveItem(&aRange);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+
+ nsINode* beginNode = aRange.GetStartParent();
+ nsINode* endNode = aRange.GetEndParent();
+
+ if (!beginNode || !endNode) {
+ // Detached range; nothing else to do here.
+ return;
+ }
+
+ // find out the length of the end node, so we can select all of it
+ int32_t beginOffset, endOffset;
+ if (endNode->IsNodeOfType(nsINode::eTEXT)) {
+ // Get the length of the text. We can't just use the offset because
+ // another range could be touching this text node but not intersect our
+ // range.
+ beginOffset = 0;
+ endOffset = static_cast<nsIContent*>(endNode)->TextLength();
+ } else {
+ // For non-text nodes, the given offsets should be sufficient.
+ beginOffset = aRange.StartOffset();
+ endOffset = aRange.EndOffset();
+ }
+
+ // clear the selected bit from the removed range's frames
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ selectFrames(presContext, &aRange, false);
+
+ // add back the selected bit for each range touching our nodes
+ nsTArray<nsRange*> affectedRanges;
+ rv = GetRangesForIntervalArray(beginNode, beginOffset,
+ endNode, endOffset,
+ true, &affectedRanges);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+ for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
+ selectFrames(presContext, affectedRanges[i], true);
+ }
+
+ int32_t cnt = mRanges.Length();
+ if (&aRange == mAnchorFocusRange) {
+ // Reset anchor to LAST range or clear it if there are no ranges.
+ setAnchorFocusRange(cnt - 1);
+
+ // When the selection is user-created it makes sense to scroll the range
+ // into view. The spell-check selection, however, is created and destroyed
+ // in the background. We don't want to scroll in this case or the view
+ // might appear to be moving randomly (bug 337871).
+ if (mSelectionType != SelectionType::eSpellCheck && cnt > 0) {
+ ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
+ }
+ }
+
+ if (!mFrameSelection)
+ return;//nothing to do
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ rv = frameSelection->NotifySelectionListeners(GetType());
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+}
+
+
+
+/*
+ * Collapse sets the whole selection to be one point.
+ */
+NS_IMETHODIMP
+Selection::Collapse(nsIDOMNode* aParentNode, int32_t aOffset)
+{
+ nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
+ return Collapse(parentNode, aOffset);
+}
+
+NS_IMETHODIMP
+Selection::CollapseNative(nsINode* aParentNode, int32_t aOffset)
+{
+ return Collapse(aParentNode, aOffset);
+}
+
+nsresult
+Selection::Collapse(nsINode* aParentNode, int32_t aOffset)
+{
+ if (!aParentNode)
+ return NS_ERROR_INVALID_ARG;
+
+ ErrorResult result;
+ Collapse(*aParentNode, static_cast<uint32_t>(aOffset), result);
+ return result.StealNSResult();
+}
+
+void
+Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
+{
+ if (!mFrameSelection) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
+ return;
+ }
+
+ nsCOMPtr<nsINode> parentNode = &aParentNode;
+
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ frameSelection->InvalidateDesiredPos();
+ if (!IsValidSelectionPoint(frameSelection, parentNode)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ nsresult result;
+
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ if (!presContext || presContext->Document() != parentNode->OwnerDoc()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ // Delete all of the current ranges
+ Clear(presContext);
+
+ // Turn off signal for table selection
+ frameSelection->ClearTableCellSelection();
+
+ // Hack to display the caret on the right line (bug 1237236).
+ if (frameSelection->GetHint() != CARET_ASSOCIATE_AFTER &&
+ parentNode->IsContent()) {
+ int32_t frameOffset;
+ nsTextFrame* f =
+ do_QueryFrame(nsCaret::GetFrameAndOffset(this, parentNode,
+ aOffset, &frameOffset));
+ if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) {
+ if ((parentNode->AsContent() == f->GetContent() &&
+ f->GetContentEnd() == int32_t(aOffset)) ||
+ (parentNode == f->GetContent()->GetParentNode() &&
+ parentNode->IndexOf(f->GetContent()) + 1 == int32_t(aOffset))) {
+ frameSelection->SetHint(CARET_ASSOCIATE_AFTER);
+ }
+ }
+ }
+
+ RefPtr<nsRange> range = new nsRange(parentNode);
+ result = range->SetEnd(parentNode, aOffset);
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ return;
+ }
+ result = range->SetStart(parentNode, aOffset);
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ return;
+ }
+
+#ifdef DEBUG_SELECTION
+ nsCOMPtr<nsIContent> content = do_QueryInterface(parentNode);
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(parentNode);
+ printf ("Sel. Collapse to %p %s %d\n", parentNode.get(),
+ content ? nsAtomCString(content->NodeInfo()->NameAtom()).get()
+ : (doc ? "DOCUMENT" : "???"),
+ aOffset);
+#endif
+
+ int32_t rangeIndex = -1;
+ result = AddItem(range, &rangeIndex);
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ return;
+ }
+ setAnchorFocusRange(0);
+ selectFrames(presContext, range, true);
+ result = frameSelection->NotifySelectionListeners(GetType());
+ if (NS_FAILED(result)) {
+ aRv.Throw(result);
+ }
+}
+
+/*
+ * Sets the whole selection to be one point
+ * at the start of the current selection
+ */
+NS_IMETHODIMP
+Selection::CollapseToStart()
+{
+ ErrorResult result;
+ CollapseToStart(result);
+ return result.StealNSResult();
+}
+
+void
+Selection::CollapseToStart(ErrorResult& aRv)
+{
+ int32_t cnt;
+ nsresult rv = GetRangeCount(&cnt);
+ if (NS_FAILED(rv) || cnt <= 0) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ // Get the first range
+ nsRange* firstRange = mRanges[0].mRange;
+ if (!firstRange) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ if (mFrameSelection) {
+ int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
+ mFrameSelection->PostReason(reason);
+ }
+ nsINode* parent = firstRange->GetStartParent();
+ if (!parent) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ Collapse(*parent, firstRange->StartOffset(), aRv);
+}
+
+/*
+ * Sets the whole selection to be one point
+ * at the end of the current selection
+ */
+NS_IMETHODIMP
+Selection::CollapseToEnd()
+{
+ ErrorResult result;
+ CollapseToEnd(result);
+ return result.StealNSResult();
+}
+
+void
+Selection::CollapseToEnd(ErrorResult& aRv)
+{
+ int32_t cnt;
+ nsresult rv = GetRangeCount(&cnt);
+ if (NS_FAILED(rv) || cnt <= 0) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ // Get the last range
+ nsRange* lastRange = mRanges[cnt - 1].mRange;
+ if (!lastRange) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ if (mFrameSelection) {
+ int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
+ mFrameSelection->PostReason(reason);
+ }
+ nsINode* parent = lastRange->GetEndParent();
+ if (!parent) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ Collapse(*parent, lastRange->EndOffset(), aRv);
+}
+
+/*
+ * IsCollapsed -- is the whole selection just one point, or unset?
+ */
+bool
+Selection::IsCollapsed() const
+{
+ uint32_t cnt = mRanges.Length();
+ if (cnt == 0) {
+ return true;
+ }
+
+ if (cnt != 1) {
+ return false;
+ }
+
+ return mRanges[0].mRange->Collapsed();
+}
+
+/* virtual */
+bool
+Selection::Collapsed()
+{
+ return IsCollapsed();
+}
+
+NS_IMETHODIMP
+Selection::GetIsCollapsed(bool* aIsCollapsed)
+{
+ NS_ENSURE_TRUE(aIsCollapsed, NS_ERROR_NULL_POINTER);
+
+ *aIsCollapsed = IsCollapsed();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::GetRangeCount(int32_t* aRangeCount)
+{
+ *aRangeCount = (int32_t)RangeCount();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
+{
+ ErrorResult result;
+ *aReturn = GetRangeAt(aIndex, result);
+ NS_IF_ADDREF(*aReturn);
+ return result.StealNSResult();
+}
+
+nsRange*
+Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv)
+{
+ nsRange* range = GetRangeAt(aIndex);
+ if (!range) {
+ aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+ return nullptr;
+ }
+
+ return range;
+}
+
+nsRange*
+Selection::GetRangeAt(int32_t aIndex) const
+{
+ RangeData empty(nullptr);
+ return mRanges.SafeElementAt(aIndex, empty).mRange;
+}
+
+/*
+utility function
+*/
+nsresult
+Selection::SetAnchorFocusToRange(nsRange* aRange)
+{
+ NS_ENSURE_STATE(mAnchorFocusRange);
+
+ bool collapsed = Collapsed();
+
+ nsresult res = RemoveItem(mAnchorFocusRange);
+ if (NS_FAILED(res))
+ return res;
+
+ int32_t aOutIndex = -1;
+ res = AddItem(aRange, &aOutIndex, !collapsed);
+ if (NS_FAILED(res))
+ return res;
+ setAnchorFocusRange(aOutIndex);
+
+ return NS_OK;
+}
+
+void
+Selection::ReplaceAnchorFocusRange(nsRange* aRange)
+{
+ NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ if (presContext) {
+ selectFrames(presContext, mAnchorFocusRange, false);
+ SetAnchorFocusToRange(aRange);
+ selectFrames(presContext, mAnchorFocusRange, true);
+ }
+}
+
+void
+Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection)
+{
+ if (aDirection == mDirection) {
+ return;
+ }
+ SetDirection(aDirection);
+
+ if (RangeCount() <= 1) {
+ return;
+ }
+
+ nsRange* firstRange = GetRangeAt(0);
+ nsRange* lastRange = GetRangeAt(RangeCount() - 1);
+
+ if (mDirection == eDirPrevious) {
+ firstRange->SetIsGenerated(false);
+ lastRange->SetIsGenerated(true);
+ setAnchorFocusRange(0);
+ } else { // aDir == eDirNext
+ firstRange->SetIsGenerated(true);
+ lastRange->SetIsGenerated(false);
+ setAnchorFocusRange(RangeCount() - 1);
+ }
+}
+
+/*
+Notes which might come in handy for extend:
+
+We can tell the direction of the selection by asking for the anchors selection
+if the begin is less than the end then we know the selection is to the "right".
+else it is a backwards selection.
+a = anchor
+1 = old cursor
+2 = new cursor
+
+ if (a <= 1 && 1 <=2) a,1,2 or (a1,2)
+ if (a < 2 && 1 > 2) a,2,1
+ if (1 < a && a <2) 1,a,2
+ if (a > 2 && 2 >1) 1,2,a
+ if (2 < a && a <1) 2,a,1
+ if (a > 1 && 1 >2) 2,1,a
+then execute
+a 1 2 select from 1 to 2
+a 2 1 deselect from 2 to 1
+1 a 2 deselect from 1 to a select from a to 2
+1 2 a deselect from 1 to 2
+2 1 a = continue selection from 2 to 1
+*/
+
+
+/*
+ * Extend extends the selection away from the anchor.
+ * We don't need to know the direction, because we always change the focus.
+ */
+NS_IMETHODIMP
+Selection::Extend(nsIDOMNode* aParentNode, int32_t aOffset)
+{
+ nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
+ return Extend(parentNode, aOffset);
+}
+
+NS_IMETHODIMP
+Selection::ExtendNative(nsINode* aParentNode, int32_t aOffset)
+{
+ return Extend(aParentNode, aOffset);
+}
+
+nsresult
+Selection::Extend(nsINode* aParentNode, int32_t aOffset)
+{
+ if (!aParentNode)
+ return NS_ERROR_INVALID_ARG;
+
+ ErrorResult result;
+ Extend(*aParentNode, static_cast<uint32_t>(aOffset), result);
+ return result.StealNSResult();
+}
+
+void
+Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
+{
+ // First, find the range containing the old focus point:
+ if (!mAnchorFocusRange) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ if (!mFrameSelection) {
+ aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
+ return;
+ }
+
+ nsresult res;
+ if (!IsValidSelectionPoint(mFrameSelection, &aParentNode)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ RefPtr<nsPresContext> presContext = GetPresContext();
+ if (!presContext || presContext->Document() != aParentNode.OwnerDoc()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+#ifdef DEBUG_SELECTION
+ nsDirection oldDirection = GetDirection();
+#endif
+ nsINode* anchorNode = GetAnchorNode();
+ nsINode* focusNode = GetFocusNode();
+ uint32_t anchorOffset = AnchorOffset();
+ uint32_t focusOffset = FocusOffset();
+
+ RefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
+
+ nsINode* startNode = range->GetStartParent();
+ nsINode* endNode = range->GetEndParent();
+ int32_t startOffset = range->StartOffset();
+ int32_t endOffset = range->EndOffset();
+
+ //compare anchor to old cursor.
+
+ // We pass |disconnected| to the following ComparePoints calls in order
+ // to avoid assertions. ComparePoints returns 1 in the disconnected case
+ // and we can end up in various cases below, but it is assumed that in
+ // any of the cases we end up, the nsRange implementation will collapse
+ // the range to the new point because we can not make a valid range with
+ // a disconnected point. This means that whatever range is currently
+ // selected will be cleared.
+ bool disconnected = false;
+ bool shouldClearRange = false;
+ int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
+ focusNode, focusOffset,
+ &disconnected);
+ //compare old cursor to new cursor
+ shouldClearRange |= disconnected;
+ int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
+ &aParentNode, aOffset,
+ &disconnected);
+ //compare anchor to new cursor
+ shouldClearRange |= disconnected;
+ int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
+ &aParentNode, aOffset,
+ &disconnected);
+
+ // If the points are disconnected, the range will be collapsed below,
+ // resulting in a range that selects nothing.
+ if (shouldClearRange) {
+ // Repaint the current range with the selection removed.
+ selectFrames(presContext, range, false);
+ }
+
+ RefPtr<nsRange> difRange = new nsRange(&aParentNode);
+ if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2 a,1,2
+ //select from 1 to 2 unless they are collapsed
+ range->SetEnd(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ SetDirection(eDirNext);
+ res = difRange->SetEnd(range->GetEndParent(), range->EndOffset());
+ nsresult tmp = difRange->SetStart(focusNode, focusOffset);
+ if (NS_FAILED(tmp)) {
+ res = tmp;
+ }
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ selectFrames(presContext, difRange , true);
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ }
+ else if (result1 == 0 && result3 > 0){//2, a1
+ //select from 2 to 1a
+ SetDirection(eDirPrevious);
+ range->SetStart(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ selectFrames(presContext, range, true);
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ }
+ else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
+ //deselect from 2 to 1
+ res = difRange->SetEnd(focusNode, focusOffset);
+ difRange->SetStart(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+
+ range->SetEnd(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ selectFrames(presContext, difRange, false); // deselect now
+ difRange->SetEnd(range->GetEndParent(), range->EndOffset());
+ selectFrames(presContext, difRange, true); // must reselect last node maybe more
+ }
+ else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
+ if (GetDirection() == eDirPrevious){
+ res = range->SetStart(endNode, endOffset);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ }
+ SetDirection(eDirNext);
+ range->SetEnd(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
+ res = difRange->SetStart(focusNode, focusOffset);
+ nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset);
+ if (NS_FAILED(tmp)) {
+ res = tmp;
+ }
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ //deselect from 1 to a
+ selectFrames(presContext, difRange , false);
+ }
+ else
+ {
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ }
+ //select from a to 2
+ selectFrames(presContext, range , true);
+ }
+ else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
+ //deselect from 1 to 2
+ difRange->SetEnd(aParentNode, aOffset, aRv);
+ res = difRange->SetStart(focusNode, focusOffset);
+ if (aRv.Failed()) {
+ return;
+ }
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ SetDirection(eDirPrevious);
+ range->SetStart(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ selectFrames(presContext, difRange , false);
+ difRange->SetStart(range->GetStartParent(), range->StartOffset());
+ selectFrames(presContext, difRange, true);//must reselect last node
+ }
+ else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
+ if (GetDirection() == eDirNext){
+ range->SetEnd(startNode, startOffset);
+ }
+ SetDirection(eDirPrevious);
+ range->SetStart(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ //deselect from a to 1
+ if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
+ res = difRange->SetStart(anchorNode, anchorOffset);
+ nsresult tmp = difRange->SetEnd(focusNode, focusOffset);
+ if (NS_FAILED(tmp)) {
+ res = tmp;
+ }
+ tmp = SetAnchorFocusToRange(range);
+ if (NS_FAILED(tmp)) {
+ res = tmp;
+ }
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ selectFrames(presContext, difRange, false);
+ }
+ else
+ {
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ }
+ //select from 2 to a
+ selectFrames(presContext, range , true);
+ }
+ else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
+ //select from 2 to 1
+ range->SetStart(aParentNode, aOffset, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ SetDirection(eDirPrevious);
+ res = difRange->SetEnd(focusNode, focusOffset);
+ nsresult tmp = difRange->SetStart(range->GetStartParent(), range->StartOffset());
+ if (NS_FAILED(tmp)) {
+ res = tmp;
+ }
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+
+ selectFrames(presContext, difRange, true);
+ res = SetAnchorFocusToRange(range);
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ return;
+ }
+ }
+
+ if (mRanges.Length() > 1) {
+ for (size_t i = 0; i < mRanges.Length(); ++i) {
+ nsRange* range = mRanges[i].mRange;
+ MOZ_ASSERT(range->IsInSelection());
+ selectFrames(presContext, range, range->IsInSelection());
+ }
+ }
+
+ DEBUG_OUT_RANGE(range);
+#ifdef DEBUG_SELECTION
+ if (GetDirection() != oldDirection) {
+ printf(" direction changed to %s\n",
+ GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
+ }
+ nsCOMPtr<nsIContent> content = do_QueryInterface(&aParentNode);
+ printf ("Sel. Extend to %p %s %d\n", content.get(),
+ nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
+#endif
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ res = frameSelection->NotifySelectionListeners(GetType());
+ if (NS_FAILED(res)) {
+ aRv.Throw(res);
+ }
+}
+
+NS_IMETHODIMP
+Selection::SelectAllChildren(nsIDOMNode* aParentNode)
+{
+ ErrorResult result;
+ nsCOMPtr<nsINode> node = do_QueryInterface(aParentNode);
+ NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG);
+ SelectAllChildren(*node, result);
+ return result.StealNSResult();
+}
+
+void
+Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
+{
+ if (mFrameSelection) {
+ mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
+ }
+ SelectionBatcher batch(this);
+
+ Collapse(aNode, 0, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ Extend(aNode, aNode.GetChildCount(), aRv);
+}
+
+NS_IMETHODIMP
+Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes)
+{
+ if (!aYes) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aYes = false;
+
+ nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+ if (!node) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ ErrorResult result;
+ *aYes = ContainsNode(*node, aAllowPartial, result);
+ return result.StealNSResult();
+}
+
+bool
+Selection::ContainsNode(nsINode& aNode, bool aAllowPartial, ErrorResult& aRv)
+{
+ nsresult rv;
+ if (mRanges.Length() == 0) {
+ return false;
+ }
+
+ // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
+ uint32_t nodeLength;
+ bool isData = aNode.IsNodeOfType(nsINode::eDATA_NODE);
+ if (isData) {
+ nodeLength = static_cast<nsIContent&>(aNode).TextLength();
+ } else {
+ nodeLength = aNode.GetChildCount();
+ }
+
+ nsTArray<nsRange*> overlappingRanges;
+ rv = GetRangesForIntervalArray(&aNode, 0, &aNode, nodeLength,
+ false, &overlappingRanges);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return false;
+ }
+ if (overlappingRanges.Length() == 0)
+ return false; // no ranges overlap
+
+ // if the caller said partial intersections are OK, we're done
+ if (aAllowPartial) {
+ return true;
+ }
+
+ // text nodes always count as inside
+ if (isData) {
+ return true;
+ }
+
+ // The caller wants to know if the node is entirely within the given range,
+ // so we have to check all intersecting ranges.
+ for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
+ bool nodeStartsBeforeRange, nodeEndsAfterRange;
+ if (NS_SUCCEEDED(nsRange::CompareNodeToRange(&aNode, overlappingRanges[i],
+ &nodeStartsBeforeRange,
+ &nodeEndsAfterRange))) {
+ if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+class PointInRectChecker : public nsLayoutUtils::RectCallback {
+public:
+ explicit PointInRectChecker(const nsPoint& aPoint)
+ : mPoint(aPoint)
+ , mMatchFound(false)
+ {
+ }
+
+ void AddRect(const nsRect& aRect) override
+ {
+ mMatchFound = mMatchFound || aRect.Contains(mPoint);
+ }
+
+ bool MatchFound()
+ {
+ return mMatchFound;
+ }
+
+private:
+ nsPoint mPoint;
+ bool mMatchFound;
+};
+
+bool
+Selection::ContainsPoint(const nsPoint& aPoint)
+{
+ if (IsCollapsed()) {
+ return false;
+ }
+ PointInRectChecker checker(aPoint);
+ for (uint32_t i = 0; i < RangeCount(); i++) {
+ nsRange* range = GetRangeAt(i);
+ nsRange::CollectClientRectsAndText(&checker, nullptr, range,
+ range->GetStartParent(), range->StartOffset(),
+ range->GetEndParent(), range->EndOffset(),
+ true, false);
+ if (checker.MatchFound()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+nsPresContext*
+Selection::GetPresContext() const
+{
+ nsIPresShell *shell = GetPresShell();
+ if (!shell) {
+ return nullptr;
+ }
+
+ return shell->GetPresContext();
+}
+
+nsIPresShell*
+Selection::GetPresShell() const
+{
+ if (!mFrameSelection)
+ return nullptr;//nothing to do
+
+ return mFrameSelection->GetShell();
+}
+
+nsIFrame *
+Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
+{
+ if (!mFrameSelection)
+ return nullptr; // nothing to do
+
+ NS_ENSURE_TRUE(aRect, nullptr);
+
+ aRect->SetRect(0, 0, 0, 0);
+
+ switch (aRegion) {
+ case nsISelectionController::SELECTION_ANCHOR_REGION:
+ case nsISelectionController::SELECTION_FOCUS_REGION:
+ return GetSelectionEndPointGeometry(aRegion, aRect);
+ case nsISelectionController::SELECTION_WHOLE_SELECTION:
+ break;
+ default:
+ return nullptr;
+ }
+
+ NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
+ "should only be SELECTION_WHOLE_SELECTION here");
+
+ nsRect anchorRect;
+ nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
+ nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
+ if (!anchorFrame)
+ return nullptr;
+
+ nsRect focusRect;
+ nsIFrame* focusFrame = GetSelectionEndPointGeometry(
+ nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
+ if (!focusFrame)
+ return nullptr;
+
+ NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
+ "points of selection in different documents?");
+ // make focusRect relative to anchorFrame
+ focusRect += focusFrame->GetOffsetTo(anchorFrame);
+
+ aRect->UnionRectEdges(anchorRect, focusRect);
+ return anchorFrame;
+}
+
+nsIFrame *
+Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect)
+{
+ if (!mFrameSelection)
+ return nullptr; // nothing to do
+
+ NS_ENSURE_TRUE(aRect, nullptr);
+
+ aRect->SetRect(0, 0, 0, 0);
+
+ nsINode *node = nullptr;
+ uint32_t nodeOffset = 0;
+ nsIFrame *frame = nullptr;
+
+ switch (aRegion) {
+ case nsISelectionController::SELECTION_ANCHOR_REGION:
+ node = GetAnchorNode();
+ nodeOffset = AnchorOffset();
+ break;
+ case nsISelectionController::SELECTION_FOCUS_REGION:
+ node = GetFocusNode();
+ nodeOffset = FocusOffset();
+ break;
+ default:
+ return nullptr;
+ }
+
+ if (!node)
+ return nullptr;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(node);
+ NS_ENSURE_TRUE(content.get(), nullptr);
+ int32_t frameOffset = 0;
+ frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
+ mFrameSelection->GetHint(),
+ &frameOffset);
+ if (!frame)
+ return nullptr;
+
+ // Figure out what node type we have, then get the
+ // appropriate rect for it's nodeOffset.
+ bool isText = node->IsNodeOfType(nsINode::eTEXT);
+
+ nsPoint pt(0, 0);
+ if (isText) {
+ nsIFrame* childFrame = nullptr;
+ frameOffset = 0;
+ nsresult rv =
+ frame->GetChildFrameContainingOffset(nodeOffset,
+ mFrameSelection->GetHint(),
+ &frameOffset, &childFrame);
+ if (NS_FAILED(rv))
+ return nullptr;
+ if (!childFrame)
+ return nullptr;
+
+ frame = childFrame;
+
+ // Get the x coordinate of the offset into the text frame.
+ rv = GetCachedFrameOffset(frame, nodeOffset, pt);
+ if (NS_FAILED(rv))
+ return nullptr;
+ }
+
+ // Return the rect relative to the frame, with zero width.
+ if (isText) {
+ aRect->x = pt.x;
+ } else if (mFrameSelection->GetHint() == CARET_ASSOCIATE_BEFORE) {
+ // It's the frame's right edge we're interested in.
+ aRect->x = frame->GetRect().width;
+ }
+ aRect->height = frame->GetRect().height;
+
+ return frame;
+}
+
+NS_IMETHODIMP
+Selection::ScrollSelectionIntoViewEvent::Run()
+{
+ if (!mSelection)
+ return NS_OK; // event revoked
+
+ int32_t flags = Selection::SCROLL_DO_FLUSH |
+ Selection::SCROLL_SYNCHRONOUS;
+
+ Selection* sel = mSelection; // workaround to satisfy static analysis
+ RefPtr<Selection> kungFuDeathGrip(sel);
+ mSelection->mScrollEvent.Forget();
+ mSelection->ScrollIntoView(mRegion, mVerticalScroll,
+ mHorizontalScroll, mFlags | flags);
+ return NS_OK;
+}
+
+nsresult
+Selection::PostScrollSelectionIntoViewEvent(
+ SelectionRegion aRegion,
+ int32_t aFlags,
+ nsIPresShell::ScrollAxis aVertical,
+ nsIPresShell::ScrollAxis aHorizontal)
+{
+ // If we've already posted an event, revoke it and place a new one at the
+ // end of the queue to make sure that any new pending reflow events are
+ // processed before we scroll. This will insure that we scroll to the
+ // correct place on screen.
+ mScrollEvent.Revoke();
+
+ RefPtr<ScrollSelectionIntoViewEvent> ev =
+ new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
+ aFlags);
+ nsresult rv = NS_DispatchToCurrentThread(ev);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mScrollEvent = ev;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
+ int16_t aVPercent, int16_t aHPercent)
+{
+ ErrorResult result;
+ ScrollIntoView(aRegion, aIsSynchronous, aVPercent, aHPercent, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+void
+Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
+ int16_t aVPercent, int16_t aHPercent,
+ ErrorResult& aRv)
+{
+ nsresult rv = ScrollIntoViewInternal(aRegion, aIsSynchronous,
+ nsIPresShell::ScrollAxis(aVPercent),
+ nsIPresShell::ScrollAxis(aHPercent));
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+}
+
+NS_IMETHODIMP
+Selection::ScrollIntoViewInternal(SelectionRegion aRegion, bool aIsSynchronous,
+ nsIPresShell::ScrollAxis aVertical,
+ nsIPresShell::ScrollAxis aHorizontal)
+{
+ return ScrollIntoView(aRegion, aVertical, aHorizontal,
+ aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0);
+}
+
+nsresult
+Selection::ScrollIntoView(SelectionRegion aRegion,
+ nsIPresShell::ScrollAxis aVertical,
+ nsIPresShell::ScrollAxis aHorizontal,
+ int32_t aFlags)
+{
+ if (!mFrameSelection)
+ return NS_OK;//nothing to do
+
+ nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
+ if (!presShell)
+ return NS_OK;
+
+ if (mFrameSelection->GetBatching())
+ return NS_OK;
+
+ if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
+ return PostScrollSelectionIntoViewEvent(aRegion, aFlags,
+ aVertical, aHorizontal);
+
+ // Now that text frame character offsets are always valid (though not
+ // necessarily correct), the worst that will happen if we don't flush here
+ // is that some callers might scroll to the wrong place. Those should
+ // either manually flush if they're in a safe position for it or use the
+ // async version of this method.
+ if (aFlags & Selection::SCROLL_DO_FLUSH) {
+ presShell->FlushPendingNotifications(Flush_Layout);
+
+ // Reget the presshell, since it might have been Destroy'ed.
+ presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr;
+ if (!presShell)
+ return NS_OK;
+ }
+
+ //
+ // Scroll the selection region into view.
+ //
+
+ nsRect rect;
+ nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
+ if (!frame)
+ return NS_ERROR_FAILURE;
+
+ // Scroll vertically to get the caret into view, but only if the container
+ // is perceived to be scrollable in that direction (i.e. there is a visible
+ // vertical scrollbar or the scroll range is at least one device pixel)
+ aVertical.mOnlyIfPerceivedScrollableDirection = true;
+
+ uint32_t flags = 0;
+ if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) {
+ flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY;
+ }
+ if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) {
+ flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
+ }
+
+ if (aFlags & Selection::SCROLL_FOR_CARET_MOVE) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
+ (uint32_t) ScrollInputMethod::MainThreadScrollCaretIntoView);
+ }
+
+ presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal,
+ flags);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::AddSelectionListener(nsISelectionListener* aNewListener)
+{
+ if (!aNewListener)
+ return NS_ERROR_NULL_POINTER;
+ ErrorResult result;
+ AddSelectionListener(aNewListener, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+void
+Selection::AddSelectionListener(nsISelectionListener* aNewListener,
+ ErrorResult& aRv)
+{
+ bool result = mSelectionListeners.AppendObject(aNewListener); // AddRefs
+ if (!result) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+}
+
+NS_IMETHODIMP
+Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
+{
+ if (!aListenerToRemove)
+ return NS_ERROR_NULL_POINTER;
+ ErrorResult result;
+ RemoveSelectionListener(aListenerToRemove, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+void
+Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
+ ErrorResult& aRv)
+{
+ bool result = mSelectionListeners.RemoveObject(aListenerToRemove); // Releases
+ if (!result) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+}
+
+nsresult
+Selection::NotifySelectionListeners()
+{
+ if (!mFrameSelection)
+ return NS_OK;//nothing to do
+
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ if (frameSelection->GetBatching()) {
+ frameSelection->SetDirty();
+ return NS_OK;
+ }
+ nsCOMArray<nsISelectionListener> selectionListeners(mSelectionListeners);
+ int32_t cnt = selectionListeners.Count();
+ if (cnt != mSelectionListeners.Count()) {
+ return NS_ERROR_OUT_OF_MEMORY; // nsCOMArray is fallible
+ }
+
+ nsCOMPtr<nsIDOMDocument> domdoc;
+ nsIPresShell* ps = GetPresShell();
+ if (ps) {
+ domdoc = do_QueryInterface(ps->GetDocument());
+ }
+
+ short reason = frameSelection->PopReason();
+ for (int32_t i = 0; i < cnt; i++) {
+ selectionListeners[i]->NotifySelectionChanged(domdoc, this, reason);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Selection::StartBatchChanges()
+{
+ if (mFrameSelection) {
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ frameSelection->StartBatchChanges();
+ }
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+Selection::EndBatchChanges()
+{
+ return EndBatchChangesInternal();
+}
+
+nsresult
+Selection::EndBatchChangesInternal(int16_t aReason)
+{
+ if (mFrameSelection) {
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ frameSelection->EndBatchChanges(aReason);
+ }
+ return NS_OK;
+}
+
+void
+Selection::AddSelectionChangeBlocker()
+{
+ mSelectionChangeBlockerCount++;
+}
+
+void
+Selection::RemoveSelectionChangeBlocker()
+{
+ MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
+ "mSelectionChangeBlockerCount has an invalid value - "
+ "maybe you have a mismatched RemoveSelectionChangeBlocker?");
+ mSelectionChangeBlockerCount--;
+}
+
+bool
+Selection::IsBlockingSelectionChangeEvents() const
+{
+ return mSelectionChangeBlockerCount > 0;
+}
+
+NS_IMETHODIMP
+Selection::DeleteFromDocument()
+{
+ ErrorResult result;
+ DeleteFromDocument(result);
+ return result.StealNSResult();
+}
+
+void
+Selection::DeleteFromDocument(ErrorResult& aRv)
+{
+ if (!mFrameSelection)
+ return;//nothing to do
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ nsresult rv = frameSelection->DeleteFromDocument();
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+}
+
+NS_IMETHODIMP
+Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
+ const nsAString& aGranularity)
+{
+ ErrorResult result;
+ Modify(aAlter, aDirection, aGranularity, result);
+ return result.StealNSResult();
+}
+
+void
+Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
+ const nsAString& aGranularity, ErrorResult& aRv)
+{
+ // Silently exit if there's no selection or no focus node.
+ if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
+ return;
+ }
+
+ if (!aAlter.LowerCaseEqualsLiteral("move") &&
+ !aAlter.LowerCaseEqualsLiteral("extend")) {
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
+
+ if (!aDirection.LowerCaseEqualsLiteral("forward") &&
+ !aDirection.LowerCaseEqualsLiteral("backward") &&
+ !aDirection.LowerCaseEqualsLiteral("left") &&
+ !aDirection.LowerCaseEqualsLiteral("right")) {
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
+
+ // Line moves are always visual.
+ bool visual = aDirection.LowerCaseEqualsLiteral("left") ||
+ aDirection.LowerCaseEqualsLiteral("right") ||
+ aGranularity.LowerCaseEqualsLiteral("line");
+
+ bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
+ aDirection.LowerCaseEqualsLiteral("right");
+
+ bool extend = aAlter.LowerCaseEqualsLiteral("extend");
+
+ nsSelectionAmount amount;
+ if (aGranularity.LowerCaseEqualsLiteral("character")) {
+ amount = eSelectCluster;
+ } else if (aGranularity.LowerCaseEqualsLiteral("word")) {
+ amount = eSelectWordNoSpace;
+ } else if (aGranularity.LowerCaseEqualsLiteral("line")) {
+ amount = eSelectLine;
+ } else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
+ amount = forward ? eSelectEndLine : eSelectBeginLine;
+ } else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
+ aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
+ aGranularity.LowerCaseEqualsLiteral("paragraph") ||
+ aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
+ aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
+ aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return;
+ } else {
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
+
+ // If the anchor doesn't equal the focus and we try to move without first
+ // collapsing the selection, MoveCaret will collapse the selection and quit.
+ // To avoid this, we need to collapse the selection first.
+ nsresult rv = NS_OK;
+ if (!extend) {
+ nsINode* focusNode = GetFocusNode();
+ // We should have checked earlier that there was a focus node.
+ if (!focusNode) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return;
+ }
+ uint32_t focusOffset = FocusOffset();
+ Collapse(focusNode, focusOffset);
+ }
+
+ // If the paragraph direction of the focused frame is right-to-left,
+ // we may have to swap the direction of movement.
+ nsIFrame *frame;
+ int32_t offset;
+ rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
+ if (NS_SUCCEEDED(rv) && frame) {
+ nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
+
+ if (paraDir == NSBIDI_RTL && visual) {
+ if (amount == eSelectBeginLine) {
+ amount = eSelectEndLine;
+ forward = !forward;
+ } else if (amount == eSelectEndLine) {
+ amount = eSelectBeginLine;
+ forward = !forward;
+ }
+ }
+ }
+
+ // MoveCaret will return an error if it can't move in the specified
+ // direction, but we just ignore this error unless it's a line move, in which
+ // case we call nsISelectionController::CompleteMove to move the cursor to
+ // the beginning/end of the line.
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+ rv = frameSelection->MoveCaret(forward ? eDirNext : eDirPrevious,
+ extend, amount,
+ visual ? nsFrameSelection::eVisual
+ : nsFrameSelection::eLogical);
+
+ if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
+ nsCOMPtr<nsISelectionController> shell =
+ do_QueryInterface(frameSelection->GetShell());
+ if (!shell)
+ return;
+ shell->CompleteMove(forward, extend);
+ }
+}
+
+/** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
+ * @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
+ */
+NS_IMETHODIMP
+Selection::SelectionLanguageChange(bool aLangRTL)
+{
+ if (!mFrameSelection)
+ return NS_ERROR_NOT_INITIALIZED; // Can't do selection
+
+ RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
+
+ // if the direction of the language hasn't changed, nothing to do
+ nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
+ if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
+ return NS_OK;
+ }
+
+ frameSelection->mKbdBidiLevel = kbdBidiLevel;
+
+ nsresult result;
+ nsIFrame *focusFrame = 0;
+
+ result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false);
+ if (NS_FAILED(result)) {
+ return result;
+ }
+ if (!focusFrame) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t frameStart, frameEnd;
+ focusFrame->GetOffsets(frameStart, frameEnd);
+ RefPtr<nsPresContext> context = GetPresContext();
+ nsBidiLevel levelBefore, levelAfter;
+ if (!context) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsBidiLevel level = focusFrame->GetEmbeddingLevel();
+ int32_t focusOffset = static_cast<int32_t>(FocusOffset());
+ if ((focusOffset != frameStart) && (focusOffset != frameEnd))
+ // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
+ // is equal to the frame level
+ levelBefore = levelAfter = level;
+ else {
+ // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
+ // before and after the cursor
+ nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
+ nsPrevNextBidiLevels levels = frameSelection->
+ GetPrevNextBidiLevels(focusContent, focusOffset, false);
+
+ levelBefore = levels.mLevelBefore;
+ levelAfter = levels.mLevelAfter;
+ }
+
+ if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
+ // if cursor is between two characters with the same orientation, changing the keyboard language
+ // must toggle the cursor level between the level of the character with the lowest level
+ // (if the new language corresponds to the orientation of that character) and this level plus 1
+ // (if the new language corresponds to the opposite orientation)
+ if ((level != levelBefore) && (level != levelAfter))
+ level = std::min(levelBefore, levelAfter);
+ if (IS_SAME_DIRECTION(level, kbdBidiLevel))
+ frameSelection->SetCaretBidiLevel(level);
+ else
+ frameSelection->SetCaretBidiLevel(level + 1);
+ }
+ else {
+ // if cursor is between characters with opposite orientations, changing the keyboard language must change
+ // the cursor level to that of the adjacent character with the orientation corresponding to the new language.
+ if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
+ frameSelection->SetCaretBidiLevel(levelBefore);
+ else
+ frameSelection->SetCaretBidiLevel(levelAfter);
+ }
+
+ // The caret might have moved, so invalidate the desired position
+ // for future usages of up-arrow or down-arrow
+ frameSelection->InvalidateDesiredPos();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(nsDirection)
+Selection::GetSelectionDirection() {
+ return mDirection;
+}
+
+NS_IMETHODIMP_(void)
+Selection::SetSelectionDirection(nsDirection aDirection) {
+ mDirection = aDirection;
+}
+
+JSObject*
+Selection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return mozilla::dom::SelectionBinding::Wrap(aCx, this, aGivenProto);
+}
+
+// AutoHideSelectionChanges
+AutoHideSelectionChanges::AutoHideSelectionChanges(const nsFrameSelection* aFrame)
+ : AutoHideSelectionChanges(
+ aFrame ? aFrame->GetSelection(SelectionType::eNormal) : nullptr)
+{}
+
+// nsAutoCopyListener
+
+nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr;
+
+NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
+
+/*
+ * What we do now:
+ * On every selection change, we copy to the clipboard anew, creating a
+ * HTML buffer, a transferable, an nsISupportsString and
+ * a huge mess every time. This is basically what nsPresShell::DoCopy does
+ * to move the selection into the clipboard for Edit->Copy.
+ *
+ * What we should do, to make our end of the deal faster:
+ * Create a singleton transferable with our own magic converter. When selection
+ * changes (use a quick cache to detect ``real'' changes), we put the new
+ * nsISelection in the transferable. Our magic converter will take care of
+ * transferable->whatever-other-format when the time comes to actually
+ * hand over the clipboard contents.
+ *
+ * Other issues:
+ * - which X clipboard should we populate?
+ * - should we use a different one than Edit->Copy, so that inadvertant
+ * selections (or simple clicks, which currently cause a selection
+ * notification, regardless of if they're in the document which currently has
+ * selection!) don't lose the contents of the ``application''? Or should we
+ * just put some intelligence in the ``is this a real selection?'' code to
+ * protect our selection against clicks in other documents that don't create
+ * selections?
+ * - maybe we should just never clear the X clipboard? That would make this
+ * problem just go away, which is very tempting.
+ *
+ * On macOS,
+ * nsIClipboard::kSelectionCache is the flag for current selection cache.
+ * Set the current selection cache on the parent process in
+ * widget cocoa nsClipboard whenever selection changes.
+ */
+
+NS_IMETHODIMP
+nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
+ nsISelection *aSel, int16_t aReason)
+{
+ if (mCachedClipboard == nsIClipboard::kSelectionCache) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ // If no active window, do nothing because a current selection changed
+ // cannot occur unless it is in the active window.
+ if (!fm->GetActiveWindow()) {
+ return NS_OK;
+ }
+ }
+
+ if (!(aReason & nsISelectionListener::MOUSEUP_REASON ||
+ aReason & nsISelectionListener::SELECTALL_REASON ||
+ aReason & nsISelectionListener::KEYPRESS_REASON))
+ return NS_OK; //dont care if we are still dragging
+
+ bool collapsed;
+ if (!aDoc || !aSel ||
+ NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) {
+#ifdef DEBUG_CLIPBOARD
+ fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
+#endif
+ // If on macOS, clear the current selection transferable cached
+ // on the parent process (nsClipboard) when the selection is empty.
+ if (mCachedClipboard == nsIClipboard::kSelectionCache) {
+ return nsCopySupport::ClearSelectionCache();
+ }
+ /* clear X clipboard? */
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+ // call the copy code
+ return nsCopySupport::HTMLCopy(aSel, doc,
+ mCachedClipboard, false);
+}
+
+/**
+ * See Bug 1288453.
+ *
+ * Update the selection cache on repaint to handle when a pre-existing
+ * selection becomes active aka the current selection.
+ *
+ * 1. Change the current selection by click n dragging another selection.
+ * - Make a selection on content page. Make a selection in a text editor.
+ * - You can click n drag the content selection to make it active again.
+ * 2. Change the current selection when switching to a tab with a selection.
+ * - Make selection in tab.
+ * - Switching tabs will make its respective selection active.
+ *
+ * Therefore, we only update the selection cache on a repaint
+ * if the current selection being repainted is not an empty selection.
+ *
+ * If the current selection is empty. The current selection cache
+ * would be cleared by nsAutoCopyListener::NotifySelectionChanged.
+ */
+nsresult
+nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
+{
+ nsIPresShell* ps = aSel->GetPresShell();
+ if (!ps) {
+ return NS_OK;
+ }
+ nsCOMPtr<nsIDocument> aDoc = ps->GetDocument();
+
+ bool collapsed;
+ if (aDoc && aSel &&
+ NS_SUCCEEDED(aSel->GetIsCollapsed(&collapsed)) && !collapsed) {
+ return nsCopySupport::HTMLCopy(aSel, aDoc,
+ nsIClipboard::kSelectionCache, false);
+ }
+
+ return NS_OK;
+}
+
+// SelectionChangeListener
+
+SelectionChangeListener::RawRangeData::RawRangeData(const nsRange* aRange)
+{
+ mozilla::ErrorResult rv;
+ mStartParent = aRange->GetStartContainer(rv);
+ rv.SuppressException();
+ mEndParent = aRange->GetEndContainer(rv);
+ rv.SuppressException();
+ mStartOffset = aRange->GetStartOffset(rv);
+ rv.SuppressException();
+ mEndOffset = aRange->GetEndOffset(rv);
+ rv.SuppressException();
+}
+
+bool
+SelectionChangeListener::RawRangeData::Equals(const nsRange* aRange)
+{
+ mozilla::ErrorResult rv;
+ bool eq = mStartParent == aRange->GetStartContainer(rv);
+ rv.SuppressException();
+ eq = eq && mEndParent == aRange->GetEndContainer(rv);
+ rv.SuppressException();
+ eq = eq && mStartOffset == aRange->GetStartOffset(rv);
+ rv.SuppressException();
+ eq = eq && mEndOffset == aRange->GetEndOffset(rv);
+ rv.SuppressException();
+ return eq;
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ SelectionChangeListener::RawRangeData& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ ImplCycleCollectionTraverse(aCallback, aField.mStartParent, "mStartParent", aFlags);
+ ImplCycleCollectionTraverse(aCallback, aField.mEndParent, "mEndParent", aFlags);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SelectionChangeListener)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SelectionChangeListener)
+ tmp->mOldRanges.Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SelectionChangeListener)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOldRanges);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SelectionChangeListener)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SelectionChangeListener)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SelectionChangeListener)
+
+NS_IMETHODIMP
+SelectionChangeListener::NotifySelectionChanged(nsIDOMDocument* aDoc,
+ nsISelection* aSel, int16_t aReason)
+{
+ RefPtr<Selection> sel = aSel->AsSelection();
+
+ nsIDocument* doc = sel->GetParentObject();
+ if (!(doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal())) &&
+ !nsFrameSelection::sSelectionEventsEnabled) {
+ return NS_OK;
+ }
+
+ // Check if the ranges have actually changed
+ // Don't bother checking this if we are hiding changes.
+ if (mOldRanges.Length() == sel->RangeCount() && !sel->IsBlockingSelectionChangeEvents()) {
+ bool changed = false;
+
+ for (size_t i = 0; i < mOldRanges.Length(); i++) {
+ if (!mOldRanges[i].Equals(sel->GetRangeAt(i))) {
+ changed = true;
+ break;
+ }
+ }
+
+ if (!changed) {
+ return NS_OK;
+ }
+ }
+
+ // The ranges have actually changed, update the mOldRanges array
+ mOldRanges.ClearAndRetainStorage();
+ for (size_t i = 0; i < sel->RangeCount(); i++) {
+ mOldRanges.AppendElement(RawRangeData(sel->GetRangeAt(i)));
+ }
+
+ // If we are hiding changes, then don't do anything else. We do this after we
+ // update mOldRanges so that changes after the changes stop being hidden don't
+ // incorrectly trigger a change, even though they didn't change anything
+ if (sel->IsBlockingSelectionChangeEvents()) {
+ return NS_OK;
+ }
+
+ // The spec currently doesn't say that we should dispatch this event on text
+ // controls, so for now we only support doing that under a pref, disabled by
+ // default.
+ // See https://github.com/w3c/selection-api/issues/53.
+ if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
+ nsCOMPtr<nsINode> target;
+
+ // Check if we should be firing this event to a different node than the
+ // document. The limiter of the nsFrameSelection will be within the native
+ // anonymous subtree of the node we want to fire the event on. We need to
+ // climb up the parent chain to escape the native anonymous subtree, and then
+ // fire the event.
+ if (const nsFrameSelection* fs = sel->GetFrameSelection()) {
+ if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
+ while (root && root->IsInNativeAnonymousSubtree()) {
+ root = root->GetParent();
+ }
+
+ target = root.forget();
+ }
+ }
+
+ // If we didn't get a target before, we can instead fire the event at the document.
+ if (!target) {
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
+ target = doc.forget();
+ }
+
+ if (target) {
+ RefPtr<AsyncEventDispatcher> asyncDispatcher =
+ new AsyncEventDispatcher(target, NS_LITERAL_STRING("selectionchange"), false);
+ asyncDispatcher->PostDOMEvent();
+ }
+ } else {
+ if (const nsFrameSelection* fs = sel->GetFrameSelection()) {
+ if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
+ if (root->IsInNativeAnonymousSubtree()) {
+ return NS_OK;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
+ if (doc) {
+ RefPtr<AsyncEventDispatcher> asyncDispatcher =
+ new AsyncEventDispatcher(doc, NS_LITERAL_STRING("selectionchange"), false);
+ asyncDispatcher->PostDOMEvent();
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/layout/generic/nsSimplePageSequenceFrame.cpp b/layout/generic/nsSimplePageSequenceFrame.cpp
new file mode 100644
index 000000000..2e74afc3b
--- /dev/null
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -0,0 +1,884 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSimplePageSequenceFrame.h"
+
+#include "nsCOMPtr.h"
+#include "nsDeviceContext.h"
+#include "nsPresContext.h"
+#include "gfxContext.h"
+#include "nsRenderingContext.h"
+#include "nsGkAtoms.h"
+#include "nsIPresShell.h"
+#include "nsIPrintSettings.h"
+#include "nsPageFrame.h"
+#include "nsSubDocumentFrame.h"
+#include "nsRegion.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsContentUtils.h"
+#include "nsDisplayList.h"
+#include "nsHTMLCanvasFrame.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsIDateTimeFormat.h"
+#include "nsServiceManagerUtils.h"
+#include <algorithm>
+
+#define OFFSET_NOT_SET -1
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+#include "mozilla/Logging.h"
+mozilla::LazyLogModule gLayoutPrintingLog("printing-layout");
+
+#define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
+
+nsSimplePageSequenceFrame*
+NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsSimplePageSequenceFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame)
+
+nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext) :
+ nsContainerFrame(aContext),
+ mTotalPages(-1),
+ mSelectionHeight(-1),
+ mYSelOffset(0),
+ mCalledBeginPage(false),
+ mCurrentCanvasListSetup(false)
+{
+ nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
+ mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
+
+ // XXX Unsafe to assume successful allocation
+ mPageData = new nsSharedPageData();
+ mPageData->mHeadFootFont =
+ *PresContext()->GetDefaultFont(kGenericFont_serif,
+ aContext->StyleFont()->mLanguage);
+ mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10);
+
+ // Doing this here so we only have to go get these formats once
+ SetPageNumberFormat("pagenumber", "%1$d", true);
+ SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
+}
+
+nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame()
+{
+ delete mPageData;
+ ResetPrintCanvasList();
+}
+
+NS_QUERYFRAME_HEAD(nsSimplePageSequenceFrame)
+ NS_QUERYFRAME_ENTRY(nsIPageSequenceFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+//----------------------------------------------------------------------
+
+void
+nsSimplePageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nscoord aWidth,
+ nscoord aHeight)
+{
+ // Aim to fill the whole size of the document, not only so we
+ // can act as a background in print preview but also handle overflow
+ // in child page frames correctly.
+ // Use availableWidth so we don't cause a needless horizontal scrollbar.
+ aDesiredSize.Width() = std::max(aReflowInput.AvailableWidth(),
+ nscoord(aWidth * PresContext()->GetPrintPreviewScale()));
+ aDesiredSize.Height() = std::max(aReflowInput.ComputedHeight(),
+ nscoord(aHeight * PresContext()->GetPrintPreviewScale()));
+}
+
+// Helper function to compute the offset needed to center a child
+// page-frame's margin-box inside our content-box.
+nscoord
+nsSimplePageSequenceFrame::ComputeCenteringMargin(
+ nscoord aContainerContentBoxWidth,
+ nscoord aChildPaddingBoxWidth,
+ const nsMargin& aChildPhysicalMargin)
+{
+ // We'll be centering our child's margin-box, so get the size of that:
+ nscoord childMarginBoxWidth =
+ aChildPaddingBoxWidth + aChildPhysicalMargin.LeftRight();
+
+ // When rendered, our child's rect will actually be scaled up by the
+ // print-preview scale factor, via ComputePageSequenceTransform().
+ // We really want to center *that scaled-up rendering* inside of
+ // aContainerContentBoxWidth. So, we scale up its margin-box here...
+ auto ppScale = PresContext()->GetPrintPreviewScale();
+ nscoord scaledChildMarginBoxWidth =
+ NSToCoordRound(childMarginBoxWidth * ppScale);
+
+ // ...and see we how much space is left over, when we subtract that scaled-up
+ // size from the container width:
+ nscoord scaledExtraSpace =
+ aContainerContentBoxWidth - scaledChildMarginBoxWidth;
+
+ if (scaledExtraSpace <= 0) {
+ // (Don't bother centering if there's zero/negative space.)
+ return 0;
+ }
+
+ // To center the child, we want to give it an additional left-margin of half
+ // of the extra space. And then, we have to scale that space back down, so
+ // that it'll produce the correct scaled-up amount when we render (because
+ // rendering will scale it back up):
+ return NSToCoordRound(scaledExtraSpace * 0.5 / ppScale);
+}
+
+void
+nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
+ "A Page Sequence is only for real pages");
+ DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow");
+
+ aStatus = NS_FRAME_COMPLETE; // we're always complete
+
+ // Don't do incremental reflow until we've taught tables how to do
+ // it right in paginated mode.
+ if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+ // Return our desired size
+ SetDesiredSize(aDesiredSize, aReflowInput, mSize.width, mSize.height);
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ if (GetRect().Width() != aDesiredSize.Width()) {
+ // Our width is changing; we need to re-center our children (our pages).
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ nsIFrame* child = e.get();
+ nsMargin pageCSSMargin = child->GetUsedMargin();
+ nscoord centeringMargin =
+ ComputeCenteringMargin(aReflowInput.ComputedWidth(),
+ child->GetRect().width,
+ pageCSSMargin);
+ nscoord newX = pageCSSMargin.left + centeringMargin;
+
+ // Adjust the child's x-position:
+ child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
+ }
+ }
+ return;
+ }
+
+ // See if we can get a Print Settings from the Context
+ if (!mPageData->mPrintSettings &&
+ aPresContext->Medium() == nsGkAtoms::print) {
+ mPageData->mPrintSettings = aPresContext->GetPrintSettings();
+ }
+
+ // now get out margins & edges
+ if (mPageData->mPrintSettings) {
+ nsIntMargin unwriteableTwips;
+ mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips);
+ NS_ASSERTION(unwriteableTwips.left >= 0 && unwriteableTwips.top >= 0 &&
+ unwriteableTwips.right >= 0 && unwriteableTwips.bottom >= 0,
+ "Unwriteable twips should be non-negative");
+
+ nsIntMargin marginTwips;
+ mPageData->mPrintSettings->GetMarginInTwips(marginTwips);
+ mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips);
+
+ int16_t printType;
+ mPageData->mPrintSettings->GetPrintRange(&printType);
+ mPrintRangeType = printType;
+
+ nsIntMargin edgeTwips;
+ mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips);
+
+ // sanity check the values. three inches are sometimes needed
+ int32_t inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0);
+ edgeTwips.top = clamped(edgeTwips.top, 0, inchInTwips);
+ edgeTwips.bottom = clamped(edgeTwips.bottom, 0, inchInTwips);
+ edgeTwips.left = clamped(edgeTwips.left, 0, inchInTwips);
+ edgeTwips.right = clamped(edgeTwips.right, 0, inchInTwips);
+
+ mPageData->mEdgePaperMargin =
+ aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips);
+ }
+
+ // *** Special Override ***
+ // If this is a sub-sdoc (meaning it doesn't take the whole page)
+ // and if this Document is in the upper left hand corner
+ // we need to suppress the top margin or it will reflow too small
+
+ nsSize pageSize = aPresContext->GetPageSize();
+
+ mPageData->mReflowSize = pageSize;
+ // If we're printing a selection, we need to reflow with
+ // unconstrained height, to make sure we'll get to the selection
+ // even if it's beyond the first page of content.
+ if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
+ mPageData->mReflowSize.height = NS_UNCONSTRAINEDSIZE;
+ }
+ mPageData->mReflowMargin = mMargin;
+
+ // We use the CSS "margin" property on the -moz-page pseudoelement
+ // to determine the space between each page in print preview.
+ // Keep a running y-offset for each page.
+ nscoord y = 0;
+ nscoord maxXMost = 0;
+
+ // Tile the pages vertically
+ ReflowOutput kidSize(aReflowInput);
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ nsIFrame* kidFrame = e.get();
+ // Set the shared data into the page frame before reflow
+ nsPageFrame * pf = static_cast<nsPageFrame*>(kidFrame);
+ pf->SetSharedPageData(mPageData);
+
+ // Reflow the page
+ ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
+ LogicalSize(kidFrame->GetWritingMode(),
+ pageSize));
+ nsReflowStatus status;
+
+ kidReflowInput.SetComputedWidth(kidReflowInput.AvailableWidth());
+ //kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight());
+ PR_PL(("AV W: %d H: %d\n", kidReflowInput.AvailableWidth(), kidReflowInput.AvailableHeight()));
+
+ nsMargin pageCSSMargin = kidReflowInput.ComputedPhysicalMargin();
+ y += pageCSSMargin.top;
+
+ nscoord x = pageCSSMargin.left;
+
+ // Place and size the page.
+ ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, x, y, 0, status);
+
+ // If the page is narrower than our width, then center it horizontally:
+ x += ComputeCenteringMargin(aReflowInput.ComputedWidth(),
+ kidSize.Width(), pageCSSMargin);
+
+ FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0);
+ y += kidSize.Height();
+ y += pageCSSMargin.bottom;
+
+ maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right);
+
+ // Is the page complete?
+ nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
+
+ if (NS_FRAME_IS_FULLY_COMPLETE(status)) {
+ NS_ASSERTION(!kidNextInFlow, "bad child flow list");
+ } else if (!kidNextInFlow) {
+ // The page isn't complete and it doesn't have a next-in-flow, so
+ // create a continuing page.
+ nsIFrame* continuingPage = aPresContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(aPresContext, kidFrame, this);
+
+ // Add it to our child list
+ mFrames.InsertFrame(nullptr, kidFrame, continuingPage);
+ }
+ }
+
+ // Get Total Page Count
+ // XXXdholbert technically we could calculate this in the loop above,
+ // instead of needing a separate walk.
+ int32_t pageTot = mFrames.GetLength();
+
+ // Set Page Number Info
+ int32_t pageNum = 1;
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ MOZ_ASSERT(e.get()->GetType() == nsGkAtoms::pageFrame,
+ "only expecting nsPageFrame children. Other children will make "
+ "this static_cast bogus & probably violate other assumptions");
+ nsPageFrame* pf = static_cast<nsPageFrame*>(e.get());
+ pf->SetPageNumInfo(pageNum, pageTot);
+ pageNum++;
+ }
+
+ // Create current Date/Time String
+ if (!mDateFormatter) {
+ mDateFormatter = nsIDateTimeFormat::Create();
+ }
+ if (!mDateFormatter) {
+ return;
+ }
+ nsAutoString formattedDateString;
+ time_t ltime;
+ time( &ltime );
+ if (NS_SUCCEEDED(mDateFormatter->FormatTime(nullptr /* nsILocale* locale */,
+ kDateFormatShort,
+ kTimeFormatNoSeconds,
+ ltime,
+ formattedDateString))) {
+ SetDateTimeStr(formattedDateString);
+ }
+
+ // Return our desired size
+ // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
+ // correct size
+ SetDesiredSize(aDesiredSize, aReflowInput, maxXMost, y);
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ // cache the size so we can set the desired size
+ // for the other reflows that happen
+ mSize.width = maxXMost;
+ mSize.height = y;
+
+ NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus);
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+//----------------------------------------------------------------------
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsSimplePageSequenceFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("SimplePageSequence"), aResult);
+}
+#endif
+
+//====================================================================
+//== Asynch Printing
+//====================================================================
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::GetCurrentPageNum(int32_t* aPageNum)
+{
+ NS_ENSURE_ARG_POINTER(aPageNum);
+
+ *aPageNum = mPageNum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::GetNumPages(int32_t* aNumPages)
+{
+ NS_ENSURE_ARG_POINTER(aNumPages);
+
+ *aNumPages = mTotalPages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::IsDoingPrintRange(bool* aDoing)
+{
+ NS_ENSURE_ARG_POINTER(aDoing);
+
+ *aDoing = mDoingPageRange;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::GetPrintRange(int32_t* aFromPage, int32_t* aToPage)
+{
+ NS_ENSURE_ARG_POINTER(aFromPage);
+ NS_ENSURE_ARG_POINTER(aToPage);
+
+ *aFromPage = mFromPageNum;
+ *aToPage = mToPageNum;
+ return NS_OK;
+}
+
+// Helper Function
+void
+nsSimplePageSequenceFrame::SetPageNumberFormat(const char* aPropName, const char* aDefPropVal, bool aPageNumOnly)
+{
+ // Doing this here so we only have to go get these formats once
+ nsXPIDLString pageNumberFormat;
+ // Now go get the Localized Page Formating String
+ nsresult rv =
+ nsContentUtils::GetLocalizedString(nsContentUtils::ePRINTING_PROPERTIES,
+ aPropName, pageNumberFormat);
+ if (NS_FAILED(rv)) { // back stop formatting
+ pageNumberFormat.AssignASCII(aDefPropVal);
+ }
+
+ SetPageNumberFormat(pageNumberFormat, aPageNumOnly);
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::StartPrint(nsPresContext* aPresContext,
+ nsIPrintSettings* aPrintSettings,
+ const nsAString& aDocTitle,
+ const nsAString& aDocURL)
+{
+ NS_ENSURE_ARG_POINTER(aPresContext);
+ NS_ENSURE_ARG_POINTER(aPrintSettings);
+
+ if (!mPageData->mPrintSettings) {
+ mPageData->mPrintSettings = aPrintSettings;
+ }
+
+ if (!aDocTitle.IsEmpty()) {
+ mPageData->mDocTitle = aDocTitle;
+ }
+ if (!aDocURL.IsEmpty()) {
+ mPageData->mDocURL = aDocURL;
+ }
+
+ aPrintSettings->GetStartPageRange(&mFromPageNum);
+ aPrintSettings->GetEndPageRange(&mToPageNum);
+ aPrintSettings->GetPageRanges(mPageRanges);
+
+ mDoingPageRange = nsIPrintSettings::kRangeSpecifiedPageRange == mPrintRangeType ||
+ nsIPrintSettings::kRangeSelection == mPrintRangeType;
+
+ // If printing a range of pages make sure at least the starting page
+ // number is valid
+ int32_t totalPages = mFrames.GetLength();
+
+ if (mDoingPageRange) {
+ if (mFromPageNum > totalPages) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+
+ // Begin printing of the document
+ nsresult rv = NS_OK;
+
+ // Determine if we are rendering only the selection
+ aPresContext->SetIsRenderingOnlySelection(nsIPrintSettings::kRangeSelection == mPrintRangeType);
+
+
+ if (mDoingPageRange) {
+ // XXX because of the hack for making the selection all print on one page
+ // we must make sure that the page is sized correctly before printing.
+ nscoord height = aPresContext->GetPageSize().height;
+
+ int32_t pageNum = 1;
+ nscoord y = 0;//mMargin.top;
+
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ nsIFrame* page = e.get();
+ if (pageNum >= mFromPageNum && pageNum <= mToPageNum) {
+ nsRect rect = page->GetRect();
+ rect.y = y;
+ rect.height = height;
+ page->SetRect(rect);
+ y += rect.height + mMargin.top + mMargin.bottom;
+ }
+ pageNum++;
+ }
+
+ // adjust total number of pages
+ if (nsIPrintSettings::kRangeSelection != mPrintRangeType) {
+ totalPages = pageNum - 1;
+ }
+ }
+
+ mPageNum = 1;
+
+ if (mTotalPages == -1) {
+ mTotalPages = totalPages;
+ }
+
+ return rv;
+}
+
+void
+GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray<RefPtr<HTMLCanvasElement> >* aArr)
+{
+ if (!aFrame) {
+ return;
+ }
+ for (nsIFrame::ChildListIterator childLists(aFrame);
+ !childLists.IsDone(); childLists.Next()) {
+
+ nsFrameList children = childLists.CurrentList();
+ for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+ nsIFrame* child = e.get();
+
+ // Check if child is a nsHTMLCanvasFrame.
+ nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child);
+
+ // If there is a canvasFrame, try to get actual canvas element.
+ if (canvasFrame) {
+ HTMLCanvasElement* canvas =
+ HTMLCanvasElement::FromContentOrNull(canvasFrame->GetContent());
+ if (canvas && canvas->GetMozPrintCallback()) {
+ aArr->AppendElement(canvas);
+ continue;
+ }
+ }
+
+ if (!child->PrincipalChildList().FirstChild()) {
+ nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child);
+ if (subdocumentFrame) {
+ // Descend into the subdocument
+ nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
+ child = root;
+ }
+ }
+ // The current child is not a nsHTMLCanvasFrame OR it is but there is
+ // no HTMLCanvasElement on it. Check if children of `child` might
+ // contain a HTMLCanvasElement.
+ GetPrintCanvasElementsInFrame(child, aArr);
+ }
+ }
+}
+
+void
+nsSimplePageSequenceFrame::DetermineWhetherToPrintPage()
+{
+ // See whether we should print this page
+ mPrintThisPage = true;
+ bool printEvenPages, printOddPages;
+ mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages, &printEvenPages);
+ mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages, &printOddPages);
+
+ // If printing a range of pages check whether the page number is in the
+ // range of pages to print
+ if (mDoingPageRange) {
+ if (mPageNum < mFromPageNum) {
+ mPrintThisPage = false;
+ } else if (mPageNum > mToPageNum) {
+ mPageNum++;
+ mPrintThisPage = false;
+ return;
+ } else {
+ int32_t length = mPageRanges.Length();
+
+ // Page ranges are pairs (start, end)
+ if (length && (length % 2 == 0)) {
+ mPrintThisPage = false;
+
+ int32_t i;
+ for (i = 0; i < length; i += 2) {
+ if (mPageRanges[i] <= mPageNum && mPageNum <= mPageRanges[i+1]) {
+ mPrintThisPage = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Check for printing of odd and even pages
+ if (mPageNum & 0x1) {
+ if (!printOddPages) {
+ mPrintThisPage = false; // don't print odd numbered page
+ }
+ } else {
+ if (!printEvenPages) {
+ mPrintThisPage = false; // don't print even numbered page
+ }
+ }
+
+ if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
+ mPrintThisPage = true;
+ }
+}
+
+nsIFrame*
+nsSimplePageSequenceFrame::GetCurrentPageFrame()
+{
+ int32_t i = 1;
+ for (nsFrameList::Enumerator childFrames(mFrames); !childFrames.AtEnd();
+ childFrames.Next()) {
+ if (i == mPageNum) {
+ return childFrames.get();
+ }
+ ++i;
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone)
+{
+ nsIFrame* currentPage = GetCurrentPageFrame();
+ if (!currentPage) {
+ *aDone = true;
+ return NS_ERROR_FAILURE;
+ }
+
+ DetermineWhetherToPrintPage();
+ // Nothing to do if the current page doesn't get printed OR rendering to
+ // preview. For preview, the `CallPrintCallback` is called from within the
+ // HTMLCanvasElement::HandlePrintCallback.
+ if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) {
+ *aDone = true;
+ return NS_OK;
+ }
+
+ // If the canvasList is null, then generate it and start the render
+ // process for all the canvas.
+ if (!mCurrentCanvasListSetup) {
+ mCurrentCanvasListSetup = true;
+ GetPrintCanvasElementsInFrame(currentPage, &mCurrentCanvasList);
+
+ if (mCurrentCanvasList.Length() != 0) {
+ nsresult rv = NS_OK;
+
+ // Begin printing of the document
+ nsDeviceContext *dc = PresContext()->DeviceContext();
+ PR_PL(("\n"));
+ PR_PL(("***************** BeginPage *****************\n"));
+ rv = dc->BeginPage();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCalledBeginPage = true;
+
+ RefPtr<gfxContext> renderingContext = dc->CreateRenderingContext();
+ NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
+
+ DrawTarget* drawTarget = renderingContext->GetDrawTarget();
+ if (NS_WARN_IF(!drawTarget)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+ HTMLCanvasElement* canvas = mCurrentCanvasList[i];
+ nsIntSize size = canvas->GetSize();
+
+ RefPtr<DrawTarget> canvasTarget =
+ drawTarget->CreateSimilarDrawTarget(size, drawTarget->GetFormat());
+ if (!canvasTarget) {
+ continue;
+ }
+
+ nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0);
+ if (!ctx) {
+ continue;
+ }
+
+ // Initialize the context with the new DrawTarget.
+ ctx->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget));
+
+ // Start the rendering process.
+ nsWeakFrame weakFrame = this;
+ canvas->DispatchPrintCallback(aCallback);
+ NS_ENSURE_STATE(weakFrame.IsAlive());
+ }
+ }
+ }
+ uint32_t doneCounter = 0;
+ for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+ HTMLCanvasElement* canvas = mCurrentCanvasList[i];
+
+ if (canvas->IsPrintCallbackDone()) {
+ doneCounter++;
+ }
+ }
+ // If all canvas have finished rendering, return true, otherwise false.
+ *aDone = doneCounter == mCurrentCanvasList.Length();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::ResetPrintCanvasList()
+{
+ for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+ HTMLCanvasElement* canvas = mCurrentCanvasList[i];
+ canvas->ResetPrintCallback();
+ }
+
+ mCurrentCanvasList.Clear();
+ mCurrentCanvasListSetup = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::PrintNextPage()
+{
+ // Print each specified page
+ // pageNum keeps track of the current page and what pages are printing
+ //
+ // printedPageNum keeps track of the current page number to be printed
+ // Note: When print al the pages or a page range the printed page shows the
+ // actual page number, when printing selection it prints the page number starting
+ // with the first page of the selection. For example if the user has a
+ // selection that starts on page 2 and ends on page 3, the page numbers when
+ // print are 1 and then two (which is different than printing a page range, where
+ // the page numbers would have been 2 and then 3)
+
+ nsIFrame* currentPage = GetCurrentPageFrame();
+ if (!currentPage) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = NS_OK;
+
+ DetermineWhetherToPrintPage();
+
+ if (mPrintThisPage) {
+ // Begin printing of the document
+ nsDeviceContext* dc = PresContext()->DeviceContext();
+
+ // XXX This is temporary fix for printing more than one page of a selection
+ // This does a poor man's "dump" pagination (see Bug 89353)
+ // It has laid out as one long page and now we are just moving or view up/down
+ // one page at a time and printing the contents of what is exposed by the rect.
+ // currently this does not work for IFrames
+ // I will soon improve this to work with IFrames
+ bool continuePrinting = true;
+ nscoord width, height;
+ width = PresContext()->GetPageSize().width;
+ height = PresContext()->GetPageSize().height;
+ height -= mMargin.top + mMargin.bottom;
+ width -= mMargin.left + mMargin.right;
+ nscoord selectionY = height;
+ nsIFrame* conFrame = currentPage->PrincipalChildList().FirstChild();
+ if (mSelectionHeight >= 0) {
+ conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -mYSelOffset));
+ nsContainerFrame::PositionChildViews(conFrame);
+ }
+
+ // cast the frame to be a page frame
+ nsPageFrame * pf = static_cast<nsPageFrame*>(currentPage);
+ pf->SetPageNumInfo(mPageNum, mTotalPages);
+ pf->SetSharedPageData(mPageData);
+
+ int32_t printedPageNum = 1;
+ while (continuePrinting) {
+ if (PresContext()->IsRootPaginatedDocument()) {
+ if (!mCalledBeginPage) {
+ PR_PL(("\n"));
+ PR_PL(("***************** BeginPage *****************\n"));
+ rv = dc->BeginPage();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ mCalledBeginPage = false;
+ }
+ }
+
+ PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum));
+
+ // CreateRenderingContext can fail
+ RefPtr<gfxContext> gCtx = dc->CreateRenderingContext();
+ NS_ENSURE_TRUE(gCtx, NS_ERROR_OUT_OF_MEMORY);
+
+ nsRenderingContext renderingContext(gCtx);
+
+ nsRect drawingRect(nsPoint(0, 0), currentPage->GetSize());
+ nsRegion drawingRegion(drawingRect);
+ nsLayoutUtils::PaintFrame(&renderingContext, currentPage,
+ drawingRegion, NS_RGBA(0,0,0,0),
+ nsDisplayListBuilderMode::PAINTING,
+ nsLayoutUtils::PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES);
+
+ if (mSelectionHeight >= 0 && selectionY < mSelectionHeight) {
+ selectionY += height;
+ printedPageNum++;
+ pf->SetPageNumInfo(printedPageNum, mTotalPages);
+ conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -height));
+ nsContainerFrame::PositionChildViews(conFrame);
+
+ PR_PL(("***************** End Page (PrintNextPage) *****************\n"));
+ rv = dc->EndPage();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ continuePrinting = false;
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::DoPageEnd()
+{
+ nsresult rv = NS_OK;
+ if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) {
+ PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
+ rv = PresContext()->DeviceContext()->EndPage();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ ResetPrintCanvasList();
+
+ mPageNum++;
+
+ return rv;
+}
+
+inline gfx::Matrix4x4
+ComputePageSequenceTransform(nsIFrame* aFrame, float aAppUnitsPerPixel)
+{
+ float scale = aFrame->PresContext()->GetPrintPreviewScale();
+ return gfx::Matrix4x4::Scaling(scale, scale, 1);
+}
+
+void
+nsSimplePageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ nsDisplayList content;
+
+ {
+ // Clear clip state while we construct the children of the
+ // nsDisplayTransform, since they'll be in a different coordinate system.
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+ clipState.Clear();
+
+ nsIFrame* child = PrincipalChildList().FirstChild();
+ nsRect dirty = aDirtyRect;
+ dirty.ScaleInverseRoundOut(PresContext()->GetPrintPreviewScale());
+
+ while (child) {
+ if (child->GetVisualOverflowRectRelativeToParent().Intersects(dirty)) {
+ child->BuildDisplayListForStackingContext(aBuilder,
+ dirty - child->GetPosition(), &content);
+ aBuilder->ResetMarkedFramesForDisplayList();
+ }
+ child = child->GetNextSibling();
+ }
+ }
+
+ content.AppendNewToTop(new (aBuilder)
+ nsDisplayTransform(aBuilder, this, &content, content.GetVisibleRect(),
+ ::ComputePageSequenceTransform));
+
+ aLists.Content()->AppendToTop(&content);
+}
+
+nsIAtom*
+nsSimplePageSequenceFrame::GetType() const
+{
+ return nsGkAtoms::sequenceFrame;
+}
+
+//------------------------------------------------------------------------------
+void
+nsSimplePageSequenceFrame::SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly)
+{
+ NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!");
+
+ if (aForPageNumOnly) {
+ mPageData->mPageNumFormat = aFormatStr;
+ } else {
+ mPageData->mPageNumAndTotalsFormat = aFormatStr;
+ }
+}
+
+//------------------------------------------------------------------------------
+void
+nsSimplePageSequenceFrame::SetDateTimeStr(const nsAString& aDateTimeStr)
+{
+ NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!");
+
+ mPageData->mDateTimeStr = aDateTimeStr;
+}
+
+//------------------------------------------------------------------------------
+// For Shrink To Fit
+//
+// Return the percentage that the page needs to shrink to
+//
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::GetSTFPercent(float& aSTFPercent)
+{
+ NS_ENSURE_TRUE(mPageData, NS_ERROR_UNEXPECTED);
+ aSTFPercent = mPageData->mShrinkToFitRatio;
+ return NS_OK;
+}
diff --git a/layout/generic/nsSimplePageSequenceFrame.h b/layout/generic/nsSimplePageSequenceFrame.h
new file mode 100644
index 000000000..c4e1e84b6
--- /dev/null
+++ b/layout/generic/nsSimplePageSequenceFrame.h
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsSimplePageSequenceFrame_h___
+#define nsSimplePageSequenceFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsIPageSequenceFrame.h"
+#include "nsContainerFrame.h"
+#include "nsIPrintSettings.h"
+
+class nsIDateTimeFormat;
+
+namespace mozilla {
+namespace dom {
+
+class HTMLCanvasElement;
+
+} // namespace dom
+} // namespace mozilla
+
+//-----------------------------------------------
+// This class maintains all the data that
+// is used by all the page frame
+// It lives while the nsSimplePageSequenceFrame lives
+class nsSharedPageData {
+public:
+ // This object a shared by all the nsPageFrames
+ // parented to a SimplePageSequenceFrame
+ nsSharedPageData() : mShrinkToFitRatio(1.0f) {}
+
+ nsString mDateTimeStr;
+ nsString mPageNumFormat;
+ nsString mPageNumAndTotalsFormat;
+ nsString mDocTitle;
+ nsString mDocURL;
+ nsFont mHeadFootFont;
+
+ nsSize mReflowSize;
+ nsMargin mReflowMargin;
+ // Margin for headers and footers; it defaults to 4/100 of an inch on UNIX
+ // and 0 elsewhere; I think it has to do with some inconsistency in page size
+ // computations
+ nsMargin mEdgePaperMargin;
+
+ nsCOMPtr<nsIPrintSettings> mPrintSettings;
+
+ // The scaling ratio we need to apply to make all pages fit horizontally. It's
+ // the minimum "ComputedWidth / OverflowWidth" ratio of all page content frames
+ // that overflowed. It's 1.0 if none overflowed horizontally.
+ float mShrinkToFitRatio;
+};
+
+// Simple page sequence frame class. Used when we're in paginated mode
+class nsSimplePageSequenceFrame : public nsContainerFrame,
+ public nsIPageSequenceFrame {
+public:
+ friend nsSimplePageSequenceFrame* NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell,
+ nsStyleContext* aContext);
+
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ // nsIFrame
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aMaxSize,
+ nsReflowStatus& aStatus) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ // nsIPageSequenceFrame
+ NS_IMETHOD SetPageNo(int32_t aPageNo) { return NS_OK;}
+ NS_IMETHOD SetSelectionHeight(nscoord aYOffset, nscoord aHeight) override { mYSelOffset = aYOffset; mSelectionHeight = aHeight; return NS_OK; }
+ NS_IMETHOD SetTotalNumPages(int32_t aTotal) override { mTotalPages = aTotal; return NS_OK; }
+
+ // For Shrink To Fit
+ NS_IMETHOD GetSTFPercent(float& aSTFPercent) override;
+
+ // Async Printing
+ NS_IMETHOD StartPrint(nsPresContext* aPresContext,
+ nsIPrintSettings* aPrintSettings,
+ const nsAString& aDocTitle,
+ const nsAString& aDocURL) override;
+ NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) override;
+ NS_IMETHOD PrintNextPage() override;
+ NS_IMETHOD ResetPrintCanvasList() override;
+ NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum) override;
+ NS_IMETHOD GetNumPages(int32_t* aNumPages) override;
+ NS_IMETHOD IsDoingPrintRange(bool* aDoing) override;
+ NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage) override;
+ NS_IMETHOD DoPageEnd() override;
+
+ // We must allow Print Preview UI to have a background, no matter what the
+ // user's settings
+ virtual bool HonorPrintBackgroundSettings() override { return false; }
+
+ virtual bool HasTransformGetter() const override { return true; }
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::sequenceFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+protected:
+ explicit nsSimplePageSequenceFrame(nsStyleContext* aContext);
+ virtual ~nsSimplePageSequenceFrame();
+
+ void SetPageNumberFormat(const char* aPropName, const char* aDefPropVal, bool aPageNumOnly);
+
+ // SharedPageData Helper methods
+ void SetDateTimeStr(const nsAString& aDateTimeStr);
+ void SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly);
+
+ // Sets the frame desired size to the size of the viewport, or the given
+ // nscoords, whichever is larger. Print scaling is applied in this function.
+ void SetDesiredSize(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nscoord aWidth, nscoord aHeight);
+
+ // Helper function to compute the offset needed to center a child
+ // page-frame's margin-box inside our content-box.
+ nscoord ComputeCenteringMargin(nscoord aContainerContentBoxWidth,
+ nscoord aChildPaddingBoxWidth,
+ const nsMargin& aChildPhysicalMargin);
+
+
+ void DetermineWhetherToPrintPage();
+ nsIFrame* GetCurrentPageFrame();
+
+ nsMargin mMargin;
+
+ // I18N date formatter service which we'll want to cache locally.
+ nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
+
+ nsSize mSize;
+ nsSharedPageData* mPageData; // data shared by all the nsPageFrames
+
+ // Asynch Printing
+ int32_t mPageNum;
+ int32_t mTotalPages;
+ int32_t mPrintRangeType;
+ int32_t mFromPageNum;
+ int32_t mToPageNum;
+ nsTArray<int32_t> mPageRanges;
+ nsTArray<RefPtr<mozilla::dom::HTMLCanvasElement> > mCurrentCanvasList;
+
+ // Selection Printing Info
+ nscoord mSelectionHeight;
+ nscoord mYSelOffset;
+
+ // Asynch Printing
+ bool mPrintThisPage;
+ bool mDoingPageRange;
+
+ bool mIsPrintingSelection;
+
+ bool mCalledBeginPage;
+
+ bool mCurrentCanvasListSetup;
+};
+
+#endif /* nsSimplePageSequenceFrame_h___ */
+
diff --git a/layout/generic/nsSplittableFrame.cpp b/layout/generic/nsSplittableFrame.cpp
new file mode 100644
index 000000000..00fbabc0c
--- /dev/null
+++ b/layout/generic/nsSplittableFrame.cpp
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * base class for rendering objects that can be split across lines,
+ * columns, or pages
+ */
+
+#include "nsSplittableFrame.h"
+#include "nsContainerFrame.h"
+#include "nsIFrameInlines.h"
+
+using namespace mozilla;
+
+void
+nsSplittableFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsFrame::Init(aContent, aParent, aPrevInFlow);
+
+ if (aPrevInFlow) {
+ // Hook the frame into the flow
+ SetPrevInFlow(aPrevInFlow);
+ aPrevInFlow->SetNextInFlow(this);
+ }
+}
+
+void
+nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ // Disconnect from the flow list
+ if (mPrevContinuation || mNextContinuation) {
+ RemoveFromFlow(this);
+ }
+
+ // Let the base class destroy the frame
+ nsFrame::DestroyFrom(aDestructRoot);
+}
+
+nsSplittableType
+nsSplittableFrame::GetSplittableType() const
+{
+ return NS_FRAME_SPLITTABLE;
+}
+
+nsIFrame* nsSplittableFrame::GetPrevContinuation() const
+{
+ return mPrevContinuation;
+}
+
+void
+nsSplittableFrame::SetPrevContinuation(nsIFrame* aFrame)
+{
+ NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev continuation with incorrect type!");
+ NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
+ mPrevContinuation = aFrame;
+ RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+}
+
+nsIFrame* nsSplittableFrame::GetNextContinuation() const
+{
+ return mNextContinuation;
+}
+
+void
+nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame)
+{
+ NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next continuation with incorrect type!");
+ NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
+ mNextContinuation = aFrame;
+ if (aFrame)
+ aFrame->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+}
+
+nsIFrame*
+nsSplittableFrame::FirstContinuation() const
+{
+ nsSplittableFrame* firstContinuation = const_cast<nsSplittableFrame*>(this);
+ while (firstContinuation->mPrevContinuation) {
+ firstContinuation = static_cast<nsSplittableFrame*>(firstContinuation->mPrevContinuation);
+ }
+ MOZ_ASSERT(firstContinuation, "post-condition failed");
+ return firstContinuation;
+}
+
+nsIFrame*
+nsSplittableFrame::LastContinuation() const
+{
+ nsSplittableFrame* lastContinuation = const_cast<nsSplittableFrame*>(this);
+ while (lastContinuation->mNextContinuation) {
+ lastContinuation = static_cast<nsSplittableFrame*>(lastContinuation->mNextContinuation);
+ }
+ MOZ_ASSERT(lastContinuation, "post-condition failed");
+ return lastContinuation;
+}
+
+#ifdef DEBUG
+bool nsSplittableFrame::IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
+{
+ int32_t iterations = 0;
+ while (aFrame1 && iterations < 10) {
+ // Bail out after 10 iterations so we don't bog down debug builds too much
+ if (aFrame1 == aFrame2)
+ return true;
+ aFrame1 = aFrame1->GetPrevContinuation();
+ ++iterations;
+ }
+ return false;
+}
+
+bool nsSplittableFrame::IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
+{
+ int32_t iterations = 0;
+ while (aFrame1 && iterations < 10) {
+ // Bail out after 10 iterations so we don't bog down debug builds too much
+ if (aFrame1 == aFrame2)
+ return true;
+ aFrame1 = aFrame1->GetNextContinuation();
+ ++iterations;
+ }
+ return false;
+}
+#endif
+
+nsIFrame* nsSplittableFrame::GetPrevInFlow() const
+{
+ return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
+}
+
+void
+nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame)
+{
+ NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev in flow with incorrect type!");
+ NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
+ mPrevContinuation = aFrame;
+ AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+}
+
+nsIFrame* nsSplittableFrame::GetNextInFlow() const
+{
+ return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
+ mNextContinuation : nullptr;
+}
+
+void
+nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame)
+{
+ NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next in flow with incorrect type!");
+ NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
+ mNextContinuation = aFrame;
+ if (aFrame)
+ aFrame->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+}
+
+nsIFrame*
+nsSplittableFrame::FirstInFlow() const
+{
+ nsSplittableFrame* firstInFlow = const_cast<nsSplittableFrame*>(this);
+ while (nsIFrame* prev = firstInFlow->GetPrevInFlow()) {
+ firstInFlow = static_cast<nsSplittableFrame*>(prev);
+ }
+ MOZ_ASSERT(firstInFlow, "post-condition failed");
+ return firstInFlow;
+}
+
+nsIFrame*
+nsSplittableFrame::LastInFlow() const
+{
+ nsSplittableFrame* lastInFlow = const_cast<nsSplittableFrame*>(this);
+ while (nsIFrame* next = lastInFlow->GetNextInFlow()) {
+ lastInFlow = static_cast<nsSplittableFrame*>(next);
+ }
+ MOZ_ASSERT(lastInFlow, "post-condition failed");
+ return lastInFlow;
+}
+
+// Remove this frame from the flow. Connects prev in flow and next in flow
+void
+nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame)
+{
+ nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
+ nsIFrame* nextContinuation = aFrame->GetNextContinuation();
+
+ // The new continuation is fluid only if the continuation on both sides
+ // of the removed frame was fluid
+ if (aFrame->GetPrevInFlow() && aFrame->GetNextInFlow()) {
+ if (prevContinuation) {
+ prevContinuation->SetNextInFlow(nextContinuation);
+ }
+ if (nextContinuation) {
+ nextContinuation->SetPrevInFlow(prevContinuation);
+ }
+ } else {
+ if (prevContinuation) {
+ prevContinuation->SetNextContinuation(nextContinuation);
+ }
+ if (nextContinuation) {
+ nextContinuation->SetPrevContinuation(prevContinuation);
+ }
+ }
+
+ aFrame->SetPrevInFlow(nullptr);
+ aFrame->SetNextInFlow(nullptr);
+}
+
+nscoord
+nsSplittableFrame::GetConsumedBSize() const
+{
+ nscoord height = 0;
+ for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
+ height += prev->GetContentRectRelativeToSelf().height;
+ }
+ return height;
+}
+
+nscoord
+nsSplittableFrame::GetEffectiveComputedBSize(const ReflowInput& aReflowInput,
+ nscoord aConsumedBSize) const
+{
+ nscoord bSize = aReflowInput.ComputedBSize();
+ if (bSize == NS_INTRINSICSIZE) {
+ return NS_INTRINSICSIZE;
+ }
+
+ if (aConsumedBSize == NS_INTRINSICSIZE) {
+ aConsumedBSize = GetConsumedBSize();
+ }
+
+ bSize -= aConsumedBSize;
+
+ // We may have stretched the frame beyond its computed height. Oh well.
+ return std::max(0, bSize);
+}
+
+nsIFrame::LogicalSides
+nsSplittableFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
+{
+ if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
+ return LogicalSides(eLogicalSideBitsBBoth);
+ }
+
+ if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+ StyleBoxDecorationBreak::Clone)) {
+ return LogicalSides();
+ }
+
+ LogicalSides skip;
+ if (GetPrevInFlow()) {
+ skip |= eLogicalSideBitsBStart;
+ }
+
+ if (aReflowInput) {
+ // We're in the midst of reflow right now, so it's possible that we haven't
+ // created a nif yet. If our content height is going to exceed our available
+ // height, though, then we're going to need a next-in-flow, it just hasn't
+ // been created yet.
+
+ if (NS_UNCONSTRAINEDSIZE != aReflowInput->AvailableBSize()) {
+ nscoord effectiveCH = this->GetEffectiveComputedBSize(*aReflowInput);
+ if (effectiveCH != NS_INTRINSICSIZE &&
+ effectiveCH > aReflowInput->AvailableBSize()) {
+ // Our content height is going to exceed our available height, so we're
+ // going to need a next-in-flow.
+ skip |= eLogicalSideBitsBEnd;
+ }
+ }
+ } else {
+ nsIFrame* nif = GetNextInFlow();
+ if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
+ skip |= eLogicalSideBitsBEnd;
+ }
+ }
+
+ return skip;
+}
+
+LogicalSides
+nsSplittableFrame::PreReflowBlockLevelLogicalSkipSides() const
+{
+ if (MOZ_UNLIKELY(IS_TRUE_OVERFLOW_CONTAINER(this))) {
+ return LogicalSides(mozilla::eLogicalSideBitsBBoth);
+ }
+ if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak !=
+ StyleBoxDecorationBreak::Clone) &&
+ GetPrevInFlow()) {
+ return LogicalSides(mozilla::eLogicalSideBitsBStart);
+ }
+ return LogicalSides();
+}
+
+#ifdef DEBUG
+void
+nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
+{
+ nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent);
+ if (nullptr != mNextContinuation) {
+ IndentBy(out, aIndent);
+ fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation);
+ }
+ if (nullptr != mPrevContinuation) {
+ IndentBy(out, aIndent);
+ fprintf(out, "<prev-continuation va=\"%p\"/>\n", (void*)mPrevContinuation);
+ }
+
+}
+#endif
diff --git a/layout/generic/nsSplittableFrame.h b/layout/generic/nsSplittableFrame.h
new file mode 100644
index 000000000..0a26933f9
--- /dev/null
+++ b/layout/generic/nsSplittableFrame.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * base class for rendering objects that can be split across lines,
+ * columns, or pages
+ */
+
+#ifndef nsSplittableFrame_h___
+#define nsSplittableFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsFrame.h"
+
+// Derived class that allows splitting
+class nsSplittableFrame : public nsFrame
+{
+public:
+ NS_DECL_ABSTRACT_FRAME(nsSplittableFrame)
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual nsSplittableType GetSplittableType() const override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ /*
+ * Frame continuations can be either fluid or not:
+ * Fluid continuations ("in-flows") are the result of line breaking,
+ * column breaking, or page breaking.
+ * Other (non-fluid) continuations can be the result of BiDi frame splitting.
+ * A "flow" is a chain of fluid continuations.
+ */
+
+ // Get the previous/next continuation, regardless of its type (fluid or non-fluid).
+ virtual nsIFrame* GetPrevContinuation() const override;
+ virtual nsIFrame* GetNextContinuation() const override;
+
+ // Set a previous/next non-fluid continuation.
+ virtual void SetPrevContinuation(nsIFrame*) override;
+ virtual void SetNextContinuation(nsIFrame*) override;
+
+ // Get the first/last continuation for this frame.
+ virtual nsIFrame* FirstContinuation() const override;
+ virtual nsIFrame* LastContinuation() const override;
+
+#ifdef DEBUG
+ // Can aFrame2 be reached from aFrame1 by following prev/next continuations?
+ static bool IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2);
+ static bool IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2);
+#endif
+
+ // Get the previous/next continuation, only if it is fluid (an "in-flow").
+ nsIFrame* GetPrevInFlow() const;
+ nsIFrame* GetNextInFlow() const;
+
+ virtual nsIFrame* GetPrevInFlowVirtual() const override { return GetPrevInFlow(); }
+ virtual nsIFrame* GetNextInFlowVirtual() const override { return GetNextInFlow(); }
+
+ // Set a previous/next fluid continuation.
+ virtual void SetPrevInFlow(nsIFrame*) override;
+ virtual void SetNextInFlow(nsIFrame*) override;
+
+ // Get the first/last frame in the current flow.
+ virtual nsIFrame* FirstInFlow() const override;
+ virtual nsIFrame* LastInFlow() const override;
+
+ // Remove the frame from the flow. Connects the frame's prev-in-flow
+ // and its next-in-flow. This should only be called in frame Destroy() methods.
+ static void RemoveFromFlow(nsIFrame* aFrame);
+
+protected:
+ explicit nsSplittableFrame(nsStyleContext* aContext) : nsFrame(aContext) {}
+
+ /**
+ * Determine the height consumed by our previous-in-flows.
+ *
+ * @note (bz) This makes laying out a splittable frame with N in-flows
+ * O(N^2)! So, use this function with caution and minimize the number
+ * of calls to this method.
+ */
+ nscoord GetConsumedBSize() const;
+
+ /**
+ * Retrieve the effective computed block size of this frame, which is the
+ * computed block size, minus the block size consumed by any previous in-flows.
+ */
+ nscoord GetEffectiveComputedBSize(const ReflowInput& aReflowInput,
+ nscoord aConsumed = NS_INTRINSICSIZE) const;
+
+ /**
+ * @see nsIFrame::GetLogicalSkipSides()
+ */
+ virtual LogicalSides GetLogicalSkipSides(const ReflowInput* aReflowInput = nullptr) const override;
+
+ /**
+ * A faster version of GetLogicalSkipSides() that is intended to be used
+ * inside Reflow before it's known if |this| frame will be COMPLETE or not.
+ * It returns a result that assumes this fragment is the last and thus
+ * should apply the block-end border/padding etc (except for "true" overflow
+ * containers which always skip block sides). You're then expected to
+ * recalculate the block-end side (as needed) when you know |this| frame's
+ * reflow status is INCOMPLETE.
+ * This method is intended for frames that breaks in the block axis.
+ */
+ LogicalSides PreReflowBlockLevelLogicalSkipSides() const;
+
+#ifdef DEBUG
+ virtual void DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) override;
+#endif
+
+ nsIFrame* mPrevContinuation;
+ nsIFrame* mNextContinuation;
+};
+
+#endif /* nsSplittableFrame_h___ */
diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp
new file mode 100644
index 000000000..47026b73c
--- /dev/null
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -0,0 +1,1263 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object for replaced elements that contain a document, such
+ * as <frame>, <iframe>, and some <object>s
+ */
+
+#include "nsSubDocumentFrame.h"
+
+#include "gfxPrefs.h"
+
+#include "mozilla/layout/RenderFrameParent.h"
+
+#include "nsCOMPtr.h"
+#include "nsGenericHTMLElement.h"
+#include "nsGenericHTMLFrameElement.h"
+#include "nsAttrValueInlines.h"
+#include "nsIDocShell.h"
+#include "nsIContentViewer.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIDocument.h"
+#include "nsView.h"
+#include "nsViewManager.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsFrameSetFrame.h"
+#include "nsIDOMHTMLFrameElement.h"
+#include "nsIScrollable.h"
+#include "nsNameSpaceManager.h"
+#include "nsDisplayList.h"
+#include "nsIScrollableFrame.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsLayoutUtils.h"
+#include "FrameLayerBuilder.h"
+#include "nsPluginFrame.h"
+#include "nsContentUtils.h"
+#include "nsIPermissionManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIDOMMutationEvent.h"
+#include "mozilla/Preferences.h"
+
+using namespace mozilla;
+using mozilla::layout::RenderFrameParent;
+
+static bool sShowPreviousPage = true;
+
+static nsIDocument*
+GetDocumentFromView(nsView* aView)
+{
+ NS_PRECONDITION(aView, "");
+
+ nsViewManager* vm = aView->GetViewManager();
+ nsIPresShell* ps = vm ? vm->GetPresShell() : nullptr;
+ return ps ? ps->GetDocument() : nullptr;
+}
+
+nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
+ : nsAtomicContainerFrame(aContext)
+ , mIsInline(false)
+ , mPostedReflowCallback(false)
+ , mDidCreateDoc(false)
+ , mCallingShow(false)
+{
+}
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsSubDocumentFrame::AccessibleType()
+{
+ return a11y::eOuterDocType;
+}
+#endif
+
+NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
+ NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
+
+class AsyncFrameInit : public Runnable
+{
+public:
+ explicit AsyncFrameInit(nsIFrame* aFrame) : mFrame(aFrame) {}
+ NS_IMETHOD Run() override
+ {
+ PROFILER_LABEL("mozilla", "AsyncFrameInit::Run", js::ProfileEntry::Category::OTHER);
+ if (mFrame.IsAlive()) {
+ static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
+ }
+ return NS_OK;
+ }
+private:
+ nsWeakFrame mFrame;
+};
+
+static void
+InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent);
+
+static void
+EndSwapDocShellsForViews(nsView* aView);
+
+void
+nsSubDocumentFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ // determine if we are a <frame> or <iframe>
+ nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
+ mIsInline = frameElem ? false : true;
+
+ static bool addedShowPreviousPage = false;
+ if (!addedShowPreviousPage) {
+ Preferences::AddBoolVarCache(&sShowPreviousPage, "layout.show_previous_page", true);
+ addedShowPreviousPage = true;
+ }
+
+ nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ // We are going to create an inner view. If we need a view for the
+ // OuterFrame but we wait for the normal view creation path in
+ // nsCSSFrameConstructor, then we will lose because the inner view's
+ // parent will already have been set to some outer view (e.g., the
+ // canvas) when it really needs to have this frame's view as its
+ // parent. So, create this frame's view right away, whether we
+ // really need it or not, and the inner view will get it as the
+ // parent.
+ if (!HasView()) {
+ nsContainerFrame::CreateViewForFrame(this, true);
+ }
+ EnsureInnerView();
+
+ // Set the primary frame now so that nsDocumentViewer::FindContainerView
+ // called from within EndSwapDocShellsForViews below can find it if needed.
+ aContent->SetPrimaryFrame(this);
+
+ // If we have a detached subdoc's root view on our frame loader, re-insert
+ // it into the view tree. This happens when we've been reframed, and
+ // ensures the presentation persists across reframes. If the frame element
+ // has changed documents however, we blow away the presentation.
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ nsCOMPtr<nsIDocument> oldContainerDoc;
+ nsIFrame* detachedFrame =
+ frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc));
+ frameloader->SetDetachedSubdocFrame(nullptr, nullptr);
+ MOZ_ASSERT(oldContainerDoc || !detachedFrame);
+ if (oldContainerDoc) {
+ nsView* detachedView =
+ detachedFrame ? detachedFrame->GetView() : nullptr;
+ if (detachedView && oldContainerDoc == aContent->OwnerDoc()) {
+ // Restore stashed presentation.
+ ::InsertViewsInReverseOrder(detachedView, mInnerView);
+ ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
+ } else {
+ // Presentation is for a different document, don't restore it.
+ frameloader->Hide();
+ }
+ }
+ }
+
+ nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
+}
+
+void
+nsSubDocumentFrame::ShowViewer()
+{
+ if (mCallingShow) {
+ return;
+ }
+
+ if (!PresContext()->IsDynamic()) {
+ // We let the printing code take care of loading the document; just
+ // create the inner view for it to use.
+ (void) EnsureInnerView();
+ } else {
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ CSSIntSize margin = GetMarginAttributes();
+ nsWeakFrame weakThis(this);
+ mCallingShow = true;
+ const nsAttrValue* attrValue =
+ GetContent()->AsElement()->GetParsedAttr(nsGkAtoms::scrolling);
+ int32_t scrolling =
+ nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue);
+ bool didCreateDoc =
+ frameloader->Show(margin.width, margin.height,
+ scrolling, scrolling, this);
+ if (!weakThis.IsAlive()) {
+ return;
+ }
+ mCallingShow = false;
+ mDidCreateDoc = didCreateDoc;
+ }
+ }
+}
+
+nsIFrame*
+nsSubDocumentFrame::GetSubdocumentRootFrame()
+{
+ if (!mInnerView)
+ return nullptr;
+ nsView* subdocView = mInnerView->GetFirstChild();
+ return subdocView ? subdocView->GetFrame() : nullptr;
+}
+
+nsIPresShell*
+nsSubDocumentFrame::GetSubdocumentPresShellForPainting(uint32_t aFlags)
+{
+ if (!mInnerView)
+ return nullptr;
+
+ nsView* subdocView = mInnerView->GetFirstChild();
+ if (!subdocView)
+ return nullptr;
+
+ nsIPresShell* presShell = nullptr;
+
+ nsIFrame* subdocRootFrame = subdocView->GetFrame();
+ if (subdocRootFrame) {
+ presShell = subdocRootFrame->PresContext()->PresShell();
+ }
+
+ // If painting is suppressed in the presshell, we try to look for a better
+ // presshell to use.
+ if (!presShell || (presShell->IsPaintingSuppressed() &&
+ !(aFlags & IGNORE_PAINT_SUPPRESSION))) {
+ // During page transition mInnerView will sometimes have two children, the
+ // first being the new page that may not have any frame, and the second
+ // being the old page that will probably have a frame.
+ nsView* nextView = subdocView->GetNextSibling();
+ nsIFrame* frame = nullptr;
+ if (nextView) {
+ frame = nextView->GetFrame();
+ }
+ if (frame) {
+ nsIPresShell* ps = frame->PresContext()->PresShell();
+ if (!presShell || (ps && !ps->IsPaintingSuppressed() && sShowPreviousPage)) {
+ subdocView = nextView;
+ subdocRootFrame = frame;
+ presShell = ps;
+ }
+ }
+ if (!presShell) {
+ // If we don't have a frame we use this roundabout way to get the pres shell.
+ if (!mFrameLoader)
+ return nullptr;
+ nsCOMPtr<nsIDocShell> docShell;
+ mFrameLoader->GetDocShell(getter_AddRefs(docShell));
+ if (!docShell)
+ return nullptr;
+ presShell = docShell->GetPresShell();
+ }
+ }
+
+ return presShell;
+}
+
+
+
+
+ScreenIntSize
+nsSubDocumentFrame::GetSubdocumentSize()
+{
+ if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ nsCOMPtr<nsIDocument> oldContainerDoc;
+ nsIFrame* detachedFrame =
+ frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc));
+ nsView* view = detachedFrame ? detachedFrame->GetView() : nullptr;
+ if (view) {
+ nsSize size = view->GetBounds().Size();
+ nsPresContext* presContext = detachedFrame->PresContext();
+ return ScreenIntSize(presContext->AppUnitsToDevPixels(size.width),
+ presContext->AppUnitsToDevPixels(size.height));
+ }
+ }
+ // Pick some default size for now. Using 10x10 because that's what the
+ // code used to do.
+ return ScreenIntSize(10, 10);
+ } else {
+ nsSize docSizeAppUnits;
+ nsPresContext* presContext = PresContext();
+ nsCOMPtr<nsIDOMHTMLFrameElement> frameElem =
+ do_QueryInterface(GetContent());
+ if (frameElem) {
+ docSizeAppUnits = GetSize();
+ } else {
+ docSizeAppUnits = GetContentRect().Size();
+ }
+ // Adjust subdocument size, according to 'object-fit' and the
+ // subdocument's intrinsic size and ratio.
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ if (subDocRoot) {
+ nsRect destRect =
+ nsLayoutUtils::ComputeObjectDestRect(nsRect(nsPoint(), docSizeAppUnits),
+ subDocRoot->GetIntrinsicSize(),
+ subDocRoot->GetIntrinsicRatio(),
+ StylePosition());
+ docSizeAppUnits = destRect.Size();
+ }
+
+ return ScreenIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
+ presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
+ }
+}
+
+static void
+WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame,
+ nsDisplayList* aList)
+{
+ nsDisplayList tempItems;
+ nsDisplayItem* item;
+ while ((item = aList->RemoveBottom()) != nullptr) {
+ if (item->GetType() == nsDisplayItem::TYPE_BACKGROUND_COLOR) {
+ nsDisplayList tmpList;
+ tmpList.AppendToTop(item);
+ item = new (aBuilder) nsDisplayOwnLayer(aBuilder, aFrame, &tmpList);
+ }
+ tempItems.AppendToTop(item);
+ }
+ aList->AppendToTop(&tempItems);
+}
+
+void
+nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ nsFrameLoader* frameLoader = FrameLoader();
+ RenderFrameParent* rfp = nullptr;
+ if (frameLoader) {
+ rfp = frameLoader->GetCurrentRenderFrame();
+ }
+
+ // If we are pointer-events:none then we don't need to HitTest background
+ bool pointerEventsNone =
+ StyleUserInterface()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE;
+ if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) {
+ nsDisplayListCollection decorations;
+ DisplayBorderBackgroundOutline(aBuilder, decorations);
+ if (rfp) {
+ // Wrap background colors of <iframe>s with remote subdocuments in their
+ // own layer so we generate a ColorLayer. This is helpful for optimizing
+ // compositing; we can skip compositing the ColorLayer when the
+ // remote content is opaque.
+ WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground());
+ }
+ decorations.MoveTo(aLists);
+ }
+
+ if (aBuilder->IsForEventDelivery() && pointerEventsNone) {
+ return;
+ }
+
+ // If we're passing pointer events to children then we have to descend into
+ // subdocuments no matter what, to determine which parts are transparent for
+ // hit-testing or event regions.
+ bool needToDescend = aBuilder->GetDescendIntoSubdocuments();
+ if (!mInnerView || !needToDescend) {
+ return;
+ }
+
+ if (rfp) {
+ rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists);
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell =
+ GetSubdocumentPresShellForPainting(
+ aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0);
+
+ if (!presShell) {
+ return;
+ }
+
+ nsIFrame* subdocRootFrame = presShell->GetRootFrame();
+
+ nsPresContext* presContext = presShell->GetPresContext();
+
+ int32_t parentAPD = PresContext()->AppUnitsPerDevPixel();
+ int32_t subdocAPD = presContext->AppUnitsPerDevPixel();
+
+ nsRect dirty;
+ bool haveDisplayPort = false;
+ bool ignoreViewportScrolling = false;
+ nsIFrame* savedIgnoreScrollFrame = nullptr;
+ if (subdocRootFrame) {
+ // get the dirty rect relative to the root frame of the subdoc
+ dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
+ // and convert into the appunits of the subdoc
+ dirty = dirty.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
+
+ if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
+ nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable();
+ MOZ_ASSERT(rootScrollableFrame);
+ // Use a copy, so the dirty rect doesn't get modified to the display port.
+ nsRect copy = dirty;
+ haveDisplayPort = rootScrollableFrame->DecideScrollableLayer(aBuilder,
+ &copy, /* aAllowCreateDisplayPort = */ true);
+ if (!gfxPrefs::LayoutUseContainersForRootFrames()) {
+ haveDisplayPort = false;
+ }
+
+ ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
+ if (ignoreViewportScrolling) {
+ savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame();
+ aBuilder->SetIgnoreScrollFrame(rootScrollFrame);
+ }
+ }
+
+ aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone);
+ } else {
+ dirty = aDirtyRect;
+ }
+
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+ if (ShouldClipSubdocument()) {
+ clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
+ }
+
+ nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable();
+ bool constructResolutionItem = subdocRootFrame &&
+ (presShell->GetResolution() != 1.0);
+ bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
+ bool needsOwnLayer = false;
+ if (constructResolutionItem ||
+ constructZoomItem ||
+ haveDisplayPort ||
+ presContext->IsRootContentDocument() ||
+ (sf && sf->IsScrollingActive(aBuilder)))
+ {
+ needsOwnLayer = true;
+ }
+ if (!needsOwnLayer && aBuilder->IsBuildingLayerEventRegions() &&
+ nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell))
+ {
+ needsOwnLayer = true;
+ }
+
+ nsDisplayList childItems;
+
+ {
+ DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
+ if (needsOwnLayer) {
+ // Clear current clip. There's no point in propagating it down, since
+ // the layer we will construct will be clipped by the current clip.
+ // In fact for nsDisplayZoom propagating it down would be incorrect since
+ // nsDisplayZoom changes the meaning of appunits.
+ nestedClipState.EnterStackingContextContents(true);
+ }
+
+ if (subdocRootFrame) {
+ nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
+ nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
+ aBuilder,
+ ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent()
+ ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
+ : aBuilder->GetCurrentScrollParentId());
+
+ aBuilder->SetAncestorHasApzAwareEventHandler(false);
+ subdocRootFrame->
+ BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
+ }
+
+ if (!aBuilder->IsForEventDelivery()) {
+ // If we are going to use a displayzoom below then any items we put under
+ // it need to have underlying frames from the subdocument. So we need to
+ // calculate the bounds based on which frame will be the underlying frame
+ // for the canvas background color item.
+ nsRect bounds = GetContentRectRelativeToSelf() +
+ aBuilder->ToReferenceFrame(this);
+ if (subdocRootFrame) {
+ bounds = bounds.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
+ }
+
+ // If we are in print preview/page layout we want to paint the grey
+ // background behind the page, not the canvas color. The canvas color gets
+ // painted on the page itself.
+ if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
+ presShell->AddPrintPreviewBackgroundItem(
+ *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
+ bounds);
+ } else {
+ // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect
+ // is used to compute the visible rect if AddCanvasBackgroundColorItem
+ // creates a display item.
+ nsIFrame* frame = subdocRootFrame ? subdocRootFrame : this;
+ nsDisplayListBuilder::AutoBuildingDisplayList
+ building(aBuilder, frame, dirty, true);
+ // Add the canvas background color to the bottom of the list. This
+ // happens after we've built the list so that AddCanvasBackgroundColorItem
+ // can monkey with the contents if necessary.
+ uint32_t flags = nsIPresShell::FORCE_DRAW;
+ presShell->AddCanvasBackgroundColorItem(
+ *aBuilder, childItems, frame, bounds, NS_RGBA(0,0,0,0), flags);
+ }
+ }
+ }
+
+ if (subdocRootFrame) {
+ aBuilder->LeavePresShell(subdocRootFrame, &childItems);
+
+ if (ignoreViewportScrolling) {
+ aBuilder->SetIgnoreScrollFrame(savedIgnoreScrollFrame);
+ }
+ }
+
+ // Generate a resolution and/or zoom item if needed. If one or both of those is
+ // created, we don't need to create a separate nsDisplaySubDocument.
+
+ uint32_t flags = nsDisplayOwnLayer::GENERATE_SUBDOC_INVALIDATIONS;
+ // If ignoreViewportScrolling is true then the top most layer we create here
+ // is going to become the scrollable layer for the root scroll frame, so we
+ // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
+ // becomes the topmost. We do this below.
+ if (constructZoomItem) {
+ uint32_t zoomFlags = flags;
+ if (ignoreViewportScrolling && !constructResolutionItem) {
+ zoomFlags |= nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER;
+ }
+ nsDisplayZoom* zoomItem =
+ new (aBuilder) nsDisplayZoom(aBuilder, subdocRootFrame, &childItems,
+ subdocAPD, parentAPD, zoomFlags);
+ childItems.AppendToTop(zoomItem);
+ needsOwnLayer = false;
+ }
+ // Wrap the zoom item in the resolution item if we have both because we want the
+ // resolution scale applied on top of the app units per dev pixel conversion.
+ if (ignoreViewportScrolling) {
+ flags |= nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER;
+ }
+ if (constructResolutionItem) {
+ nsDisplayResolution* resolutionItem =
+ new (aBuilder) nsDisplayResolution(aBuilder, subdocRootFrame, &childItems,
+ flags);
+ childItems.AppendToTop(resolutionItem);
+ needsOwnLayer = false;
+ }
+ if (needsOwnLayer) {
+ // We always want top level content documents to be in their own layer.
+ nsDisplaySubDocument* layerItem = new (aBuilder) nsDisplaySubDocument(
+ aBuilder, subdocRootFrame ? subdocRootFrame : this,
+ &childItems, flags);
+ childItems.AppendToTop(layerItem);
+ }
+
+ if (aBuilder->IsForFrameVisibility()) {
+ // We don't add the childItems to the return list as we're dealing with them here.
+ presShell->RebuildApproximateFrameVisibilityDisplayList(childItems);
+ childItems.DeleteAll();
+ } else {
+ aLists.Content()->AppendToTop(&childItems);
+ }
+}
+
+nscoord
+nsSubDocumentFrame::GetIntrinsicISize()
+{
+ if (!IsInline()) {
+ return 0; // HTML <frame> has no useful intrinsic isize
+ }
+
+ if (mContent->IsXULElement()) {
+ return 0; // XUL <iframe> and <browser> have no useful intrinsic isize
+ }
+
+ NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr,
+ "Intrinsic isize should come from the embedded document.");
+
+ // We must be an HTML <iframe>. Default to size of 300px x 150px, for IE
+ // compat (and per CSS2.1 draft).
+ WritingMode wm = GetWritingMode();
+ return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 150 : 300);
+}
+
+nscoord
+nsSubDocumentFrame::GetIntrinsicBSize()
+{
+ // <frame> processing does not use this routine, only <iframe>
+ NS_ASSERTION(IsInline(), "Shouldn't have been called");
+
+ if (mContent->IsXULElement()) {
+ return 0;
+ }
+
+ NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr,
+ "Intrinsic bsize should come from the embedded document.");
+
+ // Use size of 300px x 150px, for compatibility with IE, and per CSS2.1 draft.
+ WritingMode wm = GetWritingMode();
+ return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 300 : 150);
+}
+
+#ifdef DEBUG_FRAME_DUMP
+void
+nsSubDocumentFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+ fprintf_stderr(out, "%s\n", str.get());
+
+ if (aFlags & TRAVERSE_SUBDOCUMENT_FRAMES) {
+ nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this);
+ nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame();
+ if (subdocRootFrame) {
+ nsCString pfx(aPrefix);
+ pfx += " ";
+ subdocRootFrame->List(out, pfx.get(), aFlags);
+ }
+ }
+}
+
+nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("FrameOuter"), aResult);
+}
+#endif
+
+nsIAtom*
+nsSubDocumentFrame::GetType() const
+{
+ return nsGkAtoms::subDocumentFrame;
+}
+
+/* virtual */ nscoord
+nsSubDocumentFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_MIN_WIDTH(this, result);
+
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ if (subDocRoot) {
+ result = subDocRoot->GetMinISize(aRenderingContext);
+ } else {
+ result = GetIntrinsicISize();
+ }
+
+ return result;
+}
+
+/* virtual */ nscoord
+nsSubDocumentFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_PREF_WIDTH(this, result);
+
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ if (subDocRoot) {
+ result = subDocRoot->GetPrefISize(aRenderingContext);
+ } else {
+ result = GetIntrinsicISize();
+ }
+
+ return result;
+}
+
+/* virtual */ IntrinsicSize
+nsSubDocumentFrame::GetIntrinsicSize()
+{
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ if (subDocRoot) {
+ return subDocRoot->GetIntrinsicSize();
+ }
+ return nsAtomicContainerFrame::GetIntrinsicSize();
+}
+
+/* virtual */ nsSize
+nsSubDocumentFrame::GetIntrinsicRatio()
+{
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ if (subDocRoot) {
+ return subDocRoot->GetIntrinsicRatio();
+ }
+ return nsAtomicContainerFrame::GetIntrinsicRatio();
+}
+
+/* virtual */
+LogicalSize
+nsSubDocumentFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ if (!IsInline()) {
+ return nsFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
+ aAvailableISize, aMargin, aBorder,
+ aPadding, aFlags);
+ }
+
+ const WritingMode wm = GetWritingMode();
+ LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
+ return result.ConvertTo(aWM, wm);
+}
+
+
+/* virtual */
+LogicalSize
+nsSubDocumentFrame::ComputeSize(nsRenderingContext* aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ if (subDocRoot) {
+ return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
+ subDocRoot->GetIntrinsicSize(),
+ subDocRoot->GetIntrinsicRatio(),
+ aCBSize, aMargin, aBorder,
+ aPadding, aFlags);
+ }
+ return nsAtomicContainerFrame::ComputeSize(aRenderingContext, aWM,
+ aCBSize, aAvailableISize,
+ aMargin, aBorder, aPadding,
+ aFlags);
+}
+
+void
+nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
+ aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ NS_ASSERTION(aReflowInput.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
+ "Shouldn't have unconstrained stuff here "
+ "thanks to the rules of reflow");
+ NS_ASSERTION(NS_INTRINSICSIZE != aReflowInput.ComputedHeight(),
+ "Shouldn't have unconstrained stuff here "
+ "thanks to ComputeAutoSize");
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ NS_ASSERTION(mContent->GetPrimaryFrame() == this,
+ "Shouldn't happen");
+
+ // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
+ aDesiredSize.SetSize(aReflowInput.GetWritingMode(),
+ aReflowInput.ComputedSizeWithBorderPadding());
+
+ // "offset" is the offset of our content area from our frame's
+ // top-left corner.
+ nsPoint offset = nsPoint(aReflowInput.ComputedPhysicalBorderPadding().left,
+ aReflowInput.ComputedPhysicalBorderPadding().top);
+
+ if (mInnerView) {
+ const nsMargin& bp = aReflowInput.ComputedPhysicalBorderPadding();
+ nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(),
+ aDesiredSize.Height() - bp.TopBottom());
+
+ // Size & position the view according to 'object-fit' & 'object-position'.
+ nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
+ IntrinsicSize intrinsSize;
+ nsSize intrinsRatio;
+ if (subDocRoot) {
+ intrinsSize = subDocRoot->GetIntrinsicSize();
+ intrinsRatio = subDocRoot->GetIntrinsicRatio();
+ }
+ nsRect destRect =
+ nsLayoutUtils::ComputeObjectDestRect(nsRect(offset, innerSize),
+ intrinsSize, intrinsRatio,
+ StylePosition());
+
+ nsViewManager* vm = mInnerView->GetViewManager();
+ vm->MoveViewTo(mInnerView, destRect.x, destRect.y);
+ vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true);
+ }
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+ if (!ShouldClipSubdocument()) {
+ nsIFrame* subdocRootFrame = GetSubdocumentRootFrame();
+ if (subdocRootFrame) {
+ aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset);
+ }
+ }
+
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ if (!aPresContext->IsPaginated() && !mPostedReflowCallback) {
+ PresContext()->PresShell()->PostReflowCallback(this);
+ mPostedReflowCallback = true;
+ }
+
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x",
+ aDesiredSize.Width(), aDesiredSize.Height(), aStatus));
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+bool
+nsSubDocumentFrame::ReflowFinished()
+{
+ if (mFrameLoader) {
+ nsWeakFrame weakFrame(this);
+
+ mFrameLoader->UpdatePositionAndSize(this);
+
+ if (weakFrame.IsAlive()) {
+ // Make sure that we can post a reflow callback in the future.
+ mPostedReflowCallback = false;
+ }
+ } else {
+ mPostedReflowCallback = false;
+ }
+ return false;
+}
+
+void
+nsSubDocumentFrame::ReflowCallbackCanceled()
+{
+ mPostedReflowCallback = false;
+}
+
+nsresult
+nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ if (aNameSpaceID != kNameSpaceID_None) {
+ return NS_OK;
+ }
+
+ // If the noResize attribute changes, dis/allow frame to be resized
+ if (aAttribute == nsGkAtoms::noresize) {
+ // Note that we're not doing content type checks, but that's ok -- if
+ // they'd fail we will just end up with a null framesetFrame.
+ if (mContent->GetParent()->IsHTMLElement(nsGkAtoms::frameset)) {
+ nsIFrame* parentFrame = GetParent();
+
+ if (parentFrame) {
+ // There is no interface for nsHTMLFramesetFrame so QI'ing to
+ // concrete class, yay!
+ nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
+ if (framesetFrame) {
+ framesetFrame->RecalculateBorderResize();
+ }
+ }
+ }
+ }
+ else if (aAttribute == nsGkAtoms::showresizer) {
+ nsIFrame* rootFrame = GetSubdocumentRootFrame();
+ if (rootFrame) {
+ rootFrame->PresContext()->PresShell()->
+ FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
+ }
+ else if (aAttribute == nsGkAtoms::marginwidth ||
+ aAttribute == nsGkAtoms::marginheight) {
+
+ // Retrieve the attributes
+ CSSIntSize margins = GetMarginAttributes();
+
+ // Notify the frameloader
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader)
+ frameloader->MarginsChanged(margins.width, margins.height);
+ }
+
+ return NS_OK;
+}
+
+nsIFrame*
+NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsSubDocumentFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
+
+class nsHideViewer : public Runnable {
+public:
+ nsHideViewer(nsIContent* aFrameElement,
+ nsFrameLoader* aFrameLoader,
+ nsIPresShell* aPresShell,
+ bool aHideViewerIfFrameless)
+ : mFrameElement(aFrameElement),
+ mFrameLoader(aFrameLoader),
+ mPresShell(aPresShell),
+ mHideViewerIfFrameless(aHideViewerIfFrameless)
+ {
+ NS_ASSERTION(mFrameElement, "Must have a frame element");
+ NS_ASSERTION(mFrameLoader, "Must have a frame loader");
+ NS_ASSERTION(mPresShell, "Must have a presshell");
+ }
+
+ NS_IMETHOD Run() override
+ {
+ // Flush frames, to ensure any pending display:none changes are made.
+ // Note it can be unsafe to flush if we've destroyed the presentation
+ // for some other reason, like if we're shutting down.
+ if (!mPresShell->IsDestroying()) {
+ mPresShell->FlushPendingNotifications(Flush_Frames);
+ }
+
+ // Either the frame has been constructed by now, or it never will be,
+ // either way we want to clear the stashed views.
+ mFrameLoader->SetDetachedSubdocFrame(nullptr, nullptr);
+
+ nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame());
+ if ((!frame && mHideViewerIfFrameless) ||
+ mPresShell->IsDestroying()) {
+ // Either the frame element has no nsIFrame or the presshell is being
+ // destroyed. Hide the nsFrameLoader, which destroys the presentation.
+ mFrameLoader->Hide();
+ }
+ return NS_OK;
+ }
+private:
+ nsCOMPtr<nsIContent> mFrameElement;
+ RefPtr<nsFrameLoader> mFrameLoader;
+ nsCOMPtr<nsIPresShell> mPresShell;
+ bool mHideViewerIfFrameless;
+};
+
+static nsView*
+BeginSwapDocShellsForViews(nsView* aSibling);
+
+void
+nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ if (mPostedReflowCallback) {
+ PresContext()->PresShell()->CancelReflowCallback(this);
+ mPostedReflowCallback = false;
+ }
+
+ // Detach the subdocument's views and stash them in the frame loader.
+ // We can then reattach them if we're being reframed (for example if
+ // the frame has been made position:fixed).
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ nsView* detachedViews = ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild());
+
+ if (detachedViews && detachedViews->GetFrame()) {
+ MOZ_ASSERT(mContent->OwnerDoc());
+ frameloader->SetDetachedSubdocFrame(
+ detachedViews->GetFrame(), mContent->OwnerDoc());
+
+ // We call nsFrameLoader::HideViewer() in a script runner so that we can
+ // safely determine whether the frame is being reframed or destroyed.
+ nsContentUtils::AddScriptRunner(
+ new nsHideViewer(mContent,
+ frameloader,
+ PresContext()->PresShell(),
+ (mDidCreateDoc || mCallingShow)));
+ } else {
+ frameloader->SetDetachedSubdocFrame(nullptr, nullptr);
+ if (mDidCreateDoc || mCallingShow) {
+ frameloader->Hide();
+ }
+ }
+ }
+
+ nsAtomicContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+CSSIntSize
+nsSubDocumentFrame::GetMarginAttributes()
+{
+ CSSIntSize result(-1, -1);
+ nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
+ if (content) {
+ const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
+ if (attr && attr->Type() == nsAttrValue::eInteger)
+ result.width = attr->GetIntegerValue();
+ attr = content->GetParsedAttr(nsGkAtoms::marginheight);
+ if (attr && attr->Type() == nsAttrValue::eInteger)
+ result.height = attr->GetIntegerValue();
+ }
+ return result;
+}
+
+nsFrameLoader*
+nsSubDocumentFrame::FrameLoader()
+{
+ nsIContent* content = GetContent();
+ if (!content)
+ return nullptr;
+
+ if (!mFrameLoader) {
+ nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(content);
+ if (loaderOwner) {
+ mFrameLoader = loaderOwner->GetFrameLoader();
+ }
+ }
+ return mFrameLoader;
+}
+
+// XXX this should be called ObtainDocShell or something like that,
+// to indicate that it could have side effects
+nsresult
+nsSubDocumentFrame::GetDocShell(nsIDocShell **aDocShell)
+{
+ *aDocShell = nullptr;
+
+ NS_ENSURE_STATE(FrameLoader());
+ return mFrameLoader->GetDocShell(aDocShell);
+}
+
+static void
+DestroyDisplayItemDataForFrames(nsIFrame* aFrame)
+{
+ FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame);
+
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ DestroyDisplayItemDataForFrames(childFrames.get());
+ }
+ }
+}
+
+static bool
+BeginSwapDocShellsForDocument(nsIDocument* aDocument, void*)
+{
+ NS_PRECONDITION(aDocument, "");
+
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ // Disable painting while the views are detached, see bug 946929.
+ shell->SetNeverPainting(true);
+
+ nsIFrame* rootFrame = shell->GetRootFrame();
+ if (rootFrame) {
+ ::DestroyDisplayItemDataForFrames(rootFrame);
+ }
+ }
+ aDocument->EnumerateActivityObservers(
+ nsPluginFrame::BeginSwapDocShells, nullptr);
+ aDocument->EnumerateSubDocuments(BeginSwapDocShellsForDocument, nullptr);
+ return true;
+}
+
+static nsView*
+BeginSwapDocShellsForViews(nsView* aSibling)
+{
+ // Collect the removed sibling views in reverse order in 'removedViews'.
+ nsView* removedViews = nullptr;
+ while (aSibling) {
+ nsIDocument* doc = ::GetDocumentFromView(aSibling);
+ if (doc) {
+ ::BeginSwapDocShellsForDocument(doc, nullptr);
+ }
+ nsView* next = aSibling->GetNextSibling();
+ aSibling->GetViewManager()->RemoveChild(aSibling);
+ aSibling->SetNextSibling(removedViews);
+ removedViews = aSibling;
+ aSibling = next;
+ }
+ return removedViews;
+}
+
+static void
+InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent)
+{
+ NS_PRECONDITION(aParent, "");
+ NS_PRECONDITION(!aParent->GetFirstChild(), "inserting into non-empty list");
+
+ nsViewManager* vm = aParent->GetViewManager();
+ while (aSibling) {
+ nsView* next = aSibling->GetNextSibling();
+ aSibling->SetNextSibling(nullptr);
+ // true means 'after' in document order which is 'before' in view order,
+ // so this call prepends the child, thus reversing the siblings as we go.
+ vm->InsertChild(aParent, aSibling, nullptr, true);
+ aSibling = next;
+ }
+}
+
+nsresult
+nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther)
+{
+ if (!aOther || aOther->GetType() != nsGkAtoms::subDocumentFrame) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
+ if (!mFrameLoader || !mDidCreateDoc || mCallingShow ||
+ !other->mFrameLoader || !other->mDidCreateDoc) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ if (mInnerView && other->mInnerView) {
+ nsView* ourSubdocViews = mInnerView->GetFirstChild();
+ nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
+ nsView* otherSubdocViews = other->mInnerView->GetFirstChild();
+ nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
+
+ ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
+ ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
+ }
+ mFrameLoader.swap(other->mFrameLoader);
+ return NS_OK;
+}
+
+static bool
+EndSwapDocShellsForDocument(nsIDocument* aDocument, void*)
+{
+ NS_PRECONDITION(aDocument, "");
+
+ // Our docshell and view trees have been updated for the new hierarchy.
+ // Now also update all nsDeviceContext::mWidget to that of the
+ // container view in the new hierarchy.
+ nsCOMPtr<nsIDocShell> ds = aDocument->GetDocShell();
+ if (ds) {
+ nsCOMPtr<nsIContentViewer> cv;
+ ds->GetContentViewer(getter_AddRefs(cv));
+ while (cv) {
+ RefPtr<nsPresContext> pc;
+ cv->GetPresContext(getter_AddRefs(pc));
+ if (pc && pc->GetPresShell()) {
+ pc->GetPresShell()->SetNeverPainting(ds->IsInvisible());
+ }
+ nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr;
+ if (dc) {
+ nsView* v = cv->FindContainerView();
+ dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
+ }
+ nsCOMPtr<nsIContentViewer> prev;
+ cv->GetPreviousViewer(getter_AddRefs(prev));
+ cv = prev;
+ }
+ }
+
+ aDocument->EnumerateActivityObservers(
+ nsPluginFrame::EndSwapDocShells, nullptr);
+ aDocument->EnumerateSubDocuments(EndSwapDocShellsForDocument, nullptr);
+ return true;
+}
+
+static void
+EndSwapDocShellsForViews(nsView* aSibling)
+{
+ for ( ; aSibling; aSibling = aSibling->GetNextSibling()) {
+ nsIDocument* doc = ::GetDocumentFromView(aSibling);
+ if (doc) {
+ ::EndSwapDocShellsForDocument(doc, nullptr);
+ }
+ nsIFrame *frame = aSibling->GetFrame();
+ if (frame) {
+ nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
+ if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
+ nsIFrame::AddInPopupStateBitToDescendants(frame);
+ } else {
+ nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
+ }
+ if (frame->HasInvalidFrameInSubtree()) {
+ while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT | NS_FRAME_IS_NONDISPLAY)) {
+ parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
+ parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
+ }
+ }
+ }
+ }
+}
+
+void
+nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther)
+{
+ nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
+ nsWeakFrame weakThis(this);
+ nsWeakFrame weakOther(aOther);
+
+ if (mInnerView) {
+ ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
+ }
+ if (other->mInnerView) {
+ ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
+ }
+
+ // Now make sure we reflow both frames, in case their contents
+ // determine their size.
+ // And repaint them, for good measure, in case there's nothing
+ // interesting that happens during reflow.
+ if (weakThis.IsAlive()) {
+ PresContext()->PresShell()->
+ FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
+ InvalidateFrameSubtree();
+ }
+ if (weakOther.IsAlive()) {
+ other->PresContext()->PresShell()->
+ FrameNeedsReflow(other, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
+ other->InvalidateFrameSubtree();
+ }
+}
+
+nsView*
+nsSubDocumentFrame::EnsureInnerView()
+{
+ if (mInnerView) {
+ return mInnerView;
+ }
+
+ // create, init, set the parent of the view
+ nsView* outerView = GetView();
+ NS_ASSERTION(outerView, "Must have an outer view already");
+ nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
+
+ nsViewManager* viewMan = outerView->GetViewManager();
+ nsView* innerView = viewMan->CreateView(viewBounds, outerView);
+ if (!innerView) {
+ NS_ERROR("Could not create inner view");
+ return nullptr;
+ }
+ mInnerView = innerView;
+ viewMan->InsertChild(outerView, innerView, nullptr, true);
+
+ return mInnerView;
+}
+
+nsIFrame*
+nsSubDocumentFrame::ObtainIntrinsicSizeFrame()
+{
+ nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(GetContent());
+ if (olc) {
+ // We are an HTML <object>, <embed> or <applet> (a replaced element).
+
+ // Try to get an nsIFrame for our sub-document's document element
+ nsIFrame* subDocRoot = nullptr;
+
+ nsCOMPtr<nsIDocShell> docShell;
+ GetDocShell(getter_AddRefs(docShell));
+ if (docShell) {
+ nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
+ if (presShell) {
+ nsIScrollableFrame* scrollable = presShell->GetRootScrollFrameAsScrollable();
+ if (scrollable) {
+ nsIFrame* scrolled = scrollable->GetScrolledFrame();
+ if (scrolled) {
+ subDocRoot = scrolled->PrincipalChildList().FirstChild();
+ }
+ }
+ }
+ }
+
+ if (subDocRoot && subDocRoot->GetContent() &&
+ subDocRoot->GetContent()->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
+ return subDocRoot; // SVG documents have an intrinsic size
+ }
+ }
+ return nullptr;
+}
diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h
new file mode 100644
index 000000000..54f08d4fe
--- /dev/null
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSSUBDOCUMENTFRAME_H_
+#define NSSUBDOCUMENTFRAME_H_
+
+#include "mozilla/Attributes.h"
+#include "nsAtomicContainerFrame.h"
+#include "nsIReflowCallback.h"
+#include "nsFrameLoader.h"
+#include "Units.h"
+
+/******************************************************************************
+ * nsSubDocumentFrame
+ *****************************************************************************/
+class nsSubDocumentFrame : public nsAtomicContainerFrame,
+ public nsIReflowCallback
+{
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsSubDocumentFrame)
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit nsSubDocumentFrame(nsStyleContext* aContext);
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ NS_DECL_QUERYFRAME
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsAtomicContainerFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eReplaced |
+ nsIFrame::eReplacedSizing |
+ nsIFrame::eReplacedContainsBlock));
+ }
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+
+ virtual mozilla::IntrinsicSize GetIntrinsicSize() override;
+ virtual nsSize GetIntrinsicRatio() override;
+
+ virtual mozilla::LogicalSize
+ ComputeAutoSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext* aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+ // if the content is "visibility:hidden", then just hide the view
+ // and all our contents. We don't extend "visibility:hidden" to
+ // the child content ourselves, since it belongs to a different
+ // document and CSS doesn't inherit in there.
+ virtual bool SupportsVisibilityHidden() override { return false; }
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ nsresult GetDocShell(nsIDocShell **aDocShell);
+ nsresult BeginSwapDocShells(nsIFrame* aOther);
+ void EndSwapDocShells(nsIFrame* aOther);
+ nsView* EnsureInnerView();
+ nsIFrame* GetSubdocumentRootFrame();
+ enum {
+ IGNORE_PAINT_SUPPRESSION = 0x1
+ };
+ nsIPresShell* GetSubdocumentPresShellForPainting(uint32_t aFlags);
+ mozilla::ScreenIntSize GetSubdocumentSize();
+
+ // nsIReflowCallback
+ virtual bool ReflowFinished() override;
+ virtual void ReflowCallbackCanceled() override;
+
+ bool ShouldClipSubdocument()
+ {
+ nsFrameLoader* frameLoader = FrameLoader();
+ return !frameLoader || frameLoader->ShouldClipSubdocument();
+ }
+
+ bool ShouldClampScrollPosition()
+ {
+ nsFrameLoader* frameLoader = FrameLoader();
+ return !frameLoader || frameLoader->ShouldClampScrollPosition();
+ }
+
+ /**
+ * Return true if pointer event hit-testing should be allowed to target
+ * content in the subdocument.
+ */
+ bool PassPointerEventsToChildren();
+
+protected:
+ friend class AsyncFrameInit;
+
+ // Helper method to look up the HTML marginwidth & marginheight attributes.
+ mozilla::CSSIntSize GetMarginAttributes();
+
+ nsFrameLoader* FrameLoader();
+
+ bool IsInline() { return mIsInline; }
+
+ nscoord GetIntrinsicISize();
+ nscoord GetIntrinsicBSize();
+
+ // Show our document viewer. The document viewer is hidden via a script
+ // runner, so that we can save and restore the presentation if we're
+ // being reframed.
+ void ShowViewer();
+
+ /* Obtains the frame we should use for intrinsic size information if we are
+ * an HTML <object>, <embed> or <applet> (a replaced element - not <iframe>)
+ * and our sub-document has an intrinsic size. The frame returned is the
+ * frame for the document element of the document we're embedding.
+ *
+ * Called "Obtain*" and not "Get*" because of comment on GetDocShell that
+ * says it should be called ObtainDocShell because of its side effects.
+ */
+ nsIFrame* ObtainIntrinsicSizeFrame();
+
+ RefPtr<nsFrameLoader> mFrameLoader;
+ nsView* mInnerView;
+ bool mIsInline;
+ bool mPostedReflowCallback;
+ bool mDidCreateDoc;
+ bool mCallingShow;
+};
+
+#endif /* NSSUBDOCUMENTFRAME_H_ */
diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp
new file mode 100644
index 000000000..b9848bcf1
--- /dev/null
+++ b/layout/generic/nsTextFrame.cpp
@@ -0,0 +1,10051 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for textual content of elements */
+
+#include "nsTextFrame.h"
+
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/IntegerRange.h"
+#include "mozilla/Unused.h"
+
+#include "nsCOMPtr.h"
+#include "nsBlockFrame.h"
+#include "nsFontMetrics.h"
+#include "nsSplittableFrame.h"
+#include "nsLineLayout.h"
+#include "nsString.h"
+#include "nsUnicharUtils.h"
+#include "nsPresContext.h"
+#include "nsIContent.h"
+#include "nsStyleConsts.h"
+#include "nsStyleContext.h"
+#include "nsStyleStruct.h"
+#include "nsStyleStructInlines.h"
+#include "SVGTextFrame.h"
+#include "nsCoord.h"
+#include "nsRenderingContext.h"
+#include "nsIPresShell.h"
+#include "nsTArray.h"
+#include "nsCSSPseudoElements.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsCompatibility.h"
+#include "nsCSSColorUtils.h"
+#include "nsLayoutUtils.h"
+#include "nsDisplayList.h"
+#include "nsFrame.h"
+#include "nsIMathMLFrame.h"
+#include "nsPlaceholderFrame.h"
+#include "nsTextFrameUtils.h"
+#include "nsTextRunTransformations.h"
+#include "MathMLTextRunFactory.h"
+#include "nsUnicodeProperties.h"
+#include "nsStyleUtil.h"
+#include "nsRubyFrame.h"
+
+#include "nsTextFragment.h"
+#include "nsGkAtoms.h"
+#include "nsFrameSelection.h"
+#include "nsRange.h"
+#include "nsCSSRendering.h"
+#include "nsContentUtils.h"
+#include "nsLineBreaker.h"
+#include "nsIWordBreaker.h"
+#include "nsGenericDOMDataNode.h"
+#include "nsIFrameInlines.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+
+#include <algorithm>
+#include <limits>
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+#include "nsPrintfCString.h"
+
+#include "gfxContext.h"
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/LookAndFeel.h"
+
+#include "GeckoProfiler.h"
+
+#ifdef DEBUG
+#undef NOISY_REFLOW
+#undef NOISY_TRIM
+#else
+#undef NOISY_REFLOW
+#undef NOISY_TRIM
+#endif
+
+#ifdef DrawText
+#undef DrawText
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+
+struct TabWidth {
+ TabWidth(uint32_t aOffset, uint32_t aWidth)
+ : mOffset(aOffset), mWidth(float(aWidth))
+ { }
+
+ uint32_t mOffset; // DOM offset relative to the current frame's offset.
+ float mWidth; // extra space to be added at this position (in app units)
+};
+
+struct TabWidthStore {
+ explicit TabWidthStore(int32_t aValidForContentOffset)
+ : mLimit(0)
+ , mValidForContentOffset(aValidForContentOffset)
+ { }
+
+ // Apply tab widths to the aSpacing array, which corresponds to characters
+ // beginning at aOffset and has length aLength. (Width records outside this
+ // range will be ignored.)
+ void ApplySpacing(gfxTextRun::PropertyProvider::Spacing *aSpacing,
+ uint32_t aOffset, uint32_t aLength);
+
+ // Offset up to which tabs have been measured; positions beyond this have not
+ // been calculated yet but may be appended if needed later. It's a DOM
+ // offset relative to the current frame's offset.
+ uint32_t mLimit;
+
+ // Need to recalc tab offsets if frame content offset differs from this.
+ int32_t mValidForContentOffset;
+
+ // A TabWidth record for each tab character measured so far.
+ nsTArray<TabWidth> mWidths;
+};
+
+namespace {
+
+struct TabwidthAdaptor
+{
+ const nsTArray<TabWidth>& mWidths;
+ explicit TabwidthAdaptor(const nsTArray<TabWidth>& aWidths)
+ : mWidths(aWidths) {}
+ uint32_t operator[](size_t aIdx) const {
+ return mWidths[aIdx].mOffset;
+ }
+};
+
+} // namespace
+
+void
+TabWidthStore::ApplySpacing(gfxTextRun::PropertyProvider::Spacing *aSpacing,
+ uint32_t aOffset, uint32_t aLength)
+{
+ size_t i = 0;
+ const size_t len = mWidths.Length();
+
+ // If aOffset is non-zero, do a binary search to find where to start
+ // processing the tab widths, in case the list is really long. (See bug
+ // 953247.)
+ // We need to start from the first entry where mOffset >= aOffset.
+ if (aOffset > 0) {
+ mozilla::BinarySearch(TabwidthAdaptor(mWidths), 0, len, aOffset, &i);
+ }
+
+ uint32_t limit = aOffset + aLength;
+ while (i < len) {
+ const TabWidth& tw = mWidths[i];
+ if (tw.mOffset >= limit) {
+ break;
+ }
+ aSpacing[tw.mOffset - aOffset].mAfter += tw.mWidth;
+ i++;
+ }
+}
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(TabWidthProperty, TabWidthStore)
+
+NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(OffsetToFrameProperty, nsTextFrame)
+
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(UninflatedTextRunProperty, gfxTextRun)
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
+
+/**
+ * A glyph observer for the change of a font glyph in a text run.
+ *
+ * This is stored in {Simple, Complex}TextRunUserData.
+ */
+class GlyphObserver : public gfxFont::GlyphChangeObserver {
+public:
+ GlyphObserver(gfxFont* aFont, gfxTextRun* aTextRun)
+ : gfxFont::GlyphChangeObserver(aFont), mTextRun(aTextRun) {
+ MOZ_ASSERT(aTextRun->GetUserData());
+ }
+ virtual void NotifyGlyphsChanged() override;
+private:
+ gfxTextRun* mTextRun;
+};
+
+static const nsFrameState TEXT_REFLOW_FLAGS =
+ TEXT_FIRST_LETTER |
+ TEXT_START_OF_LINE |
+ TEXT_END_OF_LINE |
+ TEXT_HYPHEN_BREAK |
+ TEXT_TRIMMED_TRAILING_WHITESPACE |
+ TEXT_JUSTIFICATION_ENABLED |
+ TEXT_HAS_NONCOLLAPSED_CHARACTERS |
+ TEXT_SELECTION_UNDERLINE_OVERFLOWED |
+ TEXT_NO_RENDERED_GLYPHS;
+
+static const nsFrameState TEXT_WHITESPACE_FLAGS =
+ TEXT_IS_ONLY_WHITESPACE |
+ TEXT_ISNOT_ONLY_WHITESPACE;
+
+/*
+ * Some general notes
+ *
+ * Text frames delegate work to gfxTextRun objects. The gfxTextRun object
+ * transforms text to positioned glyphs. It can report the geometry of the
+ * glyphs and paint them. Text frames configure gfxTextRuns by providing text,
+ * spacing, language, and other information.
+ *
+ * A gfxTextRun can cover more than one DOM text node. This is necessary to
+ * get kerning, ligatures and shaping for text that spans multiple text nodes
+ * but is all the same font.
+ *
+ * The userdata for a gfxTextRun object can be:
+ *
+ * - A nsTextFrame* in the case a text run maps to only one flow. In this
+ * case, the textrun's user data pointer is a pointer to mStartFrame for that
+ * flow, mDOMOffsetToBeforeTransformOffset is zero, and mContentLength is the
+ * length of the text node.
+ *
+ * - A SimpleTextRunUserData in the case a text run maps to one flow, but we
+ * still have to keep a list of glyph observers.
+ *
+ * - A ComplexTextRunUserData in the case a text run maps to multiple flows,
+ * but we need to keep a list of glyph observers.
+ *
+ * - A TextRunUserData in the case a text run maps multiple flows, but it
+ * doesn't have any glyph observer for changes in SVG fonts.
+ *
+ * You can differentiate between the four different cases with the
+ * TEXT_IS_SIMPLE_FLOW and TEXT_MIGHT_HAVE_GLYPH_CHANGES flags.
+ *
+ * We go to considerable effort to make sure things work even if in-flow
+ * siblings have different style contexts (i.e., first-letter and first-line).
+ *
+ * Our convention is that unsigned integer character offsets are offsets into
+ * the transformed string. Signed integer character offsets are offsets into
+ * the DOM string.
+ *
+ * XXX currently we don't handle hyphenated breaks between text frames where the
+ * hyphen occurs at the end of the first text frame, e.g.
+ * <b>Kit&shy;</b>ty
+ */
+
+/**
+ * This is our user data for the textrun, when textRun->GetFlags() has
+ * TEXT_IS_SIMPLE_FLOW set, and also TEXT_MIGHT_HAVE_GLYPH_CHANGES.
+ *
+ * This allows having an array of observers if there are fonts whose glyphs
+ * might change, but also avoid allocation in the simple case that there aren't.
+ */
+struct SimpleTextRunUserData {
+ nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
+ nsTextFrame* mFrame;
+ explicit SimpleTextRunUserData(nsTextFrame* aFrame)
+ : mFrame(aFrame)
+ {
+ }
+};
+
+/**
+ * We use an array of these objects to record which text frames
+ * are associated with the textrun. mStartFrame is the start of a list of
+ * text frames. Some sequence of its continuations are covered by the textrun.
+ * A content textnode can have at most one TextRunMappedFlow associated with it
+ * for a given textrun.
+ *
+ * mDOMOffsetToBeforeTransformOffset is added to DOM offsets for those frames to obtain
+ * the offset into the before-transformation text of the textrun. It can be
+ * positive (when a text node starts in the middle of a text run) or
+ * negative (when a text run starts in the middle of a text node). Of course
+ * it can also be zero.
+ */
+struct TextRunMappedFlow {
+ nsTextFrame* mStartFrame;
+ int32_t mDOMOffsetToBeforeTransformOffset;
+ // The text mapped starts at mStartFrame->GetContentOffset() and is this long
+ uint32_t mContentLength;
+};
+
+/**
+ * This is the type in the gfxTextRun's userdata field in the common case that
+ * the text run maps to multiple flows, but no fonts have been found with
+ * animatable glyphs.
+ *
+ * This way, we avoid allocating and constructing the extra nsTArray.
+ */
+struct TextRunUserData {
+#ifdef DEBUG
+ TextRunMappedFlow* mMappedFlows;
+#endif
+ uint32_t mMappedFlowCount;
+ uint32_t mLastFlowIndex;
+};
+
+/**
+ * This is our user data for the textrun, when textRun->GetFlags() does not
+ * have TEXT_IS_SIMPLE_FLOW set and has the TEXT_MIGHT HAVE_GLYPH_CHANGES flag.
+ */
+struct ComplexTextRunUserData : public TextRunUserData {
+ nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
+};
+
+/**
+ * This helper object computes colors used for painting, and also IME
+ * underline information. The data is computed lazily and cached as necessary.
+ * These live for just the duration of one paint operation.
+ */
+class nsTextPaintStyle {
+public:
+ explicit nsTextPaintStyle(nsTextFrame* aFrame);
+
+ void SetResolveColors(bool aResolveColors) {
+ mResolveColors = aResolveColors;
+ }
+
+ nscolor GetTextColor();
+
+ // SVG text has its own painting process, so we should never get its stroke
+ // property from here.
+ nscolor GetWebkitTextStrokeColor() {
+ if (mFrame->IsSVGText()) {
+ return 0;
+ }
+ return mFrame->StyleColor()->
+ CalcComplexColor(mFrame->StyleText()->mWebkitTextStrokeColor);
+ }
+ float GetWebkitTextStrokeWidth() {
+ if (mFrame->IsSVGText()) {
+ return 0.0f;
+ }
+ nscoord coord = mFrame->StyleText()->mWebkitTextStrokeWidth.GetCoordValue();
+ return mFrame->PresContext()->AppUnitsToFloatDevPixels(coord);
+ }
+
+ /**
+ * Compute the colors for normally-selected text. Returns false if
+ * the normal selection is not being displayed.
+ */
+ bool GetSelectionColors(nscolor* aForeColor,
+ nscolor* aBackColor);
+ void GetHighlightColors(nscolor* aForeColor,
+ nscolor* aBackColor);
+ void GetURLSecondaryColor(nscolor* aForeColor);
+ void GetIMESelectionColors(int32_t aIndex,
+ nscolor* aForeColor,
+ nscolor* aBackColor);
+ // if this returns false, we don't need to draw underline.
+ bool GetSelectionUnderlineForPaint(int32_t aIndex,
+ nscolor* aLineColor,
+ float* aRelativeSize,
+ uint8_t* aStyle);
+
+ // if this returns false, we don't need to draw underline.
+ static bool GetSelectionUnderline(nsPresContext* aPresContext,
+ int32_t aIndex,
+ nscolor* aLineColor,
+ float* aRelativeSize,
+ uint8_t* aStyle);
+
+ // if this returns false, no text-shadow was specified for the selection
+ // and the *aShadow parameter was not modified.
+ bool GetSelectionShadow(nsCSSShadowArray** aShadow);
+
+ nsPresContext* PresContext() const { return mPresContext; }
+
+ enum {
+ eIndexRawInput = 0,
+ eIndexSelRawText,
+ eIndexConvText,
+ eIndexSelConvText,
+ eIndexSpellChecker
+ };
+
+ static int32_t GetUnderlineStyleIndexForSelectionType(
+ SelectionType aSelectionType)
+ {
+ switch (aSelectionType) {
+ case SelectionType::eIMERawClause:
+ return eIndexRawInput;
+ case SelectionType::eIMESelectedRawClause:
+ return eIndexSelRawText;
+ case SelectionType::eIMEConvertedClause:
+ return eIndexConvText;
+ case SelectionType::eIMESelectedClause:
+ return eIndexSelConvText;
+ case SelectionType::eSpellCheck:
+ return eIndexSpellChecker;
+ default:
+ NS_WARNING("non-IME selection type");
+ return eIndexRawInput;
+ }
+ }
+
+ nscolor GetSystemFieldForegroundColor();
+ nscolor GetSystemFieldBackgroundColor();
+
+protected:
+ nsTextFrame* mFrame;
+ nsPresContext* mPresContext;
+ bool mInitCommonColors;
+ bool mInitSelectionColorsAndShadow;
+ bool mResolveColors;
+
+ // Selection data
+
+ int16_t mSelectionStatus; // see nsIDocument.h SetDisplaySelection()
+ nscolor mSelectionTextColor;
+ nscolor mSelectionBGColor;
+ RefPtr<nsCSSShadowArray> mSelectionShadow;
+ bool mHasSelectionShadow;
+
+ // Common data
+
+ int32_t mSufficientContrast;
+ nscolor mFrameBackgroundColor;
+ nscolor mSystemFieldForegroundColor;
+ nscolor mSystemFieldBackgroundColor;
+
+ // selection colors and underline info, the colors are resolved colors if
+ // mResolveColors is true (which is the default), i.e., the foreground color
+ // and background color are swapped if it's needed. And also line color will
+ // be resolved from them.
+ struct nsSelectionStyle {
+ bool mInit;
+ nscolor mTextColor;
+ nscolor mBGColor;
+ nscolor mUnderlineColor;
+ uint8_t mUnderlineStyle;
+ float mUnderlineRelativeSize;
+ };
+ nsSelectionStyle mSelectionStyle[5];
+
+ // Color initializations
+ void InitCommonColors();
+ bool InitSelectionColorsAndShadow();
+
+ nsSelectionStyle* GetSelectionStyle(int32_t aIndex);
+ void InitSelectionStyle(int32_t aIndex);
+
+ bool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor);
+
+ nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
+ nscolor aBackColor);
+};
+
+static TextRunUserData*
+CreateUserData(uint32_t aMappedFlowCount)
+{
+ TextRunUserData* data = static_cast<TextRunUserData*>
+ (moz_xmalloc(sizeof(TextRunUserData) +
+ aMappedFlowCount * sizeof(TextRunMappedFlow)));
+#ifdef DEBUG
+ data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
+#endif
+ data->mMappedFlowCount = aMappedFlowCount;
+ data->mLastFlowIndex = 0;
+ return data;
+}
+
+static void
+DestroyUserData(TextRunUserData* aUserData)
+{
+ if (aUserData) {
+ free(aUserData);
+ }
+}
+
+static ComplexTextRunUserData*
+CreateComplexUserData(uint32_t aMappedFlowCount)
+{
+ ComplexTextRunUserData* data = static_cast<ComplexTextRunUserData*>
+ (moz_xmalloc(sizeof(ComplexTextRunUserData) +
+ aMappedFlowCount * sizeof(TextRunMappedFlow)));
+ new (data) ComplexTextRunUserData();
+#ifdef DEBUG
+ data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
+#endif
+ data->mMappedFlowCount = aMappedFlowCount;
+ data->mLastFlowIndex = 0;
+ return data;
+}
+
+static void
+DestroyComplexUserData(ComplexTextRunUserData* aUserData)
+{
+ if (aUserData) {
+ aUserData->~ComplexTextRunUserData();
+ free(aUserData);
+ }
+}
+
+static void
+DestroyTextRunUserData(gfxTextRun* aTextRun)
+{
+ MOZ_ASSERT(aTextRun->GetUserData());
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ delete static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
+ }
+ } else {
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ DestroyComplexUserData(
+ static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()));
+ } else {
+ DestroyUserData(
+ static_cast<TextRunUserData*>(aTextRun->GetUserData()));
+ }
+ }
+ aTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES);
+ aTextRun->SetUserData(nullptr);
+}
+
+static TextRunMappedFlow*
+GetMappedFlows(const gfxTextRun* aTextRun)
+{
+ MOZ_ASSERT(aTextRun->GetUserData(), "UserData must exist.");
+ MOZ_ASSERT(!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW),
+ "The method should not be called for simple flows.");
+ TextRunMappedFlow* flows;
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ flows = reinterpret_cast<TextRunMappedFlow*>(
+ static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()) + 1);
+ } else {
+ flows = reinterpret_cast<TextRunMappedFlow*>(
+ static_cast<TextRunUserData*>(aTextRun->GetUserData()) + 1);
+ }
+ MOZ_ASSERT(static_cast<TextRunUserData*>(aTextRun->GetUserData())->
+ mMappedFlows == flows,
+ "GetMappedFlows should return the same pointer as mMappedFlows.");
+ return flows;
+}
+
+/**
+ * These are utility functions just for helping with the complexity related with
+ * the text runs user data.
+ */
+static nsTextFrame*
+GetFrameForSimpleFlow(const gfxTextRun* aTextRun)
+{
+ MOZ_ASSERT(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW,
+ "Not so simple flow?");
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ return static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())->mFrame;
+ }
+
+ return static_cast<nsTextFrame*>(aTextRun->GetUserData());
+}
+
+/**
+ * Remove |aTextRun| from the frame continuation chain starting at
+ * |aStartContinuation| if non-null, otherwise starting at |aFrame|.
+ * Unmark |aFrame| as a text run owner if it's the frame we start at.
+ * Return true if |aStartContinuation| is non-null and was found
+ * in the next-continuation chain of |aFrame|.
+ */
+static bool
+ClearAllTextRunReferences(nsTextFrame* aFrame, gfxTextRun* aTextRun,
+ nsTextFrame* aStartContinuation,
+ nsFrameState aWhichTextRunState)
+{
+ NS_PRECONDITION(aFrame, "");
+ NS_PRECONDITION(!aStartContinuation ||
+ (!aStartContinuation->GetTextRun(nsTextFrame::eInflated) ||
+ aStartContinuation->GetTextRun(nsTextFrame::eInflated) == aTextRun) ||
+ (!aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) ||
+ aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) == aTextRun),
+ "wrong aStartContinuation for this text run");
+
+ if (!aStartContinuation || aStartContinuation == aFrame) {
+ aFrame->RemoveStateBits(aWhichTextRunState);
+ } else {
+ do {
+ NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame, "Bad frame");
+ aFrame = static_cast<nsTextFrame*>(aFrame->GetNextContinuation());
+ } while (aFrame && aFrame != aStartContinuation);
+ }
+ bool found = aStartContinuation == aFrame;
+ while (aFrame) {
+ NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame, "Bad frame");
+ if (!aFrame->RemoveTextRun(aTextRun)) {
+ break;
+ }
+ aFrame = static_cast<nsTextFrame*>(aFrame->GetNextContinuation());
+ }
+ NS_POSTCONDITION(!found || aStartContinuation, "how did we find null?");
+ return found;
+}
+
+/**
+ * Kill all references to |aTextRun| starting at |aStartContinuation|.
+ * It could be referenced by any of its owners, and all their in-flows.
+ * If |aStartContinuation| is null then process all userdata frames
+ * and their continuations.
+ * @note the caller is expected to take care of possibly destroying the
+ * text run if all userdata frames were reset (userdata is deallocated
+ * by this function though). The caller can detect this has occured by
+ * checking |aTextRun->GetUserData() == nullptr|.
+ */
+static void
+UnhookTextRunFromFrames(gfxTextRun* aTextRun, nsTextFrame* aStartContinuation)
+{
+ if (!aTextRun->GetUserData()) {
+ return;
+ }
+
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ nsTextFrame* userDataFrame = GetFrameForSimpleFlow(aTextRun);
+ nsFrameState whichTextRunState =
+ userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
+ ? TEXT_IN_TEXTRUN_USER_DATA
+ : TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
+ DebugOnly<bool> found =
+ ClearAllTextRunReferences(userDataFrame, aTextRun,
+ aStartContinuation, whichTextRunState);
+ NS_ASSERTION(!aStartContinuation || found,
+ "aStartContinuation wasn't found in simple flow text run");
+ if (!(userDataFrame->GetStateBits() & whichTextRunState)) {
+ DestroyTextRunUserData(aTextRun);
+ }
+ } else {
+ auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
+ int32_t destroyFromIndex = aStartContinuation ? -1 : 0;
+ for (uint32_t i = 0; i < userData->mMappedFlowCount; ++i) {
+ nsTextFrame* userDataFrame = userMappedFlows[i].mStartFrame;
+ nsFrameState whichTextRunState =
+ userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
+ ? TEXT_IN_TEXTRUN_USER_DATA
+ : TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
+ bool found =
+ ClearAllTextRunReferences(userDataFrame, aTextRun,
+ aStartContinuation, whichTextRunState);
+ if (found) {
+ if (userDataFrame->GetStateBits() & whichTextRunState) {
+ destroyFromIndex = i + 1;
+ } else {
+ destroyFromIndex = i;
+ }
+ aStartContinuation = nullptr;
+ }
+ }
+ NS_ASSERTION(destroyFromIndex >= 0,
+ "aStartContinuation wasn't found in multi flow text run");
+ if (destroyFromIndex == 0) {
+ DestroyTextRunUserData(aTextRun);
+ } else {
+ userData->mMappedFlowCount = uint32_t(destroyFromIndex);
+ if (userData->mLastFlowIndex >= uint32_t(destroyFromIndex)) {
+ userData->mLastFlowIndex = uint32_t(destroyFromIndex) - 1;
+ }
+ }
+ }
+}
+
+static void
+InvalidateFrameDueToGlyphsChanged(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(aFrame);
+
+ nsIPresShell* shell = aFrame->PresContext()->PresShell();
+ for (nsIFrame* f = aFrame; f;
+ f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
+ f->InvalidateFrame();
+
+ // If this is a non-display text frame within SVG <text>, we need
+ // to reflow the SVGTextFrame. (This is similar to reflowing the
+ // SVGTextFrame in response to style changes, in
+ // SVGTextFrame::DidSetStyleContext.)
+ if (f->IsSVGText() && f->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
+ auto svgTextFrame = static_cast<SVGTextFrame*>(
+ nsLayoutUtils::GetClosestFrameOfType(f,
+ nsGkAtoms::svgTextFrame));
+ svgTextFrame->ScheduleReflowSVGNonDisplayText(nsIPresShell::eResize);
+ } else {
+ // Theoretically we could just update overflow areas, perhaps using
+ // OverflowChangedTracker, but that would do a bunch of work eagerly that
+ // we should probably do lazily here since there could be a lot
+ // of text frames affected and we'd like to coalesce the work. So that's
+ // not easy to do well.
+ shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
+ }
+}
+
+void
+GlyphObserver::NotifyGlyphsChanged()
+{
+ if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ InvalidateFrameDueToGlyphsChanged(GetFrameForSimpleFlow(mTextRun));
+ return;
+ }
+
+ auto data = static_cast<TextRunUserData*>(mTextRun->GetUserData());
+ TextRunMappedFlow* userMappedFlows = GetMappedFlows(mTextRun);
+ for (uint32_t i = 0; i < data->mMappedFlowCount; ++i) {
+ InvalidateFrameDueToGlyphsChanged(userMappedFlows[i].mStartFrame);
+ }
+}
+
+int32_t nsTextFrame::GetContentEnd() const {
+ nsTextFrame* next = static_cast<nsTextFrame*>(GetNextContinuation());
+ return next ? next->GetContentOffset() : mContent->GetText()->GetLength();
+}
+
+struct FlowLengthProperty {
+ int32_t mStartOffset;
+ // The offset of the next fixed continuation after mStartOffset, or
+ // of the end of the text if there is none
+ int32_t mEndFlowOffset;
+};
+
+int32_t nsTextFrame::GetInFlowContentLength() {
+ if (!(mState & NS_FRAME_IS_BIDI)) {
+ return mContent->TextLength() - mContentOffset;
+ }
+
+ FlowLengthProperty* flowLength =
+ static_cast<FlowLengthProperty*>(mContent->GetProperty(nsGkAtoms::flowlength));
+
+ /**
+ * This frame must start inside the cached flow. If the flow starts at
+ * mContentOffset but this frame is empty, logically it might be before the
+ * start of the cached flow.
+ */
+ if (flowLength &&
+ (flowLength->mStartOffset < mContentOffset ||
+ (flowLength->mStartOffset == mContentOffset && GetContentEnd() > mContentOffset)) &&
+ flowLength->mEndFlowOffset > mContentOffset) {
+#ifdef DEBUG
+ NS_ASSERTION(flowLength->mEndFlowOffset >= GetContentEnd(),
+ "frame crosses fixed continuation boundary");
+#endif
+ return flowLength->mEndFlowOffset - mContentOffset;
+ }
+
+ nsTextFrame* nextBidi = static_cast<nsTextFrame*>(LastInFlow()->GetNextContinuation());
+ int32_t endFlow = nextBidi ? nextBidi->GetContentOffset() : mContent->TextLength();
+
+ if (!flowLength) {
+ flowLength = new FlowLengthProperty;
+ if (NS_FAILED(mContent->SetProperty(nsGkAtoms::flowlength, flowLength,
+ nsINode::DeleteProperty<FlowLengthProperty>))) {
+ delete flowLength;
+ flowLength = nullptr;
+ }
+ }
+ if (flowLength) {
+ flowLength->mStartOffset = mContentOffset;
+ flowLength->mEndFlowOffset = endFlow;
+ }
+
+ return endFlow - mContentOffset;
+}
+
+// Smarter versions of dom::IsSpaceCharacter.
+// Unicode is really annoying; sometimes a space character isn't whitespace ---
+// when it combines with another character
+// So we have several versions of IsSpace for use in different contexts.
+
+static bool IsSpaceCombiningSequenceTail(const nsTextFragment* aFrag, uint32_t aPos)
+{
+ NS_ASSERTION(aPos <= aFrag->GetLength(), "Bad offset");
+ if (!aFrag->Is2b())
+ return false;
+ return nsTextFrameUtils::IsSpaceCombiningSequenceTail(
+ aFrag->Get2b() + aPos, aFrag->GetLength() - aPos);
+}
+
+// Check whether aPos is a space for CSS 'word-spacing' purposes
+static bool
+IsCSSWordSpacingSpace(const nsTextFragment* aFrag, uint32_t aPos,
+ nsTextFrame* aFrame, const nsStyleText* aStyleText)
+{
+ NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
+
+ char16_t ch = aFrag->CharAt(aPos);
+ switch (ch) {
+ case ' ':
+ case CH_NBSP:
+ return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
+ case '\r':
+ case '\t': return !aStyleText->WhiteSpaceIsSignificant();
+ case '\n': return !aStyleText->NewlineIsSignificant(aFrame);
+ default: return false;
+ }
+}
+
+// Check whether the string aChars/aLength starts with space that's
+// trimmable according to CSS 'white-space:normal/nowrap'.
+static bool IsTrimmableSpace(const char16_t* aChars, uint32_t aLength)
+{
+ NS_ASSERTION(aLength > 0, "No text for IsSpace!");
+
+ char16_t ch = *aChars;
+ if (ch == ' ')
+ return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(aChars + 1, aLength - 1);
+ return ch == '\t' || ch == '\f' || ch == '\n' || ch == '\r';
+}
+
+// Check whether the character aCh is trimmable according to CSS
+// 'white-space:normal/nowrap'
+static bool IsTrimmableSpace(char aCh)
+{
+ return aCh == ' ' || aCh == '\t' || aCh == '\f' || aCh == '\n' || aCh == '\r';
+}
+
+static bool IsTrimmableSpace(const nsTextFragment* aFrag, uint32_t aPos,
+ const nsStyleText* aStyleText)
+{
+ NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
+
+ switch (aFrag->CharAt(aPos)) {
+ case ' ': return !aStyleText->WhiteSpaceIsSignificant() &&
+ !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
+ case '\n': return !aStyleText->NewlineIsSignificantStyle() &&
+ aStyleText->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_SPACE;
+ case '\t':
+ case '\r':
+ case '\f': return !aStyleText->WhiteSpaceIsSignificant();
+ default: return false;
+ }
+}
+
+static bool IsSelectionSpace(const nsTextFragment* aFrag, uint32_t aPos)
+{
+ NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
+ char16_t ch = aFrag->CharAt(aPos);
+ if (ch == ' ' || ch == CH_NBSP)
+ return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
+ return ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r';
+}
+
+// Count the amount of trimmable whitespace (as per CSS
+// 'white-space:normal/nowrap') in a text fragment. The first
+// character is at offset aStartOffset; the maximum number of characters
+// to check is aLength. aDirection is -1 or 1 depending on whether we should
+// progress backwards or forwards.
+static uint32_t
+GetTrimmableWhitespaceCount(const nsTextFragment* aFrag,
+ int32_t aStartOffset, int32_t aLength,
+ int32_t aDirection)
+{
+ int32_t count = 0;
+ if (aFrag->Is2b()) {
+ const char16_t* str = aFrag->Get2b() + aStartOffset;
+ int32_t fragLen = aFrag->GetLength() - aStartOffset;
+ for (; count < aLength; ++count) {
+ if (!IsTrimmableSpace(str, fragLen))
+ break;
+ str += aDirection;
+ fragLen -= aDirection;
+ }
+ } else {
+ const char* str = aFrag->Get1b() + aStartOffset;
+ for (; count < aLength; ++count) {
+ if (!IsTrimmableSpace(*str))
+ break;
+ str += aDirection;
+ }
+ }
+ return count;
+}
+
+static bool
+IsAllWhitespace(const nsTextFragment* aFrag, bool aAllowNewline)
+{
+ if (aFrag->Is2b())
+ return false;
+ int32_t len = aFrag->GetLength();
+ const char* str = aFrag->Get1b();
+ for (int32_t i = 0; i < len; ++i) {
+ char ch = str[i];
+ if (ch == ' ' || ch == '\t' || ch == '\r' || (ch == '\n' && aAllowNewline))
+ continue;
+ return false;
+ }
+ return true;
+}
+
+static void
+ClearObserversFromTextRun(gfxTextRun* aTextRun)
+{
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
+ return;
+ }
+
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())
+ ->mGlyphObservers.Clear();
+ } else {
+ static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData())
+ ->mGlyphObservers.Clear();
+ }
+}
+
+static void
+CreateObserversForAnimatedGlyphs(gfxTextRun* aTextRun)
+{
+ if (!aTextRun->GetUserData()) {
+ return;
+ }
+
+ ClearObserversFromTextRun(aTextRun);
+
+ nsTArray<gfxFont*> fontsWithAnimatedGlyphs;
+ uint32_t numGlyphRuns;
+ const gfxTextRun::GlyphRun* glyphRuns =
+ aTextRun->GetGlyphRuns(&numGlyphRuns);
+ for (uint32_t i = 0; i < numGlyphRuns; ++i) {
+ gfxFont* font = glyphRuns[i].mFont;
+ if (font->GlyphsMayChange() && !fontsWithAnimatedGlyphs.Contains(font)) {
+ fontsWithAnimatedGlyphs.AppendElement(font);
+ }
+ }
+ if (fontsWithAnimatedGlyphs.IsEmpty()) {
+ // NB: Theoretically, we should clear the TEXT_MIGHT_HAVE_GLYPH_CHANGES
+ // here. That would involve de-allocating the simple user data struct if
+ // present too, and resetting the pointer to the frame. In practice, I
+ // don't think worth doing that work here, given the flag's only purpose is
+ // to distinguish what kind of user data is there.
+ return;
+ }
+
+ nsTArray<UniquePtr<GlyphObserver>>* observers;
+
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ // Swap the frame pointer for a just-allocated SimpleTextRunUserData if
+ // appropriate.
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
+ auto frame = static_cast<nsTextFrame*>(aTextRun->GetUserData());
+ aTextRun->SetUserData(new SimpleTextRunUserData(frame));
+ }
+
+ auto data =
+ static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
+ observers = &data->mGlyphObservers;
+ } else {
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
+ auto oldData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ TextRunMappedFlow* oldMappedFlows = GetMappedFlows(aTextRun);
+ ComplexTextRunUserData* data =
+ CreateComplexUserData(oldData->mMappedFlowCount);
+ TextRunMappedFlow* dataMappedFlows =
+ reinterpret_cast<TextRunMappedFlow*>(data + 1);
+ data->mLastFlowIndex = oldData->mLastFlowIndex;
+ for (uint32_t i = 0; i < oldData->mMappedFlowCount; ++i) {
+ dataMappedFlows[i] = oldMappedFlows[i];
+ }
+ DestroyUserData(oldData);
+ aTextRun->SetUserData(data);
+ }
+ auto data = static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData());
+ observers = &data->mGlyphObservers;
+ }
+
+ aTextRun->SetFlagBits(nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES);
+
+ for (auto font : fontsWithAnimatedGlyphs) {
+ observers->AppendElement(new GlyphObserver(font, aTextRun));
+ }
+}
+
+/**
+ * This class accumulates state as we scan a paragraph of text. It detects
+ * textrun boundaries (changes from text to non-text, hard
+ * line breaks, and font changes) and builds a gfxTextRun at each boundary.
+ * It also detects linebreaker run boundaries (changes from text to non-text,
+ * and hard line breaks) and at each boundary runs the linebreaker to compute
+ * potential line breaks. It also records actual line breaks to store them in
+ * the textruns.
+ */
+class BuildTextRunsScanner {
+public:
+ BuildTextRunsScanner(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
+ nsIFrame* aLineContainer, nsTextFrame::TextRunType aWhichTextRun) :
+ mDrawTarget(aDrawTarget),
+ mLineContainer(aLineContainer),
+ mCommonAncestorWithLastFrame(nullptr),
+ mMissingFonts(aPresContext->MissingFontRecorder()),
+ mBidiEnabled(aPresContext->BidiEnabled()),
+ mSkipIncompleteTextRuns(false),
+ mWhichTextRun(aWhichTextRun),
+ mNextRunContextInfo(nsTextFrameUtils::INCOMING_NONE),
+ mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) {
+ ResetRunInfo();
+ }
+ ~BuildTextRunsScanner() {
+ NS_ASSERTION(mBreakSinks.IsEmpty(), "Should have been cleared");
+ NS_ASSERTION(mLineBreakBeforeFrames.IsEmpty(), "Should have been cleared");
+ NS_ASSERTION(mMappedFlows.IsEmpty(), "Should have been cleared");
+ }
+
+ void SetAtStartOfLine() {
+ mStartOfLine = true;
+ mCanStopOnThisLine = false;
+ }
+ void SetSkipIncompleteTextRuns(bool aSkip) {
+ mSkipIncompleteTextRuns = aSkip;
+ }
+ void SetCommonAncestorWithLastFrame(nsIFrame* aFrame) {
+ mCommonAncestorWithLastFrame = aFrame;
+ }
+ bool CanStopOnThisLine() {
+ return mCanStopOnThisLine;
+ }
+ nsIFrame* GetCommonAncestorWithLastFrame() {
+ return mCommonAncestorWithLastFrame;
+ }
+ void LiftCommonAncestorWithLastFrameToParent(nsIFrame* aFrame) {
+ if (mCommonAncestorWithLastFrame &&
+ mCommonAncestorWithLastFrame->GetParent() == aFrame) {
+ mCommonAncestorWithLastFrame = aFrame;
+ }
+ }
+ void ScanFrame(nsIFrame* aFrame);
+ bool IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun);
+ void FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak);
+ void FlushLineBreaks(gfxTextRun* aTrailingTextRun);
+ void ResetRunInfo() {
+ mLastFrame = nullptr;
+ mMappedFlows.Clear();
+ mLineBreakBeforeFrames.Clear();
+ mMaxTextLength = 0;
+ mDoubleByteText = false;
+ }
+ void AccumulateRunInfo(nsTextFrame* aFrame);
+ /**
+ * @return null to indicate either textrun construction failed or
+ * we constructed just a partial textrun to set up linebreaker and other
+ * state for following textruns.
+ */
+ already_AddRefed<gfxTextRun> BuildTextRunForFrames(void* aTextBuffer);
+ bool SetupLineBreakerContext(gfxTextRun *aTextRun);
+ void AssignTextRun(gfxTextRun* aTextRun, float aInflation);
+ nsTextFrame* GetNextBreakBeforeFrame(uint32_t* aIndex);
+ void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
+ void SetupTextEmphasisForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
+ struct FindBoundaryState {
+ nsIFrame* mStopAtFrame;
+ nsTextFrame* mFirstTextFrame;
+ nsTextFrame* mLastTextFrame;
+ bool mSeenTextRunBoundaryOnLaterLine;
+ bool mSeenTextRunBoundaryOnThisLine;
+ bool mSeenSpaceForLineBreakingOnThisLine;
+ };
+ enum FindBoundaryResult {
+ FB_CONTINUE,
+ FB_STOPPED_AT_STOP_FRAME,
+ FB_FOUND_VALID_TEXTRUN_BOUNDARY
+ };
+ FindBoundaryResult FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState);
+
+ bool ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2);
+
+ // Like TextRunMappedFlow but with some differences. mStartFrame to mEndFrame
+ // (exclusive) are a sequence of in-flow frames (if mEndFrame is null, then
+ // continuations starting from mStartFrame are a sequence of in-flow frames).
+ struct MappedFlow {
+ nsTextFrame* mStartFrame;
+ nsTextFrame* mEndFrame;
+ // When we consider breaking between elements, the nearest common
+ // ancestor of the elements containing the characters is the one whose
+ // CSS 'white-space' property governs. So this records the nearest common
+ // ancestor of mStartFrame and the previous text frame, or null if there
+ // was no previous text frame on this line.
+ nsIFrame* mAncestorControllingInitialBreak;
+
+ int32_t GetContentEnd() {
+ return mEndFrame ? mEndFrame->GetContentOffset()
+ : mStartFrame->GetContent()->GetText()->GetLength();
+ }
+ };
+
+ class BreakSink final : public nsILineBreakSink {
+ public:
+ BreakSink(gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
+ uint32_t aOffsetIntoTextRun)
+ : mTextRun(aTextRun)
+ , mDrawTarget(aDrawTarget)
+ , mOffsetIntoTextRun(aOffsetIntoTextRun)
+ {}
+
+ virtual void SetBreaks(uint32_t aOffset, uint32_t aLength,
+ uint8_t* aBreakBefore) override {
+ gfxTextRun::Range range(aOffset + mOffsetIntoTextRun,
+ aOffset + mOffsetIntoTextRun + aLength);
+ if (mTextRun->SetPotentialLineBreaks(range, aBreakBefore)) {
+ // Be conservative and assume that some breaks have been set
+ mTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_NO_BREAKS);
+ }
+ }
+
+ virtual void SetCapitalization(uint32_t aOffset, uint32_t aLength,
+ bool* aCapitalize) override {
+ MOZ_ASSERT(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED,
+ "Text run should be transformed!");
+ if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
+ nsTransformedTextRun* transformedTextRun =
+ static_cast<nsTransformedTextRun*>(mTextRun.get());
+ transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun, aLength,
+ aCapitalize);
+ }
+ }
+
+ void Finish(gfxMissingFontRecorder* aMFR) {
+ MOZ_ASSERT(!(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_UNUSED_FLAG),
+ "Flag set that should never be set! (memory safety error?)");
+ if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
+ nsTransformedTextRun* transformedTextRun =
+ static_cast<nsTransformedTextRun*>(mTextRun.get());
+ transformedTextRun->FinishSettingProperties(mDrawTarget, aMFR);
+ }
+ // The way nsTransformedTextRun is implemented, its glyph runs aren't
+ // available until after nsTransformedTextRun::FinishSettingProperties()
+ // is called. So that's why we defer checking for animated glyphs to here.
+ CreateObserversForAnimatedGlyphs(mTextRun);
+ }
+
+ RefPtr<gfxTextRun> mTextRun;
+ DrawTarget* mDrawTarget;
+ uint32_t mOffsetIntoTextRun;
+ };
+
+private:
+ AutoTArray<MappedFlow,10> mMappedFlows;
+ AutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames;
+ AutoTArray<UniquePtr<BreakSink>,10> mBreakSinks;
+ nsLineBreaker mLineBreaker;
+ RefPtr<gfxTextRun> mCurrentFramesAllSameTextRun;
+ DrawTarget* mDrawTarget;
+ nsIFrame* mLineContainer;
+ nsTextFrame* mLastFrame;
+ // The common ancestor of the current frame and the previous leaf frame
+ // on the line, or null if there was no previous leaf frame.
+ nsIFrame* mCommonAncestorWithLastFrame;
+ gfxMissingFontRecorder* mMissingFonts;
+ // mMaxTextLength is an upper bound on the size of the text in all mapped frames
+ // The value UINT32_MAX represents overflow; text will be discarded
+ uint32_t mMaxTextLength;
+ bool mDoubleByteText;
+ bool mBidiEnabled;
+ bool mStartOfLine;
+ bool mSkipIncompleteTextRuns;
+ bool mCanStopOnThisLine;
+ nsTextFrame::TextRunType mWhichTextRun;
+ uint8_t mNextRunContextInfo;
+ uint8_t mCurrentRunContextInfo;
+};
+
+static nsIFrame*
+FindLineContainer(nsIFrame* aFrame)
+{
+ while (aFrame && (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
+ aFrame->CanContinueTextRun())) {
+ aFrame = aFrame->GetParent();
+ }
+ return aFrame;
+}
+
+static bool
+IsLineBreakingWhiteSpace(char16_t aChar)
+{
+ // 0x0A (\n) is not handled as white-space by the line breaker, since
+ // we break before it, if it isn't transformed to a normal space.
+ // (If we treat it as normal white-space then we'd only break after it.)
+ // However, it does induce a line break or is converted to a regular
+ // space, and either way it can be used to bound the region of text
+ // that needs to be analyzed for line breaking.
+ return nsLineBreaker::IsSpace(aChar) || aChar == 0x0A;
+}
+
+static bool
+TextContainsLineBreakerWhiteSpace(const void* aText, uint32_t aLength,
+ bool aIsDoubleByte)
+{
+ if (aIsDoubleByte) {
+ const char16_t* chars = static_cast<const char16_t*>(aText);
+ for (uint32_t i = 0; i < aLength; ++i) {
+ if (IsLineBreakingWhiteSpace(chars[i]))
+ return true;
+ }
+ return false;
+ } else {
+ const uint8_t* chars = static_cast<const uint8_t*>(aText);
+ for (uint32_t i = 0; i < aLength; ++i) {
+ if (IsLineBreakingWhiteSpace(chars[i]))
+ return true;
+ }
+ return false;
+ }
+}
+
+struct FrameTextTraversal {
+ // These fields identify which frames should be recursively scanned
+ // The first normal frame to scan (or null, if no such frame should be scanned)
+ nsIFrame* mFrameToScan;
+ // The first overflow frame to scan (or null, if no such frame should be scanned)
+ nsIFrame* mOverflowFrameToScan;
+ // Whether to scan the siblings of mFrameToDescendInto/mOverflowFrameToDescendInto
+ bool mScanSiblings;
+
+ // These identify the boundaries of the context required for
+ // line breaking or textrun construction
+ bool mLineBreakerCanCrossFrameBoundary;
+ bool mTextRunCanCrossFrameBoundary;
+
+ nsIFrame* NextFrameToScan() {
+ nsIFrame* f;
+ if (mFrameToScan) {
+ f = mFrameToScan;
+ mFrameToScan = mScanSiblings ? f->GetNextSibling() : nullptr;
+ } else if (mOverflowFrameToScan) {
+ f = mOverflowFrameToScan;
+ mOverflowFrameToScan = mScanSiblings ? f->GetNextSibling() : nullptr;
+ } else {
+ f = nullptr;
+ }
+ return f;
+ }
+};
+
+static FrameTextTraversal
+CanTextCrossFrameBoundary(nsIFrame* aFrame, nsIAtom* aType)
+{
+ NS_ASSERTION(aType == aFrame->GetType(), "Wrong type");
+
+ FrameTextTraversal result;
+
+ bool continuesTextRun = aFrame->CanContinueTextRun();
+ if (aType == nsGkAtoms::placeholderFrame) {
+ // placeholders are "invisible", so a text run should be able to span
+ // across one. But don't descend into the out-of-flow.
+ result.mLineBreakerCanCrossFrameBoundary = true;
+ result.mOverflowFrameToScan = nullptr;
+ if (continuesTextRun) {
+ // ... Except for first-letter floats, which are really in-flow
+ // from the point of view of capitalization etc, so we'd better
+ // descend into them. But we actually need to break the textrun for
+ // first-letter floats since things look bad if, say, we try to make a
+ // ligature across the float boundary.
+ result.mFrameToScan =
+ (static_cast<nsPlaceholderFrame*>(aFrame))->GetOutOfFlowFrame();
+ result.mScanSiblings = false;
+ result.mTextRunCanCrossFrameBoundary = false;
+ } else {
+ result.mFrameToScan = nullptr;
+ result.mTextRunCanCrossFrameBoundary = true;
+ }
+ } else {
+ if (continuesTextRun) {
+ result.mFrameToScan = aFrame->PrincipalChildList().FirstChild();
+ result.mOverflowFrameToScan =
+ aFrame->GetChildList(nsIFrame::kOverflowList).FirstChild();
+ NS_WARNING_ASSERTION(
+ !result.mOverflowFrameToScan,
+ "Scanning overflow inline frames is something we should avoid");
+ result.mScanSiblings = true;
+ result.mTextRunCanCrossFrameBoundary = true;
+ result.mLineBreakerCanCrossFrameBoundary = true;
+ } else {
+ MOZ_ASSERT(aType != nsGkAtoms::rubyTextContainerFrame,
+ "Shouldn't call this method for ruby text container");
+ result.mFrameToScan = nullptr;
+ result.mOverflowFrameToScan = nullptr;
+ result.mTextRunCanCrossFrameBoundary = false;
+ result.mLineBreakerCanCrossFrameBoundary = false;
+ }
+ }
+ return result;
+}
+
+BuildTextRunsScanner::FindBoundaryResult
+BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState)
+{
+ nsIAtom* frameType = aFrame->GetType();
+ if (frameType == nsGkAtoms::rubyTextContainerFrame) {
+ // Don't stop a text run for ruby text container. We want ruby text
+ // containers to be skipped, but continue the text run across them.
+ return FB_CONTINUE;
+ }
+
+ nsTextFrame* textFrame = frameType == nsGkAtoms::textFrame
+ ? static_cast<nsTextFrame*>(aFrame) : nullptr;
+ if (textFrame) {
+ if (aState->mLastTextFrame &&
+ textFrame != aState->mLastTextFrame->GetNextInFlow() &&
+ !ContinueTextRunAcrossFrames(aState->mLastTextFrame, textFrame)) {
+ aState->mSeenTextRunBoundaryOnThisLine = true;
+ if (aState->mSeenSpaceForLineBreakingOnThisLine)
+ return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
+ }
+ if (!aState->mFirstTextFrame) {
+ aState->mFirstTextFrame = textFrame;
+ }
+ aState->mLastTextFrame = textFrame;
+ }
+
+ if (aFrame == aState->mStopAtFrame)
+ return FB_STOPPED_AT_STOP_FRAME;
+
+ if (textFrame) {
+ if (!aState->mSeenSpaceForLineBreakingOnThisLine) {
+ const nsTextFragment* frag = textFrame->GetContent()->GetText();
+ uint32_t start = textFrame->GetContentOffset();
+ const void* text = frag->Is2b()
+ ? static_cast<const void*>(frag->Get2b() + start)
+ : static_cast<const void*>(frag->Get1b() + start);
+ if (TextContainsLineBreakerWhiteSpace(text, textFrame->GetContentLength(),
+ frag->Is2b())) {
+ aState->mSeenSpaceForLineBreakingOnThisLine = true;
+ if (aState->mSeenTextRunBoundaryOnLaterLine)
+ return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
+ }
+ }
+ return FB_CONTINUE;
+ }
+
+ FrameTextTraversal traversal =
+ CanTextCrossFrameBoundary(aFrame, frameType);
+ if (!traversal.mTextRunCanCrossFrameBoundary) {
+ aState->mSeenTextRunBoundaryOnThisLine = true;
+ if (aState->mSeenSpaceForLineBreakingOnThisLine)
+ return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
+ }
+
+ for (nsIFrame* f = traversal.NextFrameToScan(); f;
+ f = traversal.NextFrameToScan()) {
+ FindBoundaryResult result = FindBoundaries(f, aState);
+ if (result != FB_CONTINUE)
+ return result;
+ }
+
+ if (!traversal.mTextRunCanCrossFrameBoundary) {
+ aState->mSeenTextRunBoundaryOnThisLine = true;
+ if (aState->mSeenSpaceForLineBreakingOnThisLine)
+ return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
+ }
+
+ return FB_CONTINUE;
+}
+
+// build text runs for the 200 lines following aForFrame, and stop after that
+// when we get a chance.
+#define NUM_LINES_TO_BUILD_TEXT_RUNS 200
+
+/**
+ * General routine for building text runs. This is hairy because of the need
+ * to build text runs that span content nodes.
+ *
+ * @param aContext The gfxContext we're using to construct this text run.
+ * @param aForFrame The nsTextFrame for which we're building this text run.
+ * @param aLineContainer the line container containing aForFrame; if null,
+ * we'll walk the ancestors to find it. It's required to be non-null
+ * when aForFrameLine is non-null.
+ * @param aForFrameLine the line containing aForFrame; if null, we'll figure
+ * out the line (slowly)
+ * @param aWhichTextRun The type of text run we want to build. If font inflation
+ * is enabled, this will be eInflated, otherwise it's eNotInflated.
+ */
+static void
+BuildTextRuns(DrawTarget* aDrawTarget, nsTextFrame* aForFrame,
+ nsIFrame* aLineContainer,
+ const nsLineList::iterator* aForFrameLine,
+ nsTextFrame::TextRunType aWhichTextRun)
+{
+ MOZ_ASSERT(aForFrame, "for no frame?");
+ NS_ASSERTION(!aForFrameLine || aLineContainer,
+ "line but no line container");
+
+ nsIFrame* lineContainerChild = aForFrame;
+ if (!aLineContainer) {
+ if (aForFrame->IsFloatingFirstLetterChild()) {
+ lineContainerChild = aForFrame->PresContext()->PresShell()->
+ GetPlaceholderFrameFor(aForFrame->GetParent());
+ }
+ aLineContainer = FindLineContainer(lineContainerChild);
+ } else {
+ NS_ASSERTION((aLineContainer == FindLineContainer(aForFrame) ||
+ (aLineContainer->GetType() == nsGkAtoms::letterFrame &&
+ aLineContainer->IsFloating())),
+ "Wrong line container hint");
+ }
+
+ if (aForFrame->HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML)) {
+ aLineContainer->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);
+ if (aForFrame->HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI)) {
+ aLineContainer->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
+ }
+ }
+ if (aForFrame->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
+ aLineContainer->AddStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT);
+ }
+
+ nsPresContext* presContext = aLineContainer->PresContext();
+ BuildTextRunsScanner scanner(presContext, aDrawTarget,
+ aLineContainer, aWhichTextRun);
+
+ nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aLineContainer);
+
+ if (!block) {
+ nsIFrame* textRunContainer = aLineContainer;
+ if (aLineContainer->GetType() == nsGkAtoms::rubyTextContainerFrame) {
+ textRunContainer = aForFrame;
+ while (textRunContainer &&
+ textRunContainer->GetType() != nsGkAtoms::rubyTextFrame) {
+ textRunContainer = textRunContainer->GetParent();
+ }
+ MOZ_ASSERT(textRunContainer &&
+ textRunContainer->GetParent() == aLineContainer);
+ } else {
+ NS_ASSERTION(
+ !aLineContainer->GetPrevInFlow() && !aLineContainer->GetNextInFlow(),
+ "Breakable non-block line containers other than "
+ "ruby text container is not supported");
+ }
+ // Just loop through all the children of the linecontainer ... it's really
+ // just one line
+ scanner.SetAtStartOfLine();
+ scanner.SetCommonAncestorWithLastFrame(nullptr);
+ for (nsIFrame* child : textRunContainer->PrincipalChildList()) {
+ scanner.ScanFrame(child);
+ }
+ // Set mStartOfLine so FlushFrames knows its textrun ends a line
+ scanner.SetAtStartOfLine();
+ scanner.FlushFrames(true, false);
+ return;
+ }
+
+ // Find the line containing 'lineContainerChild'.
+
+ bool isValid = true;
+ nsBlockInFlowLineIterator backIterator(block, &isValid);
+ if (aForFrameLine) {
+ backIterator = nsBlockInFlowLineIterator(block, *aForFrameLine);
+ } else {
+ backIterator = nsBlockInFlowLineIterator(block, lineContainerChild, &isValid);
+ NS_ASSERTION(isValid, "aForFrame not found in block, someone lied to us");
+ NS_ASSERTION(backIterator.GetContainer() == block,
+ "Someone lied to us about the block");
+ }
+ nsBlockFrame::LineIterator startLine = backIterator.GetLine();
+
+ // Find a line where we can start building text runs. We choose the last line
+ // where:
+ // -- there is a textrun boundary between the start of the line and the
+ // start of aForFrame
+ // -- there is a space between the start of the line and the textrun boundary
+ // (this is so we can be sure the line breaks will be set properly
+ // on the textruns we construct).
+ // The possibly-partial text runs up to and including the first space
+ // are not reconstructed. We construct partial text runs for that text ---
+ // for the sake of simplifying the code and feeding the linebreaker ---
+ // but we discard them instead of assigning them to frames.
+ // This is a little awkward because we traverse lines in the reverse direction
+ // but we traverse the frames in each line in the forward direction.
+ nsBlockInFlowLineIterator forwardIterator = backIterator;
+ nsIFrame* stopAtFrame = lineContainerChild;
+ nsTextFrame* nextLineFirstTextFrame = nullptr;
+ bool seenTextRunBoundaryOnLaterLine = false;
+ bool mayBeginInTextRun = true;
+ while (true) {
+ forwardIterator = backIterator;
+ nsBlockFrame::LineIterator line = backIterator.GetLine();
+ if (!backIterator.Prev() || backIterator.GetLine()->IsBlock()) {
+ mayBeginInTextRun = false;
+ break;
+ }
+
+ BuildTextRunsScanner::FindBoundaryState state = { stopAtFrame, nullptr, nullptr,
+ bool(seenTextRunBoundaryOnLaterLine), false, false };
+ nsIFrame* child = line->mFirstChild;
+ bool foundBoundary = false;
+ for (int32_t i = line->GetChildCount() - 1; i >= 0; --i) {
+ BuildTextRunsScanner::FindBoundaryResult result =
+ scanner.FindBoundaries(child, &state);
+ if (result == BuildTextRunsScanner::FB_FOUND_VALID_TEXTRUN_BOUNDARY) {
+ foundBoundary = true;
+ break;
+ } else if (result == BuildTextRunsScanner::FB_STOPPED_AT_STOP_FRAME) {
+ break;
+ }
+ child = child->GetNextSibling();
+ }
+ if (foundBoundary)
+ break;
+ if (!stopAtFrame && state.mLastTextFrame && nextLineFirstTextFrame &&
+ !scanner.ContinueTextRunAcrossFrames(state.mLastTextFrame, nextLineFirstTextFrame)) {
+ // Found a usable textrun boundary at the end of the line
+ if (state.mSeenSpaceForLineBreakingOnThisLine)
+ break;
+ seenTextRunBoundaryOnLaterLine = true;
+ } else if (state.mSeenTextRunBoundaryOnThisLine) {
+ seenTextRunBoundaryOnLaterLine = true;
+ }
+ stopAtFrame = nullptr;
+ if (state.mFirstTextFrame) {
+ nextLineFirstTextFrame = state.mFirstTextFrame;
+ }
+ }
+ scanner.SetSkipIncompleteTextRuns(mayBeginInTextRun);
+
+ // Now iterate over all text frames starting from the current line. First-in-flow
+ // text frames will be accumulated into textRunFrames as we go. When a
+ // text run boundary is required we flush textRunFrames ((re)building their
+ // gfxTextRuns as necessary).
+ bool seenStartLine = false;
+ uint32_t linesAfterStartLine = 0;
+ do {
+ nsBlockFrame::LineIterator line = forwardIterator.GetLine();
+ if (line->IsBlock())
+ break;
+ line->SetInvalidateTextRuns(false);
+ scanner.SetAtStartOfLine();
+ scanner.SetCommonAncestorWithLastFrame(nullptr);
+ nsIFrame* child = line->mFirstChild;
+ for (int32_t i = line->GetChildCount() - 1; i >= 0; --i) {
+ scanner.ScanFrame(child);
+ child = child->GetNextSibling();
+ }
+ if (line.get() == startLine.get()) {
+ seenStartLine = true;
+ }
+ if (seenStartLine) {
+ ++linesAfterStartLine;
+ if (linesAfterStartLine >= NUM_LINES_TO_BUILD_TEXT_RUNS && scanner.CanStopOnThisLine()) {
+ // Don't flush frames; we may be in the middle of a textrun
+ // that we can't end here. That's OK, we just won't build it.
+ // Note that we must already have finished the textrun for aForFrame,
+ // because we've seen the end of a textrun in a line after the line
+ // containing aForFrame.
+ scanner.FlushLineBreaks(nullptr);
+ // This flushes out mMappedFlows and mLineBreakBeforeFrames, which
+ // silences assertions in the scanner destructor.
+ scanner.ResetRunInfo();
+ return;
+ }
+ }
+ } while (forwardIterator.Next());
+
+ // Set mStartOfLine so FlushFrames knows its textrun ends a line
+ scanner.SetAtStartOfLine();
+ scanner.FlushFrames(true, false);
+}
+
+static char16_t*
+ExpandBuffer(char16_t* aDest, uint8_t* aSrc, uint32_t aCount)
+{
+ while (aCount) {
+ *aDest = *aSrc;
+ ++aDest;
+ ++aSrc;
+ --aCount;
+ }
+ return aDest;
+}
+
+bool
+BuildTextRunsScanner::IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun)
+{
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ return mMappedFlows.Length() == 1 &&
+ mMappedFlows[0].mStartFrame == GetFrameForSimpleFlow(aTextRun) &&
+ mMappedFlows[0].mEndFrame == nullptr;
+ }
+
+ auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
+ if (userData->mMappedFlowCount != mMappedFlows.Length())
+ return false;
+ for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
+ if (userMappedFlows[i].mStartFrame != mMappedFlows[i].mStartFrame ||
+ int32_t(userMappedFlows[i].mContentLength) !=
+ mMappedFlows[i].GetContentEnd() - mMappedFlows[i].mStartFrame->GetContentOffset())
+ return false;
+ }
+ return true;
+}
+
+/**
+ * This gets called when we need to make a text run for the current list of
+ * frames.
+ */
+void BuildTextRunsScanner::FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak)
+{
+ RefPtr<gfxTextRun> textRun;
+ if (!mMappedFlows.IsEmpty()) {
+ if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
+ ((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) ==
+ ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) &&
+ ((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0) ==
+ ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) &&
+ IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
+ // Optimization: We do not need to (re)build the textrun.
+ textRun = mCurrentFramesAllSameTextRun;
+
+ // Feed this run's text into the linebreaker to provide context.
+ if (!SetupLineBreakerContext(textRun)) {
+ return;
+ }
+
+ // Update mNextRunContextInfo appropriately
+ mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE;
+ if (textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) {
+ mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE;
+ }
+ if (textRun->GetFlags() & gfxTextRunFactory::TEXT_TRAILING_ARABICCHAR) {
+ mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR;
+ }
+ } else {
+ AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> buffer;
+ uint32_t bufferSize = mMaxTextLength*(mDoubleByteText ? 2 : 1);
+ if (bufferSize < mMaxTextLength || bufferSize == UINT32_MAX ||
+ !buffer.AppendElements(bufferSize, fallible)) {
+ return;
+ }
+ textRun = BuildTextRunForFrames(buffer.Elements());
+ }
+ }
+
+ if (aFlushLineBreaks) {
+ FlushLineBreaks(aSuppressTrailingBreak ? nullptr : textRun.get());
+ }
+
+ mCanStopOnThisLine = true;
+ ResetRunInfo();
+}
+
+void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun)
+{
+ bool trailingLineBreak;
+ nsresult rv = mLineBreaker.Reset(&trailingLineBreak);
+ // textRun may be null for various reasons, including because we constructed
+ // a partial textrun just to get the linebreaker and other state set up
+ // to build the next textrun.
+ if (NS_SUCCEEDED(rv) && trailingLineBreak && aTrailingTextRun) {
+ aTrailingTextRun->SetFlagBits(nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK);
+ }
+
+ for (uint32_t i = 0; i < mBreakSinks.Length(); ++i) {
+ // TODO cause frames associated with the textrun to be reflowed, if they
+ // aren't being reflowed already!
+ mBreakSinks[i]->Finish(mMissingFonts);
+ }
+ mBreakSinks.Clear();
+}
+
+void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame)
+{
+ if (mMaxTextLength != UINT32_MAX) {
+ NS_ASSERTION(mMaxTextLength < UINT32_MAX - aFrame->GetContentLength(), "integer overflow");
+ if (mMaxTextLength >= UINT32_MAX - aFrame->GetContentLength()) {
+ mMaxTextLength = UINT32_MAX;
+ } else {
+ mMaxTextLength += aFrame->GetContentLength();
+ }
+ }
+ mDoubleByteText |= aFrame->GetContent()->GetText()->Is2b();
+ mLastFrame = aFrame;
+ mCommonAncestorWithLastFrame = aFrame->GetParent();
+
+ MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
+ NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
+ mappedFlow->GetContentEnd() == aFrame->GetContentOffset(),
+ "Overlapping or discontiguous frames => BAD");
+ mappedFlow->mEndFrame = static_cast<nsTextFrame*>(aFrame->GetNextContinuation());
+ if (mCurrentFramesAllSameTextRun != aFrame->GetTextRun(mWhichTextRun)) {
+ mCurrentFramesAllSameTextRun = nullptr;
+ }
+
+ if (mStartOfLine) {
+ mLineBreakBeforeFrames.AppendElement(aFrame);
+ mStartOfLine = false;
+ }
+}
+
+static bool
+HasTerminalNewline(const nsTextFrame* aFrame)
+{
+ if (aFrame->GetContentLength() == 0)
+ return false;
+ const nsTextFragment* frag = aFrame->GetContent()->GetText();
+ return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n';
+}
+
+static gfxFont::Metrics
+GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVerticalMetrics)
+{
+ if (!aFontGroup)
+ return gfxFont::Metrics();
+ gfxFont* font = aFontGroup->GetFirstValidFont();
+ return font->GetMetrics(aVerticalMetrics ? gfxFont::eVertical
+ : gfxFont::eHorizontal);
+}
+
+static gfxFloat
+GetSpaceWidthAppUnits(const gfxTextRun* aTextRun)
+{
+ // Round the space width when converting to appunits the same way textruns
+ // do.
+ gfxFloat spaceWidthAppUnits =
+ NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
+ aTextRun->UseCenterBaseline()).spaceWidth *
+ aTextRun->GetAppUnitsPerDevUnit());
+
+ return spaceWidthAppUnits;
+}
+
+static nscoord
+LetterSpacing(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr)
+{
+ if (aFrame->IsSVGText()) {
+ return 0;
+ }
+ if (!aStyleText) {
+ aStyleText = aFrame->StyleText();
+ }
+
+ const nsStyleCoord& coord = aStyleText->mLetterSpacing;
+ if (eStyleUnit_Coord == coord.GetUnit()) {
+ return coord.GetCoordValue();
+ }
+ return 0;
+}
+
+// This function converts non-coord values (e.g. percentages) to nscoord.
+static nscoord
+WordSpacing(nsIFrame* aFrame, const gfxTextRun* aTextRun,
+ const nsStyleText* aStyleText = nullptr)
+{
+ if (aFrame->IsSVGText()) {
+ return 0;
+ }
+ if (!aStyleText) {
+ aStyleText = aFrame->StyleText();
+ }
+
+ const nsStyleCoord& coord = aStyleText->mWordSpacing;
+ if (coord.IsCoordPercentCalcUnit()) {
+ nscoord pctBasis = coord.HasPercent() ? GetSpaceWidthAppUnits(aTextRun) : 0;
+ return nsRuleNode::ComputeCoordPercentCalc(coord, pctBasis);
+ }
+ return 0;
+}
+
+// Returns gfxTextRunFactory::TEXT_ENABLE_SPACING if non-standard
+// letter-spacing or word-spacing is present.
+static uint32_t
+GetSpacingFlags(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr)
+{
+ if (aFrame->IsSVGText()) {
+ return 0;
+ }
+
+ const nsStyleText* styleText = aFrame->StyleText();
+ const nsStyleCoord& ls = styleText->mLetterSpacing;
+ const nsStyleCoord& ws = styleText->mWordSpacing;
+
+ // It's possible to have a calc() value that computes to zero but for which
+ // IsDefinitelyZero() is false, in which case we'll return
+ // TEXT_ENABLE_SPACING unnecessarily. That's ok because such cases are likely
+ // to be rare, and avoiding TEXT_ENABLE_SPACING is just an optimization.
+ bool nonStandardSpacing =
+ (eStyleUnit_Coord == ls.GetUnit() && ls.GetCoordValue() != 0) ||
+ (eStyleUnit_Coord == ws.GetUnit() && ws.GetCoordValue() != 0) ||
+ (eStyleUnit_Percent == ws.GetUnit() && ws.GetPercentValue() != 0) ||
+ (eStyleUnit_Calc == ws.GetUnit() && !ws.GetCalcValue()->IsDefinitelyZero());
+
+ return nonStandardSpacing ? gfxTextRunFactory::TEXT_ENABLE_SPACING : 0;
+}
+
+bool
+BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2)
+{
+ // We don't need to check font size inflation, since
+ // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|)
+ // ensures that text runs never cross block boundaries. This means
+ // that the font size inflation on all text frames in the text run is
+ // already guaranteed to be the same as each other (and for the line
+ // container).
+ if (mBidiEnabled) {
+ FrameBidiData data1 = aFrame1->GetBidiData();
+ FrameBidiData data2 = aFrame2->GetBidiData();
+ if (data1.embeddingLevel != data2.embeddingLevel ||
+ data2.precedingControl != kBidiLevelNone) {
+ return false;
+ }
+ }
+
+ nsStyleContext* sc1 = aFrame1->StyleContext();
+ const nsStyleText* textStyle1 = sc1->StyleText();
+ // If the first frame ends in a preformatted newline, then we end the textrun
+ // here. This avoids creating giant textruns for an entire plain text file.
+ // Note that we create a single text frame for a preformatted text node,
+ // even if it has newlines in it, so typically we won't see trailing newlines
+ // until after reflow has broken up the frame into one (or more) frames per
+ // line. That's OK though.
+ if (textStyle1->NewlineIsSignificant(aFrame1) && HasTerminalNewline(aFrame1))
+ return false;
+
+ if (aFrame1->GetContent() == aFrame2->GetContent() &&
+ aFrame1->GetNextInFlow() != aFrame2) {
+ // aFrame2 must be a non-fluid continuation of aFrame1. This can happen
+ // sometimes when the unicode-bidi property is used; the bidi resolver
+ // breaks text into different frames even though the text has the same
+ // direction. We can't allow these two frames to share the same textrun
+ // because that would violate our invariant that two flows in the same
+ // textrun have different content elements.
+ return false;
+ }
+
+ nsStyleContext* sc2 = aFrame2->StyleContext();
+ const nsStyleText* textStyle2 = sc2->StyleText();
+ if (sc1 == sc2)
+ return true;
+
+ const nsStyleFont* fontStyle1 = sc1->StyleFont();
+ const nsStyleFont* fontStyle2 = sc2->StyleFont();
+ nscoord letterSpacing1 = LetterSpacing(aFrame1);
+ nscoord letterSpacing2 = LetterSpacing(aFrame2);
+ return fontStyle1->mFont == fontStyle2->mFont &&
+ fontStyle1->mLanguage == fontStyle2->mLanguage &&
+ textStyle1->mTextTransform == textStyle2->mTextTransform &&
+ nsLayoutUtils::GetTextRunFlagsForStyle(sc1, fontStyle1, textStyle1, letterSpacing1) ==
+ nsLayoutUtils::GetTextRunFlagsForStyle(sc2, fontStyle2, textStyle2, letterSpacing2);
+}
+
+void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
+{
+ nsIAtom* frameType = aFrame->GetType();
+ if (frameType == nsGkAtoms::rubyTextContainerFrame) {
+ // Don't include any ruby text container into the text run.
+ return;
+ }
+
+ // First check if we can extend the current mapped frame block. This is common.
+ if (mMappedFlows.Length() > 0) {
+ MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
+ if (mappedFlow->mEndFrame == aFrame &&
+ (aFrame->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION)) {
+ NS_ASSERTION(frameType == nsGkAtoms::textFrame,
+ "Flow-sibling of a text frame is not a text frame?");
+
+ // Don't do this optimization if mLastFrame has a terminal newline...
+ // it's quite likely preformatted and we might want to end the textrun here.
+ // This is almost always true:
+ if (mLastFrame->StyleContext() == aFrame->StyleContext() &&
+ !HasTerminalNewline(mLastFrame)) {
+ AccumulateRunInfo(static_cast<nsTextFrame*>(aFrame));
+ return;
+ }
+ }
+ }
+
+ // Now see if we can add a new set of frames to the current textrun
+ if (frameType == nsGkAtoms::textFrame) {
+ nsTextFrame* frame = static_cast<nsTextFrame*>(aFrame);
+
+ if (mLastFrame) {
+ if (!ContinueTextRunAcrossFrames(mLastFrame, frame)) {
+ FlushFrames(false, false);
+ } else {
+ if (mLastFrame->GetContent() == frame->GetContent()) {
+ AccumulateRunInfo(frame);
+ return;
+ }
+ }
+ }
+
+ MappedFlow* mappedFlow = mMappedFlows.AppendElement();
+ if (!mappedFlow)
+ return;
+
+ mappedFlow->mStartFrame = frame;
+ mappedFlow->mAncestorControllingInitialBreak = mCommonAncestorWithLastFrame;
+
+ AccumulateRunInfo(frame);
+ if (mMappedFlows.Length() == 1) {
+ mCurrentFramesAllSameTextRun = frame->GetTextRun(mWhichTextRun);
+ mCurrentRunContextInfo = mNextRunContextInfo;
+ }
+ return;
+ }
+
+ FrameTextTraversal traversal =
+ CanTextCrossFrameBoundary(aFrame, frameType);
+ bool isBR = frameType == nsGkAtoms::brFrame;
+ if (!traversal.mLineBreakerCanCrossFrameBoundary) {
+ // BR frames are special. We do not need or want to record a break opportunity
+ // before a BR frame.
+ FlushFrames(true, isBR);
+ mCommonAncestorWithLastFrame = aFrame;
+ mNextRunContextInfo &= ~nsTextFrameUtils::INCOMING_WHITESPACE;
+ mStartOfLine = false;
+ } else if (!traversal.mTextRunCanCrossFrameBoundary) {
+ FlushFrames(false, false);
+ }
+
+ for (nsIFrame* f = traversal.NextFrameToScan(); f;
+ f = traversal.NextFrameToScan()) {
+ ScanFrame(f);
+ }
+
+ if (!traversal.mLineBreakerCanCrossFrameBoundary) {
+ // Really if we're a BR frame this is unnecessary since descendInto will be
+ // false. In fact this whole "if" statement should move into the descendInto.
+ FlushFrames(true, isBR);
+ mCommonAncestorWithLastFrame = aFrame;
+ mNextRunContextInfo &= ~nsTextFrameUtils::INCOMING_WHITESPACE;
+ } else if (!traversal.mTextRunCanCrossFrameBoundary) {
+ FlushFrames(false, false);
+ }
+
+ LiftCommonAncestorWithLastFrameToParent(aFrame->GetParent());
+}
+
+nsTextFrame*
+BuildTextRunsScanner::GetNextBreakBeforeFrame(uint32_t* aIndex)
+{
+ uint32_t index = *aIndex;
+ if (index >= mLineBreakBeforeFrames.Length())
+ return nullptr;
+ *aIndex = index + 1;
+ return static_cast<nsTextFrame*>(mLineBreakBeforeFrames.ElementAt(index));
+}
+
+static gfxFontGroup*
+GetFontGroupForFrame(nsIFrame* aFrame, float aFontSizeInflation,
+ nsFontMetrics** aOutFontMetrics = nullptr)
+{
+ RefPtr<nsFontMetrics> metrics =
+ nsLayoutUtils::GetFontMetricsForFrame(aFrame, aFontSizeInflation);
+ gfxFontGroup* fontGroup = metrics->GetThebesFontGroup();
+
+ // Populate outparam before we return:
+ if (aOutFontMetrics) {
+ metrics.forget(aOutFontMetrics);
+ }
+ // XXX this is a bit bogus, we're releasing 'metrics' so the
+ // returned font-group might actually be torn down, although because
+ // of the way the device context caches font metrics, this seems to
+ // not actually happen. But we should fix this.
+ return fontGroup;
+}
+
+static already_AddRefed<DrawTarget>
+CreateReferenceDrawTarget(nsTextFrame* aTextFrame)
+{
+ RefPtr<gfxContext> ctx =
+ aTextFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
+ RefPtr<DrawTarget> dt = ctx->GetDrawTarget();
+ return dt.forget();
+}
+
+static already_AddRefed<gfxTextRun>
+GetHyphenTextRun(const gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
+ nsTextFrame* aTextFrame)
+{
+ RefPtr<DrawTarget> dt = aDrawTarget;
+ if (!dt) {
+ dt = CreateReferenceDrawTarget(aTextFrame);
+ if (!dt) {
+ return nullptr;
+ }
+ }
+
+ return aTextRun->GetFontGroup()->
+ MakeHyphenTextRun(dt, aTextRun->GetAppUnitsPerDevUnit());
+}
+
+static_assert(NS_STYLE_WHITESPACE_NORMAL == 0, "Convention: NS_STYLE_WHITESPACE_NORMAL should be 0");
+static_assert(NS_STYLE_WHITESPACE_PRE == 1, "Convention: NS_STYLE_WHITESPACE_PRE should be 1");
+static_assert(NS_STYLE_WHITESPACE_NOWRAP == 2, "Convention: NS_STYLE_WHITESPACE_NOWRAP should be 2");
+static_assert(NS_STYLE_WHITESPACE_PRE_WRAP == 3, "Convention: NS_STYLE_WHITESPACE_PRE_WRAP should be 3");
+static_assert(NS_STYLE_WHITESPACE_PRE_LINE == 4, "Convention: NS_STYLE_WHITESPACE_PRE_LINE should be 4");
+static_assert(NS_STYLE_WHITESPACE_PRE_SPACE == 5, "Convention: NS_STYLE_WHITESPACE_PRE_SPACE should be 5");
+
+static nsTextFrameUtils::CompressionMode
+GetCSSWhitespaceToCompressionMode(nsTextFrame* aFrame,
+ const nsStyleText* aStyleText)
+{
+ static const nsTextFrameUtils::CompressionMode sModes[] =
+ {
+ nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // normal
+ nsTextFrameUtils::COMPRESS_NONE, // pre
+ nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // nowrap
+ nsTextFrameUtils::COMPRESS_NONE, // pre-wrap
+ nsTextFrameUtils::COMPRESS_WHITESPACE, // pre-line
+ nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE // -moz-pre-space
+ };
+
+ auto compression = sModes[aStyleText->mWhiteSpace];
+ if (compression == nsTextFrameUtils::COMPRESS_NONE &&
+ !aStyleText->NewlineIsSignificant(aFrame)) {
+ // If newline is set to be preserved, but then suppressed,
+ // transform newline to space.
+ compression = nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
+ }
+ return compression;
+}
+
+already_AddRefed<gfxTextRun>
+BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
+{
+ gfxSkipChars skipChars;
+
+ const void* textPtr = aTextBuffer;
+ bool anyTextTransformStyle = false;
+ bool anyMathMLStyling = false;
+ bool anyTextEmphasis = false;
+ uint8_t sstyScriptLevel = 0;
+ uint32_t mathFlags = 0;
+ uint32_t textFlags = nsTextFrameUtils::TEXT_NO_BREAKS;
+
+ if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
+ textFlags |= nsTextFrameUtils::TEXT_INCOMING_WHITESPACE;
+ }
+ if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
+ textFlags |= gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR;
+ }
+
+ AutoTArray<int32_t,50> textBreakPoints;
+ TextRunUserData dummyData;
+ TextRunMappedFlow dummyMappedFlow;
+ TextRunMappedFlow* userMappedFlows;
+ TextRunUserData* userData;
+ TextRunUserData* userDataToDestroy;
+ // If the situation is particularly simple (and common) we don't need to
+ // allocate userData.
+ if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
+ mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
+ userData = &dummyData;
+ userMappedFlows = &dummyMappedFlow;
+ userDataToDestroy = nullptr;
+ dummyData.mMappedFlowCount = mMappedFlows.Length();
+ dummyData.mLastFlowIndex = 0;
+ } else {
+ userData = CreateUserData(mMappedFlows.Length());
+ userMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
+ userDataToDestroy = userData;
+ }
+
+ uint32_t currentTransformedTextOffset = 0;
+
+ uint32_t nextBreakIndex = 0;
+ nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
+ bool isSVG = mLineContainer->IsSVGText();
+ bool enabledJustification =
+ (mLineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
+ mLineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY);
+
+ // for word-break style
+ switch (mLineContainer->StyleText()->mWordBreak) {
+ case NS_STYLE_WORDBREAK_BREAK_ALL:
+ mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_BreakAll);
+ break;
+ case NS_STYLE_WORDBREAK_KEEP_ALL:
+ mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_KeepAll);
+ break;
+ default:
+ mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_Normal);
+ break;
+ }
+
+ const nsStyleText* textStyle = nullptr;
+ const nsStyleFont* fontStyle = nullptr;
+ nsStyleContext* lastStyleContext = nullptr;
+ for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
+ MappedFlow* mappedFlow = &mMappedFlows[i];
+ nsTextFrame* f = mappedFlow->mStartFrame;
+
+ lastStyleContext = f->StyleContext();
+ // Detect use of text-transform or font-variant anywhere in the run
+ textStyle = f->StyleText();
+ if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform ||
+ // text-combine-upright requires converting from full-width
+ // characters to non-full-width correspendent in some cases.
+ lastStyleContext->IsTextCombined()) {
+ anyTextTransformStyle = true;
+ }
+ if (textStyle->HasTextEmphasis()) {
+ anyTextEmphasis = true;
+ }
+ textFlags |= GetSpacingFlags(f);
+ nsTextFrameUtils::CompressionMode compression =
+ GetCSSWhitespaceToCompressionMode(f, textStyle);
+ if ((enabledJustification || f->ShouldSuppressLineBreak()) &&
+ !textStyle->WhiteSpaceIsSignificant() && !isSVG) {
+ textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
+ }
+ fontStyle = f->StyleFont();
+ nsIFrame* parent = mLineContainer->GetParent();
+ if (NS_MATHML_MATHVARIANT_NONE != fontStyle->mMathVariant) {
+ if (NS_MATHML_MATHVARIANT_NORMAL != fontStyle->mMathVariant) {
+ anyMathMLStyling = true;
+ }
+ } else if (mLineContainer->GetStateBits() & NS_FRAME_IS_IN_SINGLE_CHAR_MI) {
+ textFlags |= nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI;
+ anyMathMLStyling = true;
+ // Test for fontstyle attribute as StyleFont() may not be accurate
+ // To be consistent in terms of ignoring CSS style changes, fontweight
+ // gets checked too.
+ if (parent) {
+ nsIContent* content = parent->GetContent();
+ if (content) {
+ if (content->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::fontstyle_,
+ NS_LITERAL_STRING("normal"),
+ eCaseMatters)) {
+ mathFlags |= MathMLTextRunFactory::MATH_FONT_STYLING_NORMAL;
+ }
+ if (content->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::fontweight_,
+ NS_LITERAL_STRING("bold"),
+ eCaseMatters)) {
+ mathFlags |= MathMLTextRunFactory::MATH_FONT_WEIGHT_BOLD;
+ }
+ }
+ }
+ }
+ if (mLineContainer->HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML)) {
+ // All MathML tokens except <mtext> use 'math' script.
+ if (!(parent && parent->GetContent() &&
+ parent->GetContent()->IsMathMLElement(nsGkAtoms::mtext_))) {
+ textFlags |= gfxTextRunFactory::TEXT_USE_MATH_SCRIPT;
+ }
+ nsIMathMLFrame* mathFrame = do_QueryFrame(parent);
+ if (mathFrame) {
+ nsPresentationData presData;
+ mathFrame->GetPresentationData(presData);
+ if (NS_MATHML_IS_DTLS_SET(presData.flags)) {
+ mathFlags |= MathMLTextRunFactory::MATH_FONT_FEATURE_DTLS;
+ anyMathMLStyling = true;
+ }
+ }
+ }
+ nsIFrame* child = mLineContainer;
+ uint8_t oldScriptLevel = 0;
+ while (parent &&
+ child->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
+ // Reconstruct the script level ignoring any user overrides. It is
+ // calculated this way instead of using scriptlevel to ensure the
+ // correct ssty font feature setting is used even if the user sets a
+ // different (especially negative) scriptlevel.
+ nsIMathMLFrame* mathFrame= do_QueryFrame(parent);
+ if (mathFrame) {
+ sstyScriptLevel += mathFrame->ScriptIncrement(child);
+ }
+ if (sstyScriptLevel < oldScriptLevel) {
+ // overflow
+ sstyScriptLevel = UINT8_MAX;
+ break;
+ }
+ child = parent;
+ parent = parent->GetParent();
+ oldScriptLevel = sstyScriptLevel;
+ }
+ if (sstyScriptLevel) {
+ anyMathMLStyling = true;
+ }
+
+ // Figure out what content is included in this flow.
+ nsIContent* content = f->GetContent();
+ const nsTextFragment* frag = content->GetText();
+ int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
+ int32_t contentEnd = mappedFlow->GetContentEnd();
+ int32_t contentLength = contentEnd - contentStart;
+
+ TextRunMappedFlow* newFlow = &userMappedFlows[i];
+ newFlow->mStartFrame = mappedFlow->mStartFrame;
+ newFlow->mDOMOffsetToBeforeTransformOffset = skipChars.GetOriginalCharCount() -
+ mappedFlow->mStartFrame->GetContentOffset();
+ newFlow->mContentLength = contentLength;
+
+ while (nextBreakBeforeFrame && nextBreakBeforeFrame->GetContent() == content) {
+ textBreakPoints.AppendElement(
+ nextBreakBeforeFrame->GetContentOffset() + newFlow->mDOMOffsetToBeforeTransformOffset);
+ nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
+ }
+
+ uint32_t analysisFlags;
+ if (frag->Is2b()) {
+ NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
+ char16_t* bufStart = static_cast<char16_t*>(aTextBuffer);
+ char16_t* bufEnd = nsTextFrameUtils::TransformText(
+ frag->Get2b() + contentStart, contentLength, bufStart,
+ compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
+ aTextBuffer = bufEnd;
+ currentTransformedTextOffset = bufEnd - static_cast<const char16_t*>(textPtr);
+ } else {
+ if (mDoubleByteText) {
+ // Need to expand the text. First transform it into a temporary buffer,
+ // then expand.
+ AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> tempBuf;
+ uint8_t* bufStart = tempBuf.AppendElements(contentLength, fallible);
+ if (!bufStart) {
+ DestroyUserData(userDataToDestroy);
+ return nullptr;
+ }
+ uint8_t* end = nsTextFrameUtils::TransformText(
+ reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
+ bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
+ aTextBuffer = ExpandBuffer(static_cast<char16_t*>(aTextBuffer),
+ tempBuf.Elements(), end - tempBuf.Elements());
+ currentTransformedTextOffset =
+ static_cast<char16_t*>(aTextBuffer) - static_cast<const char16_t*>(textPtr);
+ } else {
+ uint8_t* bufStart = static_cast<uint8_t*>(aTextBuffer);
+ uint8_t* end = nsTextFrameUtils::TransformText(
+ reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
+ bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
+ aTextBuffer = end;
+ currentTransformedTextOffset = end - static_cast<const uint8_t*>(textPtr);
+ }
+ }
+ textFlags |= analysisFlags;
+ }
+
+ void* finalUserData;
+ if (userData == &dummyData) {
+ textFlags |= nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW;
+ userData = nullptr;
+ finalUserData = mMappedFlows[0].mStartFrame;
+ } else {
+ finalUserData = userData;
+ }
+
+ uint32_t transformedLength = currentTransformedTextOffset;
+
+ // Now build the textrun
+ nsTextFrame* firstFrame = mMappedFlows[0].mStartFrame;
+ float fontInflation;
+ if (mWhichTextRun == nsTextFrame::eNotInflated) {
+ fontInflation = 1.0f;
+ } else {
+ fontInflation = nsLayoutUtils::FontSizeInflationFor(firstFrame);
+ }
+
+ gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame, fontInflation);
+ if (!fontGroup) {
+ DestroyUserData(userDataToDestroy);
+ return nullptr;
+ }
+
+ if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
+ textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
+ }
+ if (textFlags & nsTextFrameUtils::TEXT_HAS_SHY) {
+ textFlags |= gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS;
+ }
+ if (mBidiEnabled && (IS_LEVEL_RTL(firstFrame->GetEmbeddingLevel()))) {
+ textFlags |= gfxTextRunFactory::TEXT_IS_RTL;
+ }
+ if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
+ textFlags |= nsTextFrameUtils::TEXT_TRAILING_WHITESPACE;
+ }
+ if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
+ textFlags |= gfxTextRunFactory::TEXT_TRAILING_ARABICCHAR;
+ }
+ // ContinueTextRunAcrossFrames guarantees that it doesn't matter which
+ // frame's style is used, so we use a mixture of the first frame and
+ // last frame's style
+ textFlags |= nsLayoutUtils::GetTextRunFlagsForStyle(lastStyleContext,
+ fontStyle, textStyle, LetterSpacing(firstFrame, textStyle));
+ // XXX this is a bit of a hack. For performance reasons, if we're favouring
+ // performance over quality, don't try to get accurate glyph extents.
+ if (!(textFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED)) {
+ textFlags |= gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX;
+ }
+
+ // Convert linebreak coordinates to transformed string offsets
+ NS_ASSERTION(nextBreakIndex == mLineBreakBeforeFrames.Length(),
+ "Didn't find all the frames to break-before...");
+ gfxSkipCharsIterator iter(skipChars);
+ AutoTArray<uint32_t,50> textBreakPointsAfterTransform;
+ for (uint32_t i = 0; i < textBreakPoints.Length(); ++i) {
+ nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform,
+ iter.ConvertOriginalToSkipped(textBreakPoints[i]));
+ }
+ if (mStartOfLine) {
+ nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform,
+ transformedLength);
+ }
+
+ // Setup factory chain
+ UniquePtr<nsTransformingTextRunFactory> transformingFactory;
+ if (anyTextTransformStyle) {
+ transformingFactory =
+ MakeUnique<nsCaseTransformTextRunFactory>(Move(transformingFactory));
+ }
+ if (anyMathMLStyling) {
+ transformingFactory =
+ MakeUnique<MathMLTextRunFactory>(Move(transformingFactory), mathFlags,
+ sstyScriptLevel, fontInflation);
+ }
+ nsTArray<RefPtr<nsTransformedCharStyle>> styles;
+ if (transformingFactory) {
+ iter.SetOriginalOffset(0);
+ for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
+ MappedFlow* mappedFlow = &mMappedFlows[i];
+ nsTextFrame* f;
+ nsStyleContext* sc = nullptr;
+ RefPtr<nsTransformedCharStyle> charStyle;
+ for (f = mappedFlow->mStartFrame; f != mappedFlow->mEndFrame;
+ f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
+ uint32_t offset = iter.GetSkippedOffset();
+ iter.AdvanceOriginal(f->GetContentLength());
+ uint32_t end = iter.GetSkippedOffset();
+ // Text-combined frames have content-dependent transform, so we
+ // want to create new nsTransformedCharStyle for them anyway.
+ if (sc != f->StyleContext() || sc->IsTextCombined()) {
+ sc = f->StyleContext();
+ charStyle = new nsTransformedCharStyle(sc);
+ if (sc->IsTextCombined() && f->CountGraphemeClusters() > 1) {
+ charStyle->mForceNonFullWidth = true;
+ }
+ }
+ uint32_t j;
+ for (j = offset; j < end; ++j) {
+ styles.AppendElement(charStyle);
+ }
+ }
+ }
+ textFlags |= nsTextFrameUtils::TEXT_IS_TRANSFORMED;
+ NS_ASSERTION(iter.GetSkippedOffset() == transformedLength,
+ "We didn't cover all the characters in the text run!");
+ }
+
+ RefPtr<gfxTextRun> textRun;
+ gfxTextRunFactory::Parameters params =
+ { mDrawTarget, finalUserData, &skipChars,
+ textBreakPointsAfterTransform.Elements(),
+ uint32_t(textBreakPointsAfterTransform.Length()),
+ int32_t(firstFrame->PresContext()->AppUnitsPerDevPixel())};
+
+ if (mDoubleByteText) {
+ const char16_t* text = static_cast<const char16_t*>(textPtr);
+ if (transformingFactory) {
+ textRun = transformingFactory->MakeTextRun(text, transformedLength,
+ &params, fontGroup, textFlags,
+ Move(styles), true);
+ if (textRun) {
+ // ownership of the factory has passed to the textrun
+ // TODO: bug 1285316: clean up ownership transfer from the factory to
+ // the textrun
+ Unused << transformingFactory.release();
+ }
+ } else {
+ textRun = fontGroup->MakeTextRun(text, transformedLength, &params,
+ textFlags, mMissingFonts);
+ }
+ } else {
+ const uint8_t* text = static_cast<const uint8_t*>(textPtr);
+ textFlags |= gfxFontGroup::TEXT_IS_8BIT;
+ if (transformingFactory) {
+ textRun = transformingFactory->MakeTextRun(text, transformedLength,
+ &params, fontGroup, textFlags,
+ Move(styles), true);
+ if (textRun) {
+ // ownership of the factory has passed to the textrun
+ // TODO: bug 1285316: clean up ownership transfer from the factory to
+ // the textrun
+ Unused << transformingFactory.release();
+ }
+ } else {
+ textRun = fontGroup->MakeTextRun(text, transformedLength, &params,
+ textFlags, mMissingFonts);
+ }
+ }
+ if (!textRun) {
+ DestroyUserData(userDataToDestroy);
+ return nullptr;
+ }
+
+ // We have to set these up after we've created the textrun, because
+ // the breaks may be stored in the textrun during this very call.
+ // This is a bit annoying because it requires another loop over the frames
+ // making up the textrun, but I don't see a way to avoid this.
+ SetupBreakSinksForTextRun(textRun.get(), textPtr);
+
+ if (anyTextEmphasis) {
+ SetupTextEmphasisForTextRun(textRun.get(), textPtr);
+ }
+
+ if (mSkipIncompleteTextRuns) {
+ mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr,
+ transformedLength, mDoubleByteText);
+ // Since we're doing to destroy the user data now, avoid a dangling
+ // pointer. Strictly speaking we don't need to do this since it should
+ // not be used (since this textrun will not be used and will be
+ // itself deleted soon), but it's always better to not have dangling
+ // pointers around.
+ textRun->SetUserData(nullptr);
+ DestroyUserData(userDataToDestroy);
+ return nullptr;
+ }
+
+ // Actually wipe out the textruns associated with the mapped frames and associate
+ // those frames with this text run.
+ AssignTextRun(textRun.get(), fontInflation);
+ return textRun.forget();
+}
+
+// This is a cut-down version of BuildTextRunForFrames used to set up
+// context for the line-breaker, when the textrun has already been created.
+// So it does the same walk over the mMappedFlows, but doesn't actually
+// build a new textrun.
+bool
+BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
+{
+ AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> buffer;
+ uint32_t bufferSize = mMaxTextLength*(mDoubleByteText ? 2 : 1);
+ if (bufferSize < mMaxTextLength || bufferSize == UINT32_MAX) {
+ return false;
+ }
+ void *textPtr = buffer.AppendElements(bufferSize, fallible);
+ if (!textPtr) {
+ return false;
+ }
+
+ gfxSkipChars skipChars;
+
+ AutoTArray<int32_t,50> textBreakPoints;
+ TextRunUserData dummyData;
+ TextRunMappedFlow dummyMappedFlow;
+ TextRunMappedFlow* userMappedFlows;
+ TextRunUserData* userData;
+ TextRunUserData* userDataToDestroy;
+ // If the situation is particularly simple (and common) we don't need to
+ // allocate userData.
+ if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
+ mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
+ userData = &dummyData;
+ userMappedFlows = &dummyMappedFlow;
+ userDataToDestroy = nullptr;
+ dummyData.mMappedFlowCount = mMappedFlows.Length();
+ dummyData.mLastFlowIndex = 0;
+ } else {
+ userData = CreateUserData(mMappedFlows.Length());
+ userMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
+ userDataToDestroy = userData;
+ }
+
+ uint32_t nextBreakIndex = 0;
+ nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
+
+ const nsStyleText* textStyle = nullptr;
+ for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
+ MappedFlow* mappedFlow = &mMappedFlows[i];
+ nsTextFrame* f = mappedFlow->mStartFrame;
+
+ textStyle = f->StyleText();
+ nsTextFrameUtils::CompressionMode compression =
+ GetCSSWhitespaceToCompressionMode(f, textStyle);
+
+ // Figure out what content is included in this flow.
+ nsIContent* content = f->GetContent();
+ const nsTextFragment* frag = content->GetText();
+ int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
+ int32_t contentEnd = mappedFlow->GetContentEnd();
+ int32_t contentLength = contentEnd - contentStart;
+
+ TextRunMappedFlow* newFlow = &userMappedFlows[i];
+ newFlow->mStartFrame = mappedFlow->mStartFrame;
+ newFlow->mDOMOffsetToBeforeTransformOffset = skipChars.GetOriginalCharCount() -
+ mappedFlow->mStartFrame->GetContentOffset();
+ newFlow->mContentLength = contentLength;
+
+ while (nextBreakBeforeFrame && nextBreakBeforeFrame->GetContent() == content) {
+ textBreakPoints.AppendElement(
+ nextBreakBeforeFrame->GetContentOffset() + newFlow->mDOMOffsetToBeforeTransformOffset);
+ nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
+ }
+
+ uint32_t analysisFlags;
+ if (frag->Is2b()) {
+ NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
+ char16_t* bufStart = static_cast<char16_t*>(textPtr);
+ char16_t* bufEnd = nsTextFrameUtils::TransformText(
+ frag->Get2b() + contentStart, contentLength, bufStart,
+ compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
+ textPtr = bufEnd;
+ } else {
+ if (mDoubleByteText) {
+ // Need to expand the text. First transform it into a temporary buffer,
+ // then expand.
+ AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> tempBuf;
+ uint8_t* bufStart = tempBuf.AppendElements(contentLength, fallible);
+ if (!bufStart) {
+ DestroyUserData(userDataToDestroy);
+ return false;
+ }
+ uint8_t* end = nsTextFrameUtils::TransformText(
+ reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
+ bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
+ textPtr = ExpandBuffer(static_cast<char16_t*>(textPtr),
+ tempBuf.Elements(), end - tempBuf.Elements());
+ } else {
+ uint8_t* bufStart = static_cast<uint8_t*>(textPtr);
+ uint8_t* end = nsTextFrameUtils::TransformText(
+ reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
+ bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
+ textPtr = end;
+ }
+ }
+ }
+
+ // We have to set these up after we've created the textrun, because
+ // the breaks may be stored in the textrun during this very call.
+ // This is a bit annoying because it requires another loop over the frames
+ // making up the textrun, but I don't see a way to avoid this.
+ SetupBreakSinksForTextRun(aTextRun, buffer.Elements());
+
+ DestroyUserData(userDataToDestroy);
+
+ return true;
+}
+
+static bool
+HasCompressedLeadingWhitespace(nsTextFrame* aFrame, const nsStyleText* aStyleText,
+ int32_t aContentEndOffset,
+ const gfxSkipCharsIterator& aIterator)
+{
+ if (!aIterator.IsOriginalCharSkipped())
+ return false;
+
+ gfxSkipCharsIterator iter = aIterator;
+ int32_t frameContentOffset = aFrame->GetContentOffset();
+ const nsTextFragment* frag = aFrame->GetContent()->GetText();
+ while (frameContentOffset < aContentEndOffset && iter.IsOriginalCharSkipped()) {
+ if (IsTrimmableSpace(frag, frameContentOffset, aStyleText))
+ return true;
+ ++frameContentOffset;
+ iter.AdvanceOriginal(1);
+ }
+ return false;
+}
+
+void
+BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
+ const void* aTextPtr)
+{
+ // textruns have uniform language
+ const nsStyleFont *styleFont = mMappedFlows[0].mStartFrame->StyleFont();
+ // We should only use a language for hyphenation if it was specified
+ // explicitly.
+ nsIAtom* hyphenationLanguage =
+ styleFont->mExplicitLanguage ? styleFont->mLanguage.get() : nullptr;
+ // We keep this pointed at the skip-chars data for the current mappedFlow.
+ // This lets us cheaply check whether the flow has compressed initial
+ // whitespace...
+ gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
+
+ for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
+ MappedFlow* mappedFlow = &mMappedFlows[i];
+ uint32_t offset = iter.GetSkippedOffset();
+ gfxSkipCharsIterator iterNext = iter;
+ iterNext.AdvanceOriginal(mappedFlow->GetContentEnd() -
+ mappedFlow->mStartFrame->GetContentOffset());
+
+ UniquePtr<BreakSink>* breakSink =
+ mBreakSinks.AppendElement(MakeUnique<BreakSink>(aTextRun, mDrawTarget, offset));
+ if (!breakSink || !*breakSink)
+ return;
+
+ uint32_t length = iterNext.GetSkippedOffset() - offset;
+ uint32_t flags = 0;
+ nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
+ if (!initialBreakController) {
+ initialBreakController = mLineContainer;
+ }
+ if (!initialBreakController->StyleText()->
+ WhiteSpaceCanWrap(initialBreakController)) {
+ flags |= nsLineBreaker::BREAK_SUPPRESS_INITIAL;
+ }
+ nsTextFrame* startFrame = mappedFlow->mStartFrame;
+ const nsStyleText* textStyle = startFrame->StyleText();
+ if (!textStyle->WhiteSpaceCanWrap(startFrame)) {
+ flags |= nsLineBreaker::BREAK_SUPPRESS_INSIDE;
+ }
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_NO_BREAKS) {
+ flags |= nsLineBreaker::BREAK_SKIP_SETTING_NO_BREAKS;
+ }
+ if (textStyle->mTextTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE) {
+ flags |= nsLineBreaker::BREAK_NEED_CAPITALIZATION;
+ }
+ if (textStyle->mHyphens == NS_STYLE_HYPHENS_AUTO) {
+ flags |= nsLineBreaker::BREAK_USE_AUTO_HYPHENATION;
+ }
+
+ if (HasCompressedLeadingWhitespace(startFrame, textStyle,
+ mappedFlow->GetContentEnd(), iter)) {
+ mLineBreaker.AppendInvisibleWhitespace(flags);
+ }
+
+ if (length > 0) {
+ BreakSink* sink =
+ mSkipIncompleteTextRuns ? nullptr : (*breakSink).get();
+ if (mDoubleByteText) {
+ const char16_t* text = reinterpret_cast<const char16_t*>(aTextPtr);
+ mLineBreaker.AppendText(hyphenationLanguage, text + offset,
+ length, flags, sink);
+ } else {
+ const uint8_t* text = reinterpret_cast<const uint8_t*>(aTextPtr);
+ mLineBreaker.AppendText(hyphenationLanguage, text + offset,
+ length, flags, sink);
+ }
+ }
+
+ iter = iterNext;
+ }
+}
+
+static bool
+MayCharacterHaveEmphasisMark(uint32_t aCh)
+{
+ auto category = unicode::GetGeneralCategory(aCh);
+ // Comparing an unsigned variable against zero is a compile error,
+ // so we use static assert here to ensure we really don't need to
+ // compare it with the given constant.
+ static_assert(IsUnsigned<decltype(category)>::value &&
+ HB_UNICODE_GENERAL_CATEGORY_CONTROL == 0,
+ "if this constant is not zero, or category is signed, "
+ "we need to explicitly do the comparison below");
+ return !(category <= HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED ||
+ (category >= HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR &&
+ category <= HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR));
+}
+
+static bool
+MayCharacterHaveEmphasisMark(uint8_t aCh)
+{
+ // 0x00~0x1f and 0x7f~0x9f are in category Cc
+ // 0x20 and 0xa0 are in category Zs
+ bool result = !(aCh <= 0x20 || (aCh >= 0x7f && aCh <= 0xa0));
+ MOZ_ASSERT(result == MayCharacterHaveEmphasisMark(uint32_t(aCh)),
+ "result for uint8_t should match result for uint32_t");
+ return result;
+}
+
+void
+BuildTextRunsScanner::SetupTextEmphasisForTextRun(gfxTextRun* aTextRun,
+ const void* aTextPtr)
+{
+ if (!mDoubleByteText) {
+ auto text = reinterpret_cast<const uint8_t*>(aTextPtr);
+ for (auto i : MakeRange(aTextRun->GetLength())) {
+ if (!MayCharacterHaveEmphasisMark(text[i])) {
+ aTextRun->SetNoEmphasisMark(i);
+ }
+ }
+ } else {
+ auto text = reinterpret_cast<const char16_t*>(aTextPtr);
+ auto length = aTextRun->GetLength();
+ for (size_t i = 0; i < length; ++i) {
+ if (NS_IS_HIGH_SURROGATE(text[i]) && i + 1 < length &&
+ NS_IS_LOW_SURROGATE(text[i + 1])) {
+ uint32_t ch = SURROGATE_TO_UCS4(text[i], text[i + 1]);
+ if (!MayCharacterHaveEmphasisMark(ch)) {
+ aTextRun->SetNoEmphasisMark(i);
+ aTextRun->SetNoEmphasisMark(i + 1);
+ }
+ ++i;
+ } else {
+ if (!MayCharacterHaveEmphasisMark(uint32_t(text[i]))) {
+ aTextRun->SetNoEmphasisMark(i);
+ }
+ }
+ }
+ }
+}
+
+// Find the flow corresponding to aContent in aUserData
+static inline TextRunMappedFlow*
+FindFlowForContent(TextRunUserData* aUserData, nsIContent* aContent,
+ TextRunMappedFlow* userMappedFlows)
+{
+ // Find the flow that contains us
+ int32_t i = aUserData->mLastFlowIndex;
+ int32_t delta = 1;
+ int32_t sign = 1;
+ // Search starting at the current position and examine close-by
+ // positions first, moving further and further away as we go.
+ while (i >= 0 && uint32_t(i) < aUserData->mMappedFlowCount) {
+ TextRunMappedFlow* flow = &userMappedFlows[i];
+ if (flow->mStartFrame->GetContent() == aContent) {
+ return flow;
+ }
+
+ i += delta;
+ sign = -sign;
+ delta = -delta + sign;
+ }
+
+ // We ran into an array edge. Add |delta| to |i| once more to get
+ // back to the side where we still need to search, then step in
+ // the |sign| direction.
+ i += delta;
+ if (sign > 0) {
+ for (; i < int32_t(aUserData->mMappedFlowCount); ++i) {
+ TextRunMappedFlow* flow = &userMappedFlows[i];
+ if (flow->mStartFrame->GetContent() == aContent) {
+ return flow;
+ }
+ }
+ } else {
+ for (; i >= 0; --i) {
+ TextRunMappedFlow* flow = &userMappedFlows[i];
+ if (flow->mStartFrame->GetContent() == aContent) {
+ return flow;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+void
+BuildTextRunsScanner::AssignTextRun(gfxTextRun* aTextRun, float aInflation)
+{
+ for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
+ MappedFlow* mappedFlow = &mMappedFlows[i];
+ nsTextFrame* startFrame = mappedFlow->mStartFrame;
+ nsTextFrame* endFrame = mappedFlow->mEndFrame;
+ nsTextFrame* f;
+ for (f = startFrame; f != endFrame;
+ f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
+#ifdef DEBUG_roc
+ if (f->GetTextRun(mWhichTextRun)) {
+ gfxTextRun* textRun = f->GetTextRun(mWhichTextRun);
+ if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ if (mMappedFlows[0].mStartFrame != GetFrameForSimpleFlow(textRun)) {
+ NS_WARNING("REASSIGNING SIMPLE FLOW TEXT RUN!");
+ }
+ } else {
+ auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
+ if (userData->mMappedFlowCount >= mMappedFlows.Length() ||
+ userMappedFlows[userData->mMappedFlowCount - 1].mStartFrame !=
+ mMappedFlows[userdata->mMappedFlowCount - 1].mStartFrame) {
+ NS_WARNING("REASSIGNING MULTIFLOW TEXT RUN (not append)!");
+ }
+ }
+ }
+#endif
+
+ gfxTextRun* oldTextRun = f->GetTextRun(mWhichTextRun);
+ if (oldTextRun) {
+ nsTextFrame* firstFrame = nullptr;
+ uint32_t startOffset = 0;
+ if (oldTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ firstFrame = GetFrameForSimpleFlow(oldTextRun);
+ } else {
+ auto userData = static_cast<TextRunUserData*>(oldTextRun->GetUserData());
+ TextRunMappedFlow* userMappedFlows = GetMappedFlows(oldTextRun);
+ firstFrame = userMappedFlows[0].mStartFrame;
+ if (MOZ_UNLIKELY(f != firstFrame)) {
+ TextRunMappedFlow* flow =
+ FindFlowForContent(userData, f->GetContent(), userMappedFlows);
+ if (flow) {
+ startOffset = flow->mDOMOffsetToBeforeTransformOffset;
+ } else {
+ NS_ERROR("Can't find flow containing frame 'f'");
+ }
+ }
+ }
+
+ // Optimization: if |f| is the first frame in the flow then there are no
+ // prev-continuations that use |oldTextRun|.
+ nsTextFrame* clearFrom = nullptr;
+ if (MOZ_UNLIKELY(f != firstFrame)) {
+ // If all the frames in the mapped flow starting at |f| (inclusive)
+ // are empty then we let the prev-continuations keep the old text run.
+ gfxSkipCharsIterator iter(oldTextRun->GetSkipChars(), startOffset, f->GetContentOffset());
+ uint32_t textRunOffset = iter.ConvertOriginalToSkipped(f->GetContentOffset());
+ clearFrom = textRunOffset == oldTextRun->GetLength() ? f : nullptr;
+ }
+ f->ClearTextRun(clearFrom, mWhichTextRun);
+
+#ifdef DEBUG
+ if (firstFrame && !firstFrame->GetTextRun(mWhichTextRun)) {
+ // oldTextRun was destroyed - assert that we don't reference it.
+ for (uint32_t j = 0; j < mBreakSinks.Length(); ++j) {
+ NS_ASSERTION(oldTextRun != mBreakSinks[j]->mTextRun,
+ "destroyed text run is still in use");
+ }
+ }
+#endif
+ }
+ f->SetTextRun(aTextRun, mWhichTextRun, aInflation);
+ }
+ // Set this bit now; we can't set it any earlier because
+ // f->ClearTextRun() might clear it out.
+ nsFrameState whichTextRunState =
+ startFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
+ ? TEXT_IN_TEXTRUN_USER_DATA
+ : TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
+ startFrame->AddStateBits(whichTextRunState);
+ }
+}
+
+NS_QUERYFRAME_HEAD(nsTextFrame)
+ NS_QUERYFRAME_ENTRY(nsTextFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
+
+gfxSkipCharsIterator
+nsTextFrame::EnsureTextRun(TextRunType aWhichTextRun,
+ DrawTarget* aRefDrawTarget,
+ nsIFrame* aLineContainer,
+ const nsLineList::iterator* aLine,
+ uint32_t* aFlowEndInTextRun)
+{
+ gfxTextRun *textRun = GetTextRun(aWhichTextRun);
+ if (!textRun || (aLine && (*aLine)->GetInvalidateTextRuns())) {
+ RefPtr<DrawTarget> refDT = aRefDrawTarget;
+ if (!refDT) {
+ refDT = CreateReferenceDrawTarget(this);
+ }
+ if (refDT) {
+ BuildTextRuns(refDT, this, aLineContainer, aLine, aWhichTextRun);
+ }
+ textRun = GetTextRun(aWhichTextRun);
+ if (!textRun) {
+ // A text run was not constructed for this frame. This is bad. The caller
+ // will check mTextRun.
+ return gfxSkipCharsIterator(gfxPlatform::
+ GetPlatform()->EmptySkipChars(), 0);
+ }
+ TabWidthStore* tabWidths = Properties().Get(TabWidthProperty());
+ if (tabWidths && tabWidths->mValidForContentOffset != GetContentOffset()) {
+ Properties().Delete(TabWidthProperty());
+ }
+ }
+
+ if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ if (aFlowEndInTextRun) {
+ *aFlowEndInTextRun = textRun->GetLength();
+ }
+ return gfxSkipCharsIterator(textRun->GetSkipChars(), 0, mContentOffset);
+ }
+
+ auto userData = static_cast<TextRunUserData*>(textRun->GetUserData());
+ TextRunMappedFlow* userMappedFlows = GetMappedFlows(textRun);
+ TextRunMappedFlow* flow =
+ FindFlowForContent(userData, mContent, userMappedFlows);
+ if (flow) {
+ // Since textruns can only contain one flow for a given content element,
+ // this must be our flow.
+ uint32_t flowIndex = flow - userMappedFlows;
+ userData->mLastFlowIndex = flowIndex;
+ gfxSkipCharsIterator iter(textRun->GetSkipChars(),
+ flow->mDOMOffsetToBeforeTransformOffset, mContentOffset);
+ if (aFlowEndInTextRun) {
+ if (flowIndex + 1 < userData->mMappedFlowCount) {
+ gfxSkipCharsIterator end(textRun->GetSkipChars());
+ *aFlowEndInTextRun = end.ConvertOriginalToSkipped(
+ flow[1].mStartFrame->GetContentOffset() + flow[1].mDOMOffsetToBeforeTransformOffset);
+ } else {
+ *aFlowEndInTextRun = textRun->GetLength();
+ }
+ }
+ return iter;
+ }
+
+ NS_ERROR("Can't find flow containing this frame???");
+ return gfxSkipCharsIterator(gfxPlatform::GetPlatform()->EmptySkipChars(), 0);
+}
+
+static uint32_t
+GetEndOfTrimmedText(const nsTextFragment* aFrag, const nsStyleText* aStyleText,
+ uint32_t aStart, uint32_t aEnd,
+ gfxSkipCharsIterator* aIterator)
+{
+ aIterator->SetSkippedOffset(aEnd);
+ while (aIterator->GetSkippedOffset() > aStart) {
+ aIterator->AdvanceSkipped(-1);
+ if (!IsTrimmableSpace(aFrag, aIterator->GetOriginalOffset(), aStyleText))
+ return aIterator->GetSkippedOffset() + 1;
+ }
+ return aStart;
+}
+
+nsTextFrame::TrimmedOffsets
+nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
+ bool aTrimAfter, bool aPostReflow)
+{
+ NS_ASSERTION(mTextRun, "Need textrun here");
+ if (aPostReflow) {
+ // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
+ // to be set correctly. If our parent wasn't reflowed due to the frame
+ // tree being too deep then the return value doesn't matter.
+ NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
+ (GetParent()->GetStateBits() &
+ NS_FRAME_TOO_DEEP_IN_FRAME_TREE),
+ "Can only call this on frames that have been reflowed");
+ NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
+ "Can only call this on frames that are not being reflowed");
+ }
+
+ TrimmedOffsets offsets = { GetContentOffset(), GetContentLength() };
+ const nsStyleText* textStyle = StyleText();
+ // Note that pre-line newlines should still allow us to trim spaces
+ // for display
+ if (textStyle->WhiteSpaceIsSignificant())
+ return offsets;
+
+ if (!aPostReflow || (GetStateBits() & TEXT_START_OF_LINE)) {
+ int32_t whitespaceCount =
+ GetTrimmableWhitespaceCount(aFrag,
+ offsets.mStart, offsets.mLength, 1);
+ offsets.mStart += whitespaceCount;
+ offsets.mLength -= whitespaceCount;
+ }
+
+ if (aTrimAfter && (!aPostReflow || (GetStateBits() & TEXT_END_OF_LINE))) {
+ // This treats a trailing 'pre-line' newline as trimmable. That's fine,
+ // it's actually what we want since we want whitespace before it to
+ // be trimmed.
+ int32_t whitespaceCount =
+ GetTrimmableWhitespaceCount(aFrag,
+ offsets.GetEnd() - 1, offsets.mLength, -1);
+ offsets.mLength -= whitespaceCount;
+ }
+ return offsets;
+}
+
+static bool IsJustifiableCharacter(const nsTextFragment* aFrag, int32_t aPos,
+ bool aLangIsCJ)
+{
+ NS_ASSERTION(aPos >= 0, "negative position?!");
+ char16_t ch = aFrag->CharAt(aPos);
+ if (ch == '\n' || ch == '\t' || ch == '\r')
+ return true;
+ if (ch == ' ' || ch == CH_NBSP) {
+ // Don't justify spaces that are combined with diacriticals
+ if (!aFrag->Is2b())
+ return true;
+ return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(
+ aFrag->Get2b() + aPos + 1, aFrag->GetLength() - (aPos + 1));
+ }
+ if (ch < 0x2150u)
+ return false;
+ if (aLangIsCJ) {
+ if ((0x2150u <= ch && ch <= 0x22ffu) || // Number Forms, Arrows, Mathematical Operators
+ (0x2460u <= ch && ch <= 0x24ffu) || // Enclosed Alphanumerics
+ (0x2580u <= ch && ch <= 0x27bfu) || // Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats
+ (0x27f0u <= ch && ch <= 0x2bffu) || // Supplemental Arrows-A, Braille Patterns, Supplemental Arrows-B,
+ // Miscellaneous Mathematical Symbols-B, Supplemental Mathematical Operators,
+ // Miscellaneous Symbols and Arrows
+ (0x2e80u <= ch && ch <= 0x312fu) || // CJK Radicals Supplement, CJK Radicals Supplement,
+ // Ideographic Description Characters, CJK Symbols and Punctuation,
+ // Hiragana, Katakana, Bopomofo
+ (0x3190u <= ch && ch <= 0xabffu) || // Kanbun, Bopomofo Extended, Katakana Phonetic Extensions,
+ // Enclosed CJK Letters and Months, CJK Compatibility,
+ // CJK Unified Ideographs Extension A, Yijing Hexagram Symbols,
+ // CJK Unified Ideographs, Yi Syllables, Yi Radicals
+ (0xf900u <= ch && ch <= 0xfaffu) || // CJK Compatibility Ideographs
+ (0xff5eu <= ch && ch <= 0xff9fu) // Halfwidth and Fullwidth Forms(a part)
+ ) {
+ return true;
+ }
+ char16_t ch2;
+ if (NS_IS_HIGH_SURROGATE(ch) && aFrag->GetLength() > uint32_t(aPos) + 1 &&
+ NS_IS_LOW_SURROGATE(ch2 = aFrag->CharAt(aPos + 1))) {
+ uint32_t u = SURROGATE_TO_UCS4(ch, ch2);
+ if (0x20000u <= u && u <= 0x2ffffu) { // CJK Unified Ideographs Extension B,
+ // CJK Unified Ideographs Extension C,
+ // CJK Unified Ideographs Extension D,
+ // CJK Compatibility Ideographs Supplement
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+nsTextFrame::ClearMetrics(ReflowOutput& aMetrics)
+{
+ aMetrics.ClearSize();
+ aMetrics.SetBlockStartAscent(0);
+ mAscent = 0;
+
+ AddStateBits(TEXT_NO_RENDERED_GLYPHS);
+}
+
+static int32_t FindChar(const nsTextFragment* frag,
+ int32_t aOffset, int32_t aLength, char16_t ch)
+{
+ int32_t i = 0;
+ if (frag->Is2b()) {
+ const char16_t* str = frag->Get2b() + aOffset;
+ for (; i < aLength; ++i) {
+ if (*str == ch)
+ return i + aOffset;
+ ++str;
+ }
+ } else {
+ if (uint16_t(ch) <= 0xFF) {
+ const char* str = frag->Get1b() + aOffset;
+ const void* p = memchr(str, ch, aLength);
+ if (p)
+ return (static_cast<const char*>(p) - str) + aOffset;
+ }
+ }
+ return -1;
+}
+
+static bool IsChineseOrJapanese(nsTextFrame* aFrame)
+{
+ if (aFrame->ShouldSuppressLineBreak()) {
+ // Always treat ruby as CJ language so that those characters can
+ // be expanded properly even when surrounded by other language.
+ return true;
+ }
+
+ nsIAtom* language = aFrame->StyleFont()->mLanguage;
+ if (!language) {
+ return false;
+ }
+ return nsStyleUtil::MatchesLanguagePrefix(language, u"ja") ||
+ nsStyleUtil::MatchesLanguagePrefix(language, u"zh");
+}
+
+#ifdef DEBUG
+static bool IsInBounds(const gfxSkipCharsIterator& aStart, int32_t aContentLength,
+ gfxTextRun::Range aRange) {
+ if (aStart.GetSkippedOffset() > aRange.start)
+ return false;
+ if (aContentLength == INT32_MAX)
+ return true;
+ gfxSkipCharsIterator iter(aStart);
+ iter.AdvanceOriginal(aContentLength);
+ return iter.GetSkippedOffset() >= aRange.end;
+}
+#endif
+
+class MOZ_STACK_CLASS PropertyProvider : public gfxTextRun::PropertyProvider {
+ typedef gfxTextRun::Range Range;
+
+public:
+ /**
+ * Use this constructor for reflow, when we don't know what text is
+ * really mapped by the frame and we have a lot of other data around.
+ *
+ * @param aLength can be INT32_MAX to indicate we cover all the text
+ * associated with aFrame up to where its flow chain ends in the given
+ * textrun. If INT32_MAX is passed, justification and hyphen-related methods
+ * cannot be called, nor can GetOriginalLength().
+ */
+ PropertyProvider(gfxTextRun* aTextRun, const nsStyleText* aTextStyle,
+ const nsTextFragment* aFrag, nsTextFrame* aFrame,
+ const gfxSkipCharsIterator& aStart, int32_t aLength,
+ nsIFrame* aLineContainer,
+ nscoord aOffsetFromBlockOriginForTabs,
+ nsTextFrame::TextRunType aWhichTextRun)
+ : mTextRun(aTextRun), mFontGroup(nullptr),
+ mTextStyle(aTextStyle), mFrag(aFrag),
+ mLineContainer(aLineContainer),
+ mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
+ mTabWidths(nullptr), mTabWidthsAnalyzedLimit(0),
+ mLength(aLength),
+ mWordSpacing(WordSpacing(aFrame, mTextRun, aTextStyle)),
+ mLetterSpacing(LetterSpacing(aFrame, aTextStyle)),
+ mHyphenWidth(-1),
+ mOffsetFromBlockOriginForTabs(aOffsetFromBlockOriginForTabs),
+ mReflowing(true),
+ mWhichTextRun(aWhichTextRun)
+ {
+ NS_ASSERTION(mStart.IsInitialized(), "Start not initialized?");
+ }
+
+ /**
+ * Use this constructor after the frame has been reflowed and we don't
+ * have other data around. Gets everything from the frame. EnsureTextRun
+ * *must* be called before this!!!
+ */
+ PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart,
+ nsTextFrame::TextRunType aWhichTextRun)
+ : mTextRun(aFrame->GetTextRun(aWhichTextRun)), mFontGroup(nullptr),
+ mTextStyle(aFrame->StyleText()),
+ mFrag(aFrame->GetContent()->GetText()),
+ mLineContainer(nullptr),
+ mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
+ mTabWidths(nullptr), mTabWidthsAnalyzedLimit(0),
+ mLength(aFrame->GetContentLength()),
+ mWordSpacing(WordSpacing(aFrame, mTextRun)),
+ mLetterSpacing(LetterSpacing(aFrame)),
+ mHyphenWidth(-1),
+ mOffsetFromBlockOriginForTabs(0),
+ mReflowing(false),
+ mWhichTextRun(aWhichTextRun)
+ {
+ NS_ASSERTION(mTextRun, "Textrun not initialized!");
+ }
+
+ // Call this after construction if you're not going to reflow the text
+ void InitializeForDisplay(bool aTrimAfter);
+
+ void InitializeForMeasure();
+
+ virtual void GetSpacing(Range aRange, Spacing* aSpacing);
+ virtual gfxFloat GetHyphenWidth();
+ virtual void GetHyphenationBreaks(Range aRange, bool* aBreakBefore);
+ virtual int8_t GetHyphensOption() {
+ return mTextStyle->mHyphens;
+ }
+
+ virtual already_AddRefed<DrawTarget> GetDrawTarget() {
+ return CreateReferenceDrawTarget(GetFrame());
+ }
+
+ virtual uint32_t GetAppUnitsPerDevUnit() {
+ return mTextRun->GetAppUnitsPerDevUnit();
+ }
+
+ void GetSpacingInternal(Range aRange, Spacing* aSpacing, bool aIgnoreTabs);
+
+ /**
+ * Compute the justification information in given DOM range, return
+ * justification info and assignments if requested.
+ */
+ JustificationInfo ComputeJustification(
+ Range aRange, nsTArray<JustificationAssignment>* aAssignments = nullptr);
+
+ const nsStyleText* StyleText() { return mTextStyle; }
+ nsTextFrame* GetFrame() { return mFrame; }
+ // This may not be equal to the frame offset/length in because we may have
+ // adjusted for whitespace trimming according to the state bits set in the frame
+ // (for the static provider)
+ const gfxSkipCharsIterator& GetStart() const { return mStart; }
+ // May return INT32_MAX if that was given to the constructor
+ uint32_t GetOriginalLength() const {
+ NS_ASSERTION(mLength != INT32_MAX, "Length not known");
+ return mLength;
+ }
+ const nsTextFragment* GetFragment() { return mFrag; }
+
+ gfxFontGroup* GetFontGroup() {
+ if (!mFontGroup)
+ InitFontGroupAndFontMetrics();
+ return mFontGroup;
+ }
+
+ nsFontMetrics* GetFontMetrics() {
+ if (!mFontMetrics)
+ InitFontGroupAndFontMetrics();
+ return mFontMetrics;
+ }
+
+ void CalcTabWidths(Range aTransformedRange);
+
+ const gfxSkipCharsIterator& GetEndHint() { return mTempIterator; }
+
+protected:
+ void SetupJustificationSpacing(bool aPostReflow);
+
+ void InitFontGroupAndFontMetrics() {
+ float inflation = (mWhichTextRun == nsTextFrame::eInflated)
+ ? mFrame->GetFontSizeInflation() : 1.0f;
+ mFontGroup = GetFontGroupForFrame(mFrame, inflation,
+ getter_AddRefs(mFontMetrics));
+ }
+
+ RefPtr<gfxTextRun> mTextRun;
+ gfxFontGroup* mFontGroup;
+ RefPtr<nsFontMetrics> mFontMetrics;
+ const nsStyleText* mTextStyle;
+ const nsTextFragment* mFrag;
+ nsIFrame* mLineContainer;
+ nsTextFrame* mFrame;
+ gfxSkipCharsIterator mStart; // Offset in original and transformed string
+ gfxSkipCharsIterator mTempIterator;
+
+ // Either null, or pointing to the frame's TabWidthProperty.
+ TabWidthStore* mTabWidths;
+ // How far we've done tab-width calculation; this is ONLY valid when
+ // mTabWidths is nullptr (otherwise rely on mTabWidths->mLimit instead).
+ // It's a DOM offset relative to the current frame's offset.
+ uint32_t mTabWidthsAnalyzedLimit;
+
+ int32_t mLength; // DOM string length, may be INT32_MAX
+ gfxFloat mWordSpacing; // space for each whitespace char
+ gfxFloat mLetterSpacing; // space for each letter
+ gfxFloat mHyphenWidth;
+ gfxFloat mOffsetFromBlockOriginForTabs;
+
+ // The values in mJustificationSpacings corresponds to unskipped
+ // characters start from mJustificationArrayStart.
+ uint32_t mJustificationArrayStart;
+ nsTArray<Spacing> mJustificationSpacings;
+
+ bool mReflowing;
+ nsTextFrame::TextRunType mWhichTextRun;
+};
+
+/**
+ * Finds the offset of the first character of the cluster containing aPos
+ */
+static void FindClusterStart(const gfxTextRun* aTextRun,
+ int32_t aOriginalStart,
+ gfxSkipCharsIterator* aPos)
+{
+ while (aPos->GetOriginalOffset() > aOriginalStart) {
+ if (aPos->IsOriginalCharSkipped() ||
+ aTextRun->IsClusterStart(aPos->GetSkippedOffset())) {
+ break;
+ }
+ aPos->AdvanceOriginal(-1);
+ }
+}
+
+/**
+ * Finds the offset of the last character of the cluster containing aPos.
+ * If aAllowSplitLigature is false, we also check for a ligature-group
+ * start.
+ */
+static void FindClusterEnd(const gfxTextRun* aTextRun,
+ int32_t aOriginalEnd,
+ gfxSkipCharsIterator* aPos,
+ bool aAllowSplitLigature = true)
+{
+ NS_PRECONDITION(aPos->GetOriginalOffset() < aOriginalEnd,
+ "character outside string");
+ aPos->AdvanceOriginal(1);
+ while (aPos->GetOriginalOffset() < aOriginalEnd) {
+ if (aPos->IsOriginalCharSkipped() ||
+ (aTextRun->IsClusterStart(aPos->GetSkippedOffset()) &&
+ (aAllowSplitLigature ||
+ aTextRun->IsLigatureGroupStart(aPos->GetSkippedOffset())))) {
+ break;
+ }
+ aPos->AdvanceOriginal(1);
+ }
+ aPos->AdvanceOriginal(-1);
+}
+
+JustificationInfo
+PropertyProvider::ComputeJustification(
+ Range aRange, nsTArray<JustificationAssignment>* aAssignments)
+{
+ JustificationInfo info;
+
+ // Horizontal-in-vertical frame is orthogonal to the line, so it
+ // doesn't actually include any justification opportunity inside.
+ // The spec says such frame should be treated as a U+FFFC. Since we
+ // do not insert justification opportunities on the sides of that
+ // character, the sides of this frame are not justifiable either.
+ if (mFrame->StyleContext()->IsTextCombined()) {
+ return info;
+ }
+
+ bool isCJ = IsChineseOrJapanese(mFrame);
+ nsSkipCharsRunIterator run(
+ mStart, nsSkipCharsRunIterator::LENGTH_INCLUDES_SKIPPED, aRange.Length());
+ run.SetOriginalOffset(aRange.start);
+ mJustificationArrayStart = run.GetSkippedOffset();
+
+ nsTArray<JustificationAssignment> assignments;
+ assignments.SetCapacity(aRange.Length());
+ while (run.NextRun()) {
+ uint32_t originalOffset = run.GetOriginalOffset();
+ uint32_t skippedOffset = run.GetSkippedOffset();
+ uint32_t length = run.GetRunLength();
+ assignments.SetLength(skippedOffset + length - mJustificationArrayStart);
+
+ gfxSkipCharsIterator iter = run.GetPos();
+ for (uint32_t i = 0; i < length; ++i) {
+ uint32_t offset = originalOffset + i;
+ if (!IsJustifiableCharacter(mFrag, offset, isCJ)) {
+ continue;
+ }
+
+ iter.SetOriginalOffset(offset);
+
+ FindClusterStart(mTextRun, originalOffset, &iter);
+ uint32_t firstCharOffset = iter.GetSkippedOffset();
+ uint32_t firstChar = firstCharOffset > mJustificationArrayStart ?
+ firstCharOffset - mJustificationArrayStart : 0;
+ if (!firstChar) {
+ info.mIsStartJustifiable = true;
+ } else {
+ auto& assign = assignments[firstChar];
+ auto& prevAssign = assignments[firstChar - 1];
+ if (prevAssign.mGapsAtEnd) {
+ prevAssign.mGapsAtEnd = 1;
+ assign.mGapsAtStart = 1;
+ } else {
+ assign.mGapsAtStart = 2;
+ info.mInnerOpportunities++;
+ }
+ }
+
+ FindClusterEnd(mTextRun, originalOffset + length, &iter);
+ uint32_t lastChar = iter.GetSkippedOffset() - mJustificationArrayStart;
+ // Assign the two gaps temporary to the last char. If the next cluster is
+ // justifiable as well, one of the gaps will be removed by code above.
+ assignments[lastChar].mGapsAtEnd = 2;
+ info.mInnerOpportunities++;
+
+ // Skip the whole cluster
+ i = iter.GetOriginalOffset() - originalOffset;
+ }
+ }
+
+ if (!assignments.IsEmpty() && assignments.LastElement().mGapsAtEnd) {
+ // We counted the expansion opportunity after the last character,
+ // but it is not an inner opportunity.
+ MOZ_ASSERT(info.mInnerOpportunities > 0);
+ info.mInnerOpportunities--;
+ info.mIsEndJustifiable = true;
+ }
+
+ if (aAssignments) {
+ *aAssignments = Move(assignments);
+ }
+ return info;
+}
+
+// aStart, aLength in transformed string offsets
+void
+PropertyProvider::GetSpacing(Range aRange, Spacing* aSpacing)
+{
+ GetSpacingInternal(aRange, aSpacing,
+ (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) == 0);
+}
+
+static bool
+CanAddSpacingAfter(const gfxTextRun* aTextRun, uint32_t aOffset)
+{
+ if (aOffset + 1 >= aTextRun->GetLength())
+ return true;
+ return aTextRun->IsClusterStart(aOffset + 1) &&
+ aTextRun->IsLigatureGroupStart(aOffset + 1);
+}
+
+void
+PropertyProvider::GetSpacingInternal(Range aRange, Spacing* aSpacing,
+ bool aIgnoreTabs)
+{
+ NS_PRECONDITION(IsInBounds(mStart, mLength, aRange), "Range out of bounds");
+
+ uint32_t index;
+ for (index = 0; index < aRange.Length(); ++index) {
+ aSpacing[index].mBefore = 0.0;
+ aSpacing[index].mAfter = 0.0;
+ }
+
+ if (mFrame->StyleContext()->IsTextCombined()) {
+ return;
+ }
+
+ // Find our offset into the original+transformed string
+ gfxSkipCharsIterator start(mStart);
+ start.SetSkippedOffset(aRange.start);
+
+ // First, compute the word and letter spacing
+ if (mWordSpacing || mLetterSpacing) {
+ // Iterate over non-skipped characters
+ nsSkipCharsRunIterator run(
+ start, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aRange.Length());
+ while (run.NextRun()) {
+ uint32_t runOffsetInSubstring = run.GetSkippedOffset() - aRange.start;
+ gfxSkipCharsIterator iter = run.GetPos();
+ for (int32_t i = 0; i < run.GetRunLength(); ++i) {
+ if (CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i)) {
+ // End of a cluster, not in a ligature: put letter-spacing after it
+ aSpacing[runOffsetInSubstring + i].mAfter += mLetterSpacing;
+ }
+ if (IsCSSWordSpacingSpace(mFrag, i + run.GetOriginalOffset(),
+ mFrame, mTextStyle)) {
+ // It kinda sucks, but space characters can be part of clusters,
+ // and even still be whitespace (I think!)
+ iter.SetSkippedOffset(run.GetSkippedOffset() + i);
+ FindClusterEnd(mTextRun, run.GetOriginalOffset() + run.GetRunLength(),
+ &iter);
+ uint32_t runOffset = iter.GetSkippedOffset() - aRange.start;
+ aSpacing[runOffset].mAfter += mWordSpacing;
+ }
+ }
+ }
+ }
+
+ // Ignore tab spacing rather than computing it, if the tab size is 0
+ if (!aIgnoreTabs)
+ aIgnoreTabs = mFrame->StyleText()->mTabSize == 0;
+
+ // Now add tab spacing, if there is any
+ if (!aIgnoreTabs) {
+ CalcTabWidths(aRange);
+ if (mTabWidths) {
+ mTabWidths->ApplySpacing(aSpacing,
+ aRange.start - mStart.GetSkippedOffset(),
+ aRange.Length());
+ }
+ }
+
+ // Now add in justification spacing
+ if (mJustificationSpacings.Length() > 0) {
+ // If there is any spaces trimmed at the end, aStart + aLength may
+ // be larger than the flags array. When that happens, we can simply
+ // ignore those spaces.
+ auto arrayEnd = mJustificationArrayStart +
+ static_cast<uint32_t>(mJustificationSpacings.Length());
+ auto end = std::min(aRange.end, arrayEnd);
+ MOZ_ASSERT(aRange.start >= mJustificationArrayStart);
+ for (auto i = aRange.start; i < end; i++) {
+ const auto& spacing =
+ mJustificationSpacings[i - mJustificationArrayStart];
+ uint32_t offset = i - aRange.start;
+ aSpacing[offset].mBefore += spacing.mBefore;
+ aSpacing[offset].mAfter += spacing.mAfter;
+ }
+ }
+}
+
+static gfxFloat
+ComputeTabWidthAppUnits(nsIFrame* aFrame, const gfxTextRun* aTextRun)
+{
+ // Get the number of spaces from CSS -moz-tab-size
+ const nsStyleText* textStyle = aFrame->StyleText();
+
+ return textStyle->mTabSize * GetSpaceWidthAppUnits(aTextRun);
+}
+
+// aX and the result are in whole appunits.
+static gfxFloat
+AdvanceToNextTab(gfxFloat aX, nsIFrame* aFrame,
+ const gfxTextRun* aTextRun, gfxFloat* aCachedTabWidth)
+{
+ if (*aCachedTabWidth < 0) {
+ *aCachedTabWidth = ComputeTabWidthAppUnits(aFrame, aTextRun);
+ }
+
+ // Advance aX to the next multiple of *aCachedTabWidth. We must advance
+ // by at least 1 appunit.
+ // XXX should we make this 1 CSS pixel?
+ return ceil((aX + 1)/(*aCachedTabWidth))*(*aCachedTabWidth);
+}
+
+void
+PropertyProvider::CalcTabWidths(Range aRange)
+{
+ if (!mTabWidths) {
+ if (mReflowing && !mLineContainer) {
+ // Intrinsic width computation does its own tab processing. We
+ // just don't do anything here.
+ return;
+ }
+ if (!mReflowing) {
+ mTabWidths = mFrame->Properties().Get(TabWidthProperty());
+#ifdef DEBUG
+ // If we're not reflowing, we should have already computed the
+ // tab widths; check that they're available as far as the last
+ // tab character present (if any)
+ for (uint32_t i = aRange.end; i > aRange.start; --i) {
+ if (mTextRun->CharIsTab(i - 1)) {
+ uint32_t startOffset = mStart.GetSkippedOffset();
+ NS_ASSERTION(mTabWidths && mTabWidths->mLimit + startOffset >= i,
+ "Precomputed tab widths are missing!");
+ break;
+ }
+ }
+#endif
+ return;
+ }
+ }
+
+ uint32_t startOffset = mStart.GetSkippedOffset();
+ MOZ_ASSERT(aRange.start >= startOffset, "wrong start offset");
+ MOZ_ASSERT(aRange.end <= startOffset + mLength, "beyond the end");
+ uint32_t tabsEnd =
+ (mTabWidths ? mTabWidths->mLimit : mTabWidthsAnalyzedLimit) + startOffset;
+ if (tabsEnd < aRange.end) {
+ NS_ASSERTION(mReflowing,
+ "We need precomputed tab widths, but don't have enough.");
+
+ gfxFloat tabWidth = -1;
+ for (uint32_t i = tabsEnd; i < aRange.end; ++i) {
+ Spacing spacing;
+ GetSpacingInternal(Range(i, i + 1), &spacing, true);
+ mOffsetFromBlockOriginForTabs += spacing.mBefore;
+
+ if (!mTextRun->CharIsTab(i)) {
+ if (mTextRun->IsClusterStart(i)) {
+ uint32_t clusterEnd = i + 1;
+ while (clusterEnd < mTextRun->GetLength() &&
+ !mTextRun->IsClusterStart(clusterEnd)) {
+ ++clusterEnd;
+ }
+ mOffsetFromBlockOriginForTabs +=
+ mTextRun->GetAdvanceWidth(Range(i, clusterEnd), nullptr);
+ }
+ } else {
+ if (!mTabWidths) {
+ mTabWidths = new TabWidthStore(mFrame->GetContentOffset());
+ mFrame->Properties().Set(TabWidthProperty(), mTabWidths);
+ }
+ double nextTab = AdvanceToNextTab(mOffsetFromBlockOriginForTabs,
+ mFrame, mTextRun, &tabWidth);
+ mTabWidths->mWidths.AppendElement(TabWidth(i - startOffset,
+ NSToIntRound(nextTab - mOffsetFromBlockOriginForTabs)));
+ mOffsetFromBlockOriginForTabs = nextTab;
+ }
+
+ mOffsetFromBlockOriginForTabs += spacing.mAfter;
+ }
+
+ if (mTabWidths) {
+ mTabWidths->mLimit = aRange.end - startOffset;
+ }
+ }
+
+ if (!mTabWidths) {
+ // Delete any stale property that may be left on the frame
+ mFrame->Properties().Delete(TabWidthProperty());
+ mTabWidthsAnalyzedLimit = std::max(mTabWidthsAnalyzedLimit,
+ aRange.end - startOffset);
+ }
+}
+
+gfxFloat
+PropertyProvider::GetHyphenWidth()
+{
+ if (mHyphenWidth < 0) {
+ mHyphenWidth = GetFontGroup()->GetHyphenWidth(this);
+ }
+ return mHyphenWidth + mLetterSpacing;
+}
+
+void
+PropertyProvider::GetHyphenationBreaks(Range aRange, bool* aBreakBefore)
+{
+ NS_PRECONDITION(IsInBounds(mStart, mLength, aRange), "Range out of bounds");
+ NS_PRECONDITION(mLength != INT32_MAX, "Can't call this with undefined length");
+
+ if (!mTextStyle->WhiteSpaceCanWrap(mFrame) ||
+ mTextStyle->mHyphens == NS_STYLE_HYPHENS_NONE)
+ {
+ memset(aBreakBefore, false, aRange.Length() * sizeof(bool));
+ return;
+ }
+
+ // Iterate through the original-string character runs
+ nsSkipCharsRunIterator run(
+ mStart, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aRange.Length());
+ run.SetSkippedOffset(aRange.start);
+ // We need to visit skipped characters so that we can detect SHY
+ run.SetVisitSkipped();
+
+ int32_t prevTrailingCharOffset = run.GetPos().GetOriginalOffset() - 1;
+ bool allowHyphenBreakBeforeNextChar =
+ prevTrailingCharOffset >= mStart.GetOriginalOffset() &&
+ prevTrailingCharOffset < mStart.GetOriginalOffset() + mLength &&
+ mFrag->CharAt(prevTrailingCharOffset) == CH_SHY;
+
+ while (run.NextRun()) {
+ NS_ASSERTION(run.GetRunLength() > 0, "Shouldn't return zero-length runs");
+ if (run.IsSkipped()) {
+ // Check if there's a soft hyphen which would let us hyphenate before
+ // the next non-skipped character. Don't look at soft hyphens followed
+ // by other skipped characters, we won't use them.
+ allowHyphenBreakBeforeNextChar =
+ mFrag->CharAt(run.GetOriginalOffset() + run.GetRunLength() - 1) == CH_SHY;
+ } else {
+ int32_t runOffsetInSubstring = run.GetSkippedOffset() - aRange.start;
+ memset(aBreakBefore + runOffsetInSubstring, false, run.GetRunLength()*sizeof(bool));
+ // Don't allow hyphen breaks at the start of the line
+ aBreakBefore[runOffsetInSubstring] = allowHyphenBreakBeforeNextChar &&
+ (!(mFrame->GetStateBits() & TEXT_START_OF_LINE) ||
+ run.GetSkippedOffset() > mStart.GetSkippedOffset());
+ allowHyphenBreakBeforeNextChar = false;
+ }
+ }
+
+ if (mTextStyle->mHyphens == NS_STYLE_HYPHENS_AUTO) {
+ for (uint32_t i = 0; i < aRange.Length(); ++i) {
+ if (mTextRun->CanHyphenateBefore(aRange.start + i)) {
+ aBreakBefore[i] = true;
+ }
+ }
+ }
+}
+
+void
+PropertyProvider::InitializeForDisplay(bool aTrimAfter)
+{
+ nsTextFrame::TrimmedOffsets trimmed =
+ mFrame->GetTrimmedOffsets(mFrag, aTrimAfter);
+ mStart.SetOriginalOffset(trimmed.mStart);
+ mLength = trimmed.mLength;
+ SetupJustificationSpacing(true);
+}
+
+void
+PropertyProvider::InitializeForMeasure()
+{
+ nsTextFrame::TrimmedOffsets trimmed =
+ mFrame->GetTrimmedOffsets(mFrag, true, false);
+ mStart.SetOriginalOffset(trimmed.mStart);
+ mLength = trimmed.mLength;
+ SetupJustificationSpacing(false);
+}
+
+
+void
+PropertyProvider::SetupJustificationSpacing(bool aPostReflow)
+{
+ NS_PRECONDITION(mLength != INT32_MAX, "Can't call this with undefined length");
+
+ if (!(mFrame->GetStateBits() & TEXT_JUSTIFICATION_ENABLED))
+ return;
+
+ gfxSkipCharsIterator start(mStart), end(mStart);
+ // We can't just use our mLength here; when InitializeForDisplay is
+ // called with false for aTrimAfter, we still shouldn't be assigning
+ // justification space to any trailing whitespace.
+ nsTextFrame::TrimmedOffsets trimmed =
+ mFrame->GetTrimmedOffsets(mFrag, true, aPostReflow);
+ end.AdvanceOriginal(trimmed.mLength);
+ gfxSkipCharsIterator realEnd(end);
+
+ Range range(uint32_t(start.GetOriginalOffset()),
+ uint32_t(end.GetOriginalOffset()));
+ nsTArray<JustificationAssignment> assignments;
+ JustificationInfo info = ComputeJustification(range, &assignments);
+
+ auto assign = mFrame->GetJustificationAssignment();
+ auto totalGaps = JustificationUtils::CountGaps(info, assign);
+ if (!totalGaps || assignments.IsEmpty()) {
+ // Nothing to do, nothing is justifiable and we shouldn't have any
+ // justification space assigned
+ return;
+ }
+
+ // Remember that textrun measurements are in the run's orientation,
+ // so its advance "width" is actually a height in vertical writing modes,
+ // corresponding to the inline-direction of the frame.
+ gfxFloat naturalWidth =
+ mTextRun->GetAdvanceWidth(Range(mStart.GetSkippedOffset(),
+ realEnd.GetSkippedOffset()), this);
+ if (mFrame->GetStateBits() & TEXT_HYPHEN_BREAK) {
+ naturalWidth += GetHyphenWidth();
+ }
+ nscoord totalSpacing = mFrame->ISize() - naturalWidth;
+ if (totalSpacing <= 0) {
+ // No space available
+ return;
+ }
+
+ assignments[0].mGapsAtStart = assign.mGapsAtStart;
+ assignments.LastElement().mGapsAtEnd = assign.mGapsAtEnd;
+
+ MOZ_ASSERT(mJustificationSpacings.IsEmpty());
+ JustificationApplicationState state(totalGaps, totalSpacing);
+ mJustificationSpacings.SetCapacity(assignments.Length());
+ for (const JustificationAssignment& assign : assignments) {
+ Spacing* spacing = mJustificationSpacings.AppendElement();
+ spacing->mBefore = state.Consume(assign.mGapsAtStart);
+ spacing->mAfter = state.Consume(assign.mGapsAtEnd);
+ }
+}
+
+//----------------------------------------------------------------------
+
+static nscolor
+EnsureDifferentColors(nscolor colorA, nscolor colorB)
+{
+ if (colorA == colorB) {
+ nscolor res;
+ res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
+ NS_GET_G(colorA) ^ 0xff,
+ NS_GET_B(colorA) ^ 0xff);
+ return res;
+ }
+ return colorA;
+}
+
+//-----------------------------------------------------------------------------
+
+nsTextPaintStyle::nsTextPaintStyle(nsTextFrame* aFrame)
+ : mFrame(aFrame),
+ mPresContext(aFrame->PresContext()),
+ mInitCommonColors(false),
+ mInitSelectionColorsAndShadow(false),
+ mResolveColors(true),
+ mHasSelectionShadow(false)
+{
+ for (uint32_t i = 0; i < ArrayLength(mSelectionStyle); i++)
+ mSelectionStyle[i].mInit = false;
+}
+
+bool
+nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor)
+{
+ InitCommonColors();
+
+ // If the combination of selection background color and frame background color
+ // is sufficient contrast, don't exchange the selection colors.
+ int32_t backLuminosityDifference =
+ NS_LUMINOSITY_DIFFERENCE(*aBackColor, mFrameBackgroundColor);
+ if (backLuminosityDifference >= mSufficientContrast)
+ return false;
+
+ // Otherwise, we should use the higher-contrast color for the selection
+ // background color.
+ int32_t foreLuminosityDifference =
+ NS_LUMINOSITY_DIFFERENCE(*aForeColor, mFrameBackgroundColor);
+ if (backLuminosityDifference < foreLuminosityDifference) {
+ nscolor tmpColor = *aForeColor;
+ *aForeColor = *aBackColor;
+ *aBackColor = tmpColor;
+ return true;
+ }
+ return false;
+}
+
+nscolor
+nsTextPaintStyle::GetTextColor()
+{
+ if (mFrame->IsSVGText()) {
+ if (!mResolveColors)
+ return NS_SAME_AS_FOREGROUND_COLOR;
+
+ const nsStyleSVG* style = mFrame->StyleSVG();
+ switch (style->mFill.Type()) {
+ case eStyleSVGPaintType_None:
+ return NS_RGBA(0, 0, 0, 0);
+ case eStyleSVGPaintType_Color:
+ return nsLayoutUtils::GetColor(mFrame, eCSSProperty_fill);
+ default:
+ NS_ERROR("cannot resolve SVG paint to nscolor");
+ return NS_RGBA(0, 0, 0, 255);
+ }
+ }
+
+ return nsLayoutUtils::GetColor(mFrame, eCSSProperty__webkit_text_fill_color);
+}
+
+bool
+nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor,
+ nscolor* aBackColor)
+{
+ NS_ASSERTION(aForeColor, "aForeColor is null");
+ NS_ASSERTION(aBackColor, "aBackColor is null");
+
+ if (!InitSelectionColorsAndShadow())
+ return false;
+
+ *aForeColor = mSelectionTextColor;
+ *aBackColor = mSelectionBGColor;
+ return true;
+}
+
+void
+nsTextPaintStyle::GetHighlightColors(nscolor* aForeColor,
+ nscolor* aBackColor)
+{
+ NS_ASSERTION(aForeColor, "aForeColor is null");
+ NS_ASSERTION(aBackColor, "aBackColor is null");
+
+ nscolor backColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightBackground);
+ nscolor foreColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightForeground);
+ EnsureSufficientContrast(&foreColor, &backColor);
+ *aForeColor = foreColor;
+ *aBackColor = backColor;
+}
+
+void
+nsTextPaintStyle::GetURLSecondaryColor(nscolor* aForeColor)
+{
+ NS_ASSERTION(aForeColor, "aForeColor is null");
+
+ nscolor textColor = GetTextColor();
+ textColor = NS_RGBA(NS_GET_R(textColor),
+ NS_GET_G(textColor),
+ NS_GET_B(textColor),
+ (uint8_t)(255 * 0.5f));
+ // Don't use true alpha color for readability.
+ InitCommonColors();
+ *aForeColor = NS_ComposeColors(mFrameBackgroundColor, textColor);
+}
+
+void
+nsTextPaintStyle::GetIMESelectionColors(int32_t aIndex,
+ nscolor* aForeColor,
+ nscolor* aBackColor)
+{
+ NS_ASSERTION(aForeColor, "aForeColor is null");
+ NS_ASSERTION(aBackColor, "aBackColor is null");
+ NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
+
+ nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
+ *aForeColor = selectionStyle->mTextColor;
+ *aBackColor = selectionStyle->mBGColor;
+}
+
+bool
+nsTextPaintStyle::GetSelectionUnderlineForPaint(int32_t aIndex,
+ nscolor* aLineColor,
+ float* aRelativeSize,
+ uint8_t* aStyle)
+{
+ NS_ASSERTION(aLineColor, "aLineColor is null");
+ NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
+ NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
+
+ nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
+ if (selectionStyle->mUnderlineStyle == NS_STYLE_BORDER_STYLE_NONE ||
+ selectionStyle->mUnderlineColor == NS_TRANSPARENT ||
+ selectionStyle->mUnderlineRelativeSize <= 0.0f)
+ return false;
+
+ *aLineColor = selectionStyle->mUnderlineColor;
+ *aRelativeSize = selectionStyle->mUnderlineRelativeSize;
+ *aStyle = selectionStyle->mUnderlineStyle;
+ return true;
+}
+
+void
+nsTextPaintStyle::InitCommonColors()
+{
+ if (mInitCommonColors)
+ return;
+
+ nsIFrame* bgFrame =
+ nsCSSRendering::FindNonTransparentBackgroundFrame(mFrame);
+ NS_ASSERTION(bgFrame, "Cannot find NonTransparentBackgroundFrame.");
+ nscolor bgColor =
+ bgFrame->GetVisitedDependentColor(eCSSProperty_background_color);
+
+ nscolor defaultBgColor = mPresContext->DefaultBackgroundColor();
+ mFrameBackgroundColor = NS_ComposeColors(defaultBgColor, bgColor);
+
+ mSystemFieldForegroundColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID__moz_fieldtext);
+ mSystemFieldBackgroundColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID__moz_field);
+
+ if (bgFrame->IsThemed()) {
+ // Assume a native widget has sufficient contrast always
+ mSufficientContrast = 0;
+ mInitCommonColors = true;
+ return;
+ }
+
+ NS_ASSERTION(NS_GET_A(defaultBgColor) == 255,
+ "default background color is not opaque");
+
+ nscolor defaultWindowBackgroundColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground);
+ nscolor selectionTextColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
+ nscolor selectionBGColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
+
+ mSufficientContrast =
+ std::min(std::min(NS_SUFFICIENT_LUMINOSITY_DIFFERENCE,
+ NS_LUMINOSITY_DIFFERENCE(selectionTextColor,
+ selectionBGColor)),
+ NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
+ selectionBGColor));
+
+ mInitCommonColors = true;
+}
+
+nscolor
+nsTextPaintStyle::GetSystemFieldForegroundColor()
+{
+ InitCommonColors();
+ return mSystemFieldForegroundColor;
+}
+
+nscolor
+nsTextPaintStyle::GetSystemFieldBackgroundColor()
+{
+ InitCommonColors();
+ return mSystemFieldBackgroundColor;
+}
+
+static Element*
+FindElementAncestorForMozSelection(nsIContent* aContent)
+{
+ NS_ENSURE_TRUE(aContent, nullptr);
+ while (aContent && aContent->IsInNativeAnonymousSubtree()) {
+ aContent = aContent->GetBindingParent();
+ }
+ NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
+ while (aContent && !aContent->IsElement()) {
+ aContent = aContent->GetParent();
+ }
+ return aContent ? aContent->AsElement() : nullptr;
+}
+
+bool
+nsTextPaintStyle::InitSelectionColorsAndShadow()
+{
+ if (mInitSelectionColorsAndShadow)
+ return true;
+
+ int16_t selectionFlags;
+ int16_t selectionStatus = mFrame->GetSelectionStatus(&selectionFlags);
+ if (!(selectionFlags & nsISelectionDisplay::DISPLAY_TEXT) ||
+ selectionStatus < nsISelectionController::SELECTION_ON) {
+ // Not displaying the normal selection.
+ // We're not caching this fact, so every call to GetSelectionColors
+ // will come through here. We could avoid this, but it's not really worth it.
+ return false;
+ }
+
+ mInitSelectionColorsAndShadow = true;
+
+ nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(mFrame);
+ Element* selectionElement =
+ FindElementAncestorForMozSelection(nonGeneratedAncestor->GetContent());
+
+ if (selectionElement &&
+ selectionStatus == nsISelectionController::SELECTION_ON) {
+ RefPtr<nsStyleContext> sc = nullptr;
+ sc = mPresContext->StyleSet()->
+ ProbePseudoElementStyle(selectionElement,
+ CSSPseudoElementType::mozSelection,
+ mFrame->StyleContext());
+ // Use -moz-selection pseudo class.
+ if (sc) {
+ mSelectionBGColor =
+ sc->GetVisitedDependentColor(eCSSProperty_background_color);
+ mSelectionTextColor =
+ sc->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color);
+ mHasSelectionShadow =
+ nsRuleNode::HasAuthorSpecifiedRules(sc,
+ NS_AUTHOR_SPECIFIED_TEXT_SHADOW,
+ true);
+ if (mHasSelectionShadow) {
+ mSelectionShadow = sc->StyleText()->mTextShadow;
+ }
+ return true;
+ }
+ }
+
+ nscolor selectionBGColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
+
+ if (selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
+ mSelectionBGColor =
+ LookAndFeel::GetColor(
+ LookAndFeel::eColorID_TextSelectBackgroundAttention);
+ mSelectionBGColor = EnsureDifferentColors(mSelectionBGColor,
+ selectionBGColor);
+ } else if (selectionStatus != nsISelectionController::SELECTION_ON) {
+ mSelectionBGColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackgroundDisabled);
+ mSelectionBGColor = EnsureDifferentColors(mSelectionBGColor,
+ selectionBGColor);
+ } else {
+ mSelectionBGColor = selectionBGColor;
+ }
+
+ mSelectionTextColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
+
+ if (mResolveColors) {
+ // On MacOS X, we don't exchange text color and BG color.
+ if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
+ nsCSSPropertyID property = mFrame->IsSVGText()
+ ? eCSSProperty_fill
+ : eCSSProperty__webkit_text_fill_color;
+ nscoord frameColor = mFrame->GetVisitedDependentColor(property);
+ mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor);
+ } else if (mSelectionTextColor == NS_CHANGE_COLOR_IF_SAME_AS_BG) {
+ nsCSSPropertyID property = mFrame->IsSVGText()
+ ? eCSSProperty_fill
+ : eCSSProperty__webkit_text_fill_color;
+ nscolor frameColor = mFrame->GetVisitedDependentColor(property);
+ if (frameColor == mSelectionBGColor) {
+ mSelectionTextColor =
+ LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForegroundCustom);
+ }
+ } else {
+ EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
+ }
+ } else {
+ if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
+ mSelectionTextColor = NS_SAME_AS_FOREGROUND_COLOR;
+ }
+ }
+ return true;
+}
+
+nsTextPaintStyle::nsSelectionStyle*
+nsTextPaintStyle::GetSelectionStyle(int32_t aIndex)
+{
+ InitSelectionStyle(aIndex);
+ return &mSelectionStyle[aIndex];
+}
+
+struct StyleIDs {
+ LookAndFeel::ColorID mForeground, mBackground, mLine;
+ LookAndFeel::IntID mLineStyle;
+ LookAndFeel::FloatID mLineRelativeSize;
+};
+static StyleIDs SelectionStyleIDs[] = {
+ { LookAndFeel::eColorID_IMERawInputForeground,
+ LookAndFeel::eColorID_IMERawInputBackground,
+ LookAndFeel::eColorID_IMERawInputUnderline,
+ LookAndFeel::eIntID_IMERawInputUnderlineStyle,
+ LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
+ { LookAndFeel::eColorID_IMESelectedRawTextForeground,
+ LookAndFeel::eColorID_IMESelectedRawTextBackground,
+ LookAndFeel::eColorID_IMESelectedRawTextUnderline,
+ LookAndFeel::eIntID_IMESelectedRawTextUnderlineStyle,
+ LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
+ { LookAndFeel::eColorID_IMEConvertedTextForeground,
+ LookAndFeel::eColorID_IMEConvertedTextBackground,
+ LookAndFeel::eColorID_IMEConvertedTextUnderline,
+ LookAndFeel::eIntID_IMEConvertedTextUnderlineStyle,
+ LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
+ { LookAndFeel::eColorID_IMESelectedConvertedTextForeground,
+ LookAndFeel::eColorID_IMESelectedConvertedTextBackground,
+ LookAndFeel::eColorID_IMESelectedConvertedTextUnderline,
+ LookAndFeel::eIntID_IMESelectedConvertedTextUnderline,
+ LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
+ { LookAndFeel::eColorID_LAST_COLOR,
+ LookAndFeel::eColorID_LAST_COLOR,
+ LookAndFeel::eColorID_SpellCheckerUnderline,
+ LookAndFeel::eIntID_SpellCheckerUnderlineStyle,
+ LookAndFeel::eFloatID_SpellCheckerUnderlineRelativeSize }
+};
+
+void
+nsTextPaintStyle::InitSelectionStyle(int32_t aIndex)
+{
+ NS_ASSERTION(aIndex >= 0 && aIndex < 5, "aIndex is invalid");
+ nsSelectionStyle* selectionStyle = &mSelectionStyle[aIndex];
+ if (selectionStyle->mInit)
+ return;
+
+ StyleIDs* styleIDs = &SelectionStyleIDs[aIndex];
+
+ nscolor foreColor, backColor;
+ if (styleIDs->mForeground == LookAndFeel::eColorID_LAST_COLOR) {
+ foreColor = NS_SAME_AS_FOREGROUND_COLOR;
+ } else {
+ foreColor = LookAndFeel::GetColor(styleIDs->mForeground);
+ }
+ if (styleIDs->mBackground == LookAndFeel::eColorID_LAST_COLOR) {
+ backColor = NS_TRANSPARENT;
+ } else {
+ backColor = LookAndFeel::GetColor(styleIDs->mBackground);
+ }
+
+ // Convert special color to actual color
+ NS_ASSERTION(foreColor != NS_TRANSPARENT,
+ "foreColor cannot be NS_TRANSPARENT");
+ NS_ASSERTION(backColor != NS_SAME_AS_FOREGROUND_COLOR,
+ "backColor cannot be NS_SAME_AS_FOREGROUND_COLOR");
+ NS_ASSERTION(backColor != NS_40PERCENT_FOREGROUND_COLOR,
+ "backColor cannot be NS_40PERCENT_FOREGROUND_COLOR");
+
+ if (mResolveColors) {
+ foreColor = GetResolvedForeColor(foreColor, GetTextColor(), backColor);
+
+ if (NS_GET_A(backColor) > 0)
+ EnsureSufficientContrast(&foreColor, &backColor);
+ }
+
+ nscolor lineColor;
+ float relativeSize;
+ uint8_t lineStyle;
+ GetSelectionUnderline(mPresContext, aIndex,
+ &lineColor, &relativeSize, &lineStyle);
+
+ if (mResolveColors)
+ lineColor = GetResolvedForeColor(lineColor, foreColor, backColor);
+
+ selectionStyle->mTextColor = foreColor;
+ selectionStyle->mBGColor = backColor;
+ selectionStyle->mUnderlineColor = lineColor;
+ selectionStyle->mUnderlineStyle = lineStyle;
+ selectionStyle->mUnderlineRelativeSize = relativeSize;
+ selectionStyle->mInit = true;
+}
+
+/* static */ bool
+nsTextPaintStyle::GetSelectionUnderline(nsPresContext* aPresContext,
+ int32_t aIndex,
+ nscolor* aLineColor,
+ float* aRelativeSize,
+ uint8_t* aStyle)
+{
+ NS_ASSERTION(aPresContext, "aPresContext is null");
+ NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
+ NS_ASSERTION(aStyle, "aStyle is null");
+ NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
+
+ StyleIDs& styleID = SelectionStyleIDs[aIndex];
+
+ nscolor color = LookAndFeel::GetColor(styleID.mLine);
+ int32_t style = LookAndFeel::GetInt(styleID.mLineStyle);
+ if (style > NS_STYLE_TEXT_DECORATION_STYLE_MAX) {
+ NS_ERROR("Invalid underline style value is specified");
+ style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
+ }
+ float size = LookAndFeel::GetFloat(styleID.mLineRelativeSize);
+
+ NS_ASSERTION(size, "selection underline relative size must be larger than 0");
+
+ if (aLineColor) {
+ *aLineColor = color;
+ }
+ *aRelativeSize = size;
+ *aStyle = style;
+
+ return style != NS_STYLE_TEXT_DECORATION_STYLE_NONE &&
+ color != NS_TRANSPARENT &&
+ size > 0.0f;
+}
+
+bool
+nsTextPaintStyle::GetSelectionShadow(nsCSSShadowArray** aShadow)
+{
+ if (!InitSelectionColorsAndShadow()) {
+ return false;
+ }
+
+ if (mHasSelectionShadow) {
+ *aShadow = mSelectionShadow;
+ return true;
+ }
+
+ return false;
+}
+
+inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor)
+{
+ nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor),
+ NS_GET_G(aForeColor),
+ NS_GET_B(aForeColor),
+ (uint8_t)(255 * 0.4f));
+ // Don't use true alpha color for readability.
+ return NS_ComposeColors(aBackColor, foreColor);
+}
+
+nscolor
+nsTextPaintStyle::GetResolvedForeColor(nscolor aColor,
+ nscolor aDefaultForeColor,
+ nscolor aBackColor)
+{
+ if (aColor == NS_SAME_AS_FOREGROUND_COLOR)
+ return aDefaultForeColor;
+
+ if (aColor != NS_40PERCENT_FOREGROUND_COLOR)
+ return aColor;
+
+ // Get actual background color
+ nscolor actualBGColor = aBackColor;
+ if (actualBGColor == NS_TRANSPARENT) {
+ InitCommonColors();
+ actualBGColor = mFrameBackgroundColor;
+ }
+ return Get40PercentColor(aDefaultForeColor, actualBGColor);
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsTextFrame::AccessibleType()
+{
+ if (IsEmpty()) {
+ RenderedText text = GetRenderedText(0,
+ UINT32_MAX, TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+ TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
+ if (text.mString.IsEmpty()) {
+ return a11y::eNoType;
+ }
+ }
+
+ return a11y::eTextLeafType;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+void
+nsTextFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ NS_ASSERTION(!aPrevInFlow, "Can't be a continuation!");
+ NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT),
+ "Bogus content!");
+
+ // Remove any NewlineOffsetProperty or InFlowContentLengthProperty since they
+ // might be invalid if the content was modified while there was no frame
+ aContent->DeleteProperty(nsGkAtoms::newline);
+ if (PresContext()->BidiEnabled()) {
+ aContent->DeleteProperty(nsGkAtoms::flowlength);
+ }
+
+ // Since our content has a frame now, this flag is no longer needed.
+ aContent->UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE);
+
+ // We're not a continuing frame.
+ // mContentOffset = 0; not necessary since we get zeroed out at init
+ nsFrame::Init(aContent, aParent, aPrevInFlow);
+}
+
+void
+nsTextFrame::ClearFrameOffsetCache()
+{
+ // See if we need to remove ourselves from the offset cache
+ if (GetStateBits() & TEXT_IN_OFFSET_CACHE) {
+ nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
+ if (primaryFrame) {
+ // The primary frame might be null here. For example, nsLineBox::DeleteLineList
+ // just destroys the frames in order, which means that the primary frame is already
+ // dead if we're a continuing text frame, in which case, all of its properties are
+ // gone, and we don't need to worry about deleting this property here.
+ primaryFrame->Properties().Delete(OffsetToFrameProperty());
+ }
+ RemoveStateBits(TEXT_IN_OFFSET_CACHE);
+ }
+}
+
+void
+nsTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ ClearFrameOffsetCache();
+
+ // We might want to clear NS_CREATE_FRAME_IF_NON_WHITESPACE or
+ // NS_REFRAME_IF_WHITESPACE on mContent here, since our parent frame
+ // type might be changing. Not clear whether it's worth it.
+ ClearTextRuns();
+ if (mNextContinuation) {
+ mNextContinuation->SetPrevInFlow(nullptr);
+ }
+ // Let the base class destroy the frame
+ nsFrame::DestroyFrom(aDestructRoot);
+}
+
+class nsContinuingTextFrame : public nsTextFrame {
+public:
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend nsIFrame* NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ virtual nsIFrame* GetPrevContinuation() const override {
+ return mPrevContinuation;
+ }
+ virtual void SetPrevContinuation(nsIFrame* aPrevContinuation) override {
+ NS_ASSERTION (!aPrevContinuation || GetType() == aPrevContinuation->GetType(),
+ "setting a prev continuation with incorrect type!");
+ NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevContinuation, this),
+ "creating a loop in continuation chain!");
+ mPrevContinuation = aPrevContinuation;
+ RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+ }
+ virtual nsIFrame* GetPrevInFlowVirtual() const override {
+ return GetPrevInFlow();
+ }
+ nsIFrame* GetPrevInFlow() const {
+ return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
+ }
+ virtual void SetPrevInFlow(nsIFrame* aPrevInFlow) override {
+ NS_ASSERTION (!aPrevInFlow || GetType() == aPrevInFlow->GetType(),
+ "setting a prev in flow with incorrect type!");
+ NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevInFlow, this),
+ "creating a loop in continuation chain!");
+ mPrevContinuation = aPrevInFlow;
+ AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+ }
+ virtual nsIFrame* FirstInFlow() const override;
+ virtual nsIFrame* FirstContinuation() const override;
+
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+
+protected:
+ explicit nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {}
+ nsIFrame* mPrevContinuation;
+};
+
+void
+nsContinuingTextFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ NS_ASSERTION(aPrevInFlow, "Must be a continuation!");
+ // NOTE: bypassing nsTextFrame::Init!!!
+ nsFrame::Init(aContent, aParent, aPrevInFlow);
+
+ nsTextFrame* nextContinuation =
+ static_cast<nsTextFrame*>(aPrevInFlow->GetNextContinuation());
+ // Hook the frame into the flow
+ SetPrevInFlow(aPrevInFlow);
+ aPrevInFlow->SetNextInFlow(this);
+ nsTextFrame* prev = static_cast<nsTextFrame*>(aPrevInFlow);
+ mContentOffset = prev->GetContentOffset() + prev->GetContentLengthHint();
+ NS_ASSERTION(mContentOffset < int32_t(aContent->GetText()->GetLength()),
+ "Creating ContinuingTextFrame, but there is no more content");
+ if (prev->StyleContext() != StyleContext()) {
+ // We're taking part of prev's text, and its style may be different
+ // so clear its textrun which may no longer be valid (and don't set ours)
+ prev->ClearTextRuns();
+ } else {
+ float inflation = prev->GetFontSizeInflation();
+ SetFontSizeInflation(inflation);
+ mTextRun = prev->GetTextRun(nsTextFrame::eInflated);
+ if (inflation != 1.0f) {
+ gfxTextRun *uninflatedTextRun =
+ prev->GetTextRun(nsTextFrame::eNotInflated);
+ if (uninflatedTextRun) {
+ SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
+ }
+ }
+ }
+ if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
+ FrameBidiData bidiData = aPrevInFlow->GetBidiData();
+ bidiData.precedingControl = kBidiLevelNone;
+ Properties().Set(BidiDataProperty(), bidiData);
+
+ if (nextContinuation) {
+ SetNextContinuation(nextContinuation);
+ nextContinuation->SetPrevContinuation(this);
+ // Adjust next-continuations' content offset as needed.
+ while (nextContinuation &&
+ nextContinuation->GetContentOffset() < mContentOffset) {
+#ifdef DEBUG
+ FrameBidiData nextBidiData = nextContinuation->GetBidiData();
+ NS_ASSERTION(bidiData.embeddingLevel == nextBidiData.embeddingLevel &&
+ bidiData.baseLevel == nextBidiData.baseLevel,
+ "stealing text from different type of BIDI continuation");
+ MOZ_ASSERT(nextBidiData.precedingControl == kBidiLevelNone,
+ "There shouldn't be any virtual bidi formatting character "
+ "between continuations");
+#endif
+ nextContinuation->mContentOffset = mContentOffset;
+ nextContinuation = static_cast<nsTextFrame*>(nextContinuation->GetNextContinuation());
+ }
+ }
+ mState |= NS_FRAME_IS_BIDI;
+ } // prev frame is bidi
+}
+
+void
+nsContinuingTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ ClearFrameOffsetCache();
+
+ // The text associated with this frame will become associated with our
+ // prev-continuation. If that means the text has changed style, then
+ // we need to wipe out the text run for the text.
+ // Note that mPrevContinuation can be null if we're destroying the whole
+ // frame chain from the start to the end.
+ // If this frame is mentioned in the userData for a textrun (say
+ // because there's a direction change at the start of this frame), then
+ // we have to clear the textrun because we're going away and the
+ // textrun had better not keep a dangling reference to us.
+ if (IsInTextRunUserData() ||
+ (mPrevContinuation &&
+ mPrevContinuation->StyleContext() != StyleContext())) {
+ ClearTextRuns();
+ // Clear the previous continuation's text run also, so that it can rebuild
+ // the text run to include our text.
+ if (mPrevContinuation) {
+ nsTextFrame *prevContinuationText =
+ static_cast<nsTextFrame*>(mPrevContinuation);
+ prevContinuationText->ClearTextRuns();
+ }
+ }
+ nsSplittableFrame::RemoveFromFlow(this);
+ // Let the base class destroy the frame
+ nsFrame::DestroyFrom(aDestructRoot);
+}
+
+nsIFrame*
+nsContinuingTextFrame::FirstInFlow() const
+{
+ // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
+ nsIFrame *firstInFlow,
+ *previous = const_cast<nsIFrame*>
+ (static_cast<const nsIFrame*>(this));
+ do {
+ firstInFlow = previous;
+ previous = firstInFlow->GetPrevInFlow();
+ } while (previous);
+ MOZ_ASSERT(firstInFlow, "post-condition failed");
+ return firstInFlow;
+}
+
+nsIFrame*
+nsContinuingTextFrame::FirstContinuation() const
+{
+ // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
+ nsIFrame *firstContinuation,
+ *previous = const_cast<nsIFrame*>
+ (static_cast<const nsIFrame*>(mPrevContinuation));
+
+ NS_ASSERTION(previous, "How can an nsContinuingTextFrame be the first continuation?");
+
+ do {
+ firstContinuation = previous;
+ previous = firstContinuation->GetPrevContinuation();
+ } while (previous);
+ MOZ_ASSERT(firstContinuation, "post-condition failed");
+ return firstContinuation;
+}
+
+// XXX Do we want to do all the work for the first-in-flow or do the
+// work for each part? (Be careful of first-letter / first-line, though,
+// especially first-line!) Doing all the work on the first-in-flow has
+// the advantage of avoiding the potential for incremental reflow bugs,
+// but depends on our maintining the frame tree in reasonable ways even
+// for edge cases (block-within-inline splits, nextBidi, etc.)
+
+// XXX We really need to make :first-letter happen during frame
+// construction.
+
+// Needed for text frames in XUL.
+/* virtual */ nscoord
+nsTextFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
+}
+
+// Needed for text frames in XUL.
+/* virtual */ nscoord
+nsTextFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
+}
+
+/* virtual */ void
+nsContinuingTextFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData)
+{
+ // Do nothing, since the first-in-flow accounts for everything.
+ return;
+}
+
+/* virtual */ void
+nsContinuingTextFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData)
+{
+ // Do nothing, since the first-in-flow accounts for everything.
+ return;
+}
+
+static void
+DestroySelectionDetails(SelectionDetails* aDetails)
+{
+ while (aDetails) {
+ SelectionDetails* next = aDetails->mNext;
+ delete aDetails;
+ aDetails = next;
+ }
+}
+
+//----------------------------------------------------------------------
+
+#if defined(DEBUG_rbs) || defined(DEBUG_bzbarsky)
+static void
+VerifyNotDirty(nsFrameState state)
+{
+ bool isZero = state & NS_FRAME_FIRST_REFLOW;
+ bool isDirty = state & NS_FRAME_IS_DIRTY;
+ if (!isZero && isDirty)
+ NS_WARNING("internal offsets may be out-of-sync");
+}
+#define DEBUG_VERIFY_NOT_DIRTY(state) \
+VerifyNotDirty(state)
+#else
+#define DEBUG_VERIFY_NOT_DIRTY(state)
+#endif
+
+nsIFrame*
+NS_NewTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsTextFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsTextFrame)
+
+nsIFrame*
+NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsContinuingTextFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsContinuingTextFrame)
+
+nsTextFrame::~nsTextFrame()
+{
+}
+
+nsresult
+nsTextFrame::GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor)
+{
+ FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
+ if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
+ aCursor.mCursor = GetWritingMode().IsVertical()
+ ? NS_STYLE_CURSOR_VERTICAL_TEXT : NS_STYLE_CURSOR_TEXT;
+ // If this is editable, we should ignore tabindex value.
+ if (mContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ // If tabindex >= 0, use default cursor to indicate it's not selectable
+ nsIFrame *ancestorFrame = this;
+ while ((ancestorFrame = ancestorFrame->GetParent()) != nullptr) {
+ nsIContent *ancestorContent = ancestorFrame->GetContent();
+ if (ancestorContent && ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
+ nsAutoString tabIndexStr;
+ ancestorContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
+ if (!tabIndexStr.IsEmpty()) {
+ nsresult rv;
+ int32_t tabIndexVal = tabIndexStr.ToInteger(&rv);
+ if (NS_SUCCEEDED(rv) && tabIndexVal >= 0) {
+ aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
+ break;
+ }
+ }
+ }
+ }
+ return NS_OK;
+ } else {
+ return nsFrame::GetCursor(aPoint, aCursor);
+ }
+}
+
+nsIFrame*
+nsTextFrame::LastInFlow() const
+{
+ nsTextFrame* lastInFlow = const_cast<nsTextFrame*>(this);
+ while (lastInFlow->GetNextInFlow()) {
+ lastInFlow = static_cast<nsTextFrame*>(lastInFlow->GetNextInFlow());
+ }
+ MOZ_ASSERT(lastInFlow, "post-condition failed");
+ return lastInFlow;
+}
+
+nsIFrame*
+nsTextFrame::LastContinuation() const
+{
+ nsTextFrame* lastContinuation = const_cast<nsTextFrame*>(this);
+ while (lastContinuation->mNextContinuation) {
+ lastContinuation =
+ static_cast<nsTextFrame*>(lastContinuation->mNextContinuation);
+ }
+ MOZ_ASSERT(lastContinuation, "post-condition failed");
+ return lastContinuation;
+}
+
+void
+nsTextFrame::InvalidateFrame(uint32_t aDisplayItemKey)
+{
+ if (IsSVGText()) {
+ nsIFrame* svgTextFrame =
+ nsLayoutUtils::GetClosestFrameOfType(GetParent(),
+ nsGkAtoms::svgTextFrame);
+ svgTextFrame->InvalidateFrame();
+ return;
+ }
+ nsFrame::InvalidateFrame(aDisplayItemKey);
+}
+
+void
+nsTextFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
+{
+ if (IsSVGText()) {
+ nsIFrame* svgTextFrame =
+ nsLayoutUtils::GetClosestFrameOfType(GetParent(),
+ nsGkAtoms::svgTextFrame);
+ svgTextFrame->InvalidateFrame();
+ return;
+ }
+ nsFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
+}
+
+gfxTextRun*
+nsTextFrame::GetUninflatedTextRun()
+{
+ return Properties().Get(UninflatedTextRunProperty());
+}
+
+void
+nsTextFrame::SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
+ float aInflation)
+{
+ NS_ASSERTION(aTextRun, "must have text run");
+
+ // Our inflated text run is always stored in mTextRun. In the cases
+ // where our current inflation is not 1.0, however, we store two text
+ // runs, and the uninflated one goes in a frame property. We never
+ // store a single text run in both.
+ if (aWhichTextRun == eInflated) {
+ if (HasFontSizeInflation() && aInflation == 1.0f) {
+ // FIXME: Probably shouldn't do this within each SetTextRun
+ // method, but it doesn't hurt.
+ ClearTextRun(nullptr, nsTextFrame::eNotInflated);
+ }
+ SetFontSizeInflation(aInflation);
+ } else {
+ MOZ_ASSERT(aInflation == 1.0f, "unexpected inflation");
+ if (HasFontSizeInflation()) {
+ // Setting the property will not automatically increment the textrun's
+ // reference count, so we need to do it here.
+ aTextRun->AddRef();
+ Properties().Set(UninflatedTextRunProperty(), aTextRun);
+ return;
+ }
+ // fall through to setting mTextRun
+ }
+
+ mTextRun = aTextRun;
+
+ // FIXME: Add assertions testing the relationship between
+ // GetFontSizeInflation() and whether we have an uninflated text run
+ // (but be aware that text runs can go away).
+}
+
+bool
+nsTextFrame::RemoveTextRun(gfxTextRun* aTextRun)
+{
+ if (aTextRun == mTextRun) {
+ mTextRun = nullptr;
+ return true;
+ }
+ FrameProperties props = Properties();
+ if ((GetStateBits() & TEXT_HAS_FONT_INFLATION) &&
+ props.Get(UninflatedTextRunProperty()) == aTextRun) {
+ props.Delete(UninflatedTextRunProperty());
+ return true;
+ }
+ return false;
+}
+
+void
+nsTextFrame::ClearTextRun(nsTextFrame* aStartContinuation,
+ TextRunType aWhichTextRun)
+{
+ RefPtr<gfxTextRun> textRun = GetTextRun(aWhichTextRun);
+ if (!textRun) {
+ return;
+ }
+
+ DebugOnly<bool> checkmTextrun = textRun == mTextRun;
+ UnhookTextRunFromFrames(textRun, aStartContinuation);
+ MOZ_ASSERT(checkmTextrun ? !mTextRun
+ : !Properties().Get(UninflatedTextRunProperty()));
+}
+
+void
+nsTextFrame::DisconnectTextRuns()
+{
+ MOZ_ASSERT(!IsInTextRunUserData(),
+ "Textrun mentions this frame in its user data so we can't just disconnect");
+ mTextRun = nullptr;
+ if ((GetStateBits() & TEXT_HAS_FONT_INFLATION)) {
+ Properties().Delete(UninflatedTextRunProperty());
+ }
+}
+
+nsresult
+nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
+{
+ mContent->DeleteProperty(nsGkAtoms::newline);
+ if (PresContext()->BidiEnabled()) {
+ mContent->DeleteProperty(nsGkAtoms::flowlength);
+ }
+
+ // Find the first frame whose text has changed. Frames that are entirely
+ // before the text change are completely unaffected.
+ nsTextFrame* next;
+ nsTextFrame* textFrame = this;
+ while (true) {
+ next = static_cast<nsTextFrame*>(textFrame->GetNextContinuation());
+ if (!next || next->GetContentOffset() > int32_t(aInfo->mChangeStart))
+ break;
+ textFrame = next;
+ }
+
+ int32_t endOfChangedText = aInfo->mChangeStart + aInfo->mReplaceLength;
+ nsTextFrame* lastDirtiedFrame = nullptr;
+
+ nsIPresShell* shell = PresContext()->GetPresShell();
+ do {
+ // textFrame contained deleted text (or the insertion point,
+ // if this was a pure insertion).
+ textFrame->mState &= ~TEXT_WHITESPACE_FLAGS;
+ textFrame->ClearTextRuns();
+ if (!lastDirtiedFrame ||
+ lastDirtiedFrame->GetParent() != textFrame->GetParent()) {
+ // Ask the parent frame to reflow me.
+ shell->FrameNeedsReflow(textFrame, nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+ lastDirtiedFrame = textFrame;
+ } else {
+ // if the parent is a block, we're cheating here because we should
+ // be marking our line dirty, but we're not. nsTextFrame::SetLength
+ // will do that when it gets called during reflow.
+ textFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
+ textFrame->InvalidateFrame();
+
+ // Below, frames that start after the deleted text will be adjusted so that
+ // their offsets move with the trailing unchanged text. If this change
+ // deletes more text than it inserts, those frame offsets will decrease.
+ // We need to maintain the invariant that mContentOffset is non-decreasing
+ // along the continuation chain. So we need to ensure that frames that
+ // started in the deleted text are all still starting before the
+ // unchanged text.
+ if (textFrame->mContentOffset > endOfChangedText) {
+ textFrame->mContentOffset = endOfChangedText;
+ }
+
+ textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation());
+ } while (textFrame && textFrame->GetContentOffset() < int32_t(aInfo->mChangeEnd));
+
+ // This is how much the length of the string changed by --- i.e.,
+ // how much the trailing unchanged text moved.
+ int32_t sizeChange =
+ aInfo->mChangeStart + aInfo->mReplaceLength - aInfo->mChangeEnd;
+
+ if (sizeChange) {
+ // Fix the offsets of the text frames that start in the trailing
+ // unchanged text.
+ while (textFrame) {
+ textFrame->mContentOffset += sizeChange;
+ // XXX we could rescue some text runs by adjusting their user data
+ // to reflect the change in DOM offsets
+ textFrame->ClearTextRuns();
+ textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation());
+ }
+ }
+
+ return NS_OK;
+}
+
+class nsDisplayText : public nsCharClipDisplayItem {
+public:
+ nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
+ Maybe<bool> aIsSelected) :
+ nsCharClipDisplayItem(aBuilder, aFrame),
+ mOpacity(1.0f),
+ mDisableSubpixelAA(false) {
+ mIsFrameSelected = aIsSelected;
+ MOZ_COUNT_CTOR(nsDisplayText);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayText() {
+ MOZ_COUNT_DTOR(nsDisplayText);
+ }
+#endif
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+ bool* aSnap) override {
+ *aSnap = false;
+ nsRect temp = mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
+ // Bug 748228
+ temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
+ return temp;
+ }
+ virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
+ HitTestState* aState,
+ nsTArray<nsIFrame*> *aOutFrames) override {
+ if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
+ aOutFrames->AppendElement(mFrame);
+ }
+ }
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)
+
+ virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
+ {
+ if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
+ // On OS X, web authors can turn off subpixel text rendering using the
+ // CSS property -moz-osx-font-smoothing. If they do that, we don't need
+ // to use component alpha layers for the affected text.
+ if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
+ return nsRect();
+ }
+ }
+ bool snap;
+ return GetBounds(aBuilder, &snap);
+ }
+
+ virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
+
+ virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+ const nsDisplayItemGeometry* aGeometry,
+ nsRegion *aInvalidRegion) override;
+
+ virtual void DisableComponentAlpha() override {
+ mDisableSubpixelAA = true;
+ }
+
+ bool CanApplyOpacity() const override
+ {
+ nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
+ if (f->IsSelected()) {
+ return false;
+ }
+
+ const nsStyleText* textStyle = f->StyleText();
+ if (textStyle->mTextShadow) {
+ return false;
+ }
+
+ nsTextFrame::TextDecorations decorations;
+ f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, decorations);
+ if (decorations.HasDecorationLines()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ void ApplyOpacity(nsDisplayListBuilder* aBuilder,
+ float aOpacity,
+ const DisplayItemClip* aClip) override
+ {
+ NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
+ mOpacity = aOpacity;
+ if (aClip) {
+ IntersectClip(aBuilder, *aClip);
+ }
+ }
+
+ float mOpacity;
+ bool mDisableSubpixelAA;
+};
+
+class nsDisplayTextGeometry : public nsCharClipGeometry
+{
+public:
+ nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
+ : nsCharClipGeometry(aItem, aBuilder)
+ , mOpacity(aItem->mOpacity)
+ {
+ nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
+ f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, mDecorations);
+ }
+
+ /**
+ * We store the computed text decorations here since they are
+ * computed using style data from parent frames. Any changes to these
+ * styles will only invalidate the parent frame and not this frame.
+ */
+ nsTextFrame::TextDecorations mDecorations;
+ float mOpacity;
+};
+
+nsDisplayItemGeometry*
+nsDisplayText::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+ return new nsDisplayTextGeometry(this, aBuilder);
+}
+
+void
+nsDisplayText::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+ const nsDisplayItemGeometry* aGeometry,
+ nsRegion *aInvalidRegion)
+{
+ const nsDisplayTextGeometry* geometry = static_cast<const nsDisplayTextGeometry*>(aGeometry);
+ nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
+
+ nsTextFrame::TextDecorations decorations;
+ f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, decorations);
+
+ bool snap;
+ nsRect newRect = geometry->mBounds;
+ nsRect oldRect = GetBounds(aBuilder, &snap);
+ if (decorations != geometry->mDecorations ||
+ mVisIStartEdge != geometry->mVisIStartEdge ||
+ mVisIEndEdge != geometry->mVisIEndEdge ||
+ !oldRect.IsEqualInterior(newRect) ||
+ !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
+ mOpacity != geometry->mOpacity) {
+ aInvalidRegion->Or(oldRect, newRect);
+ }
+}
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TextCombineScaleFactorProperty, float)
+
+static float
+GetTextCombineScaleFactor(nsTextFrame* aFrame)
+{
+ float factor = aFrame->Properties().Get(TextCombineScaleFactorProperty());
+ return factor ? factor : 1.0f;
+}
+
+void
+nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) {
+ PROFILER_LABEL("nsDisplayText", "Paint",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
+ // antialiased pixels beyond the measured text extents.
+ // This is temporary until we do this in the actual calculation of text extents.
+ auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
+ LayoutDeviceRect extraVisible =
+ LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D);
+ extraVisible.Inflate(1);
+ nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
+
+ DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+ mDisableSubpixelAA);
+ gfxContext* ctx = aCtx->ThebesContext();
+ gfxContextAutoSaveRestore save(ctx);
+
+ gfxRect pixelVisible(extraVisible.x, extraVisible.y,
+ extraVisible.width, extraVisible.height);
+ pixelVisible.Inflate(2);
+ pixelVisible.RoundOut();
+
+ if (!aBuilder->IsForGenerateGlyphMask() &&
+ !aBuilder->IsForPaintingSelectionBG()) {
+ ctx->NewPath();
+ ctx->Rectangle(pixelVisible);
+ ctx->Clip();
+ }
+
+ NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
+ NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
+
+ nsPoint framePt = ToReferenceFrame();
+ if (f->StyleContext()->IsTextCombined()) {
+ float scaleFactor = GetTextCombineScaleFactor(f);
+ if (scaleFactor != 1.0f) {
+ // Setup matrix to compress text for text-combine-upright if
+ // necessary. This is done here because we want selection be
+ // compressed at the same time as text.
+ gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
+ gfxMatrix mat = ctx->CurrentMatrix()
+ .Translate(pt).Scale(scaleFactor, 1.0).Translate(-pt);
+ ctx->SetMatrix(mat);
+ }
+ }
+ nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
+ params.framePt = gfxPoint(framePt.x, framePt.y);
+ params.dirtyRect = extraVisible;
+
+ if (aBuilder->IsForGenerateGlyphMask()) {
+ MOZ_ASSERT(!aBuilder->IsForPaintingSelectionBG());
+ params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
+ } else if (aBuilder->IsForPaintingSelectionBG()) {
+ params.state = nsTextFrame::PaintTextParams::PaintTextBGColor;
+ } else {
+ params.state = nsTextFrame::PaintTextParams::PaintText;
+ }
+
+ f->PaintText(params, *this, mOpacity);
+}
+
+void
+nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame");
+
+ const nsStyleColor* sc = StyleColor();
+ const nsStyleText* st = StyleText();
+ bool isTextTransparent =
+ NS_GET_A(sc->CalcComplexColor(st->mWebkitTextFillColor)) == 0 &&
+ NS_GET_A(sc->CalcComplexColor(st->mWebkitTextStrokeColor)) == 0;
+ Maybe<bool> isSelected;
+ if (((GetStateBits() & TEXT_NO_RENDERED_GLYPHS) ||
+ (isTextTransparent && !StyleText()->HasTextShadow())) &&
+ aBuilder->IsForPainting() && !IsSVGText()) {
+ isSelected.emplace(IsSelected());
+ if (!isSelected.value()) {
+ TextDecorations textDecs;
+ GetTextDecorations(PresContext(), eResolvedColors, textDecs);
+ if (!textDecs.HasDecorationLines()) {
+ return;
+ }
+ }
+ }
+
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayText(aBuilder, this, isSelected));
+}
+
+static nsIFrame*
+GetGeneratedContentOwner(nsIFrame* aFrame, bool* aIsBefore)
+{
+ *aIsBefore = false;
+ while (aFrame && (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
+ if (aFrame->StyleContext()->GetPseudo() == nsCSSPseudoElements::before) {
+ *aIsBefore = true;
+ }
+ aFrame = aFrame->GetParent();
+ }
+ return aFrame;
+}
+
+SelectionDetails*
+nsTextFrame::GetSelectionDetails()
+{
+ const nsFrameSelection* frameSelection = GetConstFrameSelection();
+ if (frameSelection->GetTableCellSelection()) {
+ return nullptr;
+ }
+ if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
+ SelectionDetails* details =
+ frameSelection->LookUpSelection(mContent, GetContentOffset(),
+ GetContentLength(), false);
+ SelectionDetails* sd;
+ for (sd = details; sd; sd = sd->mNext) {
+ sd->mStart += mContentOffset;
+ sd->mEnd += mContentOffset;
+ }
+ return details;
+ }
+
+ // Check if the beginning or end of the element is selected, depending on
+ // whether we're :before content or :after content.
+ bool isBefore;
+ nsIFrame* owner = GetGeneratedContentOwner(this, &isBefore);
+ if (!owner || !owner->GetContent())
+ return nullptr;
+
+ SelectionDetails* details =
+ frameSelection->LookUpSelection(owner->GetContent(),
+ isBefore ? 0 : owner->GetContent()->GetChildCount(), 0, false);
+ SelectionDetails* sd;
+ for (sd = details; sd; sd = sd->mNext) {
+ // The entire text is selected!
+ sd->mStart = GetContentOffset();
+ sd->mEnd = GetContentEnd();
+ }
+ return details;
+}
+
+static void
+PaintSelectionBackground(DrawTarget& aDrawTarget,
+ nscolor aColor,
+ const LayoutDeviceRect& aDirtyRect,
+ const LayoutDeviceRect& aRect,
+ nsTextFrame::DrawPathCallbacks* aCallbacks)
+{
+ Rect rect = aRect.Intersect(aDirtyRect).ToUnknownRect();
+ MaybeSnapToDevicePixels(rect, aDrawTarget);
+
+ if (aCallbacks) {
+ aCallbacks->NotifySelectionBackgroundNeedsFill(rect, aColor, aDrawTarget);
+ } else {
+ ColorPattern color(ToDeviceColor(aColor));
+ aDrawTarget.FillRect(rect, color);
+ }
+}
+
+// Attempt to get the LineBaselineOffset property of aChildFrame
+// If not set, calculate this value for all child frames of aBlockFrame
+static nscoord
+LazyGetLineBaselineOffset(nsIFrame* aChildFrame, nsBlockFrame* aBlockFrame)
+{
+ bool offsetFound;
+ nscoord offset = aChildFrame->Properties().Get(
+ nsIFrame::LineBaselineOffset(), &offsetFound);
+
+ if (!offsetFound) {
+ for (nsBlockFrame::LineIterator line = aBlockFrame->LinesBegin(),
+ line_end = aBlockFrame->LinesEnd();
+ line != line_end; line++) {
+ if (line->IsInline()) {
+ int32_t n = line->GetChildCount();
+ nscoord lineBaseline = line->BStart() + line->GetLogicalAscent();
+ for (nsIFrame* lineFrame = line->mFirstChild;
+ n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
+ offset = lineBaseline - lineFrame->GetNormalPosition().y;
+ lineFrame->Properties().Set(nsIFrame::LineBaselineOffset(), offset);
+ }
+ }
+ }
+ return aChildFrame->Properties().Get(
+ nsIFrame::LineBaselineOffset(), &offsetFound);
+ } else {
+ return offset;
+ }
+}
+
+static bool IsUnderlineRight(nsIFrame* aFrame)
+{
+ nsIAtom* langAtom = aFrame->StyleFont()->mLanguage;
+ if (!langAtom) {
+ return false;
+ }
+ nsAtomString langStr(langAtom);
+ return (StringBeginsWith(langStr, NS_LITERAL_STRING("ja")) ||
+ StringBeginsWith(langStr, NS_LITERAL_STRING("ko"))) &&
+ (langStr.Length() == 2 || langStr[2] == '-');
+}
+
+void
+nsTextFrame::GetTextDecorations(
+ nsPresContext* aPresContext,
+ nsTextFrame::TextDecorationColorResolution aColorResolution,
+ nsTextFrame::TextDecorations& aDecorations)
+{
+ const nsCompatibility compatMode = aPresContext->CompatibilityMode();
+
+ bool useOverride = false;
+ nscolor overrideColor = NS_RGBA(0, 0, 0, 0);
+
+ bool nearestBlockFound = false;
+ // Use writing mode of parent frame for orthogonal text frame to work.
+ // See comment in nsTextFrame::DrawTextRunAndDecorations.
+ WritingMode wm = GetParent()->GetWritingMode();
+ bool vertical = wm.IsVertical();
+
+ nscoord ascent = GetLogicalBaseline(wm);
+ // physicalBlockStartOffset represents the offset from our baseline
+ // to f's physical block start, which is top in horizontal writing
+ // mode, and left in vertical writing modes, in our coordinate space.
+ // This physical block start is logical block start in most cases,
+ // but for vertical-rl, it is logical block end, and consequently in
+ // that case, it starts from the descent instead of ascent.
+ nscoord physicalBlockStartOffset =
+ wm.IsVerticalRL() ? GetSize().width - ascent : ascent;
+ // baselineOffset represents the offset from our baseline to f's baseline or
+ // the nearest block's baseline, in our coordinate space, whichever is closest
+ // during the particular iteration
+ nscoord baselineOffset = 0;
+
+ for (nsIFrame* f = this, *fChild = nullptr;
+ f;
+ fChild = f,
+ f = nsLayoutUtils::GetParentOrPlaceholderFor(f))
+ {
+ nsStyleContext *const context = f->StyleContext();
+ if (!context->HasTextDecorationLines()) {
+ break;
+ }
+
+ const nsStyleTextReset *const styleText = context->StyleTextReset();
+ const uint8_t textDecorations = styleText->mTextDecorationLine;
+
+ if (!useOverride &&
+ (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations)) {
+ // This handles the <a href="blah.html"><font color="green">La
+ // la la</font></a> case. The link underline should be green.
+ useOverride = true;
+ overrideColor =
+ nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
+ }
+
+ nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
+ const bool firstBlock = !nearestBlockFound && fBlock;
+
+ // Not updating positions once we hit a parent block is equivalent to
+ // the CSS 2.1 spec that blocks should propagate decorations down to their
+ // children (albeit the style should be preserved)
+ // However, if we're vertically aligned within a block, then we need to
+ // recover the correct baseline from the line by querying the FrameProperty
+ // that should be set (see nsLineLayout::VerticalAlignLine).
+ if (firstBlock) {
+ // At this point, fChild can't be null since TextFrames can't be blocks
+ if (fChild->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
+
+ // Since offset is the offset in the child's coordinate space, we have
+ // to undo the accumulation to bring the transform out of the block's
+ // coordinate space
+ const nscoord lineBaselineOffset = LazyGetLineBaselineOffset(fChild,
+ fBlock);
+
+ baselineOffset = physicalBlockStartOffset - lineBaselineOffset -
+ (vertical ? fChild->GetNormalPosition().x
+ : fChild->GetNormalPosition().y);
+ }
+ }
+ else if (!nearestBlockFound) {
+ // offset here is the offset from f's baseline to f's top/left
+ // boundary. It's descent for vertical-rl, and ascent otherwise.
+ nscoord offset = wm.IsVerticalRL() ?
+ f->GetSize().width - f->GetLogicalBaseline(wm) :
+ f->GetLogicalBaseline(wm);
+ baselineOffset = physicalBlockStartOffset - offset;
+ }
+
+ nearestBlockFound = nearestBlockFound || firstBlock;
+ physicalBlockStartOffset +=
+ vertical ? f->GetNormalPosition().x : f->GetNormalPosition().y;
+
+ const uint8_t style = styleText->mTextDecorationStyle;
+ if (textDecorations) {
+ nscolor color;
+ if (useOverride) {
+ color = overrideColor;
+ } else if (IsSVGText()) {
+ // XXX We might want to do something with text-decoration-color when
+ // painting SVG text, but it's not clear what we should do. We
+ // at least need SVG text decorations to paint with 'fill' if
+ // text-decoration-color has its initial value currentColor.
+ // We could choose to interpret currentColor as "currentFill"
+ // for SVG text, and have e.g. text-decoration-color:red to
+ // override the fill paint of the decoration.
+ color = aColorResolution == eResolvedColors ?
+ nsLayoutUtils::GetColor(f, eCSSProperty_fill) :
+ NS_SAME_AS_FOREGROUND_COLOR;
+ } else {
+ color = nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
+ }
+
+ bool swapUnderlineAndOverline = vertical && IsUnderlineRight(f);
+ const uint8_t kUnderline =
+ swapUnderlineAndOverline ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE :
+ NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+ const uint8_t kOverline =
+ swapUnderlineAndOverline ? NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE :
+ NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+
+ if (textDecorations & kUnderline) {
+ aDecorations.mUnderlines.AppendElement(
+ nsTextFrame::LineDecoration(f, baselineOffset, color, style));
+ }
+ if (textDecorations & kOverline) {
+ aDecorations.mOverlines.AppendElement(
+ nsTextFrame::LineDecoration(f, baselineOffset, color, style));
+ }
+ if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
+ aDecorations.mStrikes.AppendElement(
+ nsTextFrame::LineDecoration(f, baselineOffset, color, style));
+ }
+ }
+
+ // In all modes, if we're on an inline-block or inline-table (or
+ // inline-stack, inline-box, inline-grid), we're done.
+ // If we're on a ruby frame other than ruby text container, we
+ // should continue.
+ mozilla::StyleDisplay display = f->GetDisplay();
+ if (display != mozilla::StyleDisplay::Inline &&
+ (!nsStyleDisplay::IsRubyDisplayType(display) ||
+ display == mozilla::StyleDisplay::RubyTextContainer) &&
+ nsStyleDisplay::IsDisplayTypeInlineOutside(display)) {
+ break;
+ }
+
+ // In quirks mode, if we're on an HTML table element, we're done.
+ if (compatMode == eCompatibility_NavQuirks &&
+ f->GetContent()->IsHTMLElement(nsGkAtoms::table)) {
+ break;
+ }
+
+ // If we're on an absolutely-positioned element or a floating
+ // element, we're done.
+ if (f->IsFloating() || f->IsAbsolutelyPositioned()) {
+ break;
+ }
+
+ // If we're an outer <svg> element, which is classified as an atomic
+ // inline-level element, we're done.
+ if (f->GetType() == nsGkAtoms::svgOuterSVGFrame) {
+ break;
+ }
+ }
+}
+
+static float
+GetInflationForTextDecorations(nsIFrame* aFrame, nscoord aInflationMinFontSize)
+{
+ if (aFrame->IsSVGText()) {
+ const nsIFrame* container = aFrame;
+ while (container->GetType() != nsGkAtoms::svgTextFrame) {
+ container = container->GetParent();
+ }
+ NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
+ return
+ static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
+ }
+ return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize);
+}
+
+struct EmphasisMarkInfo
+{
+ RefPtr<gfxTextRun> textRun;
+ gfxFloat advance;
+ gfxFloat baselineOffset;
+};
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(EmphasisMarkProperty, EmphasisMarkInfo)
+
+already_AddRefed<gfxTextRun>
+GenerateTextRunForEmphasisMarks(nsTextFrame* aFrame,
+ nsFontMetrics* aFontMetrics,
+ nsStyleContext* aStyleContext,
+ const nsStyleText* aStyleText)
+{
+ const nsString& emphasisString = aStyleText->mTextEmphasisStyleString;
+ RefPtr<DrawTarget> dt = CreateReferenceDrawTarget(aFrame);
+ auto appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
+ uint32_t flags = nsLayoutUtils::GetTextRunOrientFlagsForStyle(aStyleContext);
+ if (flags == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
+ // The emphasis marks should always be rendered upright per spec.
+ flags = gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+ }
+ return aFontMetrics->GetThebesFontGroup()->
+ MakeTextRun<char16_t>(emphasisString.get(), emphasisString.Length(),
+ dt, appUnitsPerDevUnit, flags, nullptr);
+}
+
+static nsRubyFrame*
+FindFurthestInlineRubyAncestor(nsTextFrame* aFrame)
+{
+ nsRubyFrame* rubyFrame = nullptr;
+ for (nsIFrame* frame = aFrame->GetParent();
+ frame && frame->IsFrameOfType(nsIFrame::eLineParticipant);
+ frame = frame->GetParent()) {
+ if (frame->GetType() == nsGkAtoms::rubyFrame) {
+ rubyFrame = static_cast<nsRubyFrame*>(frame);
+ }
+ }
+ return rubyFrame;
+}
+
+nsRect
+nsTextFrame::UpdateTextEmphasis(WritingMode aWM, PropertyProvider& aProvider)
+{
+ const nsStyleText* styleText = StyleText();
+ if (!styleText->HasTextEmphasis()) {
+ Properties().Delete(EmphasisMarkProperty());
+ return nsRect();
+ }
+
+ nsStyleContext* styleContext = StyleContext();
+ bool isTextCombined = styleContext->IsTextCombined();
+ if (isTextCombined) {
+ styleContext = styleContext->GetParent();
+ }
+ RefPtr<nsFontMetrics> fm = nsLayoutUtils::
+ GetFontMetricsOfEmphasisMarks(styleContext, GetFontSizeInflation());
+ EmphasisMarkInfo* info = new EmphasisMarkInfo;
+ info->textRun =
+ GenerateTextRunForEmphasisMarks(this, fm, styleContext, styleText);
+ info->advance = info->textRun->GetAdvanceWidth();
+
+ // Calculate the baseline offset
+ LogicalSide side = styleText->TextEmphasisSide(aWM);
+ LogicalSize frameSize = GetLogicalSize(aWM);
+ // The overflow rect is inflated in the inline direction by half
+ // advance of the emphasis mark on each side, so that even if a mark
+ // is drawn for a zero-width character, it won't be clipped.
+ LogicalRect overflowRect(aWM, -info->advance / 2,
+ /* BStart to be computed below */ 0,
+ frameSize.ISize(aWM) + info->advance,
+ fm->MaxAscent() + fm->MaxDescent());
+ RefPtr<nsFontMetrics> baseFontMetrics = isTextCombined
+ ? nsLayoutUtils::GetInflatedFontMetricsForFrame(GetParent())
+ : do_AddRef(aProvider.GetFontMetrics());
+ // When the writing mode is vertical-lr the line is inverted, and thus
+ // the ascent and descent are swapped.
+ nscoord absOffset = (side == eLogicalSideBStart) != aWM.IsLineInverted() ?
+ baseFontMetrics->MaxAscent() + fm->MaxDescent() :
+ baseFontMetrics->MaxDescent() + fm->MaxAscent();
+ RubyBlockLeadings leadings;
+ if (nsRubyFrame* ruby = FindFurthestInlineRubyAncestor(this)) {
+ leadings = ruby->GetBlockLeadings();
+ }
+ if (side == eLogicalSideBStart) {
+ info->baselineOffset = -absOffset - leadings.mStart;
+ overflowRect.BStart(aWM) = -overflowRect.BSize(aWM) - leadings.mStart;
+ } else {
+ MOZ_ASSERT(side == eLogicalSideBEnd);
+ info->baselineOffset = absOffset + leadings.mEnd;
+ overflowRect.BStart(aWM) = frameSize.BSize(aWM) + leadings.mEnd;
+ }
+ // If text combined, fix the gap between the text frame and its parent.
+ if (isTextCombined) {
+ nscoord gap = (baseFontMetrics->MaxHeight() - frameSize.BSize(aWM)) / 2;
+ overflowRect.BStart(aWM) += gap * (side == eLogicalSideBStart ? -1 : 1);
+ }
+
+ Properties().Set(EmphasisMarkProperty(), info);
+ return overflowRect.GetPhysicalRect(aWM, frameSize.GetPhysicalSize(aWM));
+}
+
+void
+nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
+ nsIFrame* aBlock,
+ PropertyProvider& aProvider,
+ nsRect* aVisualOverflowRect,
+ bool aIncludeTextDecorations)
+{
+ const WritingMode wm = GetWritingMode();
+ bool verticalRun = mTextRun->IsVertical();
+ const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
+
+ if (IsFloatingFirstLetterChild()) {
+ bool inverted = wm.IsLineInverted();
+ // The underline/overline drawable area must be contained in the overflow
+ // rect when this is in floating first letter frame at *both* modes.
+ // In this case, aBlock is the ::first-letter frame.
+ uint8_t decorationStyle = aBlock->StyleContext()->
+ StyleTextReset()->mTextDecorationStyle;
+ // If the style is none, let's include decoration line rect as solid style
+ // since changing the style from none to solid/dotted/dashed doesn't cause
+ // reflow.
+ if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
+ decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
+ }
+ nsFontMetrics* fontMetrics = aProvider.GetFontMetrics();
+ nscoord underlineOffset, underlineSize;
+ fontMetrics->GetUnderline(underlineOffset, underlineSize);
+ nscoord maxAscent = inverted ? fontMetrics->MaxDescent()
+ : fontMetrics->MaxAscent();
+
+ nsCSSRendering::DecorationRectParams params;
+ Float gfxWidth =
+ (verticalRun ? aVisualOverflowRect->height
+ : aVisualOverflowRect->width) /
+ appUnitsPerDevUnit;
+ params.lineSize = Size(gfxWidth, underlineSize / appUnitsPerDevUnit);
+ params.ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
+ params.style = decorationStyle;
+ params.vertical = verticalRun;
+
+ params.offset = underlineOffset / appUnitsPerDevUnit;
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+ nsRect underlineRect =
+ nsCSSRendering::GetTextDecorationRect(aPresContext, params);
+ params.offset = maxAscent / appUnitsPerDevUnit;
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+ nsRect overlineRect =
+ nsCSSRendering::GetTextDecorationRect(aPresContext, params);
+
+ aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
+ aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
+
+ // XXX If strikeoutSize is much thicker than the underlineSize, it may
+ // cause overflowing from the overflow rect. However, such case
+ // isn't realistic, we don't need to compute it now.
+ }
+ if (aIncludeTextDecorations) {
+ // Use writing mode of parent frame for orthogonal text frame to
+ // work. See comment in nsTextFrame::DrawTextRunAndDecorations.
+ WritingMode parentWM = GetParent()->GetWritingMode();
+ bool verticalDec = parentWM.IsVertical();
+ bool useVerticalMetrics = verticalDec != verticalRun
+ ? verticalDec : verticalRun && mTextRun->UseCenterBaseline();
+
+ // Since CSS 2.1 requires that text-decoration defined on ancestors maintain
+ // style and position, they can be drawn at virtually any y-offset, so
+ // maxima and minima are required to reliably generate the rectangle for
+ // them
+ TextDecorations textDecs;
+ GetTextDecorations(aPresContext, eResolvedColors, textDecs);
+ if (textDecs.HasDecorationLines()) {
+ nscoord inflationMinFontSize =
+ nsLayoutUtils::InflationMinFontSizeFor(aBlock);
+
+ const nscoord measure = verticalDec ? GetSize().height : GetSize().width;
+ gfxFloat gfxWidth = measure / appUnitsPerDevUnit;
+ gfxFloat ascent = gfxFloat(GetLogicalBaseline(parentWM))
+ / appUnitsPerDevUnit;
+ nscoord frameBStart = 0;
+ if (parentWM.IsVerticalRL()) {
+ frameBStart = GetSize().width;
+ ascent = -ascent;
+ }
+ // The decoration-line offsets need to be reversed for sideways-lr mode,
+ // so we will multiply the values from metrics by this factor.
+ gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
+
+ nsCSSRendering::DecorationRectParams params;
+ params.lineSize = Size(gfxWidth, 0);
+ params.ascent = ascent;
+ params.vertical = verticalDec;
+
+ nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN);
+ typedef gfxFont::Metrics Metrics;
+ auto accumulateDecorationRect = [&](const LineDecoration& dec,
+ gfxFloat Metrics::* lineSize,
+ gfxFloat Metrics::* lineOffset) {
+ params.style = dec.mStyle;
+ // If the style is solid, let's include decoration line rect of solid
+ // style since changing the style from none to solid/dotted/dashed
+ // doesn't cause reflow.
+ if (params.style == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
+ params.style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
+ }
+
+ float inflation =
+ GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
+ const Metrics metrics =
+ GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+ useVerticalMetrics);
+
+ params.lineSize.height = metrics.*lineSize;
+ params.offset = decorationOffsetDir * metrics.*lineOffset;
+ const nsRect decorationRect =
+ nsCSSRendering::GetTextDecorationRect(aPresContext, params) +
+ (verticalDec ? nsPoint(frameBStart - dec.mBaselineOffset, 0)
+ : nsPoint(0, -dec.mBaselineOffset));
+
+ if (verticalDec) {
+ topOrLeft = std::min(decorationRect.x, topOrLeft);
+ bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight);
+ } else {
+ topOrLeft = std::min(decorationRect.y, topOrLeft);
+ bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
+ }
+ };
+
+ // Below we loop through all text decorations and compute the rectangle
+ // containing all of them, in this frame's coordinate space
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+ for (const LineDecoration& dec : textDecs.mUnderlines) {
+ accumulateDecorationRect(dec, &Metrics::underlineSize,
+ &Metrics::underlineOffset);
+ }
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+ for (const LineDecoration& dec : textDecs.mOverlines) {
+ accumulateDecorationRect(dec, &Metrics::underlineSize,
+ &Metrics::maxAscent);
+ }
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+ for (const LineDecoration& dec : textDecs.mStrikes) {
+ accumulateDecorationRect(dec, &Metrics::strikeoutSize,
+ &Metrics::strikeoutOffset);
+ }
+
+ aVisualOverflowRect->UnionRect(
+ *aVisualOverflowRect,
+ verticalDec ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
+ : nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft));
+ }
+
+ aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
+ UpdateTextEmphasis(parentWM, aProvider));
+ }
+
+ // text-stroke overflows
+ nscoord textStrokeWidth = StyleText()->mWebkitTextStrokeWidth.GetCoordValue();
+ if (textStrokeWidth > 0) {
+ nsRect strokeRect = *aVisualOverflowRect;
+ strokeRect.x -= textStrokeWidth;
+ strokeRect.y -= textStrokeWidth;
+ strokeRect.width += textStrokeWidth;
+ strokeRect.height += textStrokeWidth;
+ aVisualOverflowRect->UnionRect(*aVisualOverflowRect, strokeRect);
+ }
+
+ // Text-shadow overflows
+ nsRect shadowRect =
+ nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
+ aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
+
+ // When this frame is not selected, the text-decoration area must be in
+ // frame bounds.
+ if (!IsSelected() ||
+ !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
+ return;
+ AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
+}
+
+gfxFloat
+nsTextFrame::ComputeDescentLimitForSelectionUnderline(
+ nsPresContext* aPresContext,
+ const gfxFont::Metrics& aFontMetrics)
+{
+ gfxFloat app = aPresContext->AppUnitsPerDevPixel();
+ nscoord lineHeightApp =
+ ReflowInput::CalcLineHeight(GetContent(),
+ StyleContext(), NS_AUTOHEIGHT,
+ GetFontSizeInflation());
+ gfxFloat lineHeight = gfxFloat(lineHeightApp) / app;
+ if (lineHeight <= aFontMetrics.maxHeight) {
+ return aFontMetrics.maxDescent;
+ }
+ return aFontMetrics.maxDescent + (lineHeight - aFontMetrics.maxHeight) / 2;
+}
+
+
+// Make sure this stays in sync with DrawSelectionDecorations below
+static const RawSelectionType kRawSelectionTypesWithDecorations =
+ nsISelectionController::SELECTION_SPELLCHECK |
+ nsISelectionController::SELECTION_URLSTRIKEOUT |
+ nsISelectionController::SELECTION_IME_RAWINPUT |
+ nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT |
+ nsISelectionController::SELECTION_IME_CONVERTEDTEXT |
+ nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
+
+/* static */
+gfxFloat
+nsTextFrame::ComputeSelectionUnderlineHeight(
+ nsPresContext* aPresContext,
+ const gfxFont::Metrics& aFontMetrics,
+ SelectionType aSelectionType)
+{
+ switch (aSelectionType) {
+ case SelectionType::eIMERawClause:
+ case SelectionType::eIMESelectedRawClause:
+ case SelectionType::eIMEConvertedClause:
+ case SelectionType::eIMESelectedClause:
+ return aFontMetrics.underlineSize;
+ case SelectionType::eSpellCheck: {
+ // The thickness of the spellchecker underline shouldn't honor the font
+ // metrics. It should be constant pixels value which is decided from the
+ // default font size. Note that if the actual font size is smaller than
+ // the default font size, we should use the actual font size because the
+ // computed value from the default font size can be too thick for the
+ // current font size.
+ int32_t defaultFontSize =
+ aPresContext->AppUnitsToDevPixels(nsStyleFont(aPresContext).mFont.size);
+ gfxFloat fontSize = std::min(gfxFloat(defaultFontSize),
+ aFontMetrics.emHeight);
+ fontSize = std::max(fontSize, 1.0);
+ return ceil(fontSize / 20);
+ }
+ default:
+ NS_WARNING("Requested underline style is not valid");
+ return aFontMetrics.underlineSize;
+ }
+}
+
+enum class DecorationType
+{
+ Normal, Selection
+};
+struct nsTextFrame::PaintDecorationLineParams
+ : nsCSSRendering::DecorationRectParams
+{
+ gfxContext* context = nullptr;
+ LayoutDeviceRect dirtyRect;
+ Point pt;
+ const nscolor* overrideColor = nullptr;
+ nscolor color = NS_RGBA(0, 0, 0, 0);
+ gfxFloat icoordInFrame = 0.0f;
+ DecorationType decorationType = DecorationType::Normal;
+ DrawPathCallbacks* callbacks = nullptr;
+};
+
+void
+nsTextFrame::PaintDecorationLine(const PaintDecorationLineParams& aParams)
+{
+ nsCSSRendering::PaintDecorationLineParams params;
+ static_cast<nsCSSRendering::DecorationRectParams&>(params) = aParams;
+ params.dirtyRect = aParams.dirtyRect.ToUnknownRect();
+ params.pt = aParams.pt;
+ params.color = aParams.overrideColor ? *aParams.overrideColor : aParams.color;
+ params.icoordInFrame = Float(aParams.icoordInFrame);
+ if (aParams.callbacks) {
+ Rect path = nsCSSRendering::DecorationLineToPath(params);
+ if (aParams.decorationType == DecorationType::Normal) {
+ aParams.callbacks->PaintDecorationLine(path, params.color);
+ } else {
+ aParams.callbacks->PaintSelectionDecorationLine(path, params.color);
+ }
+ } else {
+ nsCSSRendering::PaintDecorationLine(
+ this, *aParams.context->GetDrawTarget(), params);
+ }
+}
+
+/**
+ * This, plus kRawSelectionTypesWithDecorations, encapsulates all knowledge
+ * about drawing text decoration for selections.
+ */
+void
+nsTextFrame::DrawSelectionDecorations(gfxContext* aContext,
+ const LayoutDeviceRect& aDirtyRect,
+ SelectionType aSelectionType,
+ nsTextPaintStyle& aTextPaintStyle,
+ const TextRangeStyle &aRangeStyle,
+ const Point& aPt,
+ gfxFloat aICoordInFrame,
+ gfxFloat aWidth,
+ gfxFloat aAscent,
+ const gfxFont::Metrics& aFontMetrics,
+ DrawPathCallbacks* aCallbacks,
+ bool aVertical,
+ gfxFloat aDecorationOffsetDir,
+ uint8_t aDecoration)
+{
+ PaintDecorationLineParams params;
+ params.context = aContext;
+ params.dirtyRect = aDirtyRect;
+ params.pt = aPt;
+ params.lineSize.width = aWidth;
+ params.ascent = aAscent;
+ params.offset = aDecoration == NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ?
+ aFontMetrics.underlineOffset : aFontMetrics.maxAscent;
+ params.decoration = aDecoration;
+ params.decorationType = DecorationType::Selection;
+ params.callbacks = aCallbacks;
+ params.vertical = aVertical;
+ params.descentLimit =
+ ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(),
+ aFontMetrics);
+
+ float relativeSize;
+
+ switch (aSelectionType) {
+ case SelectionType::eIMERawClause:
+ case SelectionType::eIMESelectedRawClause:
+ case SelectionType::eIMEConvertedClause:
+ case SelectionType::eIMESelectedClause:
+ case SelectionType::eSpellCheck: {
+ int32_t index = nsTextPaintStyle::
+ GetUnderlineStyleIndexForSelectionType(aSelectionType);
+ bool weDefineSelectionUnderline =
+ aTextPaintStyle.GetSelectionUnderlineForPaint(index, &params.color,
+ &relativeSize,
+ &params.style);
+ params.lineSize.height =
+ ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(),
+ aFontMetrics, aSelectionType);
+ bool isIMEType = aSelectionType != SelectionType::eSpellCheck;
+
+ if (isIMEType) {
+ // IME decoration lines should not be drawn on the both ends, i.e., we
+ // need to cut both edges of the decoration lines. Because same style
+ // IME selections can adjoin, but the users need to be able to know
+ // where are the boundaries of the selections.
+ //
+ // X: underline
+ //
+ // IME selection #1 IME selection #2 IME selection #3
+ // | | |
+ // | XXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXX
+ // +---------------------+----------------------+--------------------
+ // ^ ^ ^ ^ ^
+ // gap gap gap
+ params.pt.x += 1.0;
+ params.lineSize.width -= 2.0;
+ }
+ if (isIMEType && aRangeStyle.IsDefined()) {
+ // If IME defines the style, that should override our definition.
+ if (aRangeStyle.IsLineStyleDefined()) {
+ if (aRangeStyle.mLineStyle == TextRangeStyle::LINESTYLE_NONE) {
+ return;
+ }
+ params.style = aRangeStyle.mLineStyle;
+ relativeSize = aRangeStyle.mIsBoldLine ? 2.0f : 1.0f;
+ } else if (!weDefineSelectionUnderline) {
+ // There is no underline style definition.
+ return;
+ }
+ // If underline color is defined and that doesn't depend on the
+ // foreground color, we should use the color directly.
+ if (aRangeStyle.IsUnderlineColorDefined() &&
+ (!aRangeStyle.IsForegroundColorDefined() ||
+ aRangeStyle.mUnderlineColor != aRangeStyle.mForegroundColor)) {
+ params.color = aRangeStyle.mUnderlineColor;
+ }
+ // If foreground color or background color is defined, the both colors
+ // are computed by GetSelectionTextColors(). Then, we should use its
+ // foreground color always. The color should have sufficient contrast
+ // with the background color.
+ else if (aRangeStyle.IsForegroundColorDefined() ||
+ aRangeStyle.IsBackgroundColorDefined()) {
+ nscolor bg;
+ GetSelectionTextColors(aSelectionType, aTextPaintStyle,
+ aRangeStyle, &params.color, &bg);
+ }
+ // Otherwise, use the foreground color of the frame.
+ else {
+ params.color = aTextPaintStyle.GetTextColor();
+ }
+ } else if (!weDefineSelectionUnderline) {
+ // IME doesn't specify the selection style and we don't define selection
+ // underline.
+ return;
+ }
+ break;
+ }
+ case SelectionType::eURLStrikeout: {
+ nscoord inflationMinFontSize =
+ nsLayoutUtils::InflationMinFontSizeFor(this);
+ float inflation =
+ GetInflationForTextDecorations(this, inflationMinFontSize);
+ const gfxFont::Metrics metrics =
+ GetFirstFontMetrics(GetFontGroupForFrame(this, inflation), aVertical);
+
+ relativeSize = 2.0f;
+ aTextPaintStyle.GetURLSecondaryColor(&params.color);
+ params.style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
+ params.lineSize.height = metrics.strikeoutSize;
+ params.offset = metrics.strikeoutOffset + 0.5;
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+ break;
+ }
+ default:
+ NS_WARNING("Requested selection decorations when there aren't any");
+ return;
+ }
+ params.offset *= aDecorationOffsetDir;
+ params.lineSize.height *= relativeSize;
+ params.icoordInFrame = (aVertical ? params.pt.y - aPt.y
+ : params.pt.x - aPt.x) + aICoordInFrame;
+ PaintDecorationLine(params);
+}
+
+/* static */
+bool
+nsTextFrame::GetSelectionTextColors(SelectionType aSelectionType,
+ nsTextPaintStyle& aTextPaintStyle,
+ const TextRangeStyle &aRangeStyle,
+ nscolor* aForeground,
+ nscolor* aBackground)
+{
+ switch (aSelectionType) {
+ case SelectionType::eNormal:
+ return aTextPaintStyle.GetSelectionColors(aForeground, aBackground);
+ case SelectionType::eFind:
+ aTextPaintStyle.GetHighlightColors(aForeground, aBackground);
+ return true;
+ case SelectionType::eURLSecondary:
+ aTextPaintStyle.GetURLSecondaryColor(aForeground);
+ *aBackground = NS_RGBA(0,0,0,0);
+ return true;
+ case SelectionType::eIMERawClause:
+ case SelectionType::eIMESelectedRawClause:
+ case SelectionType::eIMEConvertedClause:
+ case SelectionType::eIMESelectedClause:
+ if (aRangeStyle.IsDefined()) {
+ if (!aRangeStyle.IsForegroundColorDefined() &&
+ !aRangeStyle.IsBackgroundColorDefined()) {
+ *aForeground = aTextPaintStyle.GetTextColor();
+ *aBackground = NS_RGBA(0,0,0,0);
+ return false;
+ }
+ if (aRangeStyle.IsForegroundColorDefined()) {
+ *aForeground = aRangeStyle.mForegroundColor;
+ if (aRangeStyle.IsBackgroundColorDefined()) {
+ *aBackground = aRangeStyle.mBackgroundColor;
+ } else {
+ // If foreground color is defined but background color isn't
+ // defined, we can guess that IME must expect that the background
+ // color is system's default field background color.
+ *aBackground = aTextPaintStyle.GetSystemFieldBackgroundColor();
+ }
+ } else { // aRangeStyle.IsBackgroundColorDefined() is true
+ *aBackground = aRangeStyle.mBackgroundColor;
+ // If background color is defined but foreground color isn't defined,
+ // we can assume that IME must expect that the foreground color is
+ // same as system's field text color.
+ *aForeground = aTextPaintStyle.GetSystemFieldForegroundColor();
+ }
+ return true;
+ }
+ aTextPaintStyle.GetIMESelectionColors(
+ nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(
+ aSelectionType),
+ aForeground, aBackground);
+ return true;
+ default:
+ *aForeground = aTextPaintStyle.GetTextColor();
+ *aBackground = NS_RGBA(0,0,0,0);
+ return false;
+ }
+}
+
+/**
+ * This sets *aShadow to the appropriate shadow, if any, for the given
+ * type of selection. Returns true if *aShadow was set.
+ * If text-shadow was not specified, *aShadow is left untouched
+ * (NOT reset to null), and the function returns false.
+ */
+static bool GetSelectionTextShadow(nsIFrame* aFrame,
+ SelectionType aSelectionType,
+ nsTextPaintStyle& aTextPaintStyle,
+ nsCSSShadowArray** aShadow)
+{
+ switch (aSelectionType) {
+ case SelectionType::eNormal:
+ return aTextPaintStyle.GetSelectionShadow(aShadow);
+ default:
+ return false;
+ }
+}
+
+/**
+ * This class lets us iterate over chunks of text in a uniform selection state,
+ * observing cluster boundaries, in content order, maintaining the current
+ * x-offset as we go, and telling whether the text chunk has a hyphen after
+ * it or not. The caller is responsible for actually computing the advance
+ * width of each chunk.
+ */
+class SelectionIterator {
+public:
+ /**
+ * aStart and aLength are in the original string. aSelectionDetails is
+ * according to the original string.
+ * @param aXOffset the offset from the origin of the frame to the start
+ * of the text (the left baseline origin for LTR, the right baseline origin
+ * for RTL)
+ */
+ SelectionIterator(SelectionDetails** aSelectionDetails,
+ gfxTextRun::Range aRange, PropertyProvider& aProvider,
+ gfxTextRun* aTextRun, gfxFloat aXOffset);
+
+ /**
+ * Returns the next segment of uniformly selected (or not) text.
+ * @param aXOffset the offset from the origin of the frame to the start
+ * of the text (the left baseline origin for LTR, the right baseline origin
+ * for RTL)
+ * @param aRange the transformed string range of the text for this segment
+ * @param aHyphenWidth if a hyphen is to be rendered after the text, the
+ * width of the hyphen, otherwise zero
+ * @param aSelectionType the selection type for this segment
+ * @param aStyle the selection style for this segment
+ * @return false if there are no more segments
+ */
+ bool GetNextSegment(gfxFloat* aXOffset, gfxTextRun::Range* aRange,
+ gfxFloat* aHyphenWidth,
+ SelectionType* aSelectionType,
+ TextRangeStyle* aStyle);
+ void UpdateWithAdvance(gfxFloat aAdvance) {
+ mXOffset += aAdvance*mTextRun->GetDirection();
+ }
+
+private:
+ SelectionDetails** mSelectionDetails;
+ PropertyProvider& mProvider;
+ RefPtr<gfxTextRun> mTextRun;
+ gfxSkipCharsIterator mIterator;
+ gfxTextRun::Range mOriginalRange;
+ gfxFloat mXOffset;
+};
+
+SelectionIterator::SelectionIterator(SelectionDetails** aSelectionDetails,
+ gfxTextRun::Range aRange,
+ PropertyProvider& aProvider,
+ gfxTextRun* aTextRun, gfxFloat aXOffset)
+ : mSelectionDetails(aSelectionDetails), mProvider(aProvider),
+ mTextRun(aTextRun), mIterator(aProvider.GetStart()),
+ mOriginalRange(aRange), mXOffset(aXOffset)
+{
+ mIterator.SetOriginalOffset(aRange.start);
+}
+
+bool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
+ gfxTextRun::Range* aRange,
+ gfxFloat* aHyphenWidth,
+ SelectionType* aSelectionType,
+ TextRangeStyle* aStyle)
+{
+ if (mIterator.GetOriginalOffset() >= int32_t(mOriginalRange.end))
+ return false;
+
+ // save offset into transformed string now
+ uint32_t runOffset = mIterator.GetSkippedOffset();
+
+ uint32_t index = mIterator.GetOriginalOffset() - mOriginalRange.start;
+ SelectionDetails* sdptr = mSelectionDetails[index];
+ SelectionType selectionType =
+ sdptr ? sdptr->mSelectionType : SelectionType::eNone;
+ TextRangeStyle style;
+ if (sdptr) {
+ style = sdptr->mTextRangeStyle;
+ }
+ for (++index; index < mOriginalRange.Length(); ++index) {
+ if (sdptr != mSelectionDetails[index])
+ break;
+ }
+ mIterator.SetOriginalOffset(index + mOriginalRange.start);
+
+ // Advance to the next cluster boundary
+ while (mIterator.GetOriginalOffset() < int32_t(mOriginalRange.end) &&
+ !mIterator.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(mIterator.GetSkippedOffset())) {
+ mIterator.AdvanceOriginal(1);
+ }
+
+ bool haveHyphenBreak =
+ (mProvider.GetFrame()->GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
+ aRange->start = runOffset;
+ aRange->end = mIterator.GetSkippedOffset();
+ *aXOffset = mXOffset;
+ *aHyphenWidth = 0;
+ if (mIterator.GetOriginalOffset() == int32_t(mOriginalRange.end) &&
+ haveHyphenBreak) {
+ *aHyphenWidth = mProvider.GetHyphenWidth();
+ }
+ *aSelectionType = selectionType;
+ *aStyle = style;
+ return true;
+}
+
+static void
+AddHyphenToMetrics(nsTextFrame* aTextFrame, const gfxTextRun* aBaseTextRun,
+ gfxTextRun::Metrics* aMetrics,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTarget)
+{
+ // Fix up metrics to include hyphen
+ RefPtr<gfxTextRun> hyphenTextRun =
+ GetHyphenTextRun(aBaseTextRun, aDrawTarget, aTextFrame);
+ if (!hyphenTextRun) {
+ return;
+ }
+
+ gfxTextRun::Metrics hyphenMetrics =
+ hyphenTextRun->MeasureText(aBoundingBoxType, aDrawTarget);
+ if (aTextFrame->GetWritingMode().IsLineInverted()) {
+ hyphenMetrics.mBoundingBox.y = -hyphenMetrics.mBoundingBox.YMost();
+ }
+ aMetrics->CombineWith(hyphenMetrics, aBaseTextRun->IsRightToLeft());
+}
+
+void
+nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
+ nsCSSShadowItem* aShadowDetails,
+ gfxRect& aBoundingBox, uint32_t aBlurFlags)
+{
+ PROFILER_LABEL("nsTextFrame", "PaintOneShadow",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ gfxPoint shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset);
+ nscoord blurRadius = std::max(aShadowDetails->mRadius, 0);
+
+ // This rect is the box which is equivalent to where the shadow will be painted.
+ // The origin of aBoundingBox is the text baseline left, so we must translate it by
+ // that much in order to make the origin the top-left corner of the text bounding box.
+ // Note that aLeftSideOffset is line-left, so actually means top offset in
+ // vertical writing modes.
+ gfxRect shadowGfxRect;
+ WritingMode wm = GetWritingMode();
+ if (wm.IsVertical()) {
+ shadowGfxRect = aBoundingBox;
+ if (wm.IsVerticalRL()) {
+ // for vertical-RL, reverse direction of x-coords of bounding box
+ shadowGfxRect.x = -shadowGfxRect.XMost();
+ }
+ shadowGfxRect += gfxPoint(aParams.textBaselinePt.x,
+ aParams.framePt.y + aParams.leftSideOffset);
+ } else {
+ shadowGfxRect =
+ aBoundingBox + gfxPoint(aParams.framePt.x + aParams.leftSideOffset,
+ aParams.textBaselinePt.y);
+ }
+ shadowGfxRect += shadowOffset;
+
+ nsRect shadowRect(NSToCoordRound(shadowGfxRect.X()),
+ NSToCoordRound(shadowGfxRect.Y()),
+ NSToCoordRound(shadowGfxRect.Width()),
+ NSToCoordRound(shadowGfxRect.Height()));
+
+ nsContextBoxBlur contextBoxBlur;
+ const auto A2D = PresContext()->AppUnitsPerDevPixel();
+ gfxContext* shadowContext = contextBoxBlur.Init(
+ shadowRect, 0, blurRadius, A2D, aParams.context,
+ LayoutDevicePixel::ToAppUnits(aParams.dirtyRect, A2D), nullptr, aBlurFlags);
+ if (!shadowContext)
+ return;
+
+ nscolor shadowColor;
+ const nscolor* decorationOverrideColor;
+ if (aShadowDetails->mHasColor) {
+ shadowColor = aShadowDetails->mColor;
+ decorationOverrideColor = &shadowColor;
+ } else {
+ shadowColor = aParams.foregroundColor;
+ decorationOverrideColor = nullptr;
+ }
+
+ aParams.context->Save();
+ aParams.context->SetColor(Color::FromABGR(shadowColor));
+
+ // Draw the text onto our alpha-only surface to capture the alpha values.
+ // Remember that the box blur context has a device offset on it, so we don't need to
+ // translate any coordinates to fit on the surface.
+ gfxFloat advanceWidth;
+ nsTextPaintStyle textPaintStyle(this);
+ DrawTextParams params(shadowContext);
+ params.advanceWidth = &advanceWidth;
+ params.dirtyRect = aParams.dirtyRect;
+ params.framePt = aParams.framePt + shadowOffset;
+ params.provider = aParams.provider;
+ params.textStyle = &textPaintStyle;
+ params.textColor =
+ aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0);
+ params.clipEdges = aParams.clipEdges;
+ params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
+ params.decorationOverrideColor = decorationOverrideColor;
+ DrawText(aParams.range, aParams.textBaselinePt + shadowOffset, params);
+
+ contextBoxBlur.DoPaint();
+ aParams.context->Restore();
+}
+
+// Paints selection backgrounds and text in the correct colors. Also computes
+// aAllTypes, the union of all selection types that are applying to this text.
+bool
+nsTextFrame::PaintTextWithSelectionColors(
+ const PaintTextSelectionParams& aParams,
+ SelectionDetails* aDetails, RawSelectionType* aAllRawSelectionTypes,
+ const nsCharClipDisplayItem::ClipEdges& aClipEdges)
+{
+ const gfxTextRun::Range& contentRange = aParams.contentRange;
+
+ // Figure out which selections control the colors to use for each character.
+ AutoTArray<SelectionDetails*,BIG_TEXT_NODE_SIZE> prevailingSelectionsBuffer;
+ SelectionDetails** prevailingSelections =
+ prevailingSelectionsBuffer.AppendElements(contentRange.Length(), fallible);
+ if (!prevailingSelections) {
+ return false;
+ }
+
+ RawSelectionType allRawSelectionTypes = 0;
+ for (uint32_t i = 0; i < contentRange.Length(); ++i) {
+ prevailingSelections[i] = nullptr;
+ }
+
+ SelectionDetails *sdptr = aDetails;
+ bool anyBackgrounds = false;
+ while (sdptr) {
+ int32_t start = std::max(0, sdptr->mStart - int32_t(contentRange.start));
+ int32_t end = std::min(int32_t(contentRange.Length()),
+ sdptr->mEnd - int32_t(contentRange.start));
+ SelectionType selectionType = sdptr->mSelectionType;
+ if (start < end) {
+ allRawSelectionTypes |= ToRawSelectionType(selectionType);
+ // Ignore selections that don't set colors
+ nscolor foreground, background;
+ if (GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
+ sdptr->mTextRangeStyle,
+ &foreground, &background)) {
+ if (NS_GET_A(background) > 0) {
+ anyBackgrounds = true;
+ }
+ for (int32_t i = start; i < end; ++i) {
+ // Favour normal selection over IME selections
+ if (!prevailingSelections[i] ||
+ selectionType < prevailingSelections[i]->mSelectionType) {
+ prevailingSelections[i] = sdptr;
+ }
+ }
+ }
+ }
+ sdptr = sdptr->mNext;
+ }
+ *aAllRawSelectionTypes = allRawSelectionTypes;
+
+ if (!allRawSelectionTypes) {
+ // Nothing is selected in the given text range. XXX can this still occur?
+ return false;
+ }
+
+ bool vertical = mTextRun->IsVertical();
+ const gfxFloat startIOffset = vertical ?
+ aParams.textBaselinePt.y - aParams.framePt.y :
+ aParams.textBaselinePt.x - aParams.framePt.x;
+ gfxFloat iOffset, hyphenWidth;
+ Range range; // in transformed string
+ TextRangeStyle rangeStyle;
+ // Draw background colors
+ if (anyBackgrounds && !aParams.IsGenerateTextMask()) {
+ int32_t appUnitsPerDevPixel =
+ aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel();
+ SelectionIterator iterator(prevailingSelections, contentRange,
+ *aParams.provider, mTextRun, startIOffset);
+ SelectionType selectionType;
+ while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
+ &selectionType, &rangeStyle)) {
+ nscolor foreground, background;
+ GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
+ rangeStyle, &foreground, &background);
+ // Draw background color
+ gfxFloat advance = hyphenWidth +
+ mTextRun->GetAdvanceWidth(range, aParams.provider);
+ if (NS_GET_A(background) > 0) {
+ nsRect bgRect;
+ gfxFloat offs = iOffset - (mTextRun->IsInlineReversed() ? advance : 0);
+ if (vertical) {
+ bgRect = nsRect(aParams.framePt.x, aParams.framePt.y + offs,
+ GetSize().width, advance);
+ } else {
+ bgRect = nsRect(aParams.framePt.x + offs, aParams.framePt.y,
+ advance, GetSize().height);
+ }
+ PaintSelectionBackground(
+ *aParams.context->GetDrawTarget(), background, aParams.dirtyRect,
+ LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel),
+ aParams.callbacks);
+ }
+ iterator.UpdateWithAdvance(advance);
+ }
+ }
+
+ if (aParams.IsPaintBGColor()) {
+ return true;
+ }
+
+ gfxFloat advance;
+ DrawTextParams params(aParams.context);
+ params.dirtyRect = aParams.dirtyRect;
+ params.framePt = aParams.framePt;
+ params.provider = aParams.provider;
+ params.textStyle = aParams.textPaintStyle;
+ params.clipEdges = &aClipEdges;
+ params.advanceWidth = &advance;
+ params.callbacks = aParams.callbacks;
+
+ PaintShadowParams shadowParams(aParams);
+ shadowParams.provider = aParams.provider;
+ shadowParams.clipEdges = &aClipEdges;
+
+ // Draw text
+ const nsStyleText* textStyle = StyleText();
+ SelectionIterator iterator(prevailingSelections, contentRange,
+ *aParams.provider, mTextRun, startIOffset);
+ SelectionType selectionType;
+ while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
+ &selectionType, &rangeStyle)) {
+ nscolor foreground, background;
+ if (aParams.IsGenerateTextMask()) {
+ foreground = NS_RGBA(0, 0, 0, 255);
+ } else {
+ GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
+ rangeStyle, &foreground, &background);
+ }
+
+ gfxPoint textBaselinePt = vertical ?
+ gfxPoint(aParams.textBaselinePt.x, aParams.framePt.y + iOffset) :
+ gfxPoint(aParams.framePt.x + iOffset, aParams.textBaselinePt.y);
+
+ // Determine what shadow, if any, to draw - either from textStyle
+ // or from the ::-moz-selection pseudo-class if specified there
+ nsCSSShadowArray* shadow = textStyle->GetTextShadow();
+ GetSelectionTextShadow(this, selectionType, *aParams.textPaintStyle,
+ &shadow);
+ if (shadow) {
+ nscoord startEdge = iOffset;
+ if (mTextRun->IsInlineReversed()) {
+ startEdge -= hyphenWidth +
+ mTextRun->GetAdvanceWidth(range, aParams.provider);
+ }
+ shadowParams.range = range;
+ shadowParams.textBaselinePt = textBaselinePt;
+ shadowParams.foregroundColor = foreground;
+ shadowParams.leftSideOffset = startEdge;
+ PaintShadows(shadow, shadowParams);
+ }
+
+ // Draw text segment
+ params.textColor = foreground;
+ params.textStrokeColor = aParams.textPaintStyle->GetWebkitTextStrokeColor();
+ params.textStrokeWidth = aParams.textPaintStyle->GetWebkitTextStrokeWidth();
+ params.drawSoftHyphen = hyphenWidth > 0;
+ DrawText(range, textBaselinePt, params);
+ advance += hyphenWidth;
+ iterator.UpdateWithAdvance(advance);
+ }
+ return true;
+}
+
+void
+nsTextFrame::PaintTextSelectionDecorations(
+ const PaintTextSelectionParams& aParams,
+ SelectionDetails* aDetails, SelectionType aSelectionType)
+{
+ // Hide text decorations if we're currently hiding @font-face fallback text
+ if (aParams.provider->GetFontGroup()->ShouldSkipDrawing())
+ return;
+
+ // Figure out which characters will be decorated for this selection.
+ const gfxTextRun::Range& contentRange = aParams.contentRange;
+ AutoTArray<SelectionDetails*, BIG_TEXT_NODE_SIZE> selectedCharsBuffer;
+ SelectionDetails** selectedChars =
+ selectedCharsBuffer.AppendElements(contentRange.Length(), fallible);
+ if (!selectedChars) {
+ return;
+ }
+ for (uint32_t i = 0; i < contentRange.Length(); ++i) {
+ selectedChars[i] = nullptr;
+ }
+
+ SelectionDetails *sdptr = aDetails;
+ while (sdptr) {
+ if (sdptr->mSelectionType == aSelectionType) {
+ int32_t start = std::max(0, sdptr->mStart - int32_t(contentRange.start));
+ int32_t end = std::min(int32_t(contentRange.Length()),
+ sdptr->mEnd - int32_t(contentRange.start));
+ for (int32_t i = start; i < end; ++i) {
+ selectedChars[i] = sdptr;
+ }
+ }
+ sdptr = sdptr->mNext;
+ }
+
+ gfxFont* firstFont = aParams.provider->GetFontGroup()->GetFirstValidFont();
+ bool verticalRun = mTextRun->IsVertical();
+ bool rightUnderline = verticalRun && IsUnderlineRight(this);
+ const uint8_t kDecoration =
+ rightUnderline ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE :
+ NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+ bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
+ gfxFont::Metrics
+ decorationMetrics(firstFont->GetMetrics(useVerticalMetrics ?
+ gfxFont::eVertical : gfxFont::eHorizontal));
+ if (!useVerticalMetrics) {
+ // The potential adjustment from using gfxFontGroup::GetUnderlineOffset
+ // is only valid for horizontal font metrics.
+ decorationMetrics.underlineOffset =
+ aParams.provider->GetFontGroup()->GetUnderlineOffset();
+ }
+
+ gfxFloat startIOffset = verticalRun ?
+ aParams.textBaselinePt.y - aParams.framePt.y :
+ aParams.textBaselinePt.x - aParams.framePt.x;
+ SelectionIterator iterator(selectedChars, contentRange,
+ *aParams.provider, mTextRun, startIOffset);
+ gfxFloat iOffset, hyphenWidth;
+ Range range;
+ int32_t app = aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel();
+ // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
+ Point pt;
+ if (verticalRun) {
+ pt.x = (aParams.textBaselinePt.x - mAscent) / app;
+ } else {
+ pt.y = (aParams.textBaselinePt.y - mAscent) / app;
+ }
+ gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
+ SelectionType nextSelectionType;
+ TextRangeStyle selectedStyle;
+ while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
+ &nextSelectionType, &selectedStyle)) {
+ gfxFloat advance = hyphenWidth +
+ mTextRun->GetAdvanceWidth(range, aParams.provider);
+ if (nextSelectionType == aSelectionType) {
+ if (verticalRun) {
+ pt.y = (aParams.framePt.y + iOffset -
+ (mTextRun->IsInlineReversed() ? advance : 0)) / app;
+ } else {
+ pt.x = (aParams.framePt.x + iOffset -
+ (mTextRun->IsInlineReversed() ? advance : 0)) / app;
+ }
+ gfxFloat width = Abs(advance) / app;
+ gfxFloat xInFrame = pt.x - (aParams.framePt.x / app);
+ DrawSelectionDecorations(
+ aParams.context, aParams.dirtyRect, aSelectionType,
+ *aParams.textPaintStyle, selectedStyle, pt, xInFrame,
+ width, mAscent / app, decorationMetrics, aParams.callbacks,
+ verticalRun, decorationOffsetDir, kDecoration);
+ }
+ iterator.UpdateWithAdvance(advance);
+ }
+}
+
+bool
+nsTextFrame::PaintTextWithSelection(
+ const PaintTextSelectionParams& aParams,
+ const nsCharClipDisplayItem::ClipEdges& aClipEdges)
+{
+ NS_ASSERTION(GetContent()->IsSelectionDescendant(), "wrong paint path");
+
+ SelectionDetails* details = GetSelectionDetails();
+ if (!details) {
+ return false;
+ }
+
+ RawSelectionType allRawSelectionTypes;
+ if (!PaintTextWithSelectionColors(aParams, details, &allRawSelectionTypes,
+ aClipEdges)) {
+ DestroySelectionDetails(details);
+ return false;
+ }
+ // Iterate through just the selection rawSelectionTypes that paint decorations
+ // and paint decorations for any that actually occur in this frame. Paint
+ // higher-numbered selection rawSelectionTypes below lower-numered ones on the
+ // general principal that lower-numbered selections are higher priority.
+ allRawSelectionTypes &= kRawSelectionTypesWithDecorations;
+ for (size_t i = kSelectionTypeCount - 1; i >= 1; --i) {
+ SelectionType selectionType = ToSelectionType(1 << (i - 1));
+ if (selectionType & allRawSelectionTypes) {
+ // There is some selection of this selectionType. Try to paint its
+ // decorations (there might not be any for this type but that's OK,
+ // PaintTextSelectionDecorations will exit early).
+ PaintTextSelectionDecorations(aParams, details, selectionType);
+ }
+ }
+
+ DestroySelectionDetails(details);
+ return true;
+}
+
+void
+nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM,
+ const gfxPoint& aTextBaselinePt,
+ const gfxPoint& aFramePt, Range aRange,
+ const nscolor* aDecorationOverrideColor,
+ PropertyProvider* aProvider)
+{
+ const EmphasisMarkInfo* info = Properties().Get(EmphasisMarkProperty());
+ if (!info) {
+ return;
+ }
+
+ bool isTextCombined = StyleContext()->IsTextCombined();
+ nscolor color = aDecorationOverrideColor ? *aDecorationOverrideColor :
+ nsLayoutUtils::GetColor(this, eCSSProperty_text_emphasis_color);
+ aContext->SetColor(Color::FromABGR(color));
+ gfxPoint pt;
+ if (!isTextCombined) {
+ pt = aTextBaselinePt;
+ } else {
+ MOZ_ASSERT(aWM.IsVertical());
+ pt = aFramePt;
+ if (aWM.IsVerticalRL()) {
+ pt.x += GetSize().width - GetLogicalBaseline(aWM);
+ } else {
+ pt.x += GetLogicalBaseline(aWM);
+ }
+ }
+ if (!aWM.IsVertical()) {
+ pt.y += info->baselineOffset;
+ } else {
+ if (aWM.IsVerticalRL()) {
+ pt.x -= info->baselineOffset;
+ } else {
+ pt.x += info->baselineOffset;
+ }
+ }
+ if (!isTextCombined) {
+ mTextRun->DrawEmphasisMarks(aContext, info->textRun.get(), info->advance,
+ pt, aRange, aProvider);
+ } else {
+ pt.y += (GetSize().height - info->advance) / 2;
+ info->textRun->Draw(Range(info->textRun.get()), pt,
+ gfxTextRun::DrawParams(aContext));
+ }
+}
+
+nscolor
+nsTextFrame::GetCaretColorAt(int32_t aOffset)
+{
+ NS_PRECONDITION(aOffset >= 0, "aOffset must be positive");
+
+ nscolor result = nsFrame::GetCaretColorAt(aOffset);
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ PropertyProvider provider(this, iter, nsTextFrame::eInflated);
+ int32_t contentOffset = provider.GetStart().GetOriginalOffset();
+ int32_t contentLength = provider.GetOriginalLength();
+ NS_PRECONDITION(aOffset >= contentOffset &&
+ aOffset <= contentOffset + contentLength,
+ "aOffset must be in the frame's range");
+ int32_t offsetInFrame = aOffset - contentOffset;
+ if (offsetInFrame < 0 || offsetInFrame >= contentLength) {
+ return result;
+ }
+
+ bool isSolidTextColor = true;
+ if (IsSVGText()) {
+ const nsStyleSVG* style = StyleSVG();
+ if (style->mFill.Type() != eStyleSVGPaintType_None &&
+ style->mFill.Type() != eStyleSVGPaintType_Color) {
+ isSolidTextColor = false;
+ }
+ }
+
+ nsTextPaintStyle textPaintStyle(this);
+ textPaintStyle.SetResolveColors(isSolidTextColor);
+ SelectionDetails* details = GetSelectionDetails();
+ SelectionDetails* sdptr = details;
+ SelectionType selectionType = SelectionType::eNone;
+ while (sdptr) {
+ int32_t start = std::max(0, sdptr->mStart - contentOffset);
+ int32_t end = std::min(contentLength, sdptr->mEnd - contentOffset);
+ if (start <= offsetInFrame && offsetInFrame < end &&
+ (selectionType == SelectionType::eNone ||
+ sdptr->mSelectionType < selectionType)) {
+ nscolor foreground, background;
+ if (GetSelectionTextColors(sdptr->mSelectionType, textPaintStyle,
+ sdptr->mTextRangeStyle,
+ &foreground, &background)) {
+ if (!isSolidTextColor &&
+ NS_IS_SELECTION_SPECIAL_COLOR(foreground)) {
+ result = NS_RGBA(0, 0, 0, 255);
+ } else {
+ result = foreground;
+ }
+ selectionType = sdptr->mSelectionType;
+ }
+ }
+ sdptr = sdptr->mNext;
+ }
+
+ DestroySelectionDetails(details);
+ return result;
+}
+
+static gfxTextRun::Range
+ComputeTransformedRange(PropertyProvider& aProvider)
+{
+ gfxSkipCharsIterator iter(aProvider.GetStart());
+ uint32_t start = iter.GetSkippedOffset();
+ iter.AdvanceOriginal(aProvider.GetOriginalLength());
+ return gfxTextRun::Range(start, iter.GetSkippedOffset());
+}
+
+bool
+nsTextFrame::MeasureCharClippedText(nscoord aVisIStartEdge,
+ nscoord aVisIEndEdge,
+ nscoord* aSnappedStartEdge,
+ nscoord* aSnappedEndEdge)
+{
+ // We need a *reference* rendering context (not one that might have a
+ // transform), so we don't have a rendering context argument.
+ // XXX get the block and line passed to us somehow! This is slow!
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return false;
+
+ PropertyProvider provider(this, iter, nsTextFrame::eInflated);
+ // Trim trailing whitespace
+ provider.InitializeForDisplay(true);
+
+ Range range = ComputeTransformedRange(provider);
+ uint32_t startOffset = range.start;
+ uint32_t maxLength = range.Length();
+ return MeasureCharClippedText(provider, aVisIStartEdge, aVisIEndEdge,
+ &startOffset, &maxLength,
+ aSnappedStartEdge, aSnappedEndEdge);
+}
+
+static uint32_t GetClusterLength(const gfxTextRun* aTextRun,
+ uint32_t aStartOffset,
+ uint32_t aMaxLength,
+ bool aIsRTL)
+{
+ uint32_t clusterLength = aIsRTL ? 0 : 1;
+ while (clusterLength < aMaxLength) {
+ if (aTextRun->IsClusterStart(aStartOffset + clusterLength)) {
+ if (aIsRTL) {
+ ++clusterLength;
+ }
+ break;
+ }
+ ++clusterLength;
+ }
+ return clusterLength;
+}
+
+bool
+nsTextFrame::MeasureCharClippedText(PropertyProvider& aProvider,
+ nscoord aVisIStartEdge,
+ nscoord aVisIEndEdge,
+ uint32_t* aStartOffset,
+ uint32_t* aMaxLength,
+ nscoord* aSnappedStartEdge,
+ nscoord* aSnappedEndEdge)
+{
+ *aSnappedStartEdge = 0;
+ *aSnappedEndEdge = 0;
+ if (aVisIStartEdge <= 0 && aVisIEndEdge <= 0) {
+ return true;
+ }
+
+ uint32_t offset = *aStartOffset;
+ uint32_t maxLength = *aMaxLength;
+ const nscoord frameISize = ISize();
+ const bool rtl = mTextRun->IsRightToLeft();
+ gfxFloat advanceWidth = 0;
+ const nscoord startEdge = rtl ? aVisIEndEdge : aVisIStartEdge;
+ if (startEdge > 0) {
+ const gfxFloat maxAdvance = gfxFloat(startEdge);
+ while (maxLength > 0) {
+ uint32_t clusterLength =
+ GetClusterLength(mTextRun, offset, maxLength, rtl);
+ advanceWidth += mTextRun->
+ GetAdvanceWidth(Range(offset, offset + clusterLength), &aProvider);
+ maxLength -= clusterLength;
+ offset += clusterLength;
+ if (advanceWidth >= maxAdvance) {
+ break;
+ }
+ }
+ nscoord* snappedStartEdge = rtl ? aSnappedEndEdge : aSnappedStartEdge;
+ *snappedStartEdge = NSToCoordFloor(advanceWidth);
+ *aStartOffset = offset;
+ }
+
+ const nscoord endEdge = rtl ? aVisIStartEdge : aVisIEndEdge;
+ if (endEdge > 0) {
+ const gfxFloat maxAdvance = gfxFloat(frameISize - endEdge);
+ while (maxLength > 0) {
+ uint32_t clusterLength =
+ GetClusterLength(mTextRun, offset, maxLength, rtl);
+ gfxFloat nextAdvance = advanceWidth + mTextRun->GetAdvanceWidth(
+ Range(offset, offset + clusterLength), &aProvider);
+ if (nextAdvance > maxAdvance) {
+ break;
+ }
+ // This cluster fits, include it.
+ advanceWidth = nextAdvance;
+ maxLength -= clusterLength;
+ offset += clusterLength;
+ }
+ maxLength = offset - *aStartOffset;
+ nscoord* snappedEndEdge = rtl ? aSnappedStartEdge : aSnappedEndEdge;
+ *snappedEndEdge = NSToCoordFloor(gfxFloat(frameISize) - advanceWidth);
+ }
+ *aMaxLength = maxLength;
+ return maxLength != 0;
+}
+
+void
+nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow,
+ const PaintShadowParams& aParams)
+{
+ if (!aShadow) {
+ return;
+ }
+
+ gfxTextRun::Metrics shadowMetrics =
+ mTextRun->MeasureText(aParams.range, gfxFont::LOOSE_INK_EXTENTS,
+ nullptr, aParams.provider);
+ if (GetWritingMode().IsLineInverted()) {
+ Swap(shadowMetrics.mAscent, shadowMetrics.mDescent);
+ shadowMetrics.mBoundingBox.y = -shadowMetrics.mBoundingBox.YMost();
+ }
+ if (GetStateBits() & TEXT_HYPHEN_BREAK) {
+ AddHyphenToMetrics(this, mTextRun, &shadowMetrics,
+ gfxFont::LOOSE_INK_EXTENTS,
+ aParams.context->GetDrawTarget());
+ }
+ // Add bounds of text decorations
+ gfxRect decorationRect(0, -shadowMetrics.mAscent,
+ shadowMetrics.mAdvanceWidth, shadowMetrics.mAscent + shadowMetrics.mDescent);
+ shadowMetrics.mBoundingBox.UnionRect(shadowMetrics.mBoundingBox,
+ decorationRect);
+
+ // If the textrun uses any color or SVG fonts, we need to force use of a mask
+ // for shadow rendering even if blur radius is zero.
+ uint32_t blurFlags = 0;
+ uint32_t numGlyphRuns;
+ const gfxTextRun::GlyphRun* run = mTextRun->GetGlyphRuns(&numGlyphRuns);
+ while (numGlyphRuns-- > 0) {
+ if (run->mFont->AlwaysNeedsMaskForShadow()) {
+ blurFlags = nsContextBoxBlur::FORCE_MASK;
+ break;
+ }
+ run++;
+ }
+
+ if (mTextRun->IsVertical()) {
+ Swap(shadowMetrics.mBoundingBox.x, shadowMetrics.mBoundingBox.y);
+ Swap(shadowMetrics.mBoundingBox.width, shadowMetrics.mBoundingBox.height);
+ }
+
+ for (uint32_t i = aShadow->Length(); i > 0; --i) {
+ PaintOneShadow(aParams, aShadow->ShadowAt(i - 1),
+ shadowMetrics.mBoundingBox, blurFlags);
+ }
+}
+
+static bool
+ShouldDrawSelection(const nsIFrame* aFrame)
+{
+ // Normal text-with-selection rendering sequence is:
+ // * Paint background > Paint text-selection-color > Paint text
+ // When we have an parent frame with background-clip-text style, rendering
+ // sequence changes to:
+ // * Paint text-selection-color > Paint background > Paint text
+ //
+ // If there is a parent frame has background-clip:text style,
+ // text-selection-color should be drawn with the background of that parent
+ // frame, so we should not draw it again while painting text frames.
+
+ if (!aFrame) {
+ return true;
+ }
+
+ const nsStyleBackground* bg = aFrame->StyleContext()->StyleBackground();
+ const nsStyleImageLayers& layers = bg->mImage;
+ NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
+ if (layers.mLayers[i].mClip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
+ return false;
+ }
+ }
+
+ return ShouldDrawSelection(aFrame->GetParent());
+}
+
+void
+nsTextFrame::PaintText(const PaintTextParams& aParams,
+ const nsCharClipDisplayItem& aItem,
+ float aOpacity /* = 1.0f */)
+{
+ // Don't pass in the rendering context here, because we need a
+ // *reference* context and rendering context might have some transform
+ // in it
+ // XXX get the block and line passed to us somehow! This is slow!
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return;
+
+ PropertyProvider provider(this, iter, nsTextFrame::eInflated);
+ if (aItem.mIsFrameSelected.isNothing()) {
+ aItem.mIsFrameSelected.emplace(IsSelected());
+ }
+ // Trim trailing whitespace, unless we're painting a selection highlight,
+ // which should include trailing spaces if present (bug 1146754).
+ provider.InitializeForDisplay(!aItem.mIsFrameSelected.value());
+
+ const bool reversed = mTextRun->IsInlineReversed();
+ const bool verticalRun = mTextRun->IsVertical();
+ WritingMode wm = GetWritingMode();
+ const gfxFloat frameWidth = GetSize().width;
+ const gfxFloat frameHeight = GetSize().height;
+ gfxPoint textBaselinePt;
+ if (verticalRun) {
+ if (wm.IsVerticalLR()) {
+ textBaselinePt.x = nsLayoutUtils::GetSnappedBaselineX(
+ this, aParams.context, nscoord(aParams.framePt.x), mAscent);
+ } else {
+ textBaselinePt.x = nsLayoutUtils::GetSnappedBaselineX(
+ this, aParams.context, nscoord(aParams.framePt.x) + frameWidth,
+ -mAscent);
+ }
+ textBaselinePt.y = reversed ? aParams.framePt.y + frameHeight
+ : aParams.framePt.y;
+ } else {
+ textBaselinePt =
+ gfxPoint(reversed ? aParams.framePt.x + frameWidth : aParams.framePt.x,
+ nsLayoutUtils::GetSnappedBaselineY(
+ this, aParams.context, aParams.framePt.y, mAscent));
+ }
+ Range range = ComputeTransformedRange(provider);
+ uint32_t startOffset = range.start;
+ uint32_t maxLength = range.Length();
+ nscoord snappedStartEdge, snappedEndEdge;
+ if (!MeasureCharClippedText(provider, aItem.mVisIStartEdge, aItem.mVisIEndEdge,
+ &startOffset, &maxLength, &snappedStartEdge, &snappedEndEdge)) {
+ return;
+ }
+ if (verticalRun) {
+ textBaselinePt.y += reversed ? -snappedEndEdge : snappedStartEdge;
+ } else {
+ textBaselinePt.x += reversed ? -snappedEndEdge : snappedStartEdge;
+ }
+ nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedStartEdge,
+ snappedEndEdge);
+ nsTextPaintStyle textPaintStyle(this);
+ textPaintStyle.SetResolveColors(!aParams.callbacks);
+
+ // Fork off to the (slower) paint-with-selection path if necessary.
+ if (aItem.mIsFrameSelected.value() &&
+ (aParams.IsPaintBGColor() || ShouldDrawSelection(this->GetParent()))) {
+ MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!");
+ gfxSkipCharsIterator tmp(provider.GetStart());
+ Range contentRange(
+ uint32_t(tmp.ConvertSkippedToOriginal(startOffset)),
+ uint32_t(tmp.ConvertSkippedToOriginal(startOffset + maxLength)));
+ PaintTextSelectionParams params(aParams);
+ params.textBaselinePt = textBaselinePt;
+ params.provider = &provider;
+ params.contentRange = contentRange;
+ params.textPaintStyle = &textPaintStyle;
+ if (PaintTextWithSelection(params, clipEdges)) {
+ return;
+ }
+ }
+
+ if (aParams.IsPaintBGColor()) {
+ return;
+ }
+
+ nscolor foregroundColor = aParams.IsGenerateTextMask()
+ ? NS_RGBA(0, 0, 0, 255)
+ : textPaintStyle.GetTextColor();
+ if (aOpacity != 1.0f) {
+ gfx::Color gfxColor = gfx::Color::FromABGR(foregroundColor);
+ gfxColor.a *= aOpacity;
+ foregroundColor = gfxColor.ToABGR();
+ }
+
+ nscolor textStrokeColor = aParams.IsGenerateTextMask()
+ ? NS_RGBA(0, 0, 0, 255)
+ : textPaintStyle.GetWebkitTextStrokeColor();
+ if (aOpacity != 1.0f) {
+ gfx::Color gfxColor = gfx::Color::FromABGR(textStrokeColor);
+ gfxColor.a *= aOpacity;
+ textStrokeColor = gfxColor.ToABGR();
+ }
+
+ range = Range(startOffset, startOffset + maxLength);
+ if (!aParams.callbacks && aParams.IsPaintText()) {
+ const nsStyleText* textStyle = StyleText();
+ PaintShadowParams shadowParams(aParams);
+ shadowParams.range = range;
+ shadowParams.textBaselinePt = textBaselinePt;
+ shadowParams.leftSideOffset = snappedStartEdge;
+ shadowParams.provider = &provider;
+ shadowParams.foregroundColor = foregroundColor;
+ shadowParams.clipEdges = &clipEdges;
+ PaintShadows(textStyle->mTextShadow, shadowParams);
+ }
+
+ gfxFloat advanceWidth;
+ DrawTextParams params(aParams.context);
+ params.dirtyRect = aParams.dirtyRect;
+ params.framePt = aParams.framePt;
+ params.provider = &provider;
+ params.advanceWidth = &advanceWidth;
+ params.textStyle = &textPaintStyle;
+ params.textColor = foregroundColor;
+ params.textStrokeColor = textStrokeColor;
+ params.textStrokeWidth = textPaintStyle.GetWebkitTextStrokeWidth();
+ params.clipEdges = &clipEdges;
+ params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
+ params.contextPaint = aParams.contextPaint;
+ params.callbacks = aParams.callbacks;
+ DrawText(range, textBaselinePt, params);
+}
+
+static void
+DrawTextRun(const gfxTextRun* aTextRun,
+ const gfxPoint& aTextBaselinePt,
+ gfxTextRun::Range aRange,
+ const nsTextFrame::DrawTextRunParams& aParams)
+{
+ gfxTextRun::DrawParams params(aParams.context);
+ params.provider = aParams.provider;
+ params.advanceWidth = aParams.advanceWidth;
+ params.contextPaint = aParams.contextPaint;
+ params.callbacks = aParams.callbacks;
+ if (aParams.callbacks) {
+ aParams.callbacks->NotifyBeforeText(aParams.textColor);
+ params.drawMode = DrawMode::GLYPH_PATH;
+ aTextRun->Draw(aRange, aTextBaselinePt, params);
+ aParams.callbacks->NotifyAfterText();
+ } else {
+ if (NS_GET_A(aParams.textColor) != 0) {
+ // Default drawMode is DrawMode::GLYPH_FILL
+ aParams.context->SetColor(Color::FromABGR(aParams.textColor));
+ } else {
+ params.drawMode = DrawMode::GLYPH_STROKE;
+ }
+
+ if (NS_GET_A(aParams.textStrokeColor) != 0 &&
+ aParams.textStrokeWidth != 0.0f) {
+ StrokeOptions strokeOpts;
+ params.drawMode |= DrawMode::GLYPH_STROKE;
+ params.textStrokeColor = aParams.textStrokeColor;
+ strokeOpts.mLineWidth = aParams.textStrokeWidth;
+ params.strokeOpts = &strokeOpts;
+ aTextRun->Draw(aRange, aTextBaselinePt, params);
+ } else {
+ aTextRun->Draw(aRange, aTextBaselinePt, params);
+ }
+ }
+}
+
+void
+nsTextFrame::DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt,
+ const DrawTextRunParams& aParams)
+{
+ MOZ_ASSERT(aParams.advanceWidth, "Must provide advanceWidth");
+ ::DrawTextRun(mTextRun, aTextBaselinePt, aRange, aParams);
+
+ if (aParams.drawSoftHyphen) {
+ // Don't use ctx as the context, because we need a reference context here,
+ // ctx may be transformed.
+ RefPtr<gfxTextRun> hyphenTextRun =
+ GetHyphenTextRun(mTextRun, nullptr, this);
+ if (hyphenTextRun) {
+ // For right-to-left text runs, the soft-hyphen is positioned at the left
+ // of the text, minus its own width
+ gfxFloat hyphenBaselineX = aTextBaselinePt.x +
+ mTextRun->GetDirection() * (*aParams.advanceWidth) -
+ (mTextRun->IsRightToLeft() ? hyphenTextRun->GetAdvanceWidth() : 0);
+ DrawTextRunParams params = aParams;
+ params.provider = nullptr;
+ params.advanceWidth = nullptr;
+ ::DrawTextRun(hyphenTextRun.get(),
+ gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
+ Range(hyphenTextRun.get()), params);
+ }
+ }
+}
+
+void
+nsTextFrame::DrawTextRunAndDecorations(Range aRange,
+ const gfxPoint& aTextBaselinePt,
+ const DrawTextParams& aParams,
+ const TextDecorations& aDecorations)
+{
+ const gfxFloat app =
+ aParams.textStyle->PresContext()->AppUnitsPerDevPixel();
+ // Writing mode of parent frame is used because the text frame may
+ // be orthogonal to its parent when text-combine-upright is used or
+ // its parent has "display: contents", and in those cases, we want
+ // to draw the decoration lines according to parents' direction
+ // rather than ours.
+ const WritingMode wm = GetParent()->GetWritingMode();
+ bool verticalDec = wm.IsVertical();
+ bool verticalRun = mTextRun->IsVertical();
+ // If the text run and the decoration is orthogonal, we choose the
+ // metrics for decoration so that decoration line won't be broken.
+ bool useVerticalMetrics = verticalDec != verticalRun
+ ? verticalDec : verticalRun && mTextRun->UseCenterBaseline();
+
+ // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
+ nscoord x = NSToCoordRound(aParams.framePt.x);
+ nscoord y = NSToCoordRound(aParams.framePt.y);
+
+ // 'measure' here is textrun-relative, so for a horizontal run it's the
+ // width, while for a vertical run it's the height of the decoration
+ const nsSize frameSize = GetSize();
+ nscoord measure = verticalDec ? frameSize.height : frameSize.width;
+
+ if (verticalDec) {
+ aParams.clipEdges->Intersect(&y, &measure);
+ } else {
+ aParams.clipEdges->Intersect(&x, &measure);
+ }
+
+ // decSize is a textrun-relative size, so its 'width' field is actually
+ // the run-relative measure, and 'height' will be the line thickness
+ gfxFloat ascent = gfxFloat(GetLogicalBaseline(wm)) / app;
+ // The starting edge of the frame in block direction
+ gfxFloat frameBStart = verticalDec ? aParams.framePt.x : aParams.framePt.y;
+
+ // In vertical-rl mode, block coordinates are measured from the
+ // right, so we need to adjust here.
+ if (wm.IsVerticalRL()) {
+ frameBStart += frameSize.width;
+ ascent = -ascent;
+ }
+
+ nscoord inflationMinFontSize =
+ nsLayoutUtils::InflationMinFontSizeFor(this);
+
+ // The decoration-line offsets need to be reversed for sideways-lr mode,
+ // so we will multiply the values from metrics by this factor.
+ gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
+
+ PaintDecorationLineParams params;
+ params.context = aParams.context;
+ params.dirtyRect = aParams.dirtyRect;
+ params.overrideColor = aParams.decorationOverrideColor;
+ params.callbacks = aParams.callbacks;
+ // pt is the physical point where the decoration is to be drawn,
+ // relative to the frame; one of its coordinates will be updated below.
+ params.pt = Point(x / app, y / app);
+ Float& bCoord = verticalDec ? params.pt.x : params.pt.y;
+ params.lineSize = Size(measure / app, 0);
+ params.ascent = ascent;
+ params.vertical = verticalDec;
+
+ // The matrix of the context may have been altered for text-combine-
+ // upright. However, we want to draw decoration lines unscaled, thus
+ // we need to revert the scaling here.
+ gfxContextMatrixAutoSaveRestore scaledRestorer;
+ if (StyleContext()->IsTextCombined()) {
+ float scaleFactor = GetTextCombineScaleFactor(this);
+ if (scaleFactor != 1.0f) {
+ scaledRestorer.SetContext(aParams.context);
+ gfxMatrix unscaled = aParams.context->CurrentMatrix();
+ gfxPoint pt(x / app, y / app);
+ unscaled.Translate(pt).Scale(1.0f / scaleFactor, 1.0f).Translate(-pt);
+ aParams.context->SetMatrix(unscaled);
+ }
+ }
+
+ typedef gfxFont::Metrics Metrics;
+ auto paintDecorationLine = [&](const LineDecoration& dec,
+ gfxFloat Metrics::* lineSize,
+ gfxFloat Metrics::* lineOffset) {
+ if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
+ return;
+ }
+
+ float inflation =
+ GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
+ const Metrics metrics =
+ GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+ useVerticalMetrics);
+
+ params.lineSize.height = metrics.*lineSize;
+ bCoord = (frameBStart - dec.mBaselineOffset) / app;
+
+ params.color = dec.mColor;
+ params.offset = decorationOffsetDir * metrics.*lineOffset;
+ params.style = dec.mStyle;
+ PaintDecorationLine(params);
+ };
+
+ // Underlines
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+ for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
+ paintDecorationLine(dec, &Metrics::underlineSize,
+ &Metrics::underlineOffset);
+ }
+ // Overlines
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+ for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
+ paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
+ }
+
+ {
+ gfxContextMatrixAutoSaveRestore unscaledRestorer;
+ if (scaledRestorer.HasMatrix()) {
+ unscaledRestorer.SetContext(aParams.context);
+ aParams.context->SetMatrix(scaledRestorer.Matrix());
+ }
+
+ // CSS 2.1 mandates that text be painted after over/underlines,
+ // and *then* line-throughs
+ DrawTextRun(aRange, aTextBaselinePt, aParams);
+ }
+
+ // Emphasis marks
+ DrawEmphasisMarks(aParams.context, wm,
+ aTextBaselinePt, aParams.framePt, aRange,
+ aParams.decorationOverrideColor, aParams.provider);
+
+ // Line-throughs
+ params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+ for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
+ paintDecorationLine(dec, &Metrics::strikeoutSize,
+ &Metrics::strikeoutOffset);
+ }
+}
+
+void
+nsTextFrame::DrawText(Range aRange, const gfxPoint& aTextBaselinePt,
+ const DrawTextParams& aParams)
+{
+ TextDecorations decorations;
+ GetTextDecorations(aParams.textStyle->PresContext(),
+ aParams.callbacks ? eUnresolvedColors : eResolvedColors,
+ decorations);
+
+ // Hide text decorations if we're currently hiding @font-face fallback text
+ const bool drawDecorations =
+ !aParams.provider->GetFontGroup()->ShouldSkipDrawing() &&
+ (decorations.HasDecorationLines() || StyleText()->HasTextEmphasis());
+ if (drawDecorations) {
+ DrawTextRunAndDecorations(aRange, aTextBaselinePt, aParams, decorations);
+ } else {
+ DrawTextRun(aRange, aTextBaselinePt, aParams);
+ }
+}
+
+int16_t
+nsTextFrame::GetSelectionStatus(int16_t* aSelectionFlags)
+{
+ // get the selection controller
+ nsCOMPtr<nsISelectionController> selectionController;
+ nsresult rv = GetSelectionController(PresContext(),
+ getter_AddRefs(selectionController));
+ if (NS_FAILED(rv) || !selectionController)
+ return nsISelectionController::SELECTION_OFF;
+
+ selectionController->GetSelectionFlags(aSelectionFlags);
+
+ int16_t selectionValue;
+ selectionController->GetDisplaySelection(&selectionValue);
+
+ return selectionValue;
+}
+
+bool
+nsTextFrame::IsVisibleInSelection(nsISelection* aSelection)
+{
+ // Check the quick way first
+ if (!GetContent()->IsSelectionDescendant())
+ return false;
+
+ SelectionDetails* details = GetSelectionDetails();
+ bool found = false;
+
+ // where are the selection points "really"
+ SelectionDetails *sdptr = details;
+ while (sdptr) {
+ if (sdptr->mEnd > GetContentOffset() &&
+ sdptr->mStart < GetContentEnd() &&
+ sdptr->mSelectionType == SelectionType::eNormal) {
+ found = true;
+ break;
+ }
+ sdptr = sdptr->mNext;
+ }
+ DestroySelectionDetails(details);
+
+ return found;
+}
+
+/**
+ * Compute the longest prefix of text whose width is <= aWidth. Return
+ * the length of the prefix. Also returns the width of the prefix in aFitWidth.
+ */
+static uint32_t
+CountCharsFit(const gfxTextRun* aTextRun, gfxTextRun::Range aRange,
+ gfxFloat aWidth, PropertyProvider* aProvider,
+ gfxFloat* aFitWidth)
+{
+ uint32_t last = 0;
+ gfxFloat width = 0;
+ for (uint32_t i = 1; i <= aRange.Length(); ++i) {
+ if (i == aRange.Length() || aTextRun->IsClusterStart(aRange.start + i)) {
+ gfxTextRun::Range range(aRange.start + last, aRange.start + i);
+ gfxFloat nextWidth = width + aTextRun->GetAdvanceWidth(range, aProvider);
+ if (nextWidth > aWidth)
+ break;
+ last = i;
+ width = nextWidth;
+ }
+ }
+ *aFitWidth = width;
+ return last;
+}
+
+nsIFrame::ContentOffsets
+nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
+{
+ return GetCharacterOffsetAtFramePointInternal(aPoint, true);
+}
+
+nsIFrame::ContentOffsets
+nsTextFrame::GetCharacterOffsetAtFramePoint(const nsPoint &aPoint)
+{
+ return GetCharacterOffsetAtFramePointInternal(aPoint, false);
+}
+
+nsIFrame::ContentOffsets
+nsTextFrame::GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
+ bool aForInsertionPoint)
+{
+ ContentOffsets offsets;
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return offsets;
+
+ PropertyProvider provider(this, iter, nsTextFrame::eInflated);
+ // Trim leading but not trailing whitespace if possible
+ provider.InitializeForDisplay(false);
+ gfxFloat width = mTextRun->IsVertical()
+ ? (mTextRun->IsInlineReversed() ? mRect.height - aPoint.y : aPoint.y)
+ : (mTextRun->IsInlineReversed() ? mRect.width - aPoint.x : aPoint.x);
+ if (StyleContext()->IsTextCombined()) {
+ width /= GetTextCombineScaleFactor(this);
+ }
+ gfxFloat fitWidth;
+ Range skippedRange = ComputeTransformedRange(provider);
+
+ uint32_t charsFit = CountCharsFit(mTextRun, skippedRange,
+ width, &provider, &fitWidth);
+
+ int32_t selectedOffset;
+ if (charsFit < skippedRange.Length()) {
+ // charsFit characters fitted, but no more could fit. See if we're
+ // more than halfway through the cluster.. If we are, choose the next
+ // cluster.
+ gfxSkipCharsIterator extraCluster(provider.GetStart());
+ extraCluster.AdvanceSkipped(charsFit);
+
+ bool allowSplitLigature = true; // Allow selection of partial ligature...
+
+ // ...but don't let selection/insertion-point split two Regional Indicator
+ // chars that are ligated in the textrun to form a single flag symbol.
+ uint32_t offs = extraCluster.GetOriginalOffset();
+ const nsTextFragment* frag = GetContent()->GetText();
+ if (offs + 1 < frag->GetLength() &&
+ NS_IS_HIGH_SURROGATE(frag->CharAt(offs)) &&
+ NS_IS_LOW_SURROGATE(frag->CharAt(offs + 1)) &&
+ gfxFontUtils::IsRegionalIndicator
+ (SURROGATE_TO_UCS4(frag->CharAt(offs), frag->CharAt(offs + 1)))) {
+ allowSplitLigature = false;
+ if (extraCluster.GetSkippedOffset() > 1 &&
+ !mTextRun->IsLigatureGroupStart(extraCluster.GetSkippedOffset())) {
+ // CountCharsFit() left us in the middle of the flag; back up over the
+ // first character of the ligature, and adjust fitWidth accordingly.
+ extraCluster.AdvanceSkipped(-2); // it's a surrogate pair: 2 code units
+ fitWidth -= mTextRun->GetAdvanceWidth(
+ Range(extraCluster.GetSkippedOffset(),
+ extraCluster.GetSkippedOffset() + 2), &provider);
+ }
+ }
+
+ gfxSkipCharsIterator extraClusterLastChar(extraCluster);
+ FindClusterEnd(mTextRun,
+ provider.GetStart().GetOriginalOffset() + provider.GetOriginalLength(),
+ &extraClusterLastChar, allowSplitLigature);
+ PropertyProvider::Spacing spacing;
+ Range extraClusterRange(extraCluster.GetSkippedOffset(),
+ extraClusterLastChar.GetSkippedOffset() + 1);
+ gfxFloat charWidth =
+ mTextRun->GetAdvanceWidth(extraClusterRange, &provider, &spacing);
+ charWidth -= spacing.mBefore + spacing.mAfter;
+ selectedOffset = !aForInsertionPoint ||
+ width <= fitWidth + spacing.mBefore + charWidth/2
+ ? extraCluster.GetOriginalOffset()
+ : extraClusterLastChar.GetOriginalOffset() + 1;
+ } else {
+ // All characters fitted, we're at (or beyond) the end of the text.
+ // XXX This could be some pathological situation where negative spacing
+ // caused characters to move backwards. We can't really handle that
+ // in the current frame system because frames can't have negative
+ // intrinsic widths.
+ selectedOffset =
+ provider.GetStart().GetOriginalOffset() + provider.GetOriginalLength();
+ // If we're at the end of a preformatted line which has a terminating
+ // linefeed, we want to reduce the offset by one to make sure that the
+ // selection is placed before the linefeed character.
+ if (HasSignificantTerminalNewline()) {
+ --selectedOffset;
+ }
+ }
+
+ offsets.content = GetContent();
+ offsets.offset = offsets.secondaryOffset = selectedOffset;
+ offsets.associate =
+ mContentOffset == offsets.offset ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
+ return offsets;
+}
+
+bool
+nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
+ nsRect& aRect)
+{
+ if (aRect.IsEmpty())
+ return false;
+
+ nsRect givenRect = aRect;
+
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
+ gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
+ gfxFont* firstFont = fontGroup->GetFirstValidFont();
+ WritingMode wm = GetWritingMode();
+ bool verticalRun = wm.IsVertical();
+ bool useVerticalMetrics = verticalRun && !wm.IsSideways();
+ const gfxFont::Metrics& metrics =
+ firstFont->GetMetrics(useVerticalMetrics ? gfxFont::eVertical
+ : gfxFont::eHorizontal);
+
+ nsCSSRendering::DecorationRectParams params;
+ params.ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
+ params.offset = fontGroup->GetUnderlineOffset();
+ params.descentLimit =
+ ComputeDescentLimitForSelectionUnderline(aPresContext, metrics);
+ params.vertical = verticalRun;
+
+ SelectionDetails *details = GetSelectionDetails();
+ for (SelectionDetails *sd = details; sd; sd = sd->mNext) {
+ if (sd->mStart == sd->mEnd ||
+ !(sd->mSelectionType & kRawSelectionTypesWithDecorations) ||
+ // URL strikeout does not use underline.
+ sd->mSelectionType == SelectionType::eURLStrikeout) {
+ continue;
+ }
+
+ float relativeSize;
+ int32_t index =
+ nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(
+ sd->mSelectionType);
+ if (sd->mSelectionType == SelectionType::eSpellCheck) {
+ if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nullptr,
+ &relativeSize,
+ &params.style)) {
+ continue;
+ }
+ } else {
+ // IME selections
+ TextRangeStyle& rangeStyle = sd->mTextRangeStyle;
+ if (rangeStyle.IsDefined()) {
+ if (!rangeStyle.IsLineStyleDefined() ||
+ rangeStyle.mLineStyle == TextRangeStyle::LINESTYLE_NONE) {
+ continue;
+ }
+ params.style = rangeStyle.mLineStyle;
+ relativeSize = rangeStyle.mIsBoldLine ? 2.0f : 1.0f;
+ } else if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index,
+ nullptr, &relativeSize,
+ &params.style)) {
+ continue;
+ }
+ }
+ nsRect decorationArea;
+
+ params.lineSize =
+ Size(aPresContext->AppUnitsToGfxUnits(aRect.width),
+ ComputeSelectionUnderlineHeight(aPresContext, metrics,
+ sd->mSelectionType));
+ relativeSize = std::max(relativeSize, 1.0f);
+ params.lineSize.height *= relativeSize;
+ decorationArea =
+ nsCSSRendering::GetTextDecorationRect(aPresContext, params);
+ aRect.UnionRect(aRect, decorationArea);
+ }
+ DestroySelectionDetails(details);
+
+ return !aRect.IsEmpty() && !givenRect.Contains(aRect);
+}
+
+bool
+nsTextFrame::IsFrameSelected() const
+{
+ NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
+ "use the public IsSelected() instead");
+ return nsRange::IsNodeSelected(GetContent(), GetContentOffset(),
+ GetContentEnd());
+}
+
+void
+nsTextFrame::SetSelectedRange(uint32_t aStart, uint32_t aEnd, bool aSelected,
+ SelectionType aSelectionType)
+{
+ NS_ASSERTION(!GetPrevContinuation(), "Should only be called for primary frame");
+ DEBUG_VERIFY_NOT_DIRTY(mState);
+
+ // Selection is collapsed, which can't affect text frame rendering
+ if (aStart == aEnd)
+ return;
+
+ nsTextFrame* f = this;
+ while (f && f->GetContentEnd() <= int32_t(aStart)) {
+ f = static_cast<nsTextFrame*>(f->GetNextContinuation());
+ }
+
+ nsPresContext* presContext = PresContext();
+ while (f && f->GetContentOffset() < int32_t(aEnd)) {
+ // We may need to reflow to recompute the overflow area for
+ // spellchecking or IME underline if their underline is thicker than
+ // the normal decoration line.
+ if (aSelectionType & kRawSelectionTypesWithDecorations) {
+ bool didHaveOverflowingSelection =
+ (f->GetStateBits() & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0;
+ nsRect r(nsPoint(0, 0), GetSize());
+ bool willHaveOverflowingSelection =
+ aSelected && f->CombineSelectionUnderlineRect(presContext, r);
+ if (didHaveOverflowingSelection || willHaveOverflowingSelection) {
+ presContext->PresShell()->FrameNeedsReflow(f,
+ nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+ }
+ }
+ // Selection might change anything. Invalidate the overflow area.
+ f->InvalidateFrame();
+
+ f = static_cast<nsTextFrame*>(f->GetNextContinuation());
+ }
+}
+
+void
+nsTextFrame::UpdateIteratorFromOffset(const PropertyProvider& aProperties,
+ int32_t& aInOffset,
+ gfxSkipCharsIterator& aIter)
+{
+ if (aInOffset < GetContentOffset()){
+ NS_WARNING("offset before this frame's content");
+ aInOffset = GetContentOffset();
+ } else if (aInOffset > GetContentEnd()) {
+ NS_WARNING("offset after this frame's content");
+ aInOffset = GetContentEnd();
+ }
+
+ int32_t trimmedOffset = aProperties.GetStart().GetOriginalOffset();
+ int32_t trimmedEnd = trimmedOffset + aProperties.GetOriginalLength();
+ aInOffset = std::max(aInOffset, trimmedOffset);
+ aInOffset = std::min(aInOffset, trimmedEnd);
+
+ aIter.SetOriginalOffset(aInOffset);
+
+ if (aInOffset < trimmedEnd &&
+ !aIter.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(aIter.GetSkippedOffset())) {
+ NS_WARNING("called for non-cluster boundary");
+ FindClusterStart(mTextRun, trimmedOffset, &aIter);
+ }
+}
+
+nsPoint
+nsTextFrame::GetPointFromIterator(const gfxSkipCharsIterator& aIter,
+ PropertyProvider& aProperties)
+{
+ Range range(aProperties.GetStart().GetSkippedOffset(),
+ aIter.GetSkippedOffset());
+ gfxFloat advance = mTextRun->GetAdvanceWidth(range, &aProperties);
+ nscoord iSize = NSToCoordCeilClamped(advance);
+ nsPoint point;
+
+ if (mTextRun->IsVertical()) {
+ point.x = 0;
+ if (mTextRun->IsInlineReversed()) {
+ point.y = mRect.height - iSize;
+ } else {
+ point.y = iSize;
+ }
+ } else {
+ point.y = 0;
+ if (mTextRun->IsInlineReversed()) {
+ point.x = mRect.width - iSize;
+ } else {
+ point.x = iSize;
+ }
+ if (StyleContext()->IsTextCombined()) {
+ point.x *= GetTextCombineScaleFactor(this);
+ }
+ }
+ return point;
+}
+
+nsresult
+nsTextFrame::GetPointFromOffset(int32_t inOffset,
+ nsPoint* outPoint)
+{
+ if (!outPoint)
+ return NS_ERROR_NULL_POINTER;
+
+ DEBUG_VERIFY_NOT_DIRTY(mState);
+ if (mState & NS_FRAME_IS_DIRTY)
+ return NS_ERROR_UNEXPECTED;
+
+ if (GetContentLength() <= 0) {
+ outPoint->x = 0;
+ outPoint->y = 0;
+ return NS_OK;
+ }
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return NS_ERROR_FAILURE;
+
+ PropertyProvider properties(this, iter, nsTextFrame::eInflated);
+ // Don't trim trailing whitespace, we want the caret to appear in the right
+ // place if it's positioned there
+ properties.InitializeForDisplay(false);
+
+ UpdateIteratorFromOffset(properties, inOffset, iter);
+
+ *outPoint = GetPointFromIterator(iter, properties);
+
+ return NS_OK;
+}
+
+nsresult
+nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aRects)
+{
+ DEBUG_VERIFY_NOT_DIRTY(mState);
+ if (mState & NS_FRAME_IS_DIRTY) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (GetContentLength() <= 0) {
+ return NS_OK;
+ }
+
+ if (!mTextRun) {
+ return NS_ERROR_FAILURE;
+ }
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ PropertyProvider properties(this, iter, nsTextFrame::eInflated);
+ // Don't trim trailing whitespace, we want the caret to appear in the right
+ // place if it's positioned there
+ properties.InitializeForDisplay(false);
+
+ UpdateIteratorFromOffset(properties, aInOffset, iter);
+
+ const int32_t kContentEnd = GetContentEnd();
+ const int32_t kEndOffset = std::min(aInOffset + aLength, kContentEnd);
+ while (aInOffset < kEndOffset) {
+ if (!iter.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(iter.GetSkippedOffset())) {
+ FindClusterStart(mTextRun,
+ properties.GetStart().GetOriginalOffset() +
+ properties.GetOriginalLength(),
+ &iter);
+ }
+
+ nsPoint point = GetPointFromIterator(iter, properties);
+ nsRect rect;
+ rect.x = point.x;
+ rect.y = point.y;
+
+ nscoord iSize = 0;
+ if (aInOffset < kContentEnd) {
+ gfxSkipCharsIterator nextIter(iter);
+ nextIter.AdvanceOriginal(1);
+ if (!nextIter.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(nextIter.GetSkippedOffset())) {
+ FindClusterEnd(mTextRun, kContentEnd, &nextIter);
+ }
+
+ gfxFloat advance =
+ mTextRun->GetAdvanceWidth(Range(iter.GetSkippedOffset(),
+ nextIter.GetSkippedOffset()),
+ &properties);
+ iSize = NSToCoordCeilClamped(advance);
+ }
+
+ if (mTextRun->IsVertical()) {
+ rect.width = mRect.width;
+ rect.height = iSize;
+ } else {
+ rect.width = iSize;
+ rect.height = mRect.height;
+
+ if (StyleContext()->IsTextCombined()) {
+ rect.width *= GetTextCombineScaleFactor(this);
+ }
+ }
+ aRects.AppendElement(rect);
+ aInOffset++;
+ // Don't advance iter if we've reached the end
+ if (aInOffset < kEndOffset) {
+ iter.AdvanceOriginal(1);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsTextFrame::GetChildFrameContainingOffset(int32_t aContentOffset,
+ bool aHint,
+ int32_t* aOutOffset,
+ nsIFrame**aOutFrame)
+{
+ DEBUG_VERIFY_NOT_DIRTY(mState);
+#if 0 //XXXrbs disable due to bug 310227
+ if (mState & NS_FRAME_IS_DIRTY)
+ return NS_ERROR_UNEXPECTED;
+#endif
+
+ NS_ASSERTION(aOutOffset && aOutFrame, "Bad out parameters");
+ NS_ASSERTION(aContentOffset >= 0, "Negative content offset, existing code was very broken!");
+ nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
+ if (this != primaryFrame) {
+ // This call needs to happen on the primary frame
+ return primaryFrame->GetChildFrameContainingOffset(aContentOffset, aHint,
+ aOutOffset, aOutFrame);
+ }
+
+ nsTextFrame* f = this;
+ int32_t offset = mContentOffset;
+
+ // Try to look up the offset to frame property
+ nsTextFrame* cachedFrame = Properties().Get(OffsetToFrameProperty());
+
+ if (cachedFrame) {
+ f = cachedFrame;
+ offset = f->GetContentOffset();
+
+ f->RemoveStateBits(TEXT_IN_OFFSET_CACHE);
+ }
+
+ if ((aContentOffset >= offset) &&
+ (aHint || aContentOffset != offset)) {
+ while (true) {
+ nsTextFrame* next = static_cast<nsTextFrame*>(f->GetNextContinuation());
+ if (!next || aContentOffset < next->GetContentOffset())
+ break;
+ if (aContentOffset == next->GetContentOffset()) {
+ if (aHint) {
+ f = next;
+ if (f->GetContentLength() == 0) {
+ continue; // use the last of the empty frames with this offset
+ }
+ }
+ break;
+ }
+ f = next;
+ }
+ } else {
+ while (true) {
+ nsTextFrame* prev = static_cast<nsTextFrame*>(f->GetPrevContinuation());
+ if (!prev || aContentOffset > f->GetContentOffset())
+ break;
+ if (aContentOffset == f->GetContentOffset()) {
+ if (!aHint) {
+ f = prev;
+ if (f->GetContentLength() == 0) {
+ continue; // use the first of the empty frames with this offset
+ }
+ }
+ break;
+ }
+ f = prev;
+ }
+ }
+
+ *aOutOffset = aContentOffset - f->GetContentOffset();
+ *aOutFrame = f;
+
+ // cache the frame we found
+ Properties().Set(OffsetToFrameProperty(), f);
+ f->AddStateBits(TEXT_IN_OFFSET_CACHE);
+
+ return NS_OK;
+}
+
+nsIFrame::FrameSearchResult
+nsTextFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
+{
+ NS_ASSERTION(aOffset && *aOffset <= GetContentLength(), "aOffset out of range");
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return CONTINUE_EMPTY;
+
+ TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), true);
+ // Check whether there are nonskipped characters in the trimmmed range
+ return (iter.ConvertOriginalToSkipped(trimmed.GetEnd()) >
+ iter.ConvertOriginalToSkipped(trimmed.mStart)) ? FOUND : CONTINUE;
+}
+
+/**
+ * This class iterates through the clusters before or after the given
+ * aPosition (which is a content offset). You can test each cluster
+ * to see if it's whitespace (as far as selection/caret movement is concerned),
+ * or punctuation, or if there is a word break before the cluster. ("Before"
+ * is interpreted according to aDirection, so if aDirection is -1, "before"
+ * means actually *after* the cluster content.)
+ */
+class MOZ_STACK_CLASS ClusterIterator {
+public:
+ ClusterIterator(nsTextFrame* aTextFrame, int32_t aPosition, int32_t aDirection,
+ nsString& aContext);
+
+ bool NextCluster();
+ bool IsWhitespace();
+ bool IsPunctuation();
+ bool HaveWordBreakBefore() { return mHaveWordBreak; }
+ int32_t GetAfterOffset();
+ int32_t GetBeforeOffset();
+
+private:
+ gfxSkipCharsIterator mIterator;
+ const nsTextFragment* mFrag;
+ nsTextFrame* mTextFrame;
+ int32_t mDirection;
+ int32_t mCharIndex;
+ nsTextFrame::TrimmedOffsets mTrimmed;
+ nsTArray<bool> mWordBreaks;
+ bool mHaveWordBreak;
+};
+
+static bool
+IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter,
+ bool aRespectClusters,
+ const gfxTextRun* aTextRun,
+ nsIFrame* aFrame)
+{
+ if (aIter.IsOriginalCharSkipped())
+ return false;
+ uint32_t index = aIter.GetSkippedOffset();
+ if (aRespectClusters && !aTextRun->IsClusterStart(index))
+ return false;
+ if (index > 0) {
+ // Check whether the proposed position is in between the two halves of a
+ // surrogate pair, or before a Variation Selector character;
+ // if so, this is not a valid character boundary.
+ // (In the case where we are respecting clusters, we won't actually get
+ // this far because the low surrogate is also marked as non-clusterStart
+ // so we'll return FALSE above.)
+ uint32_t offs = aIter.GetOriginalOffset();
+ const nsTextFragment* frag = aFrame->GetContent()->GetText();
+ uint32_t ch = frag->CharAt(offs);
+
+ if (gfxFontUtils::IsVarSelector(ch) ||
+ (NS_IS_LOW_SURROGATE(ch) && offs > 0 &&
+ NS_IS_HIGH_SURROGATE(frag->CharAt(offs - 1)))) {
+ return false;
+ }
+
+ // If the proposed position is before a high surrogate, we need to decode
+ // the surrogate pair (if valid) and check the resulting character.
+ if (NS_IS_HIGH_SURROGATE(ch) && offs + 1 < frag->GetLength()) {
+ uint32_t ch2 = frag->CharAt(offs + 1);
+ if (NS_IS_LOW_SURROGATE(ch2)) {
+ ch = SURROGATE_TO_UCS4(ch, ch2);
+ // If the character is a (Plane-14) variation selector,
+ // or a Regional Indicator character that is ligated with the previous
+ // character, this is not a valid boundary.
+ if (gfxFontUtils::IsVarSelector(ch) ||
+ (gfxFontUtils::IsRegionalIndicator(ch) &&
+ !aTextRun->IsLigatureGroupStart(index))) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+nsIFrame::FrameSearchResult
+nsTextFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters)
+{
+ int32_t contentLength = GetContentLength();
+ NS_ASSERTION(aOffset && *aOffset <= contentLength, "aOffset out of range");
+
+ bool selectable;
+ StyleUserSelect selectStyle;
+ IsSelectable(&selectable, &selectStyle);
+ if (selectStyle == StyleUserSelect::All)
+ return CONTINUE_UNSELECTABLE;
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return CONTINUE_EMPTY;
+
+ TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), false);
+
+ // A negative offset means "end of frame".
+ int32_t startOffset = GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
+
+ if (!aForward) {
+ // If at the beginning of the line, look at the previous continuation
+ for (int32_t i = std::min(trimmed.GetEnd(), startOffset) - 1;
+ i >= trimmed.mStart; --i) {
+ iter.SetOriginalOffset(i);
+ if (IsAcceptableCaretPosition(iter, aRespectClusters, mTextRun, this)) {
+ *aOffset = i - mContentOffset;
+ return FOUND;
+ }
+ }
+ *aOffset = 0;
+ } else {
+ // If we're at the end of a line, look at the next continuation
+ iter.SetOriginalOffset(startOffset);
+ if (startOffset <= trimmed.GetEnd() &&
+ !(startOffset < trimmed.GetEnd() &&
+ StyleText()->NewlineIsSignificant(this) &&
+ iter.GetSkippedOffset() < mTextRun->GetLength() &&
+ mTextRun->CharIsNewline(iter.GetSkippedOffset()))) {
+ for (int32_t i = startOffset + 1; i <= trimmed.GetEnd(); ++i) {
+ iter.SetOriginalOffset(i);
+ if (i == trimmed.GetEnd() ||
+ IsAcceptableCaretPosition(iter, aRespectClusters, mTextRun, this)) {
+ *aOffset = i - mContentOffset;
+ return FOUND;
+ }
+ }
+ }
+ *aOffset = contentLength;
+ }
+
+ return CONTINUE;
+}
+
+bool
+ClusterIterator::IsWhitespace()
+{
+ NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
+ return IsSelectionSpace(mFrag, mCharIndex);
+}
+
+bool
+ClusterIterator::IsPunctuation()
+{
+ NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
+ // Return true for all Punctuation categories (Unicode general category P?),
+ // and also for Symbol categories (S?) except for Modifier Symbol, which is
+ // kept together with any adjacent letter/number. (Bug 1066756)
+ uint8_t cat = unicode::GetGeneralCategory(mFrag->CharAt(mCharIndex));
+ switch (cat) {
+ case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
+ case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: /* Pd */
+ case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: /* Pe */
+ case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: /* Pf */
+ case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
+ case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: /* Po */
+ case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: /* Ps */
+ case HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL: /* Sc */
+ // Deliberately omitted:
+ // case HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL: /* Sk */
+ case HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL: /* Sm */
+ case HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL: /* So */
+ return true;
+ default:
+ return false;
+ }
+}
+
+int32_t
+ClusterIterator::GetBeforeOffset()
+{
+ NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
+ return mCharIndex + (mDirection > 0 ? 0 : 1);
+}
+
+int32_t
+ClusterIterator::GetAfterOffset()
+{
+ NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
+ return mCharIndex + (mDirection > 0 ? 1 : 0);
+}
+
+bool
+ClusterIterator::NextCluster()
+{
+ if (!mDirection)
+ return false;
+ const gfxTextRun* textRun = mTextFrame->GetTextRun(nsTextFrame::eInflated);
+
+ mHaveWordBreak = false;
+ while (true) {
+ bool keepGoing = false;
+ if (mDirection > 0) {
+ if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd())
+ return false;
+ keepGoing = mIterator.IsOriginalCharSkipped() ||
+ mIterator.GetOriginalOffset() < mTrimmed.mStart ||
+ !textRun->IsClusterStart(mIterator.GetSkippedOffset());
+ mCharIndex = mIterator.GetOriginalOffset();
+ mIterator.AdvanceOriginal(1);
+ } else {
+ if (mIterator.GetOriginalOffset() <= mTrimmed.mStart)
+ return false;
+ mIterator.AdvanceOriginal(-1);
+ keepGoing = mIterator.IsOriginalCharSkipped() ||
+ mIterator.GetOriginalOffset() >= mTrimmed.GetEnd() ||
+ !textRun->IsClusterStart(mIterator.GetSkippedOffset());
+ mCharIndex = mIterator.GetOriginalOffset();
+ }
+
+ if (mWordBreaks[GetBeforeOffset() - mTextFrame->GetContentOffset()]) {
+ mHaveWordBreak = true;
+ }
+ if (!keepGoing)
+ return true;
+ }
+}
+
+ClusterIterator::ClusterIterator(nsTextFrame* aTextFrame, int32_t aPosition,
+ int32_t aDirection, nsString& aContext)
+ : mTextFrame(aTextFrame), mDirection(aDirection), mCharIndex(-1)
+{
+ mIterator = aTextFrame->EnsureTextRun(nsTextFrame::eInflated);
+ if (!aTextFrame->GetTextRun(nsTextFrame::eInflated)) {
+ mDirection = 0; // signal failure
+ return;
+ }
+ mIterator.SetOriginalOffset(aPosition);
+
+ mFrag = aTextFrame->GetContent()->GetText();
+ mTrimmed = aTextFrame->GetTrimmedOffsets(mFrag, true);
+
+ int32_t textOffset = aTextFrame->GetContentOffset();
+ int32_t textLen = aTextFrame->GetContentLength();
+ if (!mWordBreaks.AppendElements(textLen + 1)) {
+ mDirection = 0; // signal failure
+ return;
+ }
+ memset(mWordBreaks.Elements(), false, (textLen + 1)*sizeof(bool));
+ int32_t textStart;
+ if (aDirection > 0) {
+ if (aContext.IsEmpty()) {
+ // No previous context, so it must be the start of a line or text run
+ mWordBreaks[0] = true;
+ }
+ textStart = aContext.Length();
+ mFrag->AppendTo(aContext, textOffset, textLen);
+ } else {
+ if (aContext.IsEmpty()) {
+ // No following context, so it must be the end of a line or text run
+ mWordBreaks[textLen] = true;
+ }
+ textStart = 0;
+ nsAutoString str;
+ mFrag->AppendTo(str, textOffset, textLen);
+ aContext.Insert(str, 0);
+ }
+ nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
+ for (int32_t i = 0; i <= textLen; ++i) {
+ int32_t indexInText = i + textStart;
+ mWordBreaks[i] |=
+ wordBreaker->BreakInBetween(aContext.get(), indexInText,
+ aContext.get() + indexInText,
+ aContext.Length() - indexInText);
+ }
+}
+
+nsIFrame::FrameSearchResult
+nsTextFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
+ int32_t* aOffset, PeekWordState* aState)
+{
+ int32_t contentLength = GetContentLength();
+ NS_ASSERTION (aOffset && *aOffset <= contentLength, "aOffset out of range");
+
+ bool selectable;
+ StyleUserSelect selectStyle;
+ IsSelectable(&selectable, &selectStyle);
+ if (selectStyle == StyleUserSelect::All)
+ return CONTINUE_UNSELECTABLE;
+
+ int32_t offset = GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
+ ClusterIterator cIter(this, offset, aForward ? 1 : -1, aState->mContext);
+
+ if (!cIter.NextCluster())
+ return CONTINUE_EMPTY;
+
+ do {
+ bool isPunctuation = cIter.IsPunctuation();
+ bool isWhitespace = cIter.IsWhitespace();
+ bool isWordBreakBefore = cIter.HaveWordBreakBefore();
+ if (aWordSelectEatSpace == isWhitespace && !aState->mSawBeforeType) {
+ aState->SetSawBeforeType();
+ aState->Update(isPunctuation, isWhitespace);
+ continue;
+ }
+ // See if we can break before the current cluster
+ if (!aState->mAtStart) {
+ bool canBreak;
+ if (isPunctuation != aState->mLastCharWasPunctuation) {
+ canBreak = BreakWordBetweenPunctuation(aState, aForward,
+ isPunctuation, isWhitespace, aIsKeyboardSelect);
+ } else if (!aState->mLastCharWasWhitespace &&
+ !isWhitespace && !isPunctuation && isWordBreakBefore) {
+ // if both the previous and the current character are not white
+ // space but this can be word break before, we don't need to eat
+ // a white space in this case. This case happens in some languages
+ // that their words are not separated by white spaces. E.g.,
+ // Japanese and Chinese.
+ canBreak = true;
+ } else {
+ canBreak = isWordBreakBefore && aState->mSawBeforeType &&
+ (aWordSelectEatSpace != isWhitespace);
+ }
+ if (canBreak) {
+ *aOffset = cIter.GetBeforeOffset() - mContentOffset;
+ return FOUND;
+ }
+ }
+ aState->Update(isPunctuation, isWhitespace);
+ } while (cIter.NextCluster());
+
+ *aOffset = cIter.GetAfterOffset() - mContentOffset;
+ return CONTINUE;
+}
+
+ // TODO this needs to be deCOMtaminated with the interface fixed in
+// nsIFrame.h, but we won't do that until the old textframe is gone.
+nsresult
+nsTextFrame::CheckVisibility(nsPresContext* aContext, int32_t aStartIndex,
+ int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *aRetval)
+{
+ if (!aRetval)
+ return NS_ERROR_NULL_POINTER;
+
+ // Text in the range is visible if there is at least one character in the range
+ // that is not skipped and is mapped by this frame (which is the primary frame)
+ // or one of its continuations.
+ for (nsTextFrame* f = this; f;
+ f = static_cast<nsTextFrame*>(GetNextContinuation())) {
+ int32_t dummyOffset = 0;
+ if (f->PeekOffsetNoAmount(true, &dummyOffset) == FOUND) {
+ *aRetval = true;
+ return NS_OK;
+ }
+ }
+
+ *aRetval = false;
+ return NS_OK;
+}
+
+nsresult
+nsTextFrame::GetOffsets(int32_t &start, int32_t &end) const
+{
+ start = GetContentOffset();
+ end = GetContentEnd();
+ return NS_OK;
+}
+
+static int32_t
+FindEndOfPunctuationRun(const nsTextFragment* aFrag,
+ const gfxTextRun* aTextRun,
+ gfxSkipCharsIterator* aIter,
+ int32_t aOffset,
+ int32_t aStart,
+ int32_t aEnd)
+{
+ int32_t i;
+
+ for (i = aStart; i < aEnd - aOffset; ++i) {
+ if (nsContentUtils::IsFirstLetterPunctuationAt(aFrag, aOffset + i)) {
+ aIter->SetOriginalOffset(aOffset + i);
+ FindClusterEnd(aTextRun, aEnd, aIter);
+ i = aIter->GetOriginalOffset() - aOffset;
+ } else {
+ break;
+ }
+ }
+ return i;
+}
+
+/**
+ * Returns true if this text frame completes the first-letter, false
+ * if it does not contain a true "letter".
+ * If returns true, then it also updates aLength to cover just the first-letter
+ * text.
+ *
+ * XXX :first-letter should be handled during frame construction
+ * (and it has a good bit in common with nextBidi)
+ *
+ * @param aLength an in/out parameter: on entry contains the maximum length to
+ * return, on exit returns length of the first-letter fragment (which may
+ * include leading and trailing punctuation, for example)
+ */
+static bool
+FindFirstLetterRange(const nsTextFragment* aFrag,
+ const gfxTextRun* aTextRun,
+ int32_t aOffset, const gfxSkipCharsIterator& aIter,
+ int32_t* aLength)
+{
+ int32_t i;
+ int32_t length = *aLength;
+ int32_t endOffset = aOffset + length;
+ gfxSkipCharsIterator iter(aIter);
+
+ // skip leading whitespace, then consume clusters that start with punctuation
+ i = FindEndOfPunctuationRun(aFrag, aTextRun, &iter, aOffset,
+ GetTrimmableWhitespaceCount(aFrag, aOffset, length, 1),
+ endOffset);
+ if (i == length)
+ return false;
+
+ // If the next character is not a letter or number, there is no first-letter.
+ // Return true so that we don't go on looking, but set aLength to 0.
+ if (!nsContentUtils::IsAlphanumericAt(aFrag, aOffset + i)) {
+ *aLength = 0;
+ return true;
+ }
+
+ // consume another cluster (the actual first letter)
+
+ // For complex scripts such as Indic and SEAsian, where first-letter
+ // should extend to entire orthographic "syllable" clusters, we don't
+ // want to allow this to split a ligature.
+ bool allowSplitLigature;
+
+ typedef unicode::Script Script;
+ switch (unicode::GetScriptCode(aFrag->CharAt(aOffset + i))) {
+ default:
+ allowSplitLigature = true;
+ break;
+
+ // For now, lacking any definitive specification of when to apply this
+ // behavior, we'll base the decision on the HarfBuzz shaping engine
+ // used for each script: those that are handled by the Indic, Tibetan,
+ // Myanmar and SEAsian shapers will apply the "don't split ligatures"
+ // rule.
+
+ // Indic
+ case Script::BENGALI:
+ case Script::DEVANAGARI:
+ case Script::GUJARATI:
+ case Script::GURMUKHI:
+ case Script::KANNADA:
+ case Script::MALAYALAM:
+ case Script::ORIYA:
+ case Script::TAMIL:
+ case Script::TELUGU:
+ case Script::SINHALA:
+ case Script::BALINESE:
+ case Script::LEPCHA:
+ case Script::REJANG:
+ case Script::SUNDANESE:
+ case Script::JAVANESE:
+ case Script::KAITHI:
+ case Script::MEETEI_MAYEK:
+ case Script::CHAKMA:
+ case Script::SHARADA:
+ case Script::TAKRI:
+ case Script::KHMER:
+
+ // Tibetan
+ case Script::TIBETAN:
+
+ // Myanmar
+ case Script::MYANMAR:
+
+ // Other SEAsian
+ case Script::BUGINESE:
+ case Script::NEW_TAI_LUE:
+ case Script::CHAM:
+ case Script::TAI_THAM:
+
+ // What about Thai/Lao - any special handling needed?
+ // Should we special-case Arabic lam-alef?
+
+ allowSplitLigature = false;
+ break;
+ }
+
+ iter.SetOriginalOffset(aOffset + i);
+ FindClusterEnd(aTextRun, endOffset, &iter, allowSplitLigature);
+
+ i = iter.GetOriginalOffset() - aOffset;
+ if (i + 1 == length)
+ return true;
+
+ // consume clusters that start with punctuation
+ i = FindEndOfPunctuationRun(aFrag, aTextRun, &iter, aOffset, i + 1, endOffset);
+ if (i < length)
+ *aLength = i;
+ return true;
+}
+
+static uint32_t
+FindStartAfterSkippingWhitespace(PropertyProvider* aProvider,
+ nsIFrame::InlineIntrinsicISizeData* aData,
+ const nsStyleText* aTextStyle,
+ gfxSkipCharsIterator* aIterator,
+ uint32_t aFlowEndInTextRun)
+{
+ if (aData->mSkipWhitespace) {
+ while (aIterator->GetSkippedOffset() < aFlowEndInTextRun &&
+ IsTrimmableSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset(), aTextStyle)) {
+ aIterator->AdvanceOriginal(1);
+ }
+ }
+ return aIterator->GetSkippedOffset();
+}
+
+float
+nsTextFrame::GetFontSizeInflation() const
+{
+ if (!HasFontSizeInflation()) {
+ return 1.0f;
+ }
+ return Properties().Get(FontSizeInflationProperty());
+}
+
+void
+nsTextFrame::SetFontSizeInflation(float aInflation)
+{
+ if (aInflation == 1.0f) {
+ if (HasFontSizeInflation()) {
+ RemoveStateBits(TEXT_HAS_FONT_INFLATION);
+ Properties().Delete(FontSizeInflationProperty());
+ }
+ return;
+ }
+
+ AddStateBits(TEXT_HAS_FONT_INFLATION);
+ Properties().Set(FontSizeInflationProperty(), aInflation);
+}
+
+/* virtual */
+void nsTextFrame::MarkIntrinsicISizesDirty()
+{
+ ClearTextRuns();
+ nsFrame::MarkIntrinsicISizesDirty();
+}
+
+// XXX this doesn't handle characters shaped by line endings. We need to
+// temporarily override the "current line ending" settings.
+void
+nsTextFrame::AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData,
+ TextRunType aTextRunType)
+{
+ uint32_t flowEndInTextRun;
+ gfxSkipCharsIterator iter =
+ EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(),
+ aData->LineContainer(), aData->mLine, &flowEndInTextRun);
+ gfxTextRun *textRun = GetTextRun(aTextRunType);
+ if (!textRun)
+ return;
+
+ // Pass null for the line container. This will disable tab spacing, but that's
+ // OK since we can't really handle tabs for intrinsic sizing anyway.
+ const nsStyleText* textStyle = StyleText();
+ const nsTextFragment* frag = mContent->GetText();
+
+ // If we're hyphenating, the PropertyProvider needs the actual length;
+ // otherwise we can just pass INT32_MAX to mean "all the text"
+ int32_t len = INT32_MAX;
+ bool hyphenating = frag->GetLength() > 0 &&
+ (textStyle->mHyphens == NS_STYLE_HYPHENS_AUTO ||
+ (textStyle->mHyphens == NS_STYLE_HYPHENS_MANUAL &&
+ (textRun->GetFlags() & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
+ if (hyphenating) {
+ gfxSkipCharsIterator tmp(iter);
+ len = std::min<int32_t>(GetContentOffset() + GetInFlowContentLength(),
+ tmp.ConvertSkippedToOriginal(flowEndInTextRun)) - iter.GetOriginalOffset();
+ }
+ PropertyProvider provider(textRun, textStyle, frag, this,
+ iter, len, nullptr, 0, aTextRunType);
+
+ bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
+ bool preformatNewlines = textStyle->NewlineIsSignificant(this);
+ bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
+ gfxFloat tabWidth = -1;
+ uint32_t start =
+ FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
+
+ // text-combine-upright frame is constantly 1em on inline-axis.
+ if (StyleContext()->IsTextCombined()) {
+ if (start < flowEndInTextRun && textRun->CanBreakLineBefore(start)) {
+ aData->OptionallyBreak();
+ }
+ aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
+ aData->mTrailingWhitespace = 0;
+ return;
+ }
+
+ AutoTArray<bool,BIG_TEXT_NODE_SIZE> hyphBuffer;
+ bool *hyphBreakBefore = nullptr;
+ if (hyphenating) {
+ hyphBreakBefore = hyphBuffer.AppendElements(flowEndInTextRun - start,
+ fallible);
+ if (hyphBreakBefore) {
+ provider.GetHyphenationBreaks(Range(start, flowEndInTextRun),
+ hyphBreakBefore);
+ }
+ }
+
+ for (uint32_t i = start, wordStart = start; i <= flowEndInTextRun; ++i) {
+ bool preformattedNewline = false;
+ bool preformattedTab = false;
+ if (i < flowEndInTextRun) {
+ // XXXldb Shouldn't we be including the newline as part of the
+ // segment that it ends rather than part of the segment that it
+ // starts?
+ preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
+ preformattedTab = preformatTabs && textRun->CharIsTab(i);
+ if (!textRun->CanBreakLineBefore(i) &&
+ !preformattedNewline &&
+ !preformattedTab &&
+ (!hyphBreakBefore || !hyphBreakBefore[i - start]))
+ {
+ // we can't break here (and it's not the end of the flow)
+ continue;
+ }
+ }
+
+ if (i > wordStart) {
+ nscoord width = NSToCoordCeilClamped(
+ textRun->GetAdvanceWidth(Range(wordStart, i), &provider));
+ width = std::max(0, width);
+ aData->mCurrentLine = NSCoordSaturatingAdd(aData->mCurrentLine, width);
+ aData->mAtStartOfLine = false;
+
+ if (collapseWhitespace) {
+ uint32_t trimStart = GetEndOfTrimmedText(frag, textStyle, wordStart, i, &iter);
+ if (trimStart == start) {
+ // This is *all* trimmable whitespace, so whatever trailingWhitespace
+ // we saw previously is still trailing...
+ aData->mTrailingWhitespace += width;
+ } else {
+ // Some non-whitespace so the old trailingWhitespace is no longer trailing
+ nscoord wsWidth = NSToCoordCeilClamped(
+ textRun->GetAdvanceWidth(Range(trimStart, i), &provider));
+ aData->mTrailingWhitespace = std::max(0, wsWidth);
+ }
+ } else {
+ aData->mTrailingWhitespace = 0;
+ }
+ }
+
+ if (preformattedTab) {
+ PropertyProvider::Spacing spacing;
+ provider.GetSpacing(Range(i, i + 1), &spacing);
+ aData->mCurrentLine += nscoord(spacing.mBefore);
+ gfxFloat afterTab =
+ AdvanceToNextTab(aData->mCurrentLine, this,
+ textRun, &tabWidth);
+ aData->mCurrentLine = nscoord(afterTab + spacing.mAfter);
+ wordStart = i + 1;
+ } else if (i < flowEndInTextRun ||
+ (i == textRun->GetLength() &&
+ (textRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK))) {
+ if (preformattedNewline) {
+ aData->ForceBreak();
+ } else if (i < flowEndInTextRun && hyphBreakBefore &&
+ hyphBreakBefore[i - start]) {
+ aData->OptionallyBreak(NSToCoordRound(provider.GetHyphenWidth()));
+ } else {
+ aData->OptionallyBreak();
+ }
+ wordStart = i;
+ }
+ }
+
+ if (start < flowEndInTextRun) {
+ // Check if we have collapsible whitespace at the end
+ aData->mSkipWhitespace =
+ IsTrimmableSpace(provider.GetFragment(),
+ iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
+ textStyle);
+ }
+}
+
+bool nsTextFrame::IsCurrentFontInflation(float aInflation) const {
+ return fabsf(aInflation - GetFontSizeInflation()) < 1e-6;
+}
+
+// XXX Need to do something here to avoid incremental reflow bugs due to
+// first-line and first-letter changing min-width
+/* virtual */ void
+nsTextFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData)
+{
+ float inflation = nsLayoutUtils::FontSizeInflationFor(this);
+ TextRunType trtype = (inflation == 1.0f) ? eNotInflated : eInflated;
+
+ if (trtype == eInflated && !IsCurrentFontInflation(inflation)) {
+ // FIXME: Ideally, if we already have a text run, we'd move it to be
+ // the uninflated text run.
+ ClearTextRun(nullptr, nsTextFrame::eInflated);
+ }
+
+ nsTextFrame* f;
+ const gfxTextRun* lastTextRun = nullptr;
+ // nsContinuingTextFrame does nothing for AddInlineMinISize; all text frames
+ // in the flow are handled right here.
+ for (f = this; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
+ // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
+ // haven't set up textruns yet for f. Except in OOM situations,
+ // lastTextRun will only be null for the first text frame.
+ if (f == this || f->GetTextRun(trtype) != lastTextRun) {
+ nsIFrame* lc;
+ if (aData->LineContainer() &&
+ aData->LineContainer() != (lc = FindLineContainer(f))) {
+ NS_ASSERTION(f != this, "wrong InlineMinISizeData container"
+ " for first continuation");
+ aData->mLine = nullptr;
+ aData->SetLineContainer(lc);
+ }
+
+ // This will process all the text frames that share the same textrun as f.
+ f->AddInlineMinISizeForFlow(aRenderingContext, aData, trtype);
+ lastTextRun = f->GetTextRun(trtype);
+ }
+ }
+}
+
+// XXX this doesn't handle characters shaped by line endings. We need to
+// temporarily override the "current line ending" settings.
+void
+nsTextFrame::AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlinePrefISizeData *aData,
+ TextRunType aTextRunType)
+{
+ uint32_t flowEndInTextRun;
+ gfxSkipCharsIterator iter =
+ EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(),
+ aData->LineContainer(), aData->mLine, &flowEndInTextRun);
+ gfxTextRun *textRun = GetTextRun(aTextRunType);
+ if (!textRun)
+ return;
+
+ // Pass null for the line container. This will disable tab spacing, but that's
+ // OK since we can't really handle tabs for intrinsic sizing anyway.
+
+ const nsStyleText* textStyle = StyleText();
+ const nsTextFragment* frag = mContent->GetText();
+ PropertyProvider provider(textRun, textStyle, frag, this,
+ iter, INT32_MAX, nullptr, 0, aTextRunType);
+
+ // text-combine-upright frame is constantly 1em on inline-axis.
+ if (StyleContext()->IsTextCombined()) {
+ aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
+ aData->mTrailingWhitespace = 0;
+ return;
+ }
+
+ bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
+ bool preformatNewlines = textStyle->NewlineIsSignificant(this);
+ bool preformatTabs = textStyle->TabIsSignificant();
+ gfxFloat tabWidth = -1;
+ uint32_t start =
+ FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
+
+ // XXX Should we consider hyphenation here?
+ // If newlines and tabs aren't preformatted, nothing to do inside
+ // the loop so make i skip to the end
+ uint32_t loopStart = (preformatNewlines || preformatTabs) ? start : flowEndInTextRun;
+ for (uint32_t i = loopStart, lineStart = start; i <= flowEndInTextRun; ++i) {
+ bool preformattedNewline = false;
+ bool preformattedTab = false;
+ if (i < flowEndInTextRun) {
+ // XXXldb Shouldn't we be including the newline as part of the
+ // segment that it ends rather than part of the segment that it
+ // starts?
+ NS_ASSERTION(preformatNewlines || preformatTabs,
+ "We can't be here unless newlines are "
+ "hard breaks or there are tabs");
+ preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
+ preformattedTab = preformatTabs && textRun->CharIsTab(i);
+ if (!preformattedNewline && !preformattedTab) {
+ // we needn't break here (and it's not the end of the flow)
+ continue;
+ }
+ }
+
+ if (i > lineStart) {
+ nscoord width = NSToCoordCeilClamped(
+ textRun->GetAdvanceWidth(Range(lineStart, i), &provider));
+ width = std::max(0, width);
+ aData->mCurrentLine = NSCoordSaturatingAdd(aData->mCurrentLine, width);
+
+ if (collapseWhitespace) {
+ uint32_t trimStart = GetEndOfTrimmedText(frag, textStyle, lineStart, i, &iter);
+ if (trimStart == start) {
+ // This is *all* trimmable whitespace, so whatever trailingWhitespace
+ // we saw previously is still trailing...
+ aData->mTrailingWhitespace += width;
+ } else {
+ // Some non-whitespace so the old trailingWhitespace is no longer trailing
+ nscoord wsWidth = NSToCoordCeilClamped(
+ textRun->GetAdvanceWidth(Range(trimStart, i), &provider));
+ aData->mTrailingWhitespace = std::max(0, wsWidth);
+ }
+ } else {
+ aData->mTrailingWhitespace = 0;
+ }
+ }
+
+ if (preformattedTab) {
+ PropertyProvider::Spacing spacing;
+ provider.GetSpacing(Range(i, i + 1), &spacing);
+ aData->mCurrentLine += nscoord(spacing.mBefore);
+ gfxFloat afterTab =
+ AdvanceToNextTab(aData->mCurrentLine, this,
+ textRun, &tabWidth);
+ aData->mCurrentLine = nscoord(afterTab + spacing.mAfter);
+ lineStart = i + 1;
+ } else if (preformattedNewline) {
+ aData->ForceBreak();
+ lineStart = i;
+ }
+ }
+
+ // Check if we have collapsible whitespace at the end
+ if (start < flowEndInTextRun) {
+ aData->mSkipWhitespace =
+ IsTrimmableSpace(provider.GetFragment(),
+ iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
+ textStyle);
+ }
+}
+
+// XXX Need to do something here to avoid incremental reflow bugs due to
+// first-line and first-letter changing pref-width
+/* virtual */ void
+nsTextFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlinePrefISizeData *aData)
+{
+ float inflation = nsLayoutUtils::FontSizeInflationFor(this);
+ TextRunType trtype = (inflation == 1.0f) ? eNotInflated : eInflated;
+
+ if (trtype == eInflated && !IsCurrentFontInflation(inflation)) {
+ // FIXME: Ideally, if we already have a text run, we'd move it to be
+ // the uninflated text run.
+ ClearTextRun(nullptr, nsTextFrame::eInflated);
+ }
+
+ nsTextFrame* f;
+ const gfxTextRun* lastTextRun = nullptr;
+ // nsContinuingTextFrame does nothing for AddInlineMinISize; all text frames
+ // in the flow are handled right here.
+ for (f = this; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
+ // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
+ // haven't set up textruns yet for f. Except in OOM situations,
+ // lastTextRun will only be null for the first text frame.
+ if (f == this || f->GetTextRun(trtype) != lastTextRun) {
+ nsIFrame* lc;
+ if (aData->LineContainer() &&
+ aData->LineContainer() != (lc = FindLineContainer(f))) {
+ NS_ASSERTION(f != this, "wrong InlinePrefISizeData container"
+ " for first continuation");
+ aData->mLine = nullptr;
+ aData->SetLineContainer(lc);
+ }
+
+ // This will process all the text frames that share the same textrun as f.
+ f->AddInlinePrefISizeForFlow(aRenderingContext, aData, trtype);
+ lastTextRun = f->GetTextRun(trtype);
+ }
+ }
+}
+
+/* virtual */
+LogicalSize
+nsTextFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ // Inlines and text don't compute size before reflow.
+ return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+}
+
+static nsRect
+RoundOut(const gfxRect& aRect)
+{
+ nsRect r;
+ r.x = NSToCoordFloor(aRect.X());
+ r.y = NSToCoordFloor(aRect.Y());
+ r.width = NSToCoordCeil(aRect.XMost()) - r.x;
+ r.height = NSToCoordCeil(aRect.YMost()) - r.y;
+ return r;
+}
+
+nsRect
+nsTextFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const
+{
+ if (StyleContext()->HasTextDecorationLines() ||
+ (GetStateBits() & TEXT_HYPHEN_BREAK)) {
+ // This is conservative, but OK.
+ return GetVisualOverflowRect();
+ }
+
+ gfxSkipCharsIterator iter =
+ const_cast<nsTextFrame*>(this)->EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return nsRect(0, 0, 0, 0);
+
+ PropertyProvider provider(const_cast<nsTextFrame*>(this), iter,
+ nsTextFrame::eInflated);
+ // Trim trailing whitespace
+ provider.InitializeForDisplay(true);
+
+ gfxTextRun::Metrics metrics =
+ mTextRun->MeasureText(ComputeTransformedRange(provider),
+ gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
+ aDrawTarget, &provider);
+ if (GetWritingMode().IsLineInverted()) {
+ metrics.mBoundingBox.y = -metrics.mBoundingBox.YMost();
+ }
+ // mAscent should be the same as metrics.mAscent, but it's what we use to
+ // paint so that's the one we'll use.
+ nsRect boundingBox = RoundOut(metrics.mBoundingBox);
+ boundingBox += nsPoint(0, mAscent);
+ if (mTextRun->IsVertical()) {
+ // Swap line-relative textMetrics dimensions to physical coordinates.
+ Swap(boundingBox.x, boundingBox.y);
+ Swap(boundingBox.width, boundingBox.height);
+ }
+ return boundingBox;
+}
+
+/* virtual */ nsresult
+nsTextFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext,
+ nscoord* aX,
+ nscoord* aXMost)
+{
+ gfxSkipCharsIterator iter =
+ const_cast<nsTextFrame*>(this)->EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return NS_ERROR_FAILURE;
+
+ PropertyProvider provider(const_cast<nsTextFrame*>(this), iter,
+ nsTextFrame::eInflated);
+ provider.InitializeForMeasure();
+
+ gfxTextRun::Metrics metrics =
+ mTextRun->MeasureText(ComputeTransformedRange(provider),
+ gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
+ aContext->GetDrawTarget(), &provider);
+ // Round it like nsTextFrame::ComputeTightBounds() to ensure consistency.
+ *aX = NSToCoordFloor(metrics.mBoundingBox.x);
+ *aXMost = NSToCoordCeil(metrics.mBoundingBox.XMost());
+
+ return NS_OK;
+}
+
+static bool
+HasSoftHyphenBefore(const nsTextFragment* aFrag, const gfxTextRun* aTextRun,
+ int32_t aStartOffset, const gfxSkipCharsIterator& aIter)
+{
+ if (aIter.GetSkippedOffset() < aTextRun->GetLength() &&
+ aTextRun->CanHyphenateBefore(aIter.GetSkippedOffset())) {
+ return true;
+ }
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_SHY))
+ return false;
+ gfxSkipCharsIterator iter = aIter;
+ while (iter.GetOriginalOffset() > aStartOffset) {
+ iter.AdvanceOriginal(-1);
+ if (!iter.IsOriginalCharSkipped())
+ break;
+ if (aFrag->CharAt(iter.GetOriginalOffset()) == CH_SHY)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Removes all frames from aFrame up to (but not including) aFirstToNotRemove,
+ * because their text has all been taken and reflowed by earlier frames.
+ */
+static void
+RemoveEmptyInFlows(nsTextFrame* aFrame, nsTextFrame* aFirstToNotRemove)
+{
+ NS_PRECONDITION(aFrame != aFirstToNotRemove, "This will go very badly");
+ // We have to be careful here, because some RemoveFrame implementations
+ // remove and destroy not only the passed-in frame but also all its following
+ // in-flows (and sometimes all its following continuations in general). So
+ // we remove |f| and everything up to but not including firstToNotRemove from
+ // the flow first, to make sure that only the things we want destroyed are
+ // destroyed.
+
+ // This sadly duplicates some of the logic from
+ // nsSplittableFrame::RemoveFromFlow. We can get away with not duplicating
+ // all of it, because we know that the prev-continuation links of
+ // firstToNotRemove and f are fluid, and non-null.
+ NS_ASSERTION(aFirstToNotRemove->GetPrevContinuation() ==
+ aFirstToNotRemove->GetPrevInFlow() &&
+ aFirstToNotRemove->GetPrevInFlow() != nullptr,
+ "aFirstToNotRemove should have a fluid prev continuation");
+ NS_ASSERTION(aFrame->GetPrevContinuation() ==
+ aFrame->GetPrevInFlow() &&
+ aFrame->GetPrevInFlow() != nullptr,
+ "aFrame should have a fluid prev continuation");
+
+ nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
+ nsIFrame* lastRemoved = aFirstToNotRemove->GetPrevContinuation();
+
+ for (nsTextFrame* f = aFrame; f != aFirstToNotRemove;
+ f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
+ // f is going to be destroyed soon, after it is unlinked from the
+ // continuation chain. If its textrun is going to be destroyed we need to
+ // do it now, before we unlink the frames to remove from the flow,
+ // because DestroyFrom calls ClearTextRuns() and that will start at the
+ // first frame with the text run and walk the continuations.
+ if (f->IsInTextRunUserData()) {
+ f->ClearTextRuns();
+ } else {
+ f->DisconnectTextRuns();
+ }
+ }
+
+ prevContinuation->SetNextInFlow(aFirstToNotRemove);
+ aFirstToNotRemove->SetPrevInFlow(prevContinuation);
+
+ aFrame->SetPrevInFlow(nullptr);
+ lastRemoved->SetNextInFlow(nullptr);
+
+ nsContainerFrame* parent = aFrame->GetParent();
+ nsBlockFrame* parentBlock = nsLayoutUtils::GetAsBlock(parent);
+ if (parentBlock) {
+ // Manually call DoRemoveFrame so we can tell it that we're
+ // removing empty frames; this will keep it from blowing away
+ // text runs.
+ parentBlock->DoRemoveFrame(aFrame, nsBlockFrame::FRAMES_ARE_EMPTY);
+ } else {
+ // Just remove it normally; use kNoReflowPrincipalList to avoid posting
+ // new reflows.
+ parent->RemoveFrame(nsIFrame::kNoReflowPrincipalList, aFrame);
+ }
+}
+
+void
+nsTextFrame::SetLength(int32_t aLength, nsLineLayout* aLineLayout,
+ uint32_t aSetLengthFlags)
+{
+ mContentLengthHint = aLength;
+ int32_t end = GetContentOffset() + aLength;
+ nsTextFrame* f = static_cast<nsTextFrame*>(GetNextInFlow());
+ if (!f)
+ return;
+
+ // If our end offset is moving, then even if frames are not being pushed or
+ // pulled, content is moving to or from the next line and the next line
+ // must be reflowed.
+ // If the next-continuation is dirty, then we should dirty the next line now
+ // because we may have skipped doing it if we dirtied it in
+ // CharacterDataChanged. This is ugly but teaching FrameNeedsReflow
+ // and ChildIsDirty to handle a range of frames would be worse.
+ if (aLineLayout &&
+ (end != f->mContentOffset || (f->GetStateBits() & NS_FRAME_IS_DIRTY))) {
+ aLineLayout->SetDirtyNextLine();
+ }
+
+ if (end < f->mContentOffset) {
+ // Our frame is shrinking. Give the text to our next in flow.
+ if (aLineLayout &&
+ HasSignificantTerminalNewline() &&
+ GetParent()->GetType() != nsGkAtoms::letterFrame &&
+ (aSetLengthFlags & ALLOW_FRAME_CREATION_AND_DESTRUCTION)) {
+ // Whatever text we hand to our next-in-flow will end up in a frame all of
+ // its own, since it ends in a forced linebreak. Might as well just put
+ // it in a separate frame now. This is important to prevent text run
+ // churn; if we did not do that, then we'd likely end up rebuilding
+ // textruns for all our following continuations.
+ // We skip this optimization when the parent is a first-letter frame
+ // because it doesn't deal well with more than one child frame.
+ // We also skip this optimization if we were called during bidi
+ // resolution, so as not to create a new frame which doesn't appear in
+ // the bidi resolver's list of frames
+ nsPresContext* presContext = PresContext();
+ nsIFrame* newFrame = presContext->PresShell()->FrameConstructor()->
+ CreateContinuingFrame(presContext, this, GetParent());
+ nsTextFrame* next = static_cast<nsTextFrame*>(newFrame);
+ nsFrameList temp(next, next);
+ GetParent()->InsertFrames(kNoReflowPrincipalList, this, temp);
+ f = next;
+ }
+
+ f->mContentOffset = end;
+ if (f->GetTextRun(nsTextFrame::eInflated) != mTextRun) {
+ ClearTextRuns();
+ f->ClearTextRuns();
+ }
+ return;
+ }
+ // Our frame is growing. Take text from our in-flow(s).
+ // We can take text from frames in lines beyond just the next line.
+ // We don't dirty those lines. That's OK, because when we reflow
+ // our empty next-in-flow, it will take text from its next-in-flow and
+ // dirty that line.
+
+ // Note that in the process we may end up removing some frames from
+ // the flow if they end up empty.
+ nsTextFrame* framesToRemove = nullptr;
+ while (f && f->mContentOffset < end) {
+ f->mContentOffset = end;
+ if (f->GetTextRun(nsTextFrame::eInflated) != mTextRun) {
+ ClearTextRuns();
+ f->ClearTextRuns();
+ }
+ nsTextFrame* next = static_cast<nsTextFrame*>(f->GetNextInFlow());
+ // Note: the "f->GetNextSibling() == next" check below is to restrict
+ // this optimization to the case where they are on the same child list.
+ // Otherwise we might remove the only child of a nsFirstLetterFrame
+ // for example and it can't handle that. See bug 597627 for details.
+ if (next && next->mContentOffset <= end && f->GetNextSibling() == next &&
+ (aSetLengthFlags & ALLOW_FRAME_CREATION_AND_DESTRUCTION)) {
+ // |f| is now empty. We may as well remove it, instead of copying all
+ // the text from |next| into it instead; the latter leads to use
+ // rebuilding textruns for all following continuations.
+ // We skip this optimization if we were called during bidi resolution,
+ // since the bidi resolver may try to handle the destroyed frame later
+ // and crash
+ if (!framesToRemove) {
+ // Remember that we have to remove this frame.
+ framesToRemove = f;
+ }
+ } else if (framesToRemove) {
+ RemoveEmptyInFlows(framesToRemove, f);
+ framesToRemove = nullptr;
+ }
+ f = next;
+ }
+ NS_POSTCONDITION(!framesToRemove || (f && f->mContentOffset == end),
+ "How did we exit the loop if we null out framesToRemove if "
+ "!next || next->mContentOffset > end ?");
+ if (framesToRemove) {
+ // We are guaranteed that we exited the loop with f not null, per the
+ // postcondition above
+ RemoveEmptyInFlows(framesToRemove, f);
+ }
+
+#ifdef DEBUG
+ f = this;
+ int32_t iterations = 0;
+ while (f && iterations < 10) {
+ f->GetContentLength(); // Assert if negative length
+ f = static_cast<nsTextFrame*>(f->GetNextContinuation());
+ ++iterations;
+ }
+ f = this;
+ iterations = 0;
+ while (f && iterations < 10) {
+ f->GetContentLength(); // Assert if negative length
+ f = static_cast<nsTextFrame*>(f->GetPrevContinuation());
+ ++iterations;
+ }
+#endif
+}
+
+bool
+nsTextFrame::IsFloatingFirstLetterChild() const
+{
+ nsIFrame* frame = GetParent();
+ return frame && frame->IsFloating() &&
+ frame->GetType() == nsGkAtoms::letterFrame;
+}
+
+bool
+nsTextFrame::IsInitialLetterChild() const
+{
+ nsIFrame* frame = GetParent();
+ return frame && frame->StyleTextReset()->mInitialLetterSize != 0.0f &&
+ frame->GetType() == nsGkAtoms::letterFrame;
+}
+
+struct NewlineProperty {
+ int32_t mStartOffset;
+ // The offset of the first \n after mStartOffset, or -1 if there is none
+ int32_t mNewlineOffset;
+};
+
+void
+nsTextFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+
+ // XXX If there's no line layout, we shouldn't even have created this
+ // frame. This may happen if, for example, this is text inside a table
+ // but not inside a cell. For now, just don't reflow.
+ if (!aReflowInput.mLineLayout) {
+ ClearMetrics(aMetrics);
+ aStatus = NS_FRAME_COMPLETE;
+ return;
+ }
+
+ ReflowText(*aReflowInput.mLineLayout, aReflowInput.AvailableWidth(),
+ aReflowInput.mRenderingContext->GetDrawTarget(), aMetrics, aStatus);
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+#ifdef ACCESSIBILITY
+/**
+ * Notifies accessibility about text reflow. Used by nsTextFrame::ReflowText.
+ */
+class MOZ_STACK_CLASS ReflowTextA11yNotifier
+{
+public:
+ ReflowTextA11yNotifier(nsPresContext* aPresContext, nsIContent* aContent) :
+ mContent(aContent), mPresContext(aPresContext)
+ {
+ }
+ ~ReflowTextA11yNotifier()
+ {
+ nsAccessibilityService* accService = nsIPresShell::AccService();
+ if (accService) {
+ accService->UpdateText(mPresContext->PresShell(), mContent);
+ }
+ }
+private:
+ ReflowTextA11yNotifier();
+ ReflowTextA11yNotifier(const ReflowTextA11yNotifier&);
+ ReflowTextA11yNotifier& operator =(const ReflowTextA11yNotifier&);
+
+ nsIContent* mContent;
+ nsPresContext* mPresContext;
+};
+#endif
+
+void
+nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
+ DrawTarget* aDrawTarget,
+ ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus)
+{
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": BeginReflow: availableWidth=%d\n", aAvailableWidth);
+#endif
+
+ nsPresContext* presContext = PresContext();
+
+#ifdef ACCESSIBILITY
+ // Schedule the update of accessible tree since rendered text might be changed.
+ if (StyleVisibility()->IsVisible()) {
+ ReflowTextA11yNotifier(presContext, mContent);
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////
+ // Set up flags and clear out state
+ /////////////////////////////////////////////////////////////////////
+
+ // Clear out the reflow state flags in mState. We also clear the whitespace
+ // flags because this can change whether the frame maps whitespace-only text
+ // or not.
+ RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
+
+ // Temporarily map all possible content while we construct our new textrun.
+ // so that when doing reflow our styles prevail over any part of the
+ // textrun we look at. Note that next-in-flows may be mapping the same
+ // content; gfxTextRun construction logic will ensure that we take priority.
+ int32_t maxContentLength = GetInFlowContentLength();
+
+ // We don't need to reflow if there is no content.
+ if (!maxContentLength) {
+ ClearMetrics(aMetrics);
+ aStatus = NS_FRAME_COMPLETE;
+ return;
+ }
+
+#ifdef NOISY_BIDI
+ printf("Reflowed textframe\n");
+#endif
+
+ const nsStyleText* textStyle = StyleText();
+
+ bool atStartOfLine = aLineLayout.LineAtStart();
+ if (atStartOfLine) {
+ AddStateBits(TEXT_START_OF_LINE);
+ }
+
+ uint32_t flowEndInTextRun;
+ nsIFrame* lineContainer = aLineLayout.LineContainerFrame();
+ const nsTextFragment* frag = mContent->GetText();
+
+ // DOM offsets of the text range we need to measure, after trimming
+ // whitespace, restricting to first-letter, and restricting preformatted text
+ // to nearest newline
+ int32_t length = maxContentLength;
+ int32_t offset = GetContentOffset();
+
+ // Restrict preformatted text to the nearest newline
+ int32_t newLineOffset = -1; // this will be -1 or a content offset
+ int32_t contentNewLineOffset = -1;
+ // Pointer to the nsGkAtoms::newline set on this frame's element
+ NewlineProperty* cachedNewlineOffset = nullptr;
+ if (textStyle->NewlineIsSignificant(this)) {
+ cachedNewlineOffset =
+ static_cast<NewlineProperty*>(mContent->GetProperty(nsGkAtoms::newline));
+ if (cachedNewlineOffset && cachedNewlineOffset->mStartOffset <= offset &&
+ (cachedNewlineOffset->mNewlineOffset == -1 ||
+ cachedNewlineOffset->mNewlineOffset >= offset)) {
+ contentNewLineOffset = cachedNewlineOffset->mNewlineOffset;
+ } else {
+ contentNewLineOffset = FindChar(frag, offset,
+ mContent->TextLength() - offset, '\n');
+ }
+ if (contentNewLineOffset < offset + length) {
+ /*
+ The new line offset could be outside this frame if the frame has been
+ split by bidi resolution. In that case we won't use it in this reflow
+ (newLineOffset will remain -1), but we will still cache it in mContent
+ */
+ newLineOffset = contentNewLineOffset;
+ }
+ if (newLineOffset >= 0) {
+ length = newLineOffset + 1 - offset;
+ }
+ }
+ if ((atStartOfLine && !textStyle->WhiteSpaceIsSignificant()) ||
+ (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML)) {
+ // Skip leading whitespace. Make sure we don't skip a 'pre-line'
+ // newline if there is one.
+ int32_t skipLength = newLineOffset >= 0 ? length - 1 : length;
+ int32_t whitespaceCount =
+ GetTrimmableWhitespaceCount(frag, offset, skipLength, 1);
+ if (whitespaceCount) {
+ offset += whitespaceCount;
+ length -= whitespaceCount;
+ // Make sure this frame maps the trimmable whitespace.
+ if (MOZ_UNLIKELY(offset > GetContentEnd())) {
+ SetLength(offset - GetContentOffset(), &aLineLayout,
+ ALLOW_FRAME_CREATION_AND_DESTRUCTION);
+ }
+ }
+ }
+
+ bool completedFirstLetter = false;
+ // Layout dependent styles are a problem because we need to reconstruct
+ // the gfxTextRun based on our layout.
+ if (aLineLayout.GetInFirstLetter() || aLineLayout.GetInFirstLine()) {
+ SetLength(maxContentLength, &aLineLayout,
+ ALLOW_FRAME_CREATION_AND_DESTRUCTION);
+
+ if (aLineLayout.GetInFirstLetter()) {
+ // floating first-letter boundaries are significant in textrun
+ // construction, so clear the textrun out every time we hit a first-letter
+ // and have changed our length (which controls the first-letter boundary)
+ ClearTextRuns();
+ // Find the length of the first-letter. We need a textrun for this.
+ // REVIEW: maybe-bogus inflation should be ok (fixed below)
+ gfxSkipCharsIterator iter =
+ EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
+ lineContainer, aLineLayout.GetLine(),
+ &flowEndInTextRun);
+
+ if (mTextRun) {
+ int32_t firstLetterLength = length;
+ if (aLineLayout.GetFirstLetterStyleOK()) {
+ completedFirstLetter =
+ FindFirstLetterRange(frag, mTextRun, offset, iter, &firstLetterLength);
+ if (newLineOffset >= 0) {
+ // Don't allow a preformatted newline to be part of a first-letter.
+ firstLetterLength = std::min(firstLetterLength, length - 1);
+ if (length == 1) {
+ // There is no text to be consumed by the first-letter before the
+ // preformatted newline. Note that the first letter is therefore
+ // complete (FindFirstLetterRange will have returned false).
+ completedFirstLetter = true;
+ }
+ }
+ } else {
+ // We're in a first-letter frame's first in flow, so if there
+ // was a first-letter, we'd be it. However, for one reason
+ // or another (e.g., preformatted line break before this text),
+ // we're not actually supposed to have first-letter style. So
+ // just make a zero-length first-letter.
+ firstLetterLength = 0;
+ completedFirstLetter = true;
+ }
+ length = firstLetterLength;
+ if (length) {
+ AddStateBits(TEXT_FIRST_LETTER);
+ }
+ // Change this frame's length to the first-letter length right now
+ // so that when we rebuild the textrun it will be built with the
+ // right first-letter boundary
+ SetLength(offset + length - GetContentOffset(), &aLineLayout,
+ ALLOW_FRAME_CREATION_AND_DESTRUCTION);
+ // Ensure that the textrun will be rebuilt
+ ClearTextRuns();
+ }
+ }
+ }
+
+ float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
+
+ if (!IsCurrentFontInflation(fontSizeInflation)) {
+ // FIXME: Ideally, if we already have a text run, we'd move it to be
+ // the uninflated text run.
+ ClearTextRun(nullptr, nsTextFrame::eInflated);
+ }
+
+ gfxSkipCharsIterator iter =
+ EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
+ lineContainer, aLineLayout.GetLine(), &flowEndInTextRun);
+
+ NS_ASSERTION(IsCurrentFontInflation(fontSizeInflation),
+ "EnsureTextRun should have set font size inflation");
+
+ if (mTextRun && iter.GetOriginalEnd() < offset + length) {
+ // The textrun does not map enough text for this frame. This can happen
+ // when the textrun was ended in the middle of a text node because a
+ // preformatted newline was encountered, and prev-in-flow frames have
+ // consumed all the text of the textrun. We need a new textrun.
+ ClearTextRuns();
+ iter = EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
+ lineContainer, aLineLayout.GetLine(),
+ &flowEndInTextRun);
+ }
+
+ if (!mTextRun) {
+ ClearMetrics(aMetrics);
+ aStatus = NS_FRAME_COMPLETE;
+ return;
+ }
+
+ NS_ASSERTION(gfxSkipCharsIterator(iter).ConvertOriginalToSkipped(offset + length)
+ <= mTextRun->GetLength(),
+ "Text run does not map enough text for our reflow");
+
+ /////////////////////////////////////////////////////////////////////
+ // See how much text should belong to this text frame, and measure it
+ /////////////////////////////////////////////////////////////////////
+
+ iter.SetOriginalOffset(offset);
+ nscoord xOffsetForTabs = (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) ?
+ (aLineLayout.GetCurrentFrameInlineDistanceFromBlock() -
+ lineContainer->GetUsedBorderAndPadding().left)
+ : -1;
+ PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length,
+ lineContainer, xOffsetForTabs, nsTextFrame::eInflated);
+
+ uint32_t transformedOffset = provider.GetStart().GetSkippedOffset();
+
+ // The metrics for the text go in here
+ gfxTextRun::Metrics textMetrics;
+ gfxFont::BoundingBoxType boundingBoxType =
+ IsFloatingFirstLetterChild() || IsInitialLetterChild()
+ ? gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS
+ : gfxFont::LOOSE_INK_EXTENTS;
+ NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
+ "We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
+
+ int32_t limitLength = length;
+ int32_t forceBreak = aLineLayout.GetForcedBreakPosition(this);
+ bool forceBreakAfter = false;
+ if (forceBreak >= length) {
+ forceBreakAfter = forceBreak == length;
+ // The break is not within the text considered for this textframe.
+ forceBreak = -1;
+ }
+ if (forceBreak >= 0) {
+ limitLength = forceBreak;
+ }
+ // This is the heart of text reflow right here! We don't know where
+ // to break, so we need to see how much text fits in the available width.
+ uint32_t transformedLength;
+ if (offset + limitLength >= int32_t(frag->GetLength())) {
+ NS_ASSERTION(offset + limitLength == int32_t(frag->GetLength()),
+ "Content offset/length out of bounds");
+ NS_ASSERTION(flowEndInTextRun >= transformedOffset,
+ "Negative flow length?");
+ transformedLength = flowEndInTextRun - transformedOffset;
+ } else {
+ // we're not looking at all the content, so we need to compute the
+ // length of the transformed substring we're looking at
+ gfxSkipCharsIterator iter(provider.GetStart());
+ iter.SetOriginalOffset(offset + limitLength);
+ transformedLength = iter.GetSkippedOffset() - transformedOffset;
+ }
+ uint32_t transformedLastBreak = 0;
+ bool usedHyphenation;
+ gfxFloat trimmedWidth = 0;
+ gfxFloat availWidth = aAvailableWidth;
+ if (StyleContext()->IsTextCombined()) {
+ // If text-combine-upright is 'all', we would compress whatever long
+ // text into ~1em width, so there is no limited on the avail width.
+ availWidth = std::numeric_limits<gfxFloat>::infinity();
+ }
+ bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
+ (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML);
+ // allow whitespace to overflow the container
+ bool whitespaceCanHang = textStyle->WhiteSpaceCanWrapStyle() &&
+ textStyle->WhiteSpaceIsSignificant();
+ gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority();
+ gfxTextRun::SuppressBreak suppressBreak = gfxTextRun::eNoSuppressBreak;
+ bool shouldSuppressLineBreak = ShouldSuppressLineBreak();
+ if (shouldSuppressLineBreak) {
+ suppressBreak = gfxTextRun::eSuppressAllBreaks;
+ } else if (!aLineLayout.LineIsBreakable()) {
+ suppressBreak = gfxTextRun::eSuppressInitialBreak;
+ }
+ uint32_t transformedCharsFit =
+ mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
+ (GetStateBits() & TEXT_START_OF_LINE) != 0,
+ availWidth,
+ &provider, suppressBreak,
+ canTrimTrailingWhitespace ? &trimmedWidth : nullptr,
+ whitespaceCanHang,
+ &textMetrics, boundingBoxType,
+ aDrawTarget,
+ &usedHyphenation, &transformedLastBreak,
+ textStyle->WordCanWrap(this), &breakPriority);
+ if (!length && !textMetrics.mAscent && !textMetrics.mDescent) {
+ // If we're measuring a zero-length piece of text, update
+ // the height manually.
+ nsFontMetrics* fm = provider.GetFontMetrics();
+ if (fm) {
+ textMetrics.mAscent = gfxFloat(fm->MaxAscent());
+ textMetrics.mDescent = gfxFloat(fm->MaxDescent());
+ }
+ }
+ if (GetWritingMode().IsLineInverted()) {
+ Swap(textMetrics.mAscent, textMetrics.mDescent);
+ textMetrics.mBoundingBox.y = -textMetrics.mBoundingBox.YMost();
+ }
+ // The "end" iterator points to the first character after the string mapped
+ // by this frame. Basically, its original-string offset is offset+charsFit
+ // after we've computed charsFit.
+ gfxSkipCharsIterator end(provider.GetEndHint());
+ end.SetSkippedOffset(transformedOffset + transformedCharsFit);
+ int32_t charsFit = end.GetOriginalOffset() - offset;
+ if (offset + charsFit == newLineOffset) {
+ // We broke before a trailing preformatted '\n'. The newline should
+ // be assigned to this frame. Note that newLineOffset will be -1 if
+ // there was no preformatted newline, so we wouldn't get here in that
+ // case.
+ ++charsFit;
+ }
+ // That might have taken us beyond our assigned content range (because
+ // we might have advanced over some skipped chars that extend outside
+ // this frame), so get back in.
+ int32_t lastBreak = -1;
+ if (charsFit >= limitLength) {
+ charsFit = limitLength;
+ if (transformedLastBreak != UINT32_MAX) {
+ // lastBreak is needed.
+ // This may set lastBreak greater than 'length', but that's OK
+ lastBreak = end.ConvertSkippedToOriginal(transformedOffset + transformedLastBreak);
+ }
+ end.SetOriginalOffset(offset + charsFit);
+ // If we were forced to fit, and the break position is after a soft hyphen,
+ // note that this is a hyphenation break.
+ if ((forceBreak >= 0 || forceBreakAfter) &&
+ HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
+ usedHyphenation = true;
+ }
+ }
+ if (usedHyphenation) {
+ // Fix up metrics to include hyphen
+ AddHyphenToMetrics(this, mTextRun, &textMetrics, boundingBoxType,
+ aDrawTarget);
+ AddStateBits(TEXT_HYPHEN_BREAK | TEXT_HAS_NONCOLLAPSED_CHARACTERS);
+ }
+ if (textMetrics.mBoundingBox.IsEmpty()) {
+ AddStateBits(TEXT_NO_RENDERED_GLYPHS);
+ }
+
+ gfxFloat trimmableWidth = 0;
+ bool brokeText = forceBreak >= 0 || transformedCharsFit < transformedLength;
+ if (canTrimTrailingWhitespace) {
+ // Optimization: if we trimmed trailing whitespace, and we can be sure
+ // this frame will be at the end of the line, then leave it trimmed off.
+ // Otherwise we have to undo the trimming, in case we're not at the end of
+ // the line. (If we actually do end up at the end of the line, we'll have
+ // to trim it off again in TrimTrailingWhiteSpace, and we'd like to avoid
+ // having to re-do it.)
+ if (brokeText ||
+ (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML)) {
+ // We're definitely going to break so our trailing whitespace should
+ // definitely be trimmed. Record that we've already done it.
+ AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
+ } else if (!(GetStateBits() & TEXT_IS_IN_TOKEN_MATHML)) {
+ // We might not be at the end of the line. (Note that even if this frame
+ // ends in breakable whitespace, it might not be at the end of the line
+ // because it might be followed by breakable, but preformatted, whitespace.)
+ // Undo the trimming.
+ textMetrics.mAdvanceWidth += trimmedWidth;
+ trimmableWidth = trimmedWidth;
+ if (mTextRun->IsRightToLeft()) {
+ // Space comes before text, so the bounding box is moved to the
+ // right by trimmdWidth
+ textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
+ }
+ }
+ }
+
+ if (!brokeText && lastBreak >= 0) {
+ // Since everything fit and no break was forced,
+ // record the last break opportunity
+ NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= availWidth,
+ "If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?");
+ MOZ_ASSERT(lastBreak >= offset, "Strange break position");
+ aLineLayout.NotifyOptionalBreakPosition(this, lastBreak - offset,
+ true, breakPriority);
+ }
+
+ int32_t contentLength = offset + charsFit - GetContentOffset();
+
+ /////////////////////////////////////////////////////////////////////
+ // Compute output metrics
+ /////////////////////////////////////////////////////////////////////
+
+ // first-letter frames should use the tight bounding box metrics for ascent/descent
+ // for good drop-cap effects
+ if (GetStateBits() & TEXT_FIRST_LETTER) {
+ textMetrics.mAscent = std::max(gfxFloat(0.0), -textMetrics.mBoundingBox.Y());
+ textMetrics.mDescent = std::max(gfxFloat(0.0), textMetrics.mBoundingBox.YMost());
+ }
+
+ // Setup metrics for caller
+ // Disallow negative widths
+ WritingMode wm = GetWritingMode();
+ LogicalSize finalSize(wm);
+ finalSize.ISize(wm) = NSToCoordCeil(std::max(gfxFloat(0.0),
+ textMetrics.mAdvanceWidth));
+
+ if (transformedCharsFit == 0 && !usedHyphenation) {
+ aMetrics.SetBlockStartAscent(0);
+ finalSize.BSize(wm) = 0;
+ } else if (boundingBoxType != gfxFont::LOOSE_INK_EXTENTS) {
+ // Use actual text metrics for floating first letter frame.
+ aMetrics.SetBlockStartAscent(NSToCoordCeil(textMetrics.mAscent));
+ finalSize.BSize(wm) = aMetrics.BlockStartAscent() +
+ NSToCoordCeil(textMetrics.mDescent);
+ } else {
+ // Otherwise, ascent should contain the overline drawable area.
+ // And also descent should contain the underline drawable area.
+ // nsFontMetrics::GetMaxAscent/GetMaxDescent contains them.
+ nsFontMetrics* fm = provider.GetFontMetrics();
+ nscoord fontAscent =
+ wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
+ nscoord fontDescent =
+ wm.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
+ aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
+ nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
+ finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
+ }
+ if (StyleContext()->IsTextCombined()) {
+ nsFontMetrics* fm = provider.GetFontMetrics();
+ gfxFloat width = finalSize.ISize(wm);
+ gfxFloat em = fm->EmHeight();
+ // Compress the characters in horizontal axis if necessary.
+ if (width <= em) {
+ Properties().Remove(TextCombineScaleFactorProperty());
+ } else {
+ Properties().Set(TextCombineScaleFactorProperty(), em / width);
+ finalSize.ISize(wm) = em;
+ }
+ // Make the characters be in an 1em square.
+ if (finalSize.BSize(wm) != em) {
+ aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
+ (em - finalSize.BSize(wm)) / 2);
+ finalSize.BSize(wm) = em;
+ }
+ }
+ aMetrics.SetSize(wm, finalSize);
+
+ NS_ASSERTION(aMetrics.BlockStartAscent() >= 0,
+ "Negative ascent???");
+ NS_ASSERTION((StyleContext()->IsTextCombined()
+ ? aMetrics.ISize(aMetrics.GetWritingMode())
+ : aMetrics.BSize(aMetrics.GetWritingMode())) -
+ aMetrics.BlockStartAscent() >= 0,
+ "Negative descent???");
+
+ mAscent = aMetrics.BlockStartAscent();
+
+ // Handle text that runs outside its normal bounds.
+ nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
+ if (mTextRun->IsVertical()) {
+ // Swap line-relative textMetrics dimensions to physical coordinates.
+ Swap(boundingBox.x, boundingBox.y);
+ Swap(boundingBox.width, boundingBox.height);
+ if (GetWritingMode().IsVerticalRL()) {
+ boundingBox.x = -boundingBox.XMost();
+ boundingBox.x += aMetrics.Width() - mAscent;
+ } else {
+ boundingBox.x += mAscent;
+ }
+ } else {
+ boundingBox.y += mAscent;
+ }
+ aMetrics.SetOverflowAreasToDesiredBounds();
+ aMetrics.VisualOverflow().UnionRect(aMetrics.VisualOverflow(), boundingBox);
+
+ // When we have text decorations, we don't need to compute their overflow now
+ // because we're guaranteed to do it later
+ // (see nsLineLayout::RelativePositionFrames)
+ UnionAdditionalOverflow(presContext, aLineLayout.LineContainerRI()->mFrame,
+ provider, &aMetrics.VisualOverflow(), false);
+
+ /////////////////////////////////////////////////////////////////////
+ // Clean up, update state
+ /////////////////////////////////////////////////////////////////////
+
+ // If all our characters are discarded or collapsed, then trimmable width
+ // from the last textframe should be preserved. Otherwise the trimmable width
+ // from this textframe overrides. (Currently in CSS trimmable width can be
+ // at most one space so there's no way for trimmable width from a previous
+ // frame to accumulate with trimmable width from this frame.)
+ if (transformedCharsFit > 0) {
+ aLineLayout.SetTrimmableISize(NSToCoordFloor(trimmableWidth));
+ AddStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
+ }
+ bool breakAfter = forceBreakAfter;
+ if (!shouldSuppressLineBreak) {
+ if (charsFit > 0 && charsFit == length &&
+ textStyle->mHyphens != NS_STYLE_HYPHENS_NONE &&
+ HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
+ bool fits =
+ textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth;
+ // Record a potential break after final soft hyphen
+ aLineLayout.NotifyOptionalBreakPosition(this, length, fits,
+ gfxBreakPriority::eNormalBreak);
+ }
+ // length == 0 means either the text is empty or it's all collapsed away
+ bool emptyTextAtStartOfLine = atStartOfLine && length == 0;
+ if (!breakAfter && charsFit == length && !emptyTextAtStartOfLine &&
+ transformedOffset + transformedLength == mTextRun->GetLength() &&
+ (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK)) {
+ // We placed all the text in the textrun and we have a break opportunity
+ // at the end of the textrun. We need to record it because the following
+ // content may not care about nsLineBreaker.
+
+ // Note that because we didn't break, we can be sure that (thanks to the
+ // code up above) textMetrics.mAdvanceWidth includes the width of any
+ // trailing whitespace. So we need to subtract trimmableWidth here
+ // because if we did break at this point, that much width would be
+ // trimmed.
+ if (textMetrics.mAdvanceWidth - trimmableWidth > availWidth) {
+ breakAfter = true;
+ } else {
+ aLineLayout.NotifyOptionalBreakPosition(this, length, true,
+ gfxBreakPriority::eNormalBreak);
+ }
+ }
+ }
+
+ // Compute reflow status
+ aStatus = contentLength == maxContentLength
+ ? NS_FRAME_COMPLETE : NS_FRAME_NOT_COMPLETE;
+
+ if (charsFit == 0 && length > 0 && !usedHyphenation) {
+ // Couldn't place any text
+ aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ } else if (contentLength > 0 && mContentOffset + contentLength - 1 == newLineOffset) {
+ // Ends in \n
+ aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
+ aLineLayout.SetLineEndsInBR(true);
+ } else if (breakAfter) {
+ aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
+ }
+ if (completedFirstLetter) {
+ aLineLayout.SetFirstLetterStyleOK(false);
+ aStatus |= NS_INLINE_BREAK_FIRST_LETTER_COMPLETE;
+ }
+
+ // Updated the cached NewlineProperty, or delete it.
+ if (contentLength < maxContentLength &&
+ textStyle->NewlineIsSignificant(this) &&
+ (contentNewLineOffset < 0 ||
+ mContentOffset + contentLength <= contentNewLineOffset)) {
+ if (!cachedNewlineOffset) {
+ cachedNewlineOffset = new NewlineProperty;
+ if (NS_FAILED(mContent->SetProperty(nsGkAtoms::newline, cachedNewlineOffset,
+ nsINode::DeleteProperty<NewlineProperty>))) {
+ delete cachedNewlineOffset;
+ cachedNewlineOffset = nullptr;
+ }
+ }
+ if (cachedNewlineOffset) {
+ cachedNewlineOffset->mStartOffset = offset;
+ cachedNewlineOffset->mNewlineOffset = contentNewLineOffset;
+ }
+ } else if (cachedNewlineOffset) {
+ mContent->DeleteProperty(nsGkAtoms::newline);
+ }
+
+ // Compute space and letter counts for justification, if required
+ if (!textStyle->WhiteSpaceIsSignificant() &&
+ (lineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
+ lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
+ shouldSuppressLineBreak) &&
+ !lineContainer->IsSVGText()) {
+ AddStateBits(TEXT_JUSTIFICATION_ENABLED);
+ Range range(uint32_t(offset), uint32_t(offset + charsFit));
+ aLineLayout.SetJustificationInfo(provider.ComputeJustification(range));
+ }
+
+ SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
+
+ InvalidateFrame();
+
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": desiredSize=%d,%d(b=%d) status=%x\n",
+ aMetrics.Width(), aMetrics.Height(), aMetrics.BlockStartAscent(),
+ aStatus);
+#endif
+}
+
+/* virtual */ bool
+nsTextFrame::CanContinueTextRun() const
+{
+ // We can continue a text run through a text frame
+ return true;
+}
+
+nsTextFrame::TrimOutput
+nsTextFrame::TrimTrailingWhiteSpace(DrawTarget* aDrawTarget)
+{
+ TrimOutput result;
+ result.mChanged = false;
+ result.mDeltaWidth = 0;
+
+ AddStateBits(TEXT_END_OF_LINE);
+
+ int32_t contentLength = GetContentLength();
+ if (!contentLength)
+ return result;
+
+ gfxSkipCharsIterator start =
+ EnsureTextRun(nsTextFrame::eInflated, aDrawTarget);
+ NS_ENSURE_TRUE(mTextRun, result);
+
+ uint32_t trimmedStart = start.GetSkippedOffset();
+
+ const nsTextFragment* frag = mContent->GetText();
+ TrimmedOffsets trimmed = GetTrimmedOffsets(frag, true);
+ gfxSkipCharsIterator trimmedEndIter = start;
+ const nsStyleText* textStyle = StyleText();
+ gfxFloat delta = 0;
+ uint32_t trimmedEnd = trimmedEndIter.ConvertOriginalToSkipped(trimmed.GetEnd());
+
+ if (!(GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) &&
+ trimmed.GetEnd() < GetContentEnd()) {
+ gfxSkipCharsIterator end = trimmedEndIter;
+ uint32_t endOffset = end.ConvertOriginalToSkipped(GetContentOffset() + contentLength);
+ if (trimmedEnd < endOffset) {
+ // We can't be dealing with tabs here ... they wouldn't be trimmed. So it's
+ // OK to pass null for the line container.
+ PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength,
+ nullptr, 0, nsTextFrame::eInflated);
+ delta = mTextRun->
+ GetAdvanceWidth(Range(trimmedEnd, endOffset), &provider);
+ result.mChanged = true;
+ }
+ }
+
+ gfxFloat advanceDelta;
+ mTextRun->SetLineBreaks(Range(trimmedStart, trimmedEnd),
+ (GetStateBits() & TEXT_START_OF_LINE) != 0, true,
+ &advanceDelta);
+ if (advanceDelta != 0) {
+ result.mChanged = true;
+ }
+
+ // aDeltaWidth is *subtracted* from our width.
+ // If advanceDelta is positive then setting the line break made us longer,
+ // so aDeltaWidth could go negative.
+ result.mDeltaWidth = NSToCoordFloor(delta - advanceDelta);
+ // If aDeltaWidth goes negative, that means this frame might not actually fit
+ // anymore!!! We need higher level line layout to recover somehow.
+ // If it's because the frame has a soft hyphen that is now being displayed,
+ // this should actually be OK, because our reflow recorded the break
+ // opportunity that allowed the soft hyphen to be used, and we wouldn't
+ // have recorded the opportunity unless the hyphen fit (or was the first
+ // opportunity on the line).
+ // Otherwise this can/ really only happen when we have glyphs with special
+ // shapes at the end of lines, I think. Breaking inside a kerning pair won't
+ // do it because that would mean we broke inside this textrun, and
+ // BreakAndMeasureText should make sure the resulting shaped substring fits.
+ // Maybe if we passed a maxTextLength? But that only happens at direction
+ // changes (so we wouldn't kern across the boundary) or for first-letter
+ // (which always fits because it starts the line!).
+ NS_WARNING_ASSERTION(result.mDeltaWidth >= 0,
+ "Negative deltawidth, something odd is happening");
+
+#ifdef NOISY_TRIM
+ ListTag(stdout);
+ printf(": trim => %d\n", result.mDeltaWidth);
+#endif
+ return result;
+}
+
+nsOverflowAreas
+nsTextFrame::RecomputeOverflow(nsIFrame* aBlockFrame)
+{
+ nsRect bounds(nsPoint(0, 0), GetSize());
+ nsOverflowAreas result(bounds, bounds);
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ if (!mTextRun)
+ return result;
+
+ PropertyProvider provider(this, iter, nsTextFrame::eInflated);
+ // Don't trim trailing space, in case we need to paint it as selected.
+ provider.InitializeForDisplay(false);
+
+ gfxTextRun::Metrics textMetrics =
+ mTextRun->MeasureText(ComputeTransformedRange(provider),
+ gfxFont::LOOSE_INK_EXTENTS, nullptr,
+ &provider);
+ if (GetWritingMode().IsLineInverted()) {
+ textMetrics.mBoundingBox.y = -textMetrics.mBoundingBox.YMost();
+ }
+ nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
+ boundingBox += nsPoint(0, mAscent);
+ if (mTextRun->IsVertical()) {
+ // Swap line-relative textMetrics dimensions to physical coordinates.
+ Swap(boundingBox.x, boundingBox.y);
+ Swap(boundingBox.width, boundingBox.height);
+ }
+ nsRect &vis = result.VisualOverflow();
+ vis.UnionRect(vis, boundingBox);
+ UnionAdditionalOverflow(PresContext(), aBlockFrame, provider, &vis, true);
+ return result;
+}
+
+static void TransformChars(nsTextFrame* aFrame, const nsStyleText* aStyle,
+ const gfxTextRun* aTextRun, uint32_t aSkippedOffset,
+ const nsTextFragment* aFrag, int32_t aFragOffset,
+ int32_t aFragLen, nsAString& aOut)
+{
+ nsAutoString fragString;
+ char16_t* out;
+ if (aStyle->mTextTransform == NS_STYLE_TEXT_TRANSFORM_NONE) {
+ // No text-transform, so we can copy directly to the output string.
+ aOut.SetLength(aOut.Length() + aFragLen);
+ out = aOut.EndWriting() - aFragLen;
+ } else {
+ // Use a temporary string as source for the transform.
+ fragString.SetLength(aFragLen);
+ out = fragString.BeginWriting();
+ }
+
+ // Copy the text, with \n and \t replaced by <space> if appropriate.
+ for (int32_t i = 0; i < aFragLen; ++i) {
+ char16_t ch = aFrag->CharAt(aFragOffset + i);
+ if ((ch == '\n' && !aStyle->NewlineIsSignificant(aFrame)) ||
+ (ch == '\t' && !aStyle->TabIsSignificant())) {
+ ch = ' ';
+ }
+ out[i] = ch;
+ }
+
+ if (aStyle->mTextTransform != NS_STYLE_TEXT_TRANSFORM_NONE) {
+ MOZ_ASSERT(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED);
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
+ // Apply text-transform according to style in the transformed run.
+ auto transformedTextRun =
+ static_cast<const nsTransformedTextRun*>(aTextRun);
+ nsAutoString convertedString;
+ AutoTArray<bool,50> charsToMergeArray;
+ AutoTArray<bool,50> deletedCharsArray;
+ nsCaseTransformTextRunFactory::TransformString(fragString,
+ convertedString,
+ false, nullptr,
+ charsToMergeArray,
+ deletedCharsArray,
+ transformedTextRun,
+ aSkippedOffset);
+ aOut.Append(convertedString);
+ } else {
+ // Should not happen (see assertion above), but as a fallback...
+ aOut.Append(fragString);
+ }
+ }
+}
+
+static bool
+LineEndsInHardLineBreak(nsTextFrame* aFrame, nsBlockFrame* aLineContainer)
+{
+ bool foundValidLine;
+ nsBlockInFlowLineIterator iter(aLineContainer, aFrame, &foundValidLine);
+ if (!foundValidLine) {
+ NS_ERROR("Invalid line!");
+ return true;
+ }
+ return !iter.GetLine()->IsLineWrapped();
+}
+
+nsIFrame::RenderedText
+nsTextFrame::GetRenderedText(uint32_t aStartOffset,
+ uint32_t aEndOffset,
+ TextOffsetType aOffsetType,
+ TrailingWhitespace aTrimTrailingWhitespace)
+{
+ NS_ASSERTION(!GetPrevContinuation(), "Must be called on first-in-flow");
+
+ // The handling of offsets could be more efficient...
+ RenderedText result;
+ nsBlockFrame* lineContainer = nullptr;
+ nsTextFrame* textFrame;
+ const nsTextFragment* textFrag = mContent->GetText();
+ uint32_t offsetInRenderedString = 0;
+ bool haveOffsets = false;
+
+ Maybe<nsBlockFrame::AutoLineCursorSetup> autoLineCursor;
+ for (textFrame = this; textFrame;
+ textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) {
+ if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
+ // We don't trust dirty frames, especially when computing rendered text.
+ break;
+ }
+
+ // Ensure the text run and grab the gfxSkipCharsIterator for it
+ gfxSkipCharsIterator iter =
+ textFrame->EnsureTextRun(nsTextFrame::eInflated);
+ if (!textFrame->mTextRun) {
+ break;
+ }
+ gfxSkipCharsIterator tmpIter = iter;
+
+ // Whether we need to trim whitespaces after the text frame.
+ bool trimAfter;
+ if (!textFrame->IsAtEndOfLine() ||
+ aTrimTrailingWhitespace !=
+ TrailingWhitespace::TRIM_TRAILING_WHITESPACE) {
+ trimAfter = false;
+ } else if (nsBlockFrame* thisLc =
+ do_QueryFrame(FindLineContainer(textFrame))) {
+ if (thisLc != lineContainer) {
+ // Setup line cursor when needed.
+ lineContainer = thisLc;
+ autoLineCursor.reset();
+ autoLineCursor.emplace(lineContainer);
+ }
+ trimAfter = LineEndsInHardLineBreak(textFrame, lineContainer);
+ } else {
+ // Weird situation where we have a line layout without a block.
+ // No soft breaks occur in this situation.
+ trimAfter = true;
+ }
+
+ // Skip to the start of the text run, past ignored chars at start of line
+ TrimmedOffsets trimmedOffsets =
+ textFrame->GetTrimmedOffsets(textFrag, trimAfter);
+ bool trimmedSignificantNewline =
+ trimmedOffsets.GetEnd() < GetContentEnd() &&
+ HasSignificantTerminalNewline();
+ uint32_t skippedToRenderedStringOffset = offsetInRenderedString -
+ tmpIter.ConvertOriginalToSkipped(trimmedOffsets.mStart);
+ uint32_t nextOffsetInRenderedString =
+ tmpIter.ConvertOriginalToSkipped(trimmedOffsets.GetEnd()) +
+ (trimmedSignificantNewline ? 1 : 0) + skippedToRenderedStringOffset;
+
+ if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) {
+ if (nextOffsetInRenderedString <= aStartOffset) {
+ offsetInRenderedString = nextOffsetInRenderedString;
+ continue;
+ }
+ if (!haveOffsets) {
+ result.mOffsetWithinNodeText =
+ tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset);
+ result.mOffsetWithinNodeRenderedText = aStartOffset;
+ haveOffsets = true;
+ }
+ if (offsetInRenderedString >= aEndOffset) {
+ break;
+ }
+ } else {
+ if (uint32_t(textFrame->GetContentEnd()) <= aStartOffset) {
+ offsetInRenderedString = nextOffsetInRenderedString;
+ continue;
+ }
+ if (!haveOffsets) {
+ result.mOffsetWithinNodeText = aStartOffset;
+ // Skip trimmed space when computed the rendered text offset.
+ int32_t clamped = std::max<int32_t>(aStartOffset, trimmedOffsets.mStart);
+ result.mOffsetWithinNodeRenderedText =
+ tmpIter.ConvertOriginalToSkipped(clamped) + skippedToRenderedStringOffset;
+ MOZ_ASSERT(result.mOffsetWithinNodeRenderedText >= offsetInRenderedString &&
+ result.mOffsetWithinNodeRenderedText <= INT32_MAX,
+ "Bad offset within rendered text");
+ haveOffsets = true;
+ }
+ if (uint32_t(textFrame->mContentOffset) >= aEndOffset) {
+ break;
+ }
+ }
+
+ int32_t startOffset;
+ int32_t endOffset;
+ if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) {
+ startOffset =
+ tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset);
+ endOffset =
+ tmpIter.ConvertSkippedToOriginal(aEndOffset - skippedToRenderedStringOffset);
+ } else {
+ startOffset = aStartOffset;
+ endOffset = std::min<uint32_t>(INT32_MAX, aEndOffset);
+ }
+ trimmedOffsets.mStart = std::max<uint32_t>(trimmedOffsets.mStart,
+ startOffset);
+ trimmedOffsets.mLength = std::min<uint32_t>(trimmedOffsets.GetEnd(),
+ endOffset) - trimmedOffsets.mStart;
+ if (trimmedOffsets.mLength <= 0) {
+ offsetInRenderedString = nextOffsetInRenderedString;
+ continue;
+ }
+
+ const nsStyleText* textStyle = textFrame->StyleText();
+ iter.SetOriginalOffset(trimmedOffsets.mStart);
+ while (iter.GetOriginalOffset() < trimmedOffsets.GetEnd()) {
+ int32_t runLength;
+ bool isSkipped = iter.IsOriginalCharSkipped(&runLength);
+ runLength = std::min(runLength,
+ trimmedOffsets.GetEnd() - iter.GetOriginalOffset());
+ if (isSkipped) {
+ for (int32_t i = 0; i < runLength; ++i) {
+ char16_t ch = textFrag->CharAt(iter.GetOriginalOffset() + i);
+ if (ch == CH_SHY) {
+ // We should preserve soft hyphens. They can't be transformed.
+ result.mString.Append(ch);
+ }
+ }
+ } else {
+ TransformChars(textFrame, textStyle, textFrame->mTextRun,
+ iter.GetSkippedOffset(), textFrag,
+ iter.GetOriginalOffset(), runLength, result.mString);
+ }
+ iter.AdvanceOriginal(runLength);
+ }
+
+ if (trimmedSignificantNewline && GetContentEnd() <= endOffset) {
+ // A significant newline was trimmed off (we must be
+ // white-space:pre-line). Put it back.
+ result.mString.Append('\n');
+ }
+ offsetInRenderedString = nextOffsetInRenderedString;
+ }
+
+ if (!haveOffsets) {
+ result.mOffsetWithinNodeText = textFrag->GetLength();
+ result.mOffsetWithinNodeRenderedText = offsetInRenderedString;
+ }
+ return result;
+}
+
+nsIAtom*
+nsTextFrame::GetType() const
+{
+ return nsGkAtoms::textFrame;
+}
+
+/* virtual */ bool
+nsTextFrame::IsEmpty()
+{
+ NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
+ !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
+ "Invalid state");
+
+ // XXXldb Should this check compatibility mode as well???
+ const nsStyleText* textStyle = StyleText();
+ if (textStyle->WhiteSpaceIsSignificant()) {
+ // XXX shouldn't we return true if the length is zero?
+ return false;
+ }
+
+ if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
+ return false;
+ }
+
+ if (mState & TEXT_IS_ONLY_WHITESPACE) {
+ return true;
+ }
+
+ bool isEmpty =
+ IsAllWhitespace(mContent->GetText(),
+ textStyle->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_LINE);
+ mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
+ return isEmpty;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+// Translate the mapped content into a string that's printable
+void
+nsTextFrame::ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const
+{
+ // Get the frames text content
+ const nsTextFragment* frag = mContent->GetText();
+ if (!frag) {
+ return;
+ }
+
+ // Compute the total length of the text content.
+ *aTotalContentLength = frag->GetLength();
+
+ int32_t contentLength = GetContentLength();
+ // Set current fragment and current fragment offset
+ if (0 == contentLength) {
+ return;
+ }
+ int32_t fragOffset = GetContentOffset();
+ int32_t n = fragOffset + contentLength;
+ while (fragOffset < n) {
+ char16_t ch = frag->CharAt(fragOffset++);
+ if (ch == '\r') {
+ aBuf.AppendLiteral("\\r");
+ } else if (ch == '\n') {
+ aBuf.AppendLiteral("\\n");
+ } else if (ch == '\t') {
+ aBuf.AppendLiteral("\\t");
+ } else if ((ch < ' ') || (ch >= 127)) {
+ aBuf.Append(nsPrintfCString("\\u%04x", ch));
+ } else {
+ aBuf.Append(ch);
+ }
+ }
+}
+
+nsresult
+nsTextFrame::GetFrameName(nsAString& aResult) const
+{
+ MakeFrameName(NS_LITERAL_STRING("Text"), aResult);
+ int32_t totalContentLength;
+ nsAutoCString tmp;
+ ToCString(tmp, &totalContentLength);
+ tmp.SetLength(std::min(tmp.Length(), 50u));
+ aResult += NS_LITERAL_STRING("\"") + NS_ConvertASCIItoUTF16(tmp) + NS_LITERAL_STRING("\"");
+ return NS_OK;
+}
+
+void
+nsTextFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+
+ str += nsPrintfCString(" [run=%p]", static_cast<void*>(mTextRun));
+
+ // Output the first/last content offset and prev/next in flow info
+ bool isComplete = uint32_t(GetContentEnd()) == GetContent()->TextLength();
+ str += nsPrintfCString("[%d,%d,%c] ", GetContentOffset(), GetContentLength(),
+ isComplete ? 'T':'F');
+
+ if (IsSelected()) {
+ str += " SELECTED";
+ }
+ fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+#ifdef DEBUG
+nsFrameState
+nsTextFrame::GetDebugStateBits() const
+{
+ // mask out our emptystate flags; those are just caches
+ return nsFrame::GetDebugStateBits() &
+ ~(TEXT_WHITESPACE_FLAGS | TEXT_REFLOW_FLAGS);
+}
+#endif
+
+void
+nsTextFrame::AdjustOffsetsForBidi(int32_t aStart, int32_t aEnd)
+{
+ AddStateBits(NS_FRAME_IS_BIDI);
+ mContent->DeleteProperty(nsGkAtoms::flowlength);
+
+ /*
+ * After Bidi resolution we may need to reassign text runs.
+ * This is called during bidi resolution from the block container, so we
+ * shouldn't be holding a local reference to a textrun anywhere.
+ */
+ ClearTextRuns();
+
+ nsTextFrame* prev = static_cast<nsTextFrame*>(GetPrevContinuation());
+ if (prev) {
+ // the bidi resolver can be very evil when columns/pages are involved. Don't
+ // let it violate our invariants.
+ int32_t prevOffset = prev->GetContentOffset();
+ aStart = std::max(aStart, prevOffset);
+ aEnd = std::max(aEnd, prevOffset);
+ prev->ClearTextRuns();
+ }
+
+ mContentOffset = aStart;
+ SetLength(aEnd - aStart, nullptr, 0);
+}
+
+/**
+ * @return true if this text frame ends with a newline character. It should return
+ * false if it is not a text frame.
+ */
+bool
+nsTextFrame::HasSignificantTerminalNewline() const
+{
+ return ::HasTerminalNewline(this) && StyleText()->NewlineIsSignificant(this);
+}
+
+bool
+nsTextFrame::IsAtEndOfLine() const
+{
+ return (GetStateBits() & TEXT_END_OF_LINE) != 0;
+}
+
+nscoord
+nsTextFrame::GetLogicalBaseline(WritingMode aWM) const
+{
+ if (!aWM.IsOrthogonalTo(GetWritingMode())) {
+ return mAscent;
+ }
+
+ // When the text frame has a writing mode orthogonal to the desired
+ // writing mode, return a baseline coincides its parent frame.
+ nsIFrame* parent = GetParent();
+ nsPoint position = GetNormalPosition();
+ nscoord parentAscent = parent->GetLogicalBaseline(aWM);
+ if (aWM.IsVerticalRL()) {
+ nscoord parentDescent = parent->GetSize().width - parentAscent;
+ nscoord descent = parentDescent - position.x;
+ return GetSize().width - descent;
+ }
+ return parentAscent - (aWM.IsVertical() ? position.x : position.y);
+}
+
+bool
+nsTextFrame::HasAnyNoncollapsedCharacters()
+{
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ int32_t offset = GetContentOffset(),
+ offsetEnd = GetContentEnd();
+ int32_t skippedOffset = iter.ConvertOriginalToSkipped(offset);
+ int32_t skippedOffsetEnd = iter.ConvertOriginalToSkipped(offsetEnd);
+ return skippedOffset != skippedOffsetEnd;
+}
+
+bool
+nsTextFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+ return true;
+ }
+
+ nsIFrame* decorationsBlock;
+ if (IsFloatingFirstLetterChild()) {
+ decorationsBlock = GetParent();
+ } else {
+ nsIFrame* f = this;
+ for (;;) {
+ nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
+ if (fBlock) {
+ decorationsBlock = fBlock;
+ break;
+ }
+
+ f = f->GetParent();
+ if (!f) {
+ NS_ERROR("Couldn't find any block ancestor (for text decorations)");
+ return nsFrame::ComputeCustomOverflow(aOverflowAreas);
+ }
+ }
+ }
+
+ aOverflowAreas = RecomputeOverflow(decorationsBlock);
+ return nsFrame::ComputeCustomOverflow(aOverflowAreas);
+}
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(JustificationAssignmentProperty, int32_t)
+
+void
+nsTextFrame::AssignJustificationGaps(
+ const mozilla::JustificationAssignment& aAssign)
+{
+ int32_t encoded = (aAssign.mGapsAtStart << 8) | aAssign.mGapsAtEnd;
+ static_assert(sizeof(aAssign) == 1,
+ "The encoding might be broken if JustificationAssignment "
+ "is larger than 1 byte");
+ Properties().Set(JustificationAssignmentProperty(), encoded);
+}
+
+mozilla::JustificationAssignment
+nsTextFrame::GetJustificationAssignment() const
+{
+ int32_t encoded = Properties().Get(JustificationAssignmentProperty());
+ mozilla::JustificationAssignment result;
+ result.mGapsAtStart = encoded >> 8;
+ result.mGapsAtEnd = encoded & 0xFF;
+ return result;
+}
+
+uint32_t
+nsTextFrame::CountGraphemeClusters() const
+{
+ const nsTextFragment* frag = GetContent()->GetText();
+ MOZ_ASSERT(frag, "Text frame must have text fragment");
+ nsAutoString content;
+ frag->AppendTo(content, GetContentOffset(), GetContentLength());
+ return unicode::CountGraphemeClusters(content.Data(), content.Length());
+}
diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h
new file mode 100644
index 000000000..425dbb737
--- /dev/null
+++ b/layout/generic/nsTextFrame.h
@@ -0,0 +1,840 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTextFrame_h__
+#define nsTextFrame_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/gfx/2D.h"
+#include "nsFrame.h"
+#include "nsSplittableFrame.h"
+#include "nsLineBox.h"
+#include "gfxSkipChars.h"
+#include "gfxTextRun.h"
+#include "nsDisplayList.h"
+#include "JustificationUtils.h"
+#include "RubyUtils.h"
+
+// Undo the windows.h damage
+#if defined(XP_WIN) && defined(DrawText)
+#undef DrawText
+#endif
+
+class nsTextPaintStyle;
+class PropertyProvider;
+struct SelectionDetails;
+class nsTextFragment;
+
+class nsDisplayTextGeometry;
+class nsDisplayText;
+
+namespace mozilla {
+class SVGContextPaint;
+};
+
+class nsTextFrame : public nsFrame {
+ typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
+ typedef mozilla::RawSelectionType RawSelectionType;
+ typedef mozilla::SelectionType SelectionType;
+ typedef mozilla::TextRangeStyle TextRangeStyle;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::Point Point;
+ typedef mozilla::gfx::Rect Rect;
+ typedef mozilla::gfx::Size Size;
+ typedef gfxTextRun::Range Range;
+
+public:
+ NS_DECL_QUERYFRAME_TARGET(nsTextFrame)
+ NS_DECL_FRAMEARENA_HELPERS
+
+ friend class nsContinuingTextFrame;
+ friend class nsDisplayTextGeometry;
+ friend class nsDisplayText;
+
+ explicit nsTextFrame(nsStyleContext* aContext)
+ : nsFrame(aContext)
+ {
+ NS_ASSERTION(mContentOffset == 0, "Bogus content offset");
+ }
+
+ // nsQueryFrame
+ NS_DECL_QUERYFRAME
+
+ // nsIFrame
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
+ virtual nsresult GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor) override;
+
+ virtual nsresult CharacterDataChanged(CharacterDataChangeInfo* aInfo) override;
+
+ virtual nsIFrame* GetNextContinuation() const override {
+ return mNextContinuation;
+ }
+ virtual void SetNextContinuation(nsIFrame* aNextContinuation) override {
+ NS_ASSERTION (!aNextContinuation || GetType() == aNextContinuation->GetType(),
+ "setting a next continuation with incorrect type!");
+ NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this),
+ "creating a loop in continuation chain!");
+ mNextContinuation = aNextContinuation;
+ if (aNextContinuation)
+ aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+ // Setting a non-fluid continuation might affect our flow length (they're
+ // quite rare so we assume it always does) so we delete our cached value:
+ GetContent()->DeleteProperty(nsGkAtoms::flowlength);
+ }
+ virtual nsIFrame* GetNextInFlowVirtual() const override { return GetNextInFlow(); }
+ nsIFrame* GetNextInFlow() const {
+ return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
+ mNextContinuation : nullptr;
+ }
+ virtual void SetNextInFlow(nsIFrame* aNextInFlow) override {
+ NS_ASSERTION (!aNextInFlow || GetType() == aNextInFlow->GetType(),
+ "setting a next in flow with incorrect type!");
+ NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this),
+ "creating a loop in continuation chain!");
+ mNextContinuation = aNextInFlow;
+ if (mNextContinuation &&
+ !mNextContinuation->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) {
+ // Changing from non-fluid to fluid continuation might affect our flow
+ // length, so we delete our cached value:
+ GetContent()->DeleteProperty(nsGkAtoms::flowlength);
+ }
+ if (aNextInFlow) {
+ aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+ }
+ }
+ virtual nsIFrame* LastInFlow() const override;
+ virtual nsIFrame* LastContinuation() const override;
+
+ virtual nsSplittableType GetSplittableType() const override {
+ return NS_FRAME_SPLITTABLE;
+ }
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::textFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ // Set the frame state bit for text frames to mark them as replaced.
+ // XXX kipp: temporary
+ return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
+ nsIFrame::eLineParticipant));
+ }
+
+ bool ShouldSuppressLineBreak() const
+ {
+ // If the parent frame of the text frame is ruby content box, it must
+ // suppress line break inside. This check is necessary, because when
+ // a whitespace is only contained by pseudo ruby frames, its style
+ // context won't have SuppressLineBreak bit set.
+ if (mozilla::RubyUtils::IsRubyContentBox(GetParent()->GetType())) {
+ return true;
+ }
+ return StyleContext()->ShouldSuppressLineBreak();
+ }
+
+ virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
+ virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+ void ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const;
+#endif
+
+#ifdef DEBUG
+ virtual nsFrameState GetDebugStateBits() const override;
+#endif
+
+ virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint) override;
+ ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint &aPoint);
+
+ /**
+ * This is called only on the primary text frame. It indicates that
+ * the selection state of the given character range has changed.
+ * Text in the range is unconditionally invalidated
+ * (Selection::Repaint depends on this).
+ * @param aSelected true if the selection has been added to the range,
+ * false otherwise
+ * @param aType the type of selection added or removed
+ */
+ void SetSelectedRange(uint32_t aStart, uint32_t aEnd, bool aSelected,
+ SelectionType aSelectionType);
+
+ virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override;
+ virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
+ bool aRespectClusters = true) override;
+ virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
+ int32_t* aOffset, PeekWordState* aState) override;
+
+ virtual nsresult CheckVisibility(nsPresContext* aContext, int32_t aStartIndex, int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *_retval) override;
+
+ // Flags for aSetLengthFlags
+ enum { ALLOW_FRAME_CREATION_AND_DESTRUCTION = 0x01 };
+
+ // Update offsets to account for new length. This may clear mTextRun.
+ void SetLength(int32_t aLength, nsLineLayout* aLineLayout,
+ uint32_t aSetLengthFlags = 0);
+
+ virtual nsresult GetOffsets(int32_t &start, int32_t &end)const override;
+
+ virtual void AdjustOffsetsForBidi(int32_t start, int32_t end) override;
+
+ virtual nsresult GetPointFromOffset(int32_t inOffset,
+ nsPoint* outPoint) override;
+ virtual nsresult GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aRects) override;
+
+ virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
+ bool inHint,
+ int32_t* outFrameContentOffset,
+ nsIFrame** outChildFrame) override;
+
+ virtual bool IsVisibleInSelection(nsISelection* aSelection) override;
+
+ virtual bool IsEmpty() override;
+ virtual bool IsSelfEmpty() override { return IsEmpty(); }
+ nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const final;
+
+ virtual bool HasSignificantTerminalNewline() const override;
+
+ /**
+ * Returns true if this text frame is logically adjacent to the end of the
+ * line.
+ */
+ bool IsAtEndOfLine() const;
+
+ /**
+ * Call this only after reflow the frame. Returns true if non-collapsed
+ * characters are present.
+ */
+ bool HasNoncollapsedCharacters() const {
+ return (GetStateBits() & TEXT_HAS_NONCOLLAPSED_CHARACTERS) != 0;
+ }
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ float GetFontSizeInflation() const;
+ bool IsCurrentFontInflation(float aInflation) const;
+ bool HasFontSizeInflation() const {
+ return (GetStateBits() & TEXT_HAS_FONT_INFLATION) != 0;
+ }
+ void SetFontSizeInflation(float aInflation);
+
+ virtual void MarkIntrinsicISizesDirty() override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+ InlineMinISizeData *aData) override;
+ virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData) override;
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+ virtual nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
+ virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+ nscoord* aX,
+ nscoord* aXMost) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+ virtual bool CanContinueTextRun() const override;
+ // Method that is called for a text frame that is logically
+ // adjacent to the end of the line (i.e. followed only by empty text frames,
+ // placeholders or inlines containing such).
+ struct TrimOutput {
+ // true if we trimmed some space or changed metrics in some other way.
+ // In this case, we should call RecomputeOverflow on this frame.
+ bool mChanged;
+ // an amount to *subtract* from the frame's width (zero if !mChanged)
+ nscoord mDeltaWidth;
+ };
+ TrimOutput TrimTrailingWhiteSpace(DrawTarget* aDrawTarget);
+ virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0,
+ uint32_t aEndOffset = UINT32_MAX,
+ TextOffsetType aOffsetType =
+ TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+ TrailingWhitespace aTrimTrailingWhitespace =
+ TrailingWhitespace::TRIM_TRAILING_WHITESPACE) override;
+
+ nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame);
+
+ enum TextRunType {
+ // Anything in reflow (but not intrinsic width calculation) or
+ // painting should use the inflated text run (i.e., with font size
+ // inflation applied).
+ eInflated,
+ // Intrinsic width calculation should use the non-inflated text run.
+ // When there is font size inflation, it will be different.
+ eNotInflated
+ };
+
+ void AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext,
+ nsIFrame::InlineMinISizeData *aData,
+ TextRunType aTextRunType);
+ void AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext,
+ InlinePrefISizeData *aData,
+ TextRunType aTextRunType);
+
+ /**
+ * Calculate the horizontal bounds of the grapheme clusters that fit entirely
+ * inside the given left[top]/right[bottom] edges (which are positive lengths
+ * from the respective frame edge). If an input value is zero it is ignored
+ * and the result for that edge is zero. All out parameter values are
+ * undefined when the method returns false.
+ * @return true if at least one whole grapheme cluster fit between the edges
+ */
+ bool MeasureCharClippedText(nscoord aVisIStartEdge, nscoord aVisIEndEdge,
+ nscoord* aSnappedStartEdge,
+ nscoord* aSnappedEndEdge);
+ /**
+ * Same as above; this method also the returns the corresponding text run
+ * offset and number of characters that fit. All out parameter values are
+ * undefined when the method returns false.
+ * @return true if at least one whole grapheme cluster fit between the edges
+ */
+ bool MeasureCharClippedText(PropertyProvider& aProvider,
+ nscoord aVisIStartEdge, nscoord aVisIEndEdge,
+ uint32_t* aStartOffset, uint32_t* aMaxLength,
+ nscoord* aSnappedStartEdge,
+ nscoord* aSnappedEndEdge);
+
+ /**
+ * Object with various callbacks for PaintText() to invoke for different parts
+ * of the frame's text rendering, when we're generating paths rather than
+ * painting.
+ *
+ * Callbacks are invoked in the following order:
+ *
+ * NotifySelectionBackgroundNeedsFill?
+ * PaintDecorationLine*
+ * NotifyBeforeText
+ * NotifyGlyphPathEmitted*
+ * NotifyAfterText
+ * PaintDecorationLine*
+ * PaintSelectionDecorationLine*
+ *
+ * The color of each part of the frame's text rendering is passed as an argument
+ * to the NotifyBefore* callback for that part. The nscolor can take on one of
+ * the three selection special colors defined in LookAndFeel.h --
+ * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and
+ * NS_40PERCENT_FOREGROUND_COLOR.
+ */
+ struct DrawPathCallbacks : gfxTextRunDrawCallbacks
+ {
+ /**
+ * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
+ */
+ explicit DrawPathCallbacks(bool aShouldPaintSVGGlyphs = false)
+ : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs)
+ {
+ }
+
+ /**
+ * Called to have the selection highlight drawn before the text is drawn
+ * over the top.
+ */
+ virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
+ nscolor aColor,
+ DrawTarget& aDrawTarget) { }
+
+ /**
+ * Called before (for under/over-line) or after (for line-through) the text
+ * is drawn to have a text decoration line drawn.
+ */
+ virtual void PaintDecorationLine(Rect aPath, nscolor aColor) { }
+
+ /**
+ * Called after selected text is drawn to have a decoration line drawn over
+ * the text. (All types of text decoration are drawn after the text when
+ * text is selected.)
+ */
+ virtual void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) { }
+
+ /**
+ * Called just before any paths have been emitted to the gfxContext
+ * for the glyphs of the frame's text.
+ */
+ virtual void NotifyBeforeText(nscolor aColor) { }
+
+ /**
+ * Called just after all the paths have been emitted to the gfxContext
+ * for the glyphs of the frame's text.
+ */
+ virtual void NotifyAfterText() { }
+
+ /**
+ * Called just before a path corresponding to a selection decoration line
+ * has been emitted to the gfxContext.
+ */
+ virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor) { }
+
+ /**
+ * Called just after a path corresponding to a selection decoration line
+ * has been emitted to the gfxContext.
+ */
+ virtual void NotifySelectionDecorationLinePathEmitted() { }
+ };
+
+ struct PaintTextParams
+ {
+ gfxContext* context;
+ gfxPoint framePt;
+ LayoutDeviceRect dirtyRect;
+ mozilla::SVGContextPaint* contextPaint = nullptr;
+ DrawPathCallbacks* callbacks = nullptr;
+ enum {
+ PaintText, // Normal text painting.
+ PaintTextBGColor, // Only paint background color of the selected text
+ // range in this state.
+ GenerateTextMask // To generate a mask from a text frame. Should
+ // only paint text itself with opaque color.
+ // Text shadow, text selection color and text
+ // decoration are all discarded in this state.
+ };
+ uint8_t state = PaintText;
+ explicit PaintTextParams(gfxContext* aContext) : context(aContext) {}
+
+ bool IsPaintText() const { return state == PaintText; }
+ bool IsGenerateTextMask() const { return state == GenerateTextMask; }
+ bool IsPaintBGColor() const { return state == PaintTextBGColor; }
+ };
+
+ struct PaintTextSelectionParams : PaintTextParams
+ {
+ gfxPoint textBaselinePt;
+ PropertyProvider* provider = nullptr;
+ Range contentRange;
+ nsTextPaintStyle* textPaintStyle = nullptr;
+ explicit PaintTextSelectionParams(const PaintTextParams& aParams)
+ : PaintTextParams(aParams) {}
+ };
+
+ struct DrawTextRunParams
+ {
+ gfxContext* context;
+ PropertyProvider* provider = nullptr;
+ gfxFloat* advanceWidth = nullptr;
+ mozilla::SVGContextPaint* contextPaint = nullptr;
+ DrawPathCallbacks* callbacks = nullptr;
+ nscolor textColor = NS_RGBA(0, 0, 0, 0);
+ nscolor textStrokeColor = NS_RGBA(0, 0, 0, 0);
+ float textStrokeWidth = 0.0f;
+ bool drawSoftHyphen = false;
+ explicit DrawTextRunParams(gfxContext* aContext)
+ : context(aContext) {}
+ };
+
+ struct DrawTextParams : DrawTextRunParams
+ {
+ gfxPoint framePt;
+ LayoutDeviceRect dirtyRect;
+ const nsTextPaintStyle* textStyle = nullptr;
+ const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr;
+ const nscolor* decorationOverrideColor = nullptr;
+ explicit DrawTextParams(gfxContext* aContext)
+ : DrawTextRunParams(aContext) {}
+ };
+
+ // Primary frame paint method called from nsDisplayText. Can also be used
+ // to generate paths rather than paint the frame's text by passing a callback
+ // object. The private DrawText() is what applies the text to a graphics
+ // context.
+ void PaintText(const PaintTextParams& aParams,
+ const nsCharClipDisplayItem& aItem,
+ float aOpacity = 1.0f);
+ // helper: paint text frame when we're impacted by at least one selection.
+ // Return false if the text was not painted and we should continue with
+ // the fast path.
+ bool PaintTextWithSelection(const PaintTextSelectionParams& aParams,
+ const nsCharClipDisplayItem::ClipEdges& aClipEdges);
+ // helper: paint text with foreground and background colors determined
+ // by selection(s). Also computes a mask of all selection types applying to
+ // our text, returned in aAllTypes.
+ // Return false if the text was not painted and we should continue with
+ // the fast path.
+ bool PaintTextWithSelectionColors(
+ const PaintTextSelectionParams& aParams,
+ SelectionDetails* aDetails,
+ RawSelectionType* aAllRawSelectionTypes,
+ const nsCharClipDisplayItem::ClipEdges& aClipEdges);
+ // helper: paint text decorations for text selected by aSelectionType
+ void PaintTextSelectionDecorations(const PaintTextSelectionParams& aParams,
+ SelectionDetails* aDetails,
+ SelectionType aSelectionType);
+
+ void DrawEmphasisMarks(gfxContext* aContext,
+ mozilla::WritingMode aWM,
+ const gfxPoint& aTextBaselinePt,
+ const gfxPoint& aFramePt, Range aRange,
+ const nscolor* aDecorationOverrideColor,
+ PropertyProvider* aProvider);
+
+ virtual nscolor GetCaretColorAt(int32_t aOffset) override;
+
+ int16_t GetSelectionStatus(int16_t* aSelectionFlags);
+
+ int32_t GetContentOffset() const { return mContentOffset; }
+ int32_t GetContentLength() const
+ {
+ NS_ASSERTION(GetContentEnd() - mContentOffset >= 0, "negative length");
+ return GetContentEnd() - mContentOffset;
+ }
+ int32_t GetContentEnd() const;
+ // This returns the length the frame thinks it *should* have after it was
+ // last reflowed (0 if it hasn't been reflowed yet). This should be used only
+ // when setting up the text offsets for a new continuation frame.
+ int32_t GetContentLengthHint() const { return mContentLengthHint; }
+
+ // Compute the length of the content mapped by this frame
+ // and all its in-flow siblings. Basically this means starting at mContentOffset
+ // and going to the end of the text node or the next bidi continuation
+ // boundary.
+ int32_t GetInFlowContentLength();
+
+ /**
+ * Acquires the text run for this content, if necessary.
+ * @param aWhichTextRun indicates whether to get an inflated or non-inflated
+ * text run
+ * @param aRefDrawTarget the DrawTarget to use as a reference for creating the
+ * textrun, if available (if not, we'll create one which will just be slower)
+ * @param aLineContainer the block ancestor for this frame, or nullptr if
+ * unknown
+ * @param aFlowEndInTextRun if non-null, this returns the textrun offset of
+ * end of the text associated with this frame and its in-flow siblings
+ * @return a gfxSkipCharsIterator set up to map DOM offsets for this frame
+ * to offsets into the textrun; its initial offset is set to this frame's
+ * content offset
+ */
+ gfxSkipCharsIterator EnsureTextRun(TextRunType aWhichTextRun,
+ DrawTarget* aRefDrawTarget = nullptr,
+ nsIFrame* aLineContainer = nullptr,
+ const nsLineList::iterator* aLine = nullptr,
+ uint32_t* aFlowEndInTextRun = nullptr);
+
+ gfxTextRun* GetTextRun(TextRunType aWhichTextRun) {
+ if (aWhichTextRun == eInflated || !HasFontSizeInflation())
+ return mTextRun;
+ return GetUninflatedTextRun();
+ }
+ gfxTextRun* GetUninflatedTextRun();
+ void SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
+ float aInflation);
+ bool IsInTextRunUserData() const {
+ return GetStateBits() &
+ (TEXT_IN_TEXTRUN_USER_DATA | TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA);
+ }
+ /**
+ * Notify the frame that it should drop its pointer to a text run.
+ * Returns whether the text run was removed (i.e., whether it was
+ * associated with this frame, either as its inflated or non-inflated
+ * text run.
+ */
+ bool RemoveTextRun(gfxTextRun* aTextRun);
+ /**
+ * Clears out |mTextRun| (or the uninflated text run, when aInflated
+ * is nsTextFrame::eNotInflated and there is inflation) from all frames that hold a
+ * reference to it, starting at |aStartContinuation|, or if it's
+ * nullptr, starting at |this|. Deletes the text run if all references
+ * were cleared and it's not cached.
+ */
+ void ClearTextRun(nsTextFrame* aStartContinuation,
+ TextRunType aWhichTextRun);
+
+ void ClearTextRuns() {
+ ClearTextRun(nullptr, nsTextFrame::eInflated);
+ if (HasFontSizeInflation()) {
+ ClearTextRun(nullptr, nsTextFrame::eNotInflated);
+ }
+ }
+
+ /**
+ * Wipe out references to textrun(s) without deleting the textruns.
+ */
+ void DisconnectTextRuns();
+
+ // Get the DOM content range mapped by this frame after excluding
+ // whitespace subject to start-of-line and end-of-line trimming.
+ // The textrun must have been created before calling this.
+ struct TrimmedOffsets {
+ int32_t mStart;
+ int32_t mLength;
+ int32_t GetEnd() const { return mStart + mLength; }
+ };
+ TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag,
+ bool aTrimAfter, bool aPostReflow = true);
+
+ // Similar to Reflow(), but for use from nsLineLayout
+ void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
+ DrawTarget* aDrawTarget,
+ ReflowOutput& aMetrics, nsReflowStatus& aStatus);
+
+ bool IsFloatingFirstLetterChild() const;
+
+ bool IsInitialLetterChild() const;
+
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
+
+ void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
+ mozilla::JustificationAssignment GetJustificationAssignment() const;
+
+ uint32_t CountGraphemeClusters() const;
+
+protected:
+ virtual ~nsTextFrame();
+
+ RefPtr<gfxTextRun> mTextRun;
+ nsIFrame* mNextContinuation;
+ // The key invariant here is that mContentOffset never decreases along
+ // a next-continuation chain. And of course mContentOffset is always <= the
+ // the text node's content length, and the mContentOffset for the first frame
+ // is always 0. Furthermore the text mapped by a frame is determined by
+ // GetContentOffset() and GetContentLength()/GetContentEnd(), which get
+ // the length from the difference between this frame's offset and the next
+ // frame's offset, or the text length if there is no next frame. This means
+ // the frames always map the text node without overlapping or leaving any gaps.
+ int32_t mContentOffset;
+ // This does *not* indicate the length of text currently mapped by the frame;
+ // instead it's a hint saying that this frame *wants* to map this much text
+ // so if we create a new continuation, this is where that continuation should
+ // start.
+ int32_t mContentLengthHint;
+ nscoord mAscent;
+
+ /**
+ * Return true if the frame is part of a Selection.
+ * Helper method to implement the public IsSelected() API.
+ */
+ virtual bool IsFrameSelected() const override;
+
+ // The caller of this method must call DestroySelectionDetails() on the
+ // return value, if that return value is not null. Calling
+ // DestroySelectionDetails() on a null value is still OK, just not necessary.
+ SelectionDetails* GetSelectionDetails();
+
+ void UnionAdditionalOverflow(nsPresContext* aPresContext,
+ nsIFrame* aBlock,
+ PropertyProvider& aProvider,
+ nsRect* aVisualOverflowRect,
+ bool aIncludeTextDecorations);
+
+ // Update information of emphasis marks, and return the visial
+ // overflow rect of the emphasis marks.
+ nsRect UpdateTextEmphasis(mozilla::WritingMode aWM,
+ PropertyProvider& aProvider);
+
+ struct PaintShadowParams
+ {
+ gfxTextRun::Range range;
+ LayoutDeviceRect dirtyRect;
+ gfxPoint framePt;
+ gfxPoint textBaselinePt;
+ gfxContext* context;
+ nscolor foregroundColor = NS_RGBA(0, 0, 0, 0);
+ const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr;
+ PropertyProvider* provider = nullptr;
+ nscoord leftSideOffset = 0;
+ explicit PaintShadowParams(const PaintTextParams& aParams)
+ : dirtyRect(aParams.dirtyRect)
+ , framePt(aParams.framePt)
+ , context(aParams.context) {}
+ };
+
+ void PaintOneShadow(const PaintShadowParams& aParams,
+ nsCSSShadowItem* aShadowDetails,
+ gfxRect& aBoundingBox,
+ uint32_t aBlurFlags);
+
+ void PaintShadows(nsCSSShadowArray* aShadow,
+ const PaintShadowParams& aParams);
+
+ struct LineDecoration {
+ nsIFrame* mFrame;
+
+ // This is represents the offset from our baseline to mFrame's baseline;
+ // positive offsets are *above* the baseline and negative offsets below
+ nscoord mBaselineOffset;
+
+ nscolor mColor;
+ uint8_t mStyle;
+
+ LineDecoration(nsIFrame *const aFrame,
+ const nscoord aOff,
+ const nscolor aColor,
+ const uint8_t aStyle)
+ : mFrame(aFrame),
+ mBaselineOffset(aOff),
+ mColor(aColor),
+ mStyle(aStyle)
+ {}
+
+ LineDecoration(const LineDecoration& aOther)
+ : mFrame(aOther.mFrame),
+ mBaselineOffset(aOther.mBaselineOffset),
+ mColor(aOther.mColor),
+ mStyle(aOther.mStyle)
+ {}
+
+ bool operator==(const LineDecoration& aOther) const {
+ return mFrame == aOther.mFrame &&
+ mStyle == aOther.mStyle &&
+ mColor == aOther.mColor &&
+ mBaselineOffset == aOther.mBaselineOffset;
+ }
+
+ bool operator!=(const LineDecoration& aOther) const {
+ return !(*this == aOther);
+ }
+ };
+ struct TextDecorations {
+ AutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes;
+
+ TextDecorations() { }
+
+ bool HasDecorationLines() const {
+ return HasUnderline() || HasOverline() || HasStrikeout();
+ }
+ bool HasUnderline() const {
+ return !mUnderlines.IsEmpty();
+ }
+ bool HasOverline() const {
+ return !mOverlines.IsEmpty();
+ }
+ bool HasStrikeout() const {
+ return !mStrikes.IsEmpty();
+ }
+ bool operator==(const TextDecorations& aOther) const {
+ return mOverlines == aOther.mOverlines &&
+ mUnderlines == aOther.mUnderlines &&
+ mStrikes == aOther.mStrikes;
+ }
+
+ bool operator!=(const TextDecorations& aOther) const {
+ return !(*this == aOther);
+ }
+
+ };
+ enum TextDecorationColorResolution {
+ eResolvedColors,
+ eUnresolvedColors
+ };
+ void GetTextDecorations(nsPresContext* aPresContext,
+ TextDecorationColorResolution aColorResolution,
+ TextDecorations& aDecorations);
+
+ void DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt,
+ const DrawTextRunParams& aParams);
+
+ void DrawTextRunAndDecorations(Range aRange, const gfxPoint& aTextBaselinePt,
+ const DrawTextParams& aParams,
+ const TextDecorations& aDecorations);
+
+ void DrawText(Range aRange, const gfxPoint& aTextBaselinePt,
+ const DrawTextParams& aParams);
+
+ // Set non empty rect to aRect, it should be overflow rect or frame rect.
+ // If the result rect is larger than the given rect, this returns true.
+ bool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
+ nsRect& aRect);
+
+ /**
+ * Utility methods to paint selection.
+ */
+ void DrawSelectionDecorations(gfxContext* aContext,
+ const LayoutDeviceRect& aDirtyRect,
+ mozilla::SelectionType aSelectionType,
+ nsTextPaintStyle& aTextPaintStyle,
+ const TextRangeStyle &aRangeStyle,
+ const Point& aPt,
+ gfxFloat aICoordInFrame,
+ gfxFloat aWidth,
+ gfxFloat aAscent,
+ const gfxFont::Metrics& aFontMetrics,
+ DrawPathCallbacks* aCallbacks,
+ bool aVertical,
+ gfxFloat aDecorationOffsetDir,
+ uint8_t aDecoration);
+
+ struct PaintDecorationLineParams;
+ void PaintDecorationLine(const PaintDecorationLineParams& aParams);
+ /**
+ * ComputeDescentLimitForSelectionUnderline() computes the most far position
+ * where we can put selection underline.
+ *
+ * @return The maximum underline offset from the baseline (positive value
+ * means that the underline can put below the baseline).
+ */
+ gfxFloat ComputeDescentLimitForSelectionUnderline(
+ nsPresContext* aPresContext,
+ const gfxFont::Metrics& aFontMetrics);
+ /**
+ * This function encapsulates all knowledge of how selections affect
+ * foreground and background colors.
+ * @param aForeground the foreground color to use
+ * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
+ * background should be painted
+ * @return true if the selection affects colors, false otherwise
+ */
+ static bool GetSelectionTextColors(SelectionType aSelectionType,
+ nsTextPaintStyle& aTextPaintStyle,
+ const TextRangeStyle &aRangeStyle,
+ nscolor* aForeground,
+ nscolor* aBackground);
+ /**
+ * ComputeSelectionUnderlineHeight() computes selection underline height of
+ * the specified selection type from the font metrics.
+ */
+ static gfxFloat ComputeSelectionUnderlineHeight(
+ nsPresContext* aPresContext,
+ const gfxFont::Metrics& aFontMetrics,
+ SelectionType aSelectionType);
+
+ ContentOffsets GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
+ bool aForInsertionPoint);
+
+ void ClearFrameOffsetCache();
+
+ virtual bool HasAnyNoncollapsedCharacters() override;
+
+ void ClearMetrics(ReflowOutput& aMetrics);
+
+ /**
+ * UpdateIteratorFromOffset() updates the iterator from a given offset.
+ * Also, aInOffset may be updated to cluster start if aInOffset isn't
+ * the offset of cluster start.
+ */
+ void UpdateIteratorFromOffset(const PropertyProvider& aProperties,
+ int32_t& aInOffset,
+ gfxSkipCharsIterator& aIter);
+
+ nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter,
+ PropertyProvider& aProperties);
+};
+
+#endif
diff --git a/layout/generic/nsTextFrameUtils.cpp b/layout/generic/nsTextFrameUtils.cpp
new file mode 100644
index 000000000..d07400f2b
--- /dev/null
+++ b/layout/generic/nsTextFrameUtils.cpp
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTextFrameUtils.h"
+
+#include "nsBidiUtils.h"
+#include "nsCharTraits.h"
+#include "nsIContent.h"
+#include "nsStyleStruct.h"
+#include "nsTextFragment.h"
+#include "nsUnicharUtils.h"
+#include <algorithm>
+
+static bool IsDiscardable(char16_t ch, uint32_t* aFlags)
+{
+ // Unlike IS_DISCARDABLE, we don't discard \r. \r will be ignored by gfxTextRun
+ // and discarding it would force us to copy text in many cases of preformatted
+ // text containing \r\n.
+ if (ch == CH_SHY) {
+ *aFlags |= nsTextFrameUtils::TEXT_HAS_SHY;
+ return true;
+ }
+ return IsBidiControl(ch);
+}
+
+static bool IsDiscardable(uint8_t ch, uint32_t* aFlags)
+{
+ if (ch == CH_SHY) {
+ *aFlags |= nsTextFrameUtils::TEXT_HAS_SHY;
+ return true;
+ }
+ return false;
+}
+
+char16_t*
+nsTextFrameUtils::TransformText(const char16_t* aText, uint32_t aLength,
+ char16_t* aOutput,
+ CompressionMode aCompression,
+ uint8_t* aIncomingFlags,
+ gfxSkipChars* aSkipChars,
+ uint32_t* aAnalysisFlags)
+{
+ uint32_t flags = 0;
+ char16_t* outputStart = aOutput;
+
+ bool lastCharArabic = false;
+
+ if (aCompression == COMPRESS_NONE ||
+ aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
+ // Skip discardables.
+ uint32_t i;
+ for (i = 0; i < aLength; ++i) {
+ char16_t ch = aText[i];
+ if (IsDiscardable(ch, &flags)) {
+ aSkipChars->SkipChar();
+ } else {
+ aSkipChars->KeepChar();
+ if (ch > ' ') {
+ lastCharArabic = IS_ARABIC_CHAR(ch);
+ } else if (aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
+ if (ch == '\t' || ch == '\n') {
+ ch = ' ';
+ flags |= TEXT_WAS_TRANSFORMED;
+ }
+ } else {
+ // aCompression == COMPRESS_NONE
+ if (ch == '\t') {
+ flags |= TEXT_HAS_TAB;
+ }
+ }
+ *aOutput++ = ch;
+ }
+ }
+ if (lastCharArabic) {
+ *aIncomingFlags |= INCOMING_ARABICCHAR;
+ } else {
+ *aIncomingFlags &= ~INCOMING_ARABICCHAR;
+ }
+ *aIncomingFlags &= ~INCOMING_WHITESPACE;
+ } else {
+ bool inWhitespace = (*aIncomingFlags & INCOMING_WHITESPACE) != 0;
+ uint32_t i;
+ for (i = 0; i < aLength; ++i) {
+ char16_t ch = aText[i];
+ bool nowInWhitespace;
+ if (ch == ' ' &&
+ (i + 1 >= aLength ||
+ !IsSpaceCombiningSequenceTail(&aText[i + 1], aLength - (i + 1)))) {
+ nowInWhitespace = true;
+ } else if (ch == '\n' && aCompression == COMPRESS_WHITESPACE_NEWLINE) {
+ if ((i > 0 && IS_ZERO_WIDTH_SPACE(aText[i - 1])) ||
+ (i + 1 < aLength && IS_ZERO_WIDTH_SPACE(aText[i + 1]))) {
+ aSkipChars->SkipChar();
+ continue;
+ }
+ uint32_t ucs4before;
+ uint32_t ucs4after;
+ if (i > 1 &&
+ NS_IS_LOW_SURROGATE(aText[i - 1]) &&
+ NS_IS_HIGH_SURROGATE(aText[i - 2])) {
+ ucs4before = SURROGATE_TO_UCS4(aText[i - 2], aText[i - 1]);
+ } else if (i > 0) {
+ ucs4before = aText[i - 1];
+ }
+ if (i + 2 < aLength &&
+ NS_IS_HIGH_SURROGATE(aText[i + 1]) &&
+ NS_IS_LOW_SURROGATE(aText[i + 2])) {
+ ucs4after = SURROGATE_TO_UCS4(aText[i + 1], aText[i + 2]);
+ } else if (i + 1 < aLength) {
+ ucs4after = aText[i + 1];
+ }
+ if (i > 0 && IsSegmentBreakSkipChar(ucs4before) &&
+ i + 1 < aLength && IsSegmentBreakSkipChar(ucs4after)) {
+ // Discard newlines between characters that have F, W, or H
+ // EastAsianWidth property and neither side is Hangul.
+ aSkipChars->SkipChar();
+ continue;
+ }
+ nowInWhitespace = true;
+ } else {
+ nowInWhitespace = ch == '\t';
+ }
+
+ if (!nowInWhitespace) {
+ if (IsDiscardable(ch, &flags)) {
+ aSkipChars->SkipChar();
+ nowInWhitespace = inWhitespace;
+ } else {
+ *aOutput++ = ch;
+ aSkipChars->KeepChar();
+ lastCharArabic = IS_ARABIC_CHAR(ch);
+ }
+ } else {
+ if (inWhitespace) {
+ aSkipChars->SkipChar();
+ } else {
+ if (ch != ' ') {
+ flags |= TEXT_WAS_TRANSFORMED;
+ }
+ *aOutput++ = ' ';
+ aSkipChars->KeepChar();
+ }
+ }
+ inWhitespace = nowInWhitespace;
+ }
+ if (lastCharArabic) {
+ *aIncomingFlags |= INCOMING_ARABICCHAR;
+ } else {
+ *aIncomingFlags &= ~INCOMING_ARABICCHAR;
+ }
+ if (inWhitespace) {
+ *aIncomingFlags |= INCOMING_WHITESPACE;
+ } else {
+ *aIncomingFlags &= ~INCOMING_WHITESPACE;
+ }
+ }
+
+ if (outputStart + aLength != aOutput) {
+ flags |= TEXT_WAS_TRANSFORMED;
+ }
+ *aAnalysisFlags = flags;
+ return aOutput;
+}
+
+uint8_t*
+nsTextFrameUtils::TransformText(const uint8_t* aText, uint32_t aLength,
+ uint8_t* aOutput,
+ CompressionMode aCompression,
+ uint8_t* aIncomingFlags,
+ gfxSkipChars* aSkipChars,
+ uint32_t* aAnalysisFlags)
+{
+ uint32_t flags = 0;
+ uint8_t* outputStart = aOutput;
+
+ if (aCompression == COMPRESS_NONE ||
+ aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
+ // Skip discardables.
+ uint32_t i;
+ for (i = 0; i < aLength; ++i) {
+ uint8_t ch = aText[i];
+ if (IsDiscardable(ch, &flags)) {
+ aSkipChars->SkipChar();
+ } else {
+ aSkipChars->KeepChar();
+ if (aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
+ if (ch == '\t' || ch == '\n') {
+ ch = ' ';
+ flags |= TEXT_WAS_TRANSFORMED;
+ }
+ } else {
+ // aCompression == COMPRESS_NONE
+ if (ch == '\t') {
+ flags |= TEXT_HAS_TAB;
+ }
+ }
+ *aOutput++ = ch;
+ }
+ }
+ *aIncomingFlags &= ~(INCOMING_ARABICCHAR | INCOMING_WHITESPACE);
+ } else {
+ bool inWhitespace = (*aIncomingFlags & INCOMING_WHITESPACE) != 0;
+ uint32_t i;
+ for (i = 0; i < aLength; ++i) {
+ uint8_t ch = aText[i];
+ bool nowInWhitespace = ch == ' ' || ch == '\t' ||
+ (ch == '\n' && aCompression == COMPRESS_WHITESPACE_NEWLINE);
+ if (!nowInWhitespace) {
+ if (IsDiscardable(ch, &flags)) {
+ aSkipChars->SkipChar();
+ nowInWhitespace = inWhitespace;
+ } else {
+ *aOutput++ = ch;
+ aSkipChars->KeepChar();
+ }
+ } else {
+ if (inWhitespace) {
+ aSkipChars->SkipChar();
+ } else {
+ if (ch != ' ') {
+ flags |= TEXT_WAS_TRANSFORMED;
+ }
+ *aOutput++ = ' ';
+ aSkipChars->KeepChar();
+ }
+ }
+ inWhitespace = nowInWhitespace;
+ }
+ *aIncomingFlags &= ~INCOMING_ARABICCHAR;
+ if (inWhitespace) {
+ *aIncomingFlags |= INCOMING_WHITESPACE;
+ } else {
+ *aIncomingFlags &= ~INCOMING_WHITESPACE;
+ }
+ }
+
+ if (outputStart + aLength != aOutput) {
+ flags |= TEXT_WAS_TRANSFORMED;
+ }
+ *aAnalysisFlags = flags;
+ return aOutput;
+}
+
+uint32_t
+nsTextFrameUtils::ComputeApproximateLengthWithWhitespaceCompression(
+ nsIContent *aContent, const nsStyleText *aStyleText)
+{
+ const nsTextFragment *frag = aContent->GetText();
+ // This is an approximation so we don't really need anything
+ // too fancy here.
+ uint32_t len;
+ if (aStyleText->WhiteSpaceIsSignificant()) {
+ len = frag->GetLength();
+ } else {
+ bool is2b = frag->Is2b();
+ union {
+ const char *s1b;
+ const char16_t *s2b;
+ } u;
+ if (is2b) {
+ u.s2b = frag->Get2b();
+ } else {
+ u.s1b = frag->Get1b();
+ }
+ bool prevWS = true; // more important to ignore blocks with
+ // only whitespace than get inline boundaries
+ // exactly right
+ len = 0;
+ for (uint32_t i = 0, i_end = frag->GetLength(); i < i_end; ++i) {
+ char16_t c = is2b ? u.s2b[i] : u.s1b[i];
+ if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
+ if (!prevWS) {
+ ++len;
+ }
+ prevWS = true;
+ } else {
+ ++len;
+ prevWS = false;
+ }
+ }
+ }
+ return len;
+}
+
+bool nsSkipCharsRunIterator::NextRun() {
+ do {
+ if (mRunLength) {
+ mIterator.AdvanceOriginal(mRunLength);
+ NS_ASSERTION(mRunLength > 0, "No characters in run (initial length too large?)");
+ if (!mSkipped || mLengthIncludesSkipped) {
+ mRemainingLength -= mRunLength;
+ }
+ }
+ if (!mRemainingLength)
+ return false;
+ int32_t length;
+ mSkipped = mIterator.IsOriginalCharSkipped(&length);
+ mRunLength = std::min(length, mRemainingLength);
+ } while (!mVisitSkipped && mSkipped);
+
+ return true;
+}
diff --git a/layout/generic/nsTextFrameUtils.h b/layout/generic/nsTextFrameUtils.h
new file mode 100644
index 000000000..6274705bc
--- /dev/null
+++ b/layout/generic/nsTextFrameUtils.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSTEXTFRAMEUTILS_H_
+#define NSTEXTFRAMEUTILS_H_
+
+#include "gfxSkipChars.h"
+#include "nsBidiUtils.h"
+#include "nsUnicodeProperties.h"
+
+class nsIContent;
+struct nsStyleText;
+
+#define BIG_TEXT_NODE_SIZE 4096
+
+#define CH_NBSP 160
+#define CH_SHY 173
+#define CH_CJKSP 12288 // U+3000 IDEOGRAPHIC SPACE (CJK Full-Width Space)
+
+class nsTextFrameUtils {
+public:
+ // These constants are used as textrun flags for textframe textruns.
+ enum {
+ // The following flags are set by TransformText
+
+ // the text has at least one untransformed tab character
+ TEXT_HAS_TAB = 0x010000,
+ // the original text has at least one soft hyphen character
+ TEXT_HAS_SHY = 0x020000,
+ TEXT_WAS_TRANSFORMED = 0x040000,
+ TEXT_UNUSED_FLAG = 0x080000,
+
+ // The following flags are set by nsTextFrame
+
+ TEXT_IS_SIMPLE_FLOW = 0x100000,
+ TEXT_INCOMING_WHITESPACE = 0x200000,
+ TEXT_TRAILING_WHITESPACE = 0x400000,
+ TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000,
+ TEXT_NO_BREAKS = 0x1000000,
+ TEXT_IS_TRANSFORMED = 0x2000000,
+ // This gets set if there's a break opportunity at the end of the textrun.
+ // We normally don't use this break opportunity because the following text
+ // will have a break opportunity at the start, but it's useful for line
+ // layout to know about it in case the following content is not text
+ TEXT_HAS_TRAILING_BREAK = 0x4000000,
+
+ // This is set if the textrun was created for a textframe whose
+ // NS_FRAME_IS_IN_SINGLE_CHAR_MI flag is set. This occurs if the textframe
+ // belongs to a MathML <mi> element whose embedded text consists of a
+ // single character.
+ TEXT_IS_SINGLE_CHAR_MI = 0x8000000,
+
+ // This is set if the text run might be observing for glyph changes.
+ TEXT_MIGHT_HAVE_GLYPH_CHANGES = 0x10000000,
+
+ // The following are defined by gfxTextRunWordCache rather than here,
+ // so that it also has access to the _INCOMING flag
+ // TEXT_TRAILING_ARABICCHAR
+ // TEXT_INCOMING_ARABICCHAR
+
+ // This is defined in gfxTextRunFactory to allow access in gfxFont.
+ // TEXT_USE_MATH_SCRIPT
+ };
+
+ // These constants are used in TransformText to represent context information
+ // from previous textruns.
+ enum {
+ INCOMING_NONE = 0,
+ INCOMING_WHITESPACE = 1,
+ INCOMING_ARABICCHAR = 2
+ };
+
+ /**
+ * Returns true if aChars/aLength are something that make a space
+ * character not be whitespace when they follow the space character
+ * (combining mark or join control, ignoring intervening direction
+ * controls).
+ */
+ static bool
+ IsSpaceCombiningSequenceTail(const char16_t* aChars, int32_t aLength) {
+ return aLength > 0 &&
+ (mozilla::unicode::IsClusterExtender(aChars[0]) ||
+ (IsBidiControl(aChars[0]) &&
+ IsSpaceCombiningSequenceTail(aChars + 1, aLength - 1)
+ )
+ );
+ }
+
+ enum CompressionMode {
+ COMPRESS_NONE,
+ COMPRESS_WHITESPACE,
+ COMPRESS_WHITESPACE_NEWLINE,
+ COMPRESS_NONE_TRANSFORM_TO_SPACE
+ };
+
+ /**
+ * Create a text run from a run of Unicode text. The text may have whitespace
+ * compressed. A preformatted tab is sent to the text run as a single space.
+ * (Tab spacing must be performed by textframe later.) Certain other
+ * characters are discarded.
+ *
+ * @param aCompressWhitespace control what is compressed to a
+ * single space character: no compression, compress spaces (not followed
+ * by combining mark) and tabs, compress those plus newlines, or
+ * no compression except newlines are discarded.
+ * @param aIncomingFlags a flag indicating whether there was whitespace
+ * or an Arabic character preceding this text. We set it to indicate if
+ * there's an Arabic character or whitespace preceding the end of this text.
+ */
+ static char16_t* TransformText(const char16_t* aText, uint32_t aLength,
+ char16_t* aOutput,
+ CompressionMode aCompression,
+ uint8_t * aIncomingFlags,
+ gfxSkipChars* aSkipChars,
+ uint32_t* aAnalysisFlags);
+
+ static uint8_t* TransformText(const uint8_t* aText, uint32_t aLength,
+ uint8_t* aOutput,
+ CompressionMode aCompression,
+ uint8_t * aIncomingFlags,
+ gfxSkipChars* aSkipChars,
+ uint32_t* aAnalysisFlags);
+
+ static void
+ AppendLineBreakOffset(nsTArray<uint32_t>* aArray, uint32_t aOffset)
+ {
+ if (aArray->Length() > 0 && (*aArray)[aArray->Length() - 1] == aOffset)
+ return;
+ aArray->AppendElement(aOffset);
+ }
+
+ static uint32_t
+ ComputeApproximateLengthWithWhitespaceCompression(nsIContent *aContent,
+ const nsStyleText
+ *aStyleText);
+};
+
+class nsSkipCharsRunIterator {
+public:
+ enum LengthMode {
+ LENGTH_UNSKIPPED_ONLY = false,
+ LENGTH_INCLUDES_SKIPPED = true
+ };
+ nsSkipCharsRunIterator(const gfxSkipCharsIterator& aStart,
+ LengthMode aLengthIncludesSkipped, uint32_t aLength)
+ : mIterator(aStart), mRemainingLength(aLength), mRunLength(0),
+ mVisitSkipped(false),
+ mLengthIncludesSkipped(aLengthIncludesSkipped) {
+ }
+ void SetVisitSkipped() { mVisitSkipped = true; }
+ void SetOriginalOffset(int32_t aOffset) {
+ mIterator.SetOriginalOffset(aOffset);
+ }
+ void SetSkippedOffset(uint32_t aOffset) {
+ mIterator.SetSkippedOffset(aOffset);
+ }
+
+ // guaranteed to return only positive-length runs
+ bool NextRun();
+ bool IsSkipped() const { return mSkipped; }
+ // Always returns something > 0
+ int32_t GetRunLength() const { return mRunLength; }
+ const gfxSkipCharsIterator& GetPos() const { return mIterator; }
+ int32_t GetOriginalOffset() const { return mIterator.GetOriginalOffset(); }
+ uint32_t GetSkippedOffset() const { return mIterator.GetSkippedOffset(); }
+
+private:
+ gfxSkipCharsIterator mIterator;
+ int32_t mRemainingLength;
+ int32_t mRunLength;
+ bool mSkipped;
+ bool mVisitSkipped;
+ bool mLengthIncludesSkipped;
+};
+
+#endif /*NSTEXTFRAMEUTILS_H_*/
diff --git a/layout/generic/nsTextRunTransformations.cpp b/layout/generic/nsTextRunTransformations.cpp
new file mode 100644
index 000000000..84d669ed6
--- /dev/null
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -0,0 +1,692 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTextRunTransformations.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+#include "nsSpecialCasingData.h"
+#include "mozilla/gfx/2D.h"
+#include "nsTextFrameUtils.h"
+#include "nsIPersistentProperties2.h"
+#include "GreekCasing.h"
+#include "IrishCasing.h"
+
+using namespace mozilla;
+
+// Unicode characters needing special casing treatment in tr/az languages
+#define LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE 0x0130
+#define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
+
+// Greek sigma needs custom handling for the lowercase transform; for details
+// see comments under "case NS_STYLE_TEXT_TRANSFORM_LOWERCASE" within
+// nsCaseTransformTextRunFactory::RebuildTextRun(), and bug 740120.
+#define GREEK_CAPITAL_LETTER_SIGMA 0x03A3
+#define GREEK_SMALL_LETTER_FINAL_SIGMA 0x03C2
+#define GREEK_SMALL_LETTER_SIGMA 0x03C3
+
+already_AddRefed<nsTransformedTextRun>
+nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams,
+ nsTransformingTextRunFactory* aFactory,
+ gfxFontGroup* aFontGroup,
+ const char16_t* aString, uint32_t aLength,
+ const uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory)
+{
+ NS_ASSERTION(!(aFlags & gfxTextRunFactory::TEXT_IS_8BIT),
+ "didn't expect text to be marked as 8-bit here");
+
+ void *storage = AllocateStorageForTextRun(sizeof(nsTransformedTextRun), aLength);
+ if (!storage) {
+ return nullptr;
+ }
+
+ RefPtr<nsTransformedTextRun> result =
+ new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup,
+ aString, aLength, aFlags,
+ Move(aStyles), aOwnsFactory);
+ return result.forget();
+}
+
+void
+nsTransformedTextRun::SetCapitalization(uint32_t aStart, uint32_t aLength,
+ bool* aCapitalization)
+{
+ if (mCapitalize.IsEmpty()) {
+ if (!mCapitalize.AppendElements(GetLength()))
+ return;
+ memset(mCapitalize.Elements(), 0, GetLength()*sizeof(bool));
+ }
+ memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(bool));
+ mNeedsRebuild = true;
+}
+
+bool
+nsTransformedTextRun::SetPotentialLineBreaks(Range aRange,
+ const uint8_t* aBreakBefore)
+{
+ bool changed = gfxTextRun::SetPotentialLineBreaks(aRange, aBreakBefore);
+ if (changed) {
+ mNeedsRebuild = true;
+ }
+ return changed;
+}
+
+size_t
+nsTransformedTextRun::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ size_t total = gfxTextRun::SizeOfExcludingThis(aMallocSizeOf);
+ total += mStyles.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ total += mCapitalize.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ if (mOwnsFactory) {
+ total += aMallocSizeOf(mFactory);
+ }
+ return total;
+}
+
+size_t
+nsTransformedTextRun::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+already_AddRefed<nsTransformedTextRun>
+nsTransformingTextRunFactory::MakeTextRun(const char16_t* aString, uint32_t aLength,
+ const gfxTextRunFactory::Parameters* aParams,
+ gfxFontGroup* aFontGroup, uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory)
+{
+ return nsTransformedTextRun::Create(aParams, this, aFontGroup,
+ aString, aLength, aFlags, Move(aStyles),
+ aOwnsFactory);
+}
+
+already_AddRefed<nsTransformedTextRun>
+nsTransformingTextRunFactory::MakeTextRun(const uint8_t* aString, uint32_t aLength,
+ const gfxTextRunFactory::Parameters* aParams,
+ gfxFontGroup* aFontGroup, uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory)
+{
+ // We'll only have a Unicode code path to minimize the amount of code needed
+ // for these rarely used features
+ NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString), aLength);
+ return MakeTextRun(unicodeString.get(), aLength, aParams, aFontGroup,
+ aFlags & ~(gfxFontGroup::TEXT_IS_PERSISTENT | gfxFontGroup::TEXT_IS_8BIT),
+ Move(aStyles), aOwnsFactory);
+}
+
+void
+MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
+ const bool* aCharsToMerge, const bool* aDeletedChars)
+{
+ aDest->ResetGlyphRuns();
+
+ gfxTextRun::GlyphRunIterator iter(aSrc, gfxTextRun::Range(aSrc));
+ uint32_t offset = 0;
+ AutoTArray<gfxTextRun::DetailedGlyph,2> glyphs;
+ while (iter.NextRun()) {
+ const gfxTextRun::GlyphRun* run = iter.GetGlyphRun();
+ nsresult rv = aDest->AddGlyphRun(run->mFont, run->mMatchType,
+ offset, false, run->mOrientation);
+ if (NS_FAILED(rv))
+ return;
+
+ bool anyMissing = false;
+ uint32_t mergeRunStart = iter.GetStringStart();
+ const gfxTextRun::CompressedGlyph *srcGlyphs = aSrc->GetCharacterGlyphs();
+ gfxTextRun::CompressedGlyph mergedGlyph = srcGlyphs[mergeRunStart];
+ uint32_t stringEnd = iter.GetStringEnd();
+ for (uint32_t k = iter.GetStringStart(); k < stringEnd; ++k) {
+ const gfxTextRun::CompressedGlyph g = srcGlyphs[k];
+ if (g.IsSimpleGlyph()) {
+ if (!anyMissing) {
+ gfxTextRun::DetailedGlyph details;
+ details.mGlyphID = g.GetSimpleGlyph();
+ details.mAdvance = g.GetSimpleAdvance();
+ details.mXOffset = 0;
+ details.mYOffset = 0;
+ glyphs.AppendElement(details);
+ }
+ } else {
+ if (g.IsMissing()) {
+ anyMissing = true;
+ glyphs.Clear();
+ }
+ if (g.GetGlyphCount() > 0) {
+ glyphs.AppendElements(aSrc->GetDetailedGlyphs(k), g.GetGlyphCount());
+ }
+ }
+
+ if (k + 1 < iter.GetStringEnd() && aCharsToMerge[k + 1]) {
+ // next char is supposed to merge with current, so loop without
+ // writing current merged glyph to the destination
+ continue;
+ }
+
+ // If the start of the merge run is actually a character that should
+ // have been merged with the previous character (this can happen
+ // if there's a font change in the middle of a case-mapped character,
+ // that decomposed into a sequence of base+diacritics, for example),
+ // just discard the entire merge run. See comment at start of this
+ // function.
+ NS_WARNING_ASSERTION(
+ !aCharsToMerge[mergeRunStart],
+ "unable to merge across a glyph run boundary, glyph(s) discarded");
+ if (!aCharsToMerge[mergeRunStart]) {
+ if (anyMissing) {
+ mergedGlyph.SetMissing(glyphs.Length());
+ } else {
+ mergedGlyph.SetComplex(mergedGlyph.IsClusterStart(),
+ mergedGlyph.IsLigatureGroupStart(),
+ glyphs.Length());
+ }
+ aDest->SetGlyphs(offset, mergedGlyph, glyphs.Elements());
+ ++offset;
+
+ while (offset < aDest->GetLength() && aDeletedChars[offset]) {
+ aDest->SetGlyphs(offset++, gfxTextRun::CompressedGlyph(), nullptr);
+ }
+ }
+
+ glyphs.Clear();
+ anyMissing = false;
+ mergeRunStart = k + 1;
+ if (mergeRunStart < stringEnd) {
+ mergedGlyph = srcGlyphs[mergeRunStart];
+ }
+ }
+ NS_ASSERTION(glyphs.Length() == 0,
+ "Leftover glyphs, don't request merging of the last character with its next!");
+ }
+ NS_ASSERTION(offset == aDest->GetLength(), "Bad offset calculations");
+}
+
+gfxTextRunFactory::Parameters
+GetParametersForInner(nsTransformedTextRun* aTextRun, uint32_t* aFlags,
+ DrawTarget* aRefDrawTarget)
+{
+ gfxTextRunFactory::Parameters params =
+ { aRefDrawTarget, nullptr, nullptr,
+ nullptr, 0, aTextRun->GetAppUnitsPerDevUnit()
+ };
+ *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
+ return params;
+}
+
+// Some languages have special casing conventions that differ from the
+// default Unicode mappings.
+// The enum values here are named for well-known exemplar languages that
+// exhibit the behavior in question; multiple lang tags may map to the
+// same setting here, if the behavior is shared by other languages.
+enum LanguageSpecificCasingBehavior {
+ eLSCB_None, // default non-lang-specific behavior
+ eLSCB_Dutch, // treat "ij" digraph as a unit for capitalization
+ eLSCB_Greek, // strip accent when uppercasing Greek vowels
+ eLSCB_Irish, // keep prefix letters as lowercase when uppercasing Irish
+ eLSCB_Turkish // preserve dotted/dotless-i distinction in uppercase
+};
+
+static LanguageSpecificCasingBehavior
+GetCasingFor(const nsIAtom* aLang)
+{
+ if (!aLang) {
+ return eLSCB_None;
+ }
+ if (aLang == nsGkAtoms::tr ||
+ aLang == nsGkAtoms::az ||
+ aLang == nsGkAtoms::ba ||
+ aLang == nsGkAtoms::crh ||
+ aLang == nsGkAtoms::tt) {
+ return eLSCB_Turkish;
+ }
+ if (aLang == nsGkAtoms::nl) {
+ return eLSCB_Dutch;
+ }
+ if (aLang == nsGkAtoms::el) {
+ return eLSCB_Greek;
+ }
+ if (aLang == nsGkAtoms::ga) {
+ return eLSCB_Irish;
+ }
+
+ // Is there a region subtag we should ignore?
+ nsAtomString langStr(const_cast<nsIAtom*>(aLang));
+ int index = langStr.FindChar('-');
+ if (index > 0) {
+ langStr.Truncate(index);
+ nsCOMPtr<nsIAtom> truncatedLang = NS_Atomize(langStr);
+ return GetCasingFor(truncatedLang);
+ }
+
+ return eLSCB_None;
+}
+
+bool
+nsCaseTransformTextRunFactory::TransformString(
+ const nsAString& aString,
+ nsString& aConvertedString,
+ bool aAllUppercase,
+ const nsIAtom* aLanguage,
+ nsTArray<bool>& aCharsToMergeArray,
+ nsTArray<bool>& aDeletedCharsArray,
+ const nsTransformedTextRun* aTextRun,
+ uint32_t aOffsetInTextRun,
+ nsTArray<uint8_t>* aCanBreakBeforeArray,
+ nsTArray<RefPtr<nsTransformedCharStyle>>* aStyleArray)
+{
+ bool auxiliaryOutputArrays = aCanBreakBeforeArray && aStyleArray;
+ MOZ_ASSERT(!auxiliaryOutputArrays || aTextRun,
+ "text run must be provided to use aux output arrays");
+
+ uint32_t length = aString.Length();
+ const char16_t* str = aString.BeginReading();
+
+ bool mergeNeeded = false;
+
+ bool capitalizeDutchIJ = false;
+ bool prevIsLetter = false;
+ bool ntPrefix = false; // true immediately after a word-initial 'n' or 't'
+ // when doing Irish lowercasing
+ uint32_t sigmaIndex = uint32_t(-1);
+ nsIUGenCategory::nsUGenCategory cat;
+
+ uint8_t style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE : 0;
+ bool forceNonFullWidth = false;
+ const nsIAtom* lang = aLanguage;
+
+ LanguageSpecificCasingBehavior languageSpecificCasing = GetCasingFor(lang);
+ mozilla::GreekCasing::State greekState;
+ mozilla::IrishCasing::State irishState;
+ uint32_t irishMark = uint32_t(-1); // location of possible prefix letter(s)
+ // in the output string
+ uint32_t irishMarkSrc = uint32_t(-1); // corresponding location in source
+ // string (may differ from output due to
+ // expansions like eszet -> 'SS')
+
+ for (uint32_t i = 0; i < length; ++i, ++aOffsetInTextRun) {
+ uint32_t ch = str[i];
+
+ RefPtr<nsTransformedCharStyle> charStyle;
+ if (aTextRun) {
+ charStyle = aTextRun->mStyles[aOffsetInTextRun];
+ style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE :
+ charStyle->mTextTransform;
+ forceNonFullWidth = charStyle->mForceNonFullWidth;
+
+ nsIAtom* newLang = charStyle->mExplicitLanguage
+ ? charStyle->mLanguage.get() : nullptr;
+ if (lang != newLang) {
+ lang = newLang;
+ languageSpecificCasing = GetCasingFor(lang);
+ greekState.Reset();
+ irishState.Reset();
+ irishMark = uint32_t(-1);
+ irishMarkSrc = uint32_t(-1);
+ }
+ }
+
+ int extraChars = 0;
+ const mozilla::unicode::MultiCharMapping *mcm;
+ bool inhibitBreakBefore = false; // have we just deleted preceding hyphen?
+
+ if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
+ NS_IS_LOW_SURROGATE(str[i + 1])) {
+ ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
+ }
+
+ switch (style) {
+ case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
+ if (languageSpecificCasing == eLSCB_Turkish) {
+ if (ch == 'I') {
+ ch = LATIN_SMALL_LETTER_DOTLESS_I;
+ prevIsLetter = true;
+ sigmaIndex = uint32_t(-1);
+ break;
+ }
+ if (ch == LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE) {
+ ch = 'i';
+ prevIsLetter = true;
+ sigmaIndex = uint32_t(-1);
+ break;
+ }
+ }
+
+ cat = mozilla::unicode::GetGenCategory(ch);
+
+ if (languageSpecificCasing == eLSCB_Irish &&
+ cat == nsIUGenCategory::kLetter) {
+ // See bug 1018805 for Irish lowercasing requirements
+ if (!prevIsLetter && (ch == 'n' || ch == 't')) {
+ ntPrefix = true;
+ } else {
+ if (ntPrefix && mozilla::IrishCasing::IsUpperVowel(ch)) {
+ aConvertedString.Append('-');
+ ++extraChars;
+ }
+ ntPrefix = false;
+ }
+ } else {
+ ntPrefix = false;
+ }
+
+ // Special lowercasing behavior for Greek Sigma: note that this is listed
+ // as context-sensitive in Unicode's SpecialCasing.txt, but is *not* a
+ // language-specific mapping; it applies regardless of the language of
+ // the element.
+ //
+ // The lowercase mapping for CAPITAL SIGMA should be to SMALL SIGMA (i.e.
+ // the non-final form) whenever there is a following letter, or when the
+ // CAPITAL SIGMA occurs in isolation (neither preceded nor followed by a
+ // LETTER); and to FINAL SIGMA when it is preceded by another letter but
+ // not followed by one.
+ //
+ // To implement the context-sensitive nature of this mapping, we keep
+ // track of whether the previous character was a letter. If not, CAPITAL
+ // SIGMA will map directly to SMALL SIGMA. If the previous character
+ // was a letter, CAPITAL SIGMA maps to FINAL SIGMA and we record the
+ // position in the converted string; if we then encounter another letter,
+ // that FINAL SIGMA is replaced with a standard SMALL SIGMA.
+
+ // If sigmaIndex is not -1, it marks where we have provisionally mapped
+ // a CAPITAL SIGMA to FINAL SIGMA; if we now find another letter, we
+ // need to change it to SMALL SIGMA.
+ if (sigmaIndex != uint32_t(-1)) {
+ if (cat == nsIUGenCategory::kLetter) {
+ aConvertedString.SetCharAt(GREEK_SMALL_LETTER_SIGMA, sigmaIndex);
+ }
+ }
+
+ if (ch == GREEK_CAPITAL_LETTER_SIGMA) {
+ // If preceding char was a letter, map to FINAL instead of SMALL,
+ // and note where it occurred by setting sigmaIndex; we'll change it
+ // to standard SMALL SIGMA later if another letter follows
+ if (prevIsLetter) {
+ ch = GREEK_SMALL_LETTER_FINAL_SIGMA;
+ sigmaIndex = aConvertedString.Length();
+ } else {
+ // CAPITAL SIGMA not preceded by a letter is unconditionally mapped
+ // to SMALL SIGMA
+ ch = GREEK_SMALL_LETTER_SIGMA;
+ sigmaIndex = uint32_t(-1);
+ }
+ prevIsLetter = true;
+ break;
+ }
+
+ // ignore diacritics for the purpose of contextual sigma mapping;
+ // otherwise, reset prevIsLetter appropriately and clear the
+ // sigmaIndex marker
+ if (cat != nsIUGenCategory::kMark) {
+ prevIsLetter = (cat == nsIUGenCategory::kLetter);
+ sigmaIndex = uint32_t(-1);
+ }
+
+ mcm = mozilla::unicode::SpecialLower(ch);
+ if (mcm) {
+ int j = 0;
+ while (j < 2 && mcm->mMappedChars[j + 1]) {
+ aConvertedString.Append(mcm->mMappedChars[j]);
+ ++extraChars;
+ ++j;
+ }
+ ch = mcm->mMappedChars[j];
+ break;
+ }
+
+ ch = ToLowerCase(ch);
+ break;
+
+ case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
+ if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
+ ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
+ break;
+ }
+
+ if (languageSpecificCasing == eLSCB_Greek) {
+ ch = mozilla::GreekCasing::UpperCase(ch, greekState);
+ break;
+ }
+
+ if (languageSpecificCasing == eLSCB_Irish) {
+ bool mark;
+ uint8_t action;
+ ch = mozilla::IrishCasing::UpperCase(ch, irishState, mark, action);
+ if (mark) {
+ irishMark = aConvertedString.Length();
+ irishMarkSrc = i;
+ break;
+ } else if (action) {
+ nsString& str = aConvertedString; // shorthand
+ switch (action) {
+ case 1:
+ // lowercase a single prefix letter
+ NS_ASSERTION(str.Length() > 0 && irishMark < str.Length(),
+ "bad irishMark!");
+ str.SetCharAt(ToLowerCase(str[irishMark]), irishMark);
+ irishMark = uint32_t(-1);
+ irishMarkSrc = uint32_t(-1);
+ break;
+ case 2:
+ // lowercase two prefix letters (immediately before current pos)
+ NS_ASSERTION(str.Length() >= 2 && irishMark == str.Length() - 2,
+ "bad irishMark!");
+ str.SetCharAt(ToLowerCase(str[irishMark]), irishMark);
+ str.SetCharAt(ToLowerCase(str[irishMark + 1]), irishMark + 1);
+ irishMark = uint32_t(-1);
+ irishMarkSrc = uint32_t(-1);
+ break;
+ case 3:
+ // lowercase one prefix letter, and delete following hyphen
+ // (which must be the immediately-preceding char)
+ NS_ASSERTION(str.Length() >= 2 && irishMark == str.Length() - 2,
+ "bad irishMark!");
+ MOZ_ASSERT(irishMark != uint32_t(-1) && irishMarkSrc != uint32_t(-1),
+ "failed to set irishMarks");
+ str.Replace(irishMark, 2, ToLowerCase(str[irishMark]));
+ aDeletedCharsArray[irishMarkSrc + 1] = true;
+ // Remove the trailing entries (corresponding to the deleted hyphen)
+ // from the auxiliary arrays.
+ aCharsToMergeArray.SetLength(aCharsToMergeArray.Length() - 1);
+ if (auxiliaryOutputArrays) {
+ aStyleArray->SetLength(aStyleArray->Length() - 1);
+ aCanBreakBeforeArray->SetLength(aCanBreakBeforeArray->Length() - 1);
+ inhibitBreakBefore = true;
+ }
+ mergeNeeded = true;
+ irishMark = uint32_t(-1);
+ irishMarkSrc = uint32_t(-1);
+ break;
+ }
+ // ch has been set to the uppercase for current char;
+ // No need to check for SpecialUpper here as none of the characters
+ // that could trigger an Irish casing action have special mappings.
+ break;
+ }
+ // If we didn't have any special action to perform, fall through
+ // to check for special uppercase (ß)
+ }
+
+ mcm = mozilla::unicode::SpecialUpper(ch);
+ if (mcm) {
+ int j = 0;
+ while (j < 2 && mcm->mMappedChars[j + 1]) {
+ aConvertedString.Append(mcm->mMappedChars[j]);
+ ++extraChars;
+ ++j;
+ }
+ ch = mcm->mMappedChars[j];
+ break;
+ }
+
+ ch = ToUpperCase(ch);
+ break;
+
+ case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
+ if (aTextRun) {
+ if (capitalizeDutchIJ && ch == 'j') {
+ ch = 'J';
+ capitalizeDutchIJ = false;
+ break;
+ }
+ capitalizeDutchIJ = false;
+ if (aOffsetInTextRun < aTextRun->mCapitalize.Length() &&
+ aTextRun->mCapitalize[aOffsetInTextRun]) {
+ if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
+ ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
+ break;
+ }
+ if (languageSpecificCasing == eLSCB_Dutch && ch == 'i') {
+ ch = 'I';
+ capitalizeDutchIJ = true;
+ break;
+ }
+
+ mcm = mozilla::unicode::SpecialTitle(ch);
+ if (mcm) {
+ int j = 0;
+ while (j < 2 && mcm->mMappedChars[j + 1]) {
+ aConvertedString.Append(mcm->mMappedChars[j]);
+ ++extraChars;
+ ++j;
+ }
+ ch = mcm->mMappedChars[j];
+ break;
+ }
+
+ ch = ToTitleCase(ch);
+ }
+ }
+ break;
+
+ case NS_STYLE_TEXT_TRANSFORM_FULL_WIDTH:
+ ch = mozilla::unicode::GetFullWidth(ch);
+ break;
+
+ default:
+ break;
+ }
+
+ if (forceNonFullWidth) {
+ ch = mozilla::unicode::GetFullWidthInverse(ch);
+ }
+
+ if (ch == uint32_t(-1)) {
+ aDeletedCharsArray.AppendElement(true);
+ mergeNeeded = true;
+ } else {
+ aDeletedCharsArray.AppendElement(false);
+ aCharsToMergeArray.AppendElement(false);
+ if (auxiliaryOutputArrays) {
+ aStyleArray->AppendElement(charStyle);
+ aCanBreakBeforeArray->AppendElement(
+ inhibitBreakBefore ? gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_NONE
+ : aTextRun->CanBreakBefore(aOffsetInTextRun));
+ }
+
+ if (IS_IN_BMP(ch)) {
+ aConvertedString.Append(ch);
+ } else {
+ aConvertedString.Append(H_SURROGATE(ch));
+ aConvertedString.Append(L_SURROGATE(ch));
+ i++;
+ aOffsetInTextRun++;
+ aDeletedCharsArray.AppendElement(true); // not exactly deleted, but the
+ // trailing surrogate is skipped
+ ++extraChars;
+ }
+
+ while (extraChars-- > 0) {
+ mergeNeeded = true;
+ aCharsToMergeArray.AppendElement(true);
+ if (auxiliaryOutputArrays) {
+ aStyleArray->AppendElement(charStyle);
+ aCanBreakBeforeArray->AppendElement(
+ gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_NONE);
+ }
+ }
+ }
+ }
+
+ return mergeNeeded;
+}
+
+void
+nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
+ DrawTarget* aRefDrawTarget,
+ gfxMissingFontRecorder* aMFR)
+{
+ nsAutoString convertedString;
+ AutoTArray<bool,50> charsToMergeArray;
+ AutoTArray<bool,50> deletedCharsArray;
+ AutoTArray<uint8_t,50> canBreakBeforeArray;
+ AutoTArray<RefPtr<nsTransformedCharStyle>,50> styleArray;
+
+ bool mergeNeeded = TransformString(aTextRun->mString,
+ convertedString,
+ mAllUppercase,
+ nullptr,
+ charsToMergeArray,
+ deletedCharsArray,
+ aTextRun, 0,
+ &canBreakBeforeArray,
+ &styleArray);
+
+ uint32_t flags;
+ gfxTextRunFactory::Parameters innerParams =
+ GetParametersForInner(aTextRun, &flags, aRefDrawTarget);
+ gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
+
+ RefPtr<nsTransformedTextRun> transformedChild;
+ RefPtr<gfxTextRun> cachedChild;
+ gfxTextRun* child;
+
+ if (mInnerTransformingTextRunFactory) {
+ transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
+ convertedString.BeginReading(), convertedString.Length(),
+ &innerParams, fontGroup, flags, Move(styleArray), false);
+ child = transformedChild.get();
+ } else {
+ cachedChild = fontGroup->MakeTextRun(
+ convertedString.BeginReading(), convertedString.Length(),
+ &innerParams, flags, aMFR);
+ child = cachedChild.get();
+ }
+ if (!child)
+ return;
+ // Copy potential linebreaks into child so they're preserved
+ // (and also child will be shaped appropriately)
+ NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
+ "Dropped characters or break-before values somewhere!");
+ gfxTextRun::Range range(0, uint32_t(canBreakBeforeArray.Length()));
+ child->SetPotentialLineBreaks(range, canBreakBeforeArray.Elements());
+ if (transformedChild) {
+ transformedChild->FinishSettingProperties(aRefDrawTarget, aMFR);
+ }
+
+ if (mergeNeeded) {
+ // Now merge multiple characters into one multi-glyph character as required
+ // and deal with skipping deleted accent chars
+ NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
+ "source length mismatch");
+ NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
+ "destination length mismatch");
+ MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
+ deletedCharsArray.Elements());
+ } else {
+ // No merging to do, so just copy; this produces a more optimized textrun.
+ // We can't steal the data because the child may be cached and stealing
+ // the data would break the cache.
+ aTextRun->ResetGlyphRuns();
+ aTextRun->CopyGlyphDataFrom(child, gfxTextRun::Range(child), 0);
+ }
+}
diff --git a/layout/generic/nsTextRunTransformations.h b/layout/generic/nsTextRunTransformations.h
new file mode 100644
index 000000000..662a5d2f8
--- /dev/null
+++ b/layout/generic/nsTextRunTransformations.h
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSTEXTRUNTRANSFORMATIONS_H_
+#define NSTEXTRUNTRANSFORMATIONS_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+#include "gfxTextRun.h"
+#include "nsStyleContext.h"
+
+class nsTransformedTextRun;
+
+struct nsTransformedCharStyle final {
+ NS_INLINE_DECL_REFCOUNTING(nsTransformedCharStyle)
+
+ explicit nsTransformedCharStyle(nsStyleContext* aContext)
+ : mFont(aContext->StyleFont()->mFont)
+ , mLanguage(aContext->StyleFont()->mLanguage)
+ , mPresContext(aContext->PresContext())
+ , mScriptSizeMultiplier(aContext->StyleFont()->mScriptSizeMultiplier)
+ , mTextTransform(aContext->StyleText()->mTextTransform)
+ , mMathVariant(aContext->StyleFont()->mMathVariant)
+ , mExplicitLanguage(aContext->StyleFont()->mExplicitLanguage) {}
+
+ nsFont mFont;
+ nsCOMPtr<nsIAtom> mLanguage;
+ RefPtr<nsPresContext> mPresContext;
+ float mScriptSizeMultiplier;
+ uint8_t mTextTransform;
+ uint8_t mMathVariant;
+ bool mExplicitLanguage;
+ bool mForceNonFullWidth = false;
+
+private:
+ ~nsTransformedCharStyle() {}
+ nsTransformedCharStyle(const nsTransformedCharStyle& aOther) = delete;
+ nsTransformedCharStyle& operator=(const nsTransformedCharStyle& aOther) = delete;
+};
+
+class nsTransformingTextRunFactory {
+public:
+ virtual ~nsTransformingTextRunFactory() {}
+
+ // Default 8-bit path just transforms to Unicode and takes that path
+ already_AddRefed<nsTransformedTextRun>
+ MakeTextRun(const uint8_t* aString, uint32_t aLength,
+ const gfxFontGroup::Parameters* aParams,
+ gfxFontGroup* aFontGroup, uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory);
+
+ already_AddRefed<nsTransformedTextRun>
+ MakeTextRun(const char16_t* aString, uint32_t aLength,
+ const gfxFontGroup::Parameters* aParams,
+ gfxFontGroup* aFontGroup, uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory);
+
+ virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
+ mozilla::gfx::DrawTarget* aRefDrawTarget,
+ gfxMissingFontRecorder* aMFR) = 0;
+};
+
+/**
+ * Builds textruns that transform the text in some way (e.g., capitalize)
+ * and then render the text using some other textrun implementation.
+ */
+class nsCaseTransformTextRunFactory : public nsTransformingTextRunFactory {
+public:
+ // We could add an optimization here so that when there is no inner
+ // factory, no title-case conversion, and no upper-casing of SZLIG, we override
+ // MakeTextRun (after making it virtual in the superclass) and have it
+ // just convert the string to uppercase or lowercase and create the textrun
+ // via the fontgroup.
+
+ // Takes ownership of aInnerTransformTextRunFactory
+ explicit nsCaseTransformTextRunFactory(UniquePtr<nsTransformingTextRunFactory> aInnerTransformingTextRunFactory,
+ bool aAllUppercase = false)
+ : mInnerTransformingTextRunFactory(Move(aInnerTransformingTextRunFactory)),
+ mAllUppercase(aAllUppercase) {}
+
+ virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
+ mozilla::gfx::DrawTarget* aRefDrawTarget,
+ gfxMissingFontRecorder* aMFR) override;
+
+ // Perform a transformation on the given string, writing the result into
+ // aConvertedString. If aAllUppercase is true, the transform is (global)
+ // upper-casing, and aLanguage is used to determine any language-specific
+ // behavior; otherwise, an nsTransformedTextRun should be passed in
+ // as aTextRun and its styles will be used to determine the transform(s)
+ // to be applied.
+ // If such an input textrun is provided, then its line-breaks and styles
+ // will be copied to the output arrays, which must also be provided by
+ // the caller. For the global upper-casing usage (no input textrun),
+ // these are ignored.
+ static bool TransformString(const nsAString& aString,
+ nsString& aConvertedString,
+ bool aAllUppercase,
+ const nsIAtom* aLanguage,
+ nsTArray<bool>& aCharsToMergeArray,
+ nsTArray<bool>& aDeletedCharsArray,
+ const nsTransformedTextRun* aTextRun = nullptr,
+ uint32_t aOffsetInTextRun = 0,
+ nsTArray<uint8_t>* aCanBreakBeforeArray = nullptr,
+ nsTArray<RefPtr<nsTransformedCharStyle>>* aStyleArray = nullptr);
+
+protected:
+ mozilla::UniquePtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
+ bool mAllUppercase;
+};
+
+/**
+ * So that we can reshape as necessary, we store enough information
+ * to fully rebuild the textrun contents.
+ */
+class nsTransformedTextRun final : public gfxTextRun {
+public:
+
+ static already_AddRefed<nsTransformedTextRun>
+ Create(const gfxTextRunFactory::Parameters* aParams,
+ nsTransformingTextRunFactory* aFactory,
+ gfxFontGroup* aFontGroup,
+ const char16_t* aString, uint32_t aLength,
+ const uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory);
+
+ ~nsTransformedTextRun() {
+ if (mOwnsFactory) {
+ delete mFactory;
+ }
+ }
+
+ void SetCapitalization(uint32_t aStart, uint32_t aLength,
+ bool* aCapitalization);
+ virtual bool SetPotentialLineBreaks(Range aRange,
+ const uint8_t* aBreakBefore);
+ /**
+ * Called after SetCapitalization and SetPotentialLineBreaks
+ * are done and before we request any data from the textrun. Also always
+ * called after a Create.
+ */
+ void FinishSettingProperties(mozilla::gfx::DrawTarget* aRefDrawTarget,
+ gfxMissingFontRecorder* aMFR)
+ {
+ if (mNeedsRebuild) {
+ mNeedsRebuild = false;
+ mFactory->RebuildTextRun(this, aRefDrawTarget, aMFR);
+ }
+ }
+
+ // override the gfxTextRun impls to account for additional members here
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) MOZ_MUST_OVERRIDE;
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) MOZ_MUST_OVERRIDE;
+
+ nsTransformingTextRunFactory *mFactory;
+ nsTArray<RefPtr<nsTransformedCharStyle>> mStyles;
+ nsTArray<bool> mCapitalize;
+ nsString mString;
+ bool mOwnsFactory;
+ bool mNeedsRebuild;
+
+private:
+ nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
+ nsTransformingTextRunFactory* aFactory,
+ gfxFontGroup* aFontGroup,
+ const char16_t* aString, uint32_t aLength,
+ const uint32_t aFlags,
+ nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
+ bool aOwnsFactory)
+ : gfxTextRun(aParams, aLength, aFontGroup, aFlags),
+ mFactory(aFactory), mStyles(aStyles), mString(aString, aLength),
+ mOwnsFactory(aOwnsFactory), mNeedsRebuild(true)
+ {
+ mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
+ }
+};
+
+/**
+ * Copy a given textrun, but merge certain characters into a single logical
+ * character. Glyphs for a character are added to the glyph list for the previous
+ * character and then the merged character is eliminated. Visually the results
+ * are identical.
+ *
+ * This is used for text-transform:uppercase when we encounter a SZLIG,
+ * whose uppercase form is "SS", or other ligature or precomposed form
+ * that expands to multiple codepoints during case transformation,
+ * and for Greek text when combining diacritics have been deleted.
+ *
+ * This function is unable to merge characters when they occur in different
+ * glyph runs. This only happens in tricky edge cases where a character was
+ * decomposed by case-mapping (e.g. there's no precomposed uppercase version
+ * of an accented lowercase letter), and then font-matching caused the
+ * diacritics to be assigned to a different font than the base character.
+ * In this situation, the diacritic(s) get discarded, which is less than
+ * ideal, but they probably weren't going to render very well anyway.
+ * Bug 543200 will improve this by making font-matching operate on entire
+ * clusters instead of individual codepoints.
+ *
+ * For simplicity, this produces a textrun containing all DetailedGlyphs,
+ * no simple glyphs. So don't call it unless you really have merging to do.
+ *
+ * @param aCharsToMerge when aCharsToMerge[i] is true, this character in aSrc
+ * is merged into the previous character
+ *
+ * @param aDeletedChars when aDeletedChars[i] is true, the character at this
+ * position in aDest was deleted (has no corresponding char in aSrc)
+ */
+void
+MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
+ const bool* aCharsToMerge, const bool* aDeletedChars);
+
+gfxTextRunFactory::Parameters
+GetParametersForInner(nsTransformedTextRun* aTextRun, uint32_t* aFlags,
+ mozilla::gfx::DrawTarget* aRefDrawTarget);
+
+
+#endif /*NSTEXTRUNTRANSFORMATIONS_H_*/
diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp
new file mode 100644
index 000000000..9f27684a7
--- /dev/null
+++ b/layout/generic/nsVideoFrame.cpp
@@ -0,0 +1,695 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for the HTML <video> element */
+
+#include "nsVideoFrame.h"
+
+#include "nsCOMPtr.h"
+#include "nsGkAtoms.h"
+
+#include "mozilla/dom/HTMLVideoElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsDisplayList.h"
+#include "nsGenericHTMLElement.h"
+#include "nsPresContext.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsBoxLayoutState.h"
+#include "nsBoxFrame.h"
+#include "nsImageFrame.h"
+#include "nsIImageLoadingContent.h"
+#include "nsContentUtils.h"
+#include "ImageContainer.h"
+#include "ImageLayers.h"
+#include "nsContentList.h"
+#include "nsStyleUtil.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+
+nsIFrame*
+NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsVideoFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
+
+// A matrix to obtain a correct-rotated video frame.
+static Matrix
+ComputeRotationMatrix(gfxFloat aRotatedWidth,
+ gfxFloat aRotatedHeight,
+ VideoInfo::Rotation aDegrees)
+{
+ Matrix shiftVideoCenterToOrigin;
+ if (aDegrees == VideoInfo::Rotation::kDegree_90 ||
+ aDegrees == VideoInfo::Rotation::kDegree_270) {
+ shiftVideoCenterToOrigin = Matrix::Translation(-aRotatedHeight / 2.0,
+ -aRotatedWidth / 2.0);
+ } else {
+ shiftVideoCenterToOrigin = Matrix::Translation(-aRotatedWidth / 2.0,
+ -aRotatedHeight / 2.0);
+ }
+
+ Matrix rotation = Matrix::Rotation(gfx::Float(aDegrees / 180.0 * M_PI));
+ Matrix shiftLeftTopToOrigin = Matrix::Translation(aRotatedWidth / 2.0,
+ aRotatedHeight / 2.0);
+ return shiftVideoCenterToOrigin * rotation * shiftLeftTopToOrigin;
+}
+
+static void
+SwapScaleWidthHeightForRotation(IntSize& aSize, VideoInfo::Rotation aDegrees)
+{
+ if (aDegrees == VideoInfo::Rotation::kDegree_90 ||
+ aDegrees == VideoInfo::Rotation::kDegree_270) {
+ int32_t tmpWidth = aSize.width;
+ aSize.width = aSize.height;
+ aSize.height = tmpWidth;
+ }
+}
+
+nsVideoFrame::nsVideoFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+{
+ EnableVisibilityTracking();
+}
+
+nsVideoFrame::~nsVideoFrame()
+{
+}
+
+NS_QUERYFRAME_HEAD(nsVideoFrame)
+ NS_QUERYFRAME_ENTRY(nsVideoFrame)
+ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+nsresult
+nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+ nsNodeInfoManager *nodeInfoManager = GetContent()->GetComposedDoc()->NodeInfoManager();
+ RefPtr<NodeInfo> nodeInfo;
+ Element *element;
+
+ if (HasVideoElement()) {
+ // Create an anonymous image element as a child to hold the poster
+ // image. We may not have a poster image now, but one could be added
+ // before we load, or on a subsequent load.
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::img,
+ nullptr,
+ kNameSpaceID_XHTML,
+ nsIDOMNode::ELEMENT_NODE);
+ NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+ element = NS_NewHTMLImageElement(nodeInfo.forget());
+ mPosterImage = element;
+ NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY);
+
+ // Set the nsImageLoadingContent::ImageState() to 0. This means that the
+ // image will always report its state as 0, so it will never be reframed
+ // to show frames for loading or the broken image icon. This is important,
+ // as the image is native anonymous, and so can't be reframed (currently).
+ nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
+ NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE);
+
+ imgContent->ForceImageState(true, 0);
+ // And now have it update its internal state
+ element->UpdateState(false);
+
+ UpdatePosterSource(false);
+
+ if (!aElements.AppendElement(mPosterImage))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Set up the caption overlay div for showing any TextTrack data
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::div,
+ nullptr,
+ kNameSpaceID_XHTML,
+ nsIDOMNode::ELEMENT_NODE);
+ NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+ mCaptionDiv = NS_NewHTMLDivElement(nodeInfo.forget());
+ NS_ENSURE_TRUE(mCaptionDiv, NS_ERROR_OUT_OF_MEMORY);
+ nsGenericHTMLElement* div = static_cast<nsGenericHTMLElement*>(mCaptionDiv.get());
+ div->SetClassName(NS_LITERAL_STRING("caption-box"));
+
+ if (!aElements.AppendElement(mCaptionDiv))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Set up "videocontrols" XUL element which will be XBL-bound to the
+ // actual controls.
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::videocontrols,
+ nullptr,
+ kNameSpaceID_XUL,
+ nsIDOMNode::ELEMENT_NODE);
+ NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+ NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
+ if (!aElements.AppendElement(mVideoControls))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+void
+nsVideoFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFliter)
+{
+ if (mPosterImage) {
+ aElements.AppendElement(mPosterImage);
+ }
+
+ if (mVideoControls) {
+ aElements.AppendElement(mVideoControls);
+ }
+
+ if (mCaptionDiv) {
+ aElements.AppendElement(mCaptionDiv);
+ }
+}
+
+void
+nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ nsContentUtils::DestroyAnonymousContent(&mCaptionDiv);
+ nsContentUtils::DestroyAnonymousContent(&mVideoControls);
+ nsContentUtils::DestroyAnonymousContent(&mPosterImage);
+ nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
+bool
+nsVideoFrame::IsLeaf() const
+{
+ return true;
+}
+
+already_AddRefed<Layer>
+nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ const ContainerLayerParameters& aContainerParameters)
+{
+ nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
+ HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
+
+ nsIntSize videoSizeInPx;
+ if (NS_FAILED(element->GetVideoSize(&videoSizeInPx)) ||
+ area.IsEmpty()) {
+ return nullptr;
+ }
+
+ RefPtr<ImageContainer> container = element->GetImageContainer();
+ if (!container)
+ return nullptr;
+
+ // Retrieve the size of the decoded video frame, before being scaled
+ // by pixel aspect ratio.
+ mozilla::gfx::IntSize frameSize = container->GetCurrentSize();
+ if (frameSize.width == 0 || frameSize.height == 0) {
+ // No image, or zero-sized image. No point creating a layer.
+ return nullptr;
+ }
+
+ // Convert video size from pixel units into app units, to get an aspect-ratio
+ // (which has to be represented as a nsSize) and an IntrinsicSize that we
+ // can pass to ComputeObjectRenderRect.
+ nsSize aspectRatio(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.width),
+ nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.height));
+ IntrinsicSize intrinsicSize;
+ intrinsicSize.width.SetCoordValue(aspectRatio.width);
+ intrinsicSize.height.SetCoordValue(aspectRatio.height);
+
+ nsRect dest = nsLayoutUtils::ComputeObjectDestRect(area,
+ intrinsicSize,
+ aspectRatio,
+ StylePosition());
+
+ gfxRect destGFXRect = PresContext()->AppUnitsToGfxUnits(dest);
+ destGFXRect.Round();
+ if (destGFXRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ VideoInfo::Rotation rotationDeg = element->RotationDegrees();
+ IntSize scaleHint(static_cast<int32_t>(destGFXRect.Width()),
+ static_cast<int32_t>(destGFXRect.Height()));
+ // scaleHint is set regardless of rotation, so swap w/h if needed.
+ SwapScaleWidthHeightForRotation(scaleHint, rotationDeg);
+ container->SetScaleHint(scaleHint);
+
+ RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
+ (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
+ if (!layer) {
+ layer = aManager->CreateImageLayer();
+ if (!layer)
+ return nullptr;
+ }
+
+ layer->SetContainer(container);
+ layer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
+ // Set a transform on the layer to draw the video in the right place
+ gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
+
+ Matrix preTransform = ComputeRotationMatrix(destGFXRect.Width(),
+ destGFXRect.Height(),
+ rotationDeg);
+
+ Matrix transform = preTransform * Matrix::Translation(p.x, p.y);
+
+ layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
+ layer->SetScaleToSize(scaleHint, ScaleMode::STRETCH);
+ RefPtr<Layer> result = layer.forget();
+ return result.forget();
+}
+
+class DispatchResizeToControls : public Runnable
+{
+public:
+ explicit DispatchResizeToControls(nsIContent* aContent)
+ : mContent(aContent) {}
+ NS_IMETHOD Run() override {
+ nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
+ NS_LITERAL_STRING("resizevideocontrols"),
+ false, false);
+ return NS_OK;
+ }
+ nsCOMPtr<nsIContent> mContent;
+};
+
+void
+nsVideoFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("enter nsVideoFrame::Reflow: availSize=%d,%d",
+ aReflowInput.AvailableWidth(),
+ aReflowInput.AvailableHeight()));
+
+ NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
+
+ aStatus = NS_FRAME_COMPLETE;
+
+ aMetrics.Width() = aReflowInput.ComputedWidth();
+ aMetrics.Height() = aReflowInput.ComputedHeight();
+
+ // stash this away so we can compute our inner area later
+ mBorderPadding = aReflowInput.ComputedPhysicalBorderPadding();
+
+ aMetrics.Width() += mBorderPadding.left + mBorderPadding.right;
+ aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom;
+
+ // Reflow the child frames. We may have up to two, an image frame
+ // which is the poster, and a box frame, which is the video controls.
+ for (nsIFrame* child : mFrames) {
+ if (child->GetContent() == mPosterImage) {
+ // Reflow the poster frame.
+ nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
+ ReflowOutput kidDesiredSize(aReflowInput);
+ WritingMode wm = imageFrame->GetWritingMode();
+ LogicalSize availableSize = aReflowInput.AvailableSize(wm);
+ LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
+ ConvertTo(wm, aMetrics.GetWritingMode());
+ ReflowInput kidReflowInput(aPresContext,
+ aReflowInput,
+ imageFrame,
+ availableSize,
+ &cbSize);
+
+ nsRect posterRenderRect;
+ if (ShouldDisplayPoster()) {
+ posterRenderRect =
+ nsRect(nsPoint(mBorderPadding.left, mBorderPadding.top),
+ nsSize(aReflowInput.ComputedWidth(),
+ aReflowInput.ComputedHeight()));
+ }
+ kidReflowInput.SetComputedWidth(posterRenderRect.width);
+ kidReflowInput.SetComputedHeight(posterRenderRect.height);
+ ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput,
+ posterRenderRect.x, posterRenderRect.y, 0, aStatus);
+ FinishReflowChild(imageFrame, aPresContext,
+ kidDesiredSize, &kidReflowInput,
+ posterRenderRect.x, posterRenderRect.y, 0);
+ } else if (child->GetContent() == mVideoControls) {
+ // Reflow the video controls frame.
+ nsBoxLayoutState boxState(PresContext(), aReflowInput.mRenderingContext);
+ nsSize size = child->GetSize();
+ nsBoxFrame::LayoutChildAt(boxState,
+ child,
+ nsRect(mBorderPadding.left,
+ mBorderPadding.top,
+ aReflowInput.ComputedWidth(),
+ aReflowInput.ComputedHeight()));
+ if (child->GetSize() != size) {
+ RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent());
+ nsContentUtils::AddScriptRunner(event);
+ }
+ } else if (child->GetContent() == mCaptionDiv) {
+ // Reflow to caption div
+ ReflowOutput kidDesiredSize(aReflowInput);
+ WritingMode wm = child->GetWritingMode();
+ LogicalSize availableSize = aReflowInput.AvailableSize(wm);
+ LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
+ ConvertTo(wm, aMetrics.GetWritingMode());
+ ReflowInput kidReflowInput(aPresContext,
+ aReflowInput,
+ child,
+ availableSize,
+ &cbSize);
+ nsSize size(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
+ size.width -= kidReflowInput.ComputedPhysicalBorderPadding().LeftRight();
+ size.height -= kidReflowInput.ComputedPhysicalBorderPadding().TopBottom();
+
+ kidReflowInput.SetComputedWidth(std::max(size.width, 0));
+ kidReflowInput.SetComputedHeight(std::max(size.height, 0));
+
+ ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput,
+ mBorderPadding.left, mBorderPadding.top, 0, aStatus);
+ FinishReflowChild(child, aPresContext,
+ kidDesiredSize, &kidReflowInput,
+ mBorderPadding.left, mBorderPadding.top, 0);
+ }
+ }
+ aMetrics.SetOverflowAreasToDesiredBounds();
+
+ FinishAndStoreOverflow(&aMetrics);
+
+ NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
+ ("exit nsVideoFrame::Reflow: size=%d,%d",
+ aMetrics.Width(), aMetrics.Height()));
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
+}
+
+class nsDisplayVideo : public nsDisplayItem {
+public:
+ nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame)
+ : nsDisplayItem(aBuilder, aFrame)
+ {
+ MOZ_COUNT_CTOR(nsDisplayVideo);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayVideo() {
+ MOZ_COUNT_DTOR(nsDisplayVideo);
+ }
+#endif
+
+ NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
+
+ // It would be great if we could override GetOpaqueRegion to return nonempty here,
+ // but it's probably not safe to do so in general. Video frames are
+ // updated asynchronously from decoder threads, and it's possible that
+ // we might have an opaque video frame when GetOpaqueRegion is called, but
+ // when we come to paint, the video frame is transparent or has gone
+ // away completely (e.g. because of a decoder error). The problem would
+ // be especially acute if we have off-main-thread rendering.
+
+ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
+ {
+ *aSnap = true;
+ nsIFrame* f = Frame();
+ return f->GetContentRectRelativeToSelf() + ToReferenceFrame();
+ }
+
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) override
+ {
+ return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
+ }
+
+ virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) override
+ {
+ if (aManager->IsCompositingCheap()) {
+ // Since ImageLayers don't require additional memory of the
+ // video frames we have to have anyway, we can't save much by
+ // making layers inactive. Also, for many accelerated layer
+ // managers calling imageContainer->GetCurrentAsSurface can be
+ // very expensive. So just always be active when compositing is
+ // cheap (i.e. hardware accelerated).
+ return LAYER_ACTIVE;
+ }
+ HTMLMediaElement* elem =
+ static_cast<HTMLMediaElement*>(mFrame->GetContent());
+ return elem->IsPotentiallyPlaying() ? LAYER_ACTIVE_FORCE : LAYER_INACTIVE;
+ }
+};
+
+void
+nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ if (!IsVisibleForPainting(aBuilder))
+ return;
+
+ DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame");
+
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ const bool shouldDisplayPoster = ShouldDisplayPoster();
+
+ // NOTE: If we're displaying a poster image (instead of video data), we can
+ // trust the nsImageFrame to constrain its drawing to its content rect
+ // (which happens to be the same as our content rect).
+ uint32_t clipFlags;
+ if (shouldDisplayPoster ||
+ !nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition())) {
+ clipFlags =
+ DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
+ } else {
+ clipFlags = 0;
+ }
+
+ DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
+ clip(aBuilder, this, clipFlags);
+
+ if (HasVideoElement() && !shouldDisplayPoster) {
+ aLists.Content()->AppendNewToTop(
+ new (aBuilder) nsDisplayVideo(aBuilder, this));
+ }
+
+ // Add child frames to display list. We expect various children,
+ // but only want to draw mPosterImage conditionally. Others we
+ // always add to the display list.
+ for (nsIFrame* child : mFrames) {
+ if (child->GetContent() != mPosterImage || shouldDisplayPoster) {
+ child->BuildDisplayListForStackingContext(aBuilder,
+ aDirtyRect - child->GetOffsetTo(this),
+ aLists.Content());
+ } else if (child->GetType() == nsGkAtoms::boxFrame) {
+ child->BuildDisplayListForStackingContext(aBuilder,
+ aDirtyRect - child->GetOffsetTo(this),
+ aLists.Content());
+ }
+ }
+}
+
+nsIAtom*
+nsVideoFrame::GetType() const
+{
+ return nsGkAtoms::HTMLVideoFrame;
+}
+
+#ifdef ACCESSIBILITY
+a11y::AccType
+nsVideoFrame::AccessibleType()
+{
+ return a11y::eHTMLMediaType;
+}
+#endif
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+nsVideoFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("HTMLVideo"), aResult);
+}
+#endif
+
+LogicalSize
+nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext,
+ WritingMode aWM,
+ const LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const LogicalSize& aMargin,
+ const LogicalSize& aBorder,
+ const LogicalSize& aPadding,
+ ComputeSizeFlags aFlags)
+{
+ nsSize size = GetVideoIntrinsicSize(aRenderingContext);
+
+ IntrinsicSize intrinsicSize;
+ intrinsicSize.width.SetCoordValue(size.width);
+ intrinsicSize.height.SetCoordValue(size.height);
+
+ // Only video elements have an intrinsic ratio.
+ nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0);
+
+ return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
+ intrinsicSize, intrinsicRatio,
+ aCBSize, aMargin, aBorder, aPadding,
+ aFlags);
+}
+
+nscoord nsVideoFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nsSize size = GetVideoIntrinsicSize(aRenderingContext);
+ nscoord result = GetWritingMode().IsVertical() ? size.height : size.width;
+ DISPLAY_MIN_WIDTH(this, result);
+ return result;
+}
+
+nscoord nsVideoFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nsSize size = GetVideoIntrinsicSize(aRenderingContext);
+ nscoord result = GetWritingMode().IsVertical() ? size.height : size.width;
+ DISPLAY_PREF_WIDTH(this, result);
+ return result;
+}
+
+nsSize nsVideoFrame::GetIntrinsicRatio()
+{
+ if (!HasVideoElement()) {
+ // Audio elements have no intrinsic ratio.
+ return nsSize(0, 0);
+ }
+
+ return GetVideoIntrinsicSize(nullptr);
+}
+
+bool nsVideoFrame::ShouldDisplayPoster()
+{
+ if (!HasVideoElement())
+ return false;
+
+ HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
+ if (element->GetPlayedOrSeeked() && HasVideoData())
+ return false;
+
+ nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
+ NS_ENSURE_TRUE(imgContent, false);
+
+ nsCOMPtr<imgIRequest> request;
+ nsresult res = imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(request));
+ if (NS_FAILED(res) || !request) {
+ return false;
+ }
+
+ uint32_t status = 0;
+ res = request->GetImageStatus(&status);
+ if (NS_FAILED(res) || (status & imgIRequest::STATUS_ERROR))
+ return false;
+
+ return true;
+}
+
+nsSize
+nsVideoFrame::GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext)
+{
+ // Defaulting size to 300x150 if no size given.
+ nsIntSize size(300, 150);
+
+ if (!HasVideoElement()) {
+ if (!mFrames.FirstChild()) {
+ return nsSize(0, 0);
+ }
+
+ // Ask the controls frame what its preferred height is
+ nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0);
+ nscoord prefHeight = mFrames.LastChild()->GetXULPrefSize(boxState).height;
+ return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight);
+ }
+
+ HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
+ if (NS_FAILED(element->GetVideoSize(&size)) && ShouldDisplayPoster()) {
+ // Use the poster image frame's size.
+ nsIFrame *child = mPosterImage->GetPrimaryFrame();
+ nsImageFrame* imageFrame = do_QueryFrame(child);
+ nsSize imgsize;
+ if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) {
+ return imgsize;
+ }
+ }
+
+ return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
+ nsPresContext::CSSPixelsToAppUnits(size.height));
+}
+
+void
+nsVideoFrame::UpdatePosterSource(bool aNotify)
+{
+ NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
+ HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
+
+ if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster) &&
+ !element->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::poster,
+ nsGkAtoms::_empty,
+ eIgnoreCase)) {
+ nsAutoString posterStr;
+ element->GetPoster(posterStr);
+ mPosterImage->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::src,
+ posterStr,
+ aNotify);
+ } else {
+ mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::src, aNotify);
+ }
+}
+
+nsresult
+nsVideoFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ if (aAttribute == nsGkAtoms::poster && HasVideoElement()) {
+ UpdatePosterSource(true);
+ }
+ return nsContainerFrame::AttributeChanged(aNameSpaceID,
+ aAttribute,
+ aModType);
+}
+
+void
+nsVideoFrame::OnVisibilityChange(Visibility aNewVisibility,
+ Maybe<OnNonvisible> aNonvisibleAction)
+{
+ if (HasVideoElement()) {
+ nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
+ mediaDomElement->OnVisibilityChange(aNewVisibility);
+ }
+
+ nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mPosterImage);
+ if (imageLoader) {
+ imageLoader->OnVisibilityChange(aNewVisibility,
+ aNonvisibleAction);
+ }
+
+ nsContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
+}
+
+bool nsVideoFrame::HasVideoElement() {
+ nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
+ return mediaDomElement->IsVideo();
+}
+
+bool nsVideoFrame::HasVideoData()
+{
+ if (!HasVideoElement())
+ return false;
+ HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
+ nsIntSize size(0, 0);
+ element->GetVideoSize(&size);
+ return size != nsIntSize(0,0);
+}
diff --git a/layout/generic/nsVideoFrame.h b/layout/generic/nsVideoFrame.h
new file mode 100644
index 000000000..36e9f9ac3
--- /dev/null
+++ b/layout/generic/nsVideoFrame.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for the HTML <video> element */
+
+#ifndef nsVideoFrame_h___
+#define nsVideoFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+#include "nsIAnonymousContentCreator.h"
+#include "nsTArrayForwardDeclare.h"
+#include "FrameLayerBuilder.h"
+
+namespace mozilla {
+namespace layers {
+class Layer;
+class LayerManager;
+} // namespace layers
+} // namespace mozilla
+
+class nsAString;
+class nsPresContext;
+class nsDisplayItem;
+
+class nsVideoFrame : public nsContainerFrame
+ , public nsIAnonymousContentCreator
+{
+public:
+ template <typename T> using Maybe = mozilla::Maybe<T>;
+ using Nothing = mozilla::Nothing;
+ using Visibility = mozilla::Visibility;
+
+ typedef mozilla::layers::Layer Layer;
+ typedef mozilla::layers::LayerManager LayerManager;
+ typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
+
+ explicit nsVideoFrame(nsStyleContext* aContext);
+
+ NS_DECL_QUERYFRAME
+ NS_DECL_QUERYFRAME_TARGET(nsVideoFrame)
+ NS_DECL_FRAMEARENA_HELPERS
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ virtual nsresult AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType) override;
+
+ void OnVisibilityChange(Visibility aNewVisibility,
+ Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
+
+ /* get the size of the video's display */
+ nsSize GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext);
+ virtual nsSize GetIntrinsicRatio() override;
+ virtual mozilla::LogicalSize
+ ComputeSize(nsRenderingContext *aRenderingContext,
+ mozilla::WritingMode aWritingMode,
+ const mozilla::LogicalSize& aCBSize,
+ nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorder,
+ const mozilla::LogicalSize& aPadding,
+ ComputeSizeFlags aFlags) override;
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+ virtual bool IsLeaf() const override;
+
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+#ifdef ACCESSIBILITY
+ virtual mozilla::a11y::AccType AccessibleType() override;
+#endif
+
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool IsFrameOfType(uint32_t aFlags) const override
+ {
+ return nsSplittableFrame::IsFrameOfType(aFlags &
+ ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
+ }
+
+ virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
+ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
+ uint32_t aFilters) override;
+
+ nsIContent* GetPosterImage() { return mPosterImage; }
+
+ // Returns true if we should display the poster. Note that once we show
+ // a video frame, the poster will never be displayed again.
+ bool ShouldDisplayPoster();
+
+ nsIContent *GetCaptionOverlay() { return mCaptionDiv; }
+
+ nsIContent *GetVideoControls() { return mVideoControls; }
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+ already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ const ContainerLayerParameters& aContainerParameters);
+
+protected:
+
+ // Returns true if we're rendering for a video element. We still create
+ // nsVideoFrame to render controls for an audio element.
+ bool HasVideoElement();
+
+ // Returns true if there is video data to render. Can return false
+ // when we're the frame for an audio element, or we've created a video
+ // element for a media which is audio-only.
+ bool HasVideoData();
+
+ // Sets the mPosterImage's src attribute to be the video's poster attribute,
+ // if we're the frame for a video element. Only call on frames for video
+ // elements, not for frames for audio elements.
+ void UpdatePosterSource(bool aNotify);
+
+ virtual ~nsVideoFrame();
+
+ nsMargin mBorderPadding;
+
+ // Anonymous child which is bound via XBL to the video controls.
+ nsCOMPtr<nsIContent> mVideoControls;
+
+ // Anonymous child which is the image element of the poster frame.
+ nsCOMPtr<nsIContent> mPosterImage;
+
+ // Anonymous child which is the text track caption display div.
+ nsCOMPtr<nsIContent> mCaptionDiv;
+
+};
+
+#endif /* nsVideoFrame_h___ */
diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp
new file mode 100644
index 000000000..39491a0ed
--- /dev/null
+++ b/layout/generic/nsViewportFrame.cpp
@@ -0,0 +1,416 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object that is the root of the frame tree, which contains
+ * the document's scrollbars and contains fixed-positioned elements
+ */
+
+#include "nsViewportFrame.h"
+#include "nsGkAtoms.h"
+#include "nsIScrollableFrame.h"
+#include "nsSubDocumentFrame.h"
+#include "nsCanvasFrame.h"
+#include "nsAbsoluteContainingBlock.h"
+#include "GeckoProfiler.h"
+#include "nsIMozBrowserFrame.h"
+
+using namespace mozilla;
+typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
+
+ViewportFrame*
+NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) ViewportFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(ViewportFrame)
+NS_QUERYFRAME_HEAD(ViewportFrame)
+ NS_QUERYFRAME_ENTRY(ViewportFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
+
+void
+ViewportFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
+ if (parent) {
+ nsFrameState state = parent->GetStateBits();
+
+ mState |= state & (NS_FRAME_IN_POPUP);
+ }
+}
+
+void
+ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ PROFILER_LABEL("ViewportFrame", "BuildDisplayList",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (nsIFrame* kid = mFrames.FirstChild()) {
+ // make the kid's BorderBackground our own. This ensures that the canvas
+ // frame's background becomes our own background and therefore appears
+ // below negative z-index elements.
+ BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
+ }
+
+ nsDisplayList topLayerList;
+ BuildDisplayListForTopLayer(aBuilder, &topLayerList);
+ if (!topLayerList.IsEmpty()) {
+ // Wrap the whole top layer in a single item with maximum z-index,
+ // and append it at the very end, so that it stays at the topmost.
+ nsDisplayWrapList* wrapList =
+ new (aBuilder) nsDisplayWrapList(aBuilder, this, &topLayerList);
+ wrapList->SetOverrideZIndex(
+ std::numeric_limits<decltype(wrapList->ZIndex())>::max());
+ aLists.PositionedDescendants()->AppendNewToTop(wrapList);
+ }
+}
+
+#ifdef DEBUG
+/**
+ * Returns whether we are going to put an element in the top layer for
+ * fullscreen. This function should matches the CSS rule in ua.css.
+ */
+static bool
+ShouldInTopLayerForFullscreen(Element* aElement)
+{
+ if (!aElement->GetParent()) {
+ return false;
+ }
+ nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aElement);
+ if (browserFrame && browserFrame->GetReallyIsBrowserOrApp()) {
+ return false;
+ }
+ return true;
+}
+#endif // DEBUG
+
+static void
+BuildDisplayListForTopLayerFrame(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame,
+ nsDisplayList* aList)
+{
+ nsRect dirty;
+ DisplayListClipState::AutoClipMultiple clipState(aBuilder);
+ nsDisplayListBuilder::OutOfFlowDisplayData*
+ savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(aFrame);
+ if (savedOutOfFlowData) {
+ dirty = savedOutOfFlowData->mDirtyRect;
+ clipState.SetClipForContainingBlockDescendants(
+ &savedOutOfFlowData->mContainingBlockClip);
+ clipState.SetScrollClipForContainingBlockDescendants(
+ aBuilder, savedOutOfFlowData->mContainingBlockScrollClip);
+ }
+ nsDisplayList list;
+ aFrame->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
+ aList->AppendToTop(&list);
+}
+
+void
+ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
+ nsDisplayList* aList)
+{
+ nsIDocument* doc = PresContext()->Document();
+ nsTArray<Element*> fullscreenStack = doc->GetFullscreenStack();
+ for (Element* elem : fullscreenStack) {
+ if (nsIFrame* frame = elem->GetPrimaryFrame()) {
+ // There are two cases where an element in fullscreen is not in
+ // the top layer:
+ // 1. When building display list for purpose other than painting,
+ // it is possible that there is inconsistency between the style
+ // info and the content tree.
+ // 2. This is an element which we are not going to put in the top
+ // layer for fullscreen. See ShouldInTopLayerForFullscreen().
+ // In both cases, we want to skip the frame here and paint it in
+ // the normal path.
+ if (frame->StyleDisplay()->mTopLayer == NS_STYLE_TOP_LAYER_NONE) {
+ MOZ_ASSERT(!aBuilder->IsForPainting() ||
+ !ShouldInTopLayerForFullscreen(elem));
+ continue;
+ }
+ MOZ_ASSERT(ShouldInTopLayerForFullscreen(elem));
+ // Inner SVG, MathML elements, as well as children of some XUL
+ // elements are not allowed to be out-of-flow. They should not
+ // be handled as top layer element here.
+ if (!(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ MOZ_ASSERT(!elem->GetParent()->IsHTMLElement(), "HTML element "
+ "should always be out-of-flow if in the top layer");
+ continue;
+ }
+ if (nsIFrame* backdropPh =
+ frame->GetChildList(kBackdropList).FirstChild()) {
+ MOZ_ASSERT(backdropPh->GetType() == nsGkAtoms::placeholderFrame);
+ nsIFrame* backdropFrame =
+ static_cast<nsPlaceholderFrame*>(backdropPh)->GetOutOfFlowFrame();
+ MOZ_ASSERT(backdropFrame);
+ BuildDisplayListForTopLayerFrame(aBuilder, backdropFrame, aList);
+ }
+ BuildDisplayListForTopLayerFrame(aBuilder, frame, aList);
+ }
+ }
+
+ nsIPresShell* shell = PresContext()->PresShell();
+ if (nsCanvasFrame* canvasFrame = shell->GetCanvasFrame()) {
+ if (Element* container = canvasFrame->GetCustomContentContainer()) {
+ if (nsIFrame* frame = container->GetPrimaryFrame()) {
+ BuildDisplayListForTopLayerFrame(aBuilder, frame, aList);
+ }
+ }
+ }
+}
+
+#ifdef DEBUG
+void
+ViewportFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
+ NS_ASSERTION(GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!");
+ nsContainerFrame::AppendFrames(aListID, aFrameList);
+}
+
+void
+ViewportFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
+ NS_ASSERTION(GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!");
+ nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
+}
+
+void
+ViewportFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
+ nsContainerFrame::RemoveFrame(aListID, aOldFrame);
+}
+#endif
+
+/* virtual */ nscoord
+ViewportFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_MIN_WIDTH(this, result);
+ if (mFrames.IsEmpty())
+ result = 0;
+ else
+ result = mFrames.FirstChild()->GetMinISize(aRenderingContext);
+
+ return result;
+}
+
+/* virtual */ nscoord
+ViewportFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord result;
+ DISPLAY_PREF_WIDTH(this, result);
+ if (mFrames.IsEmpty())
+ result = 0;
+ else
+ result = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
+
+ return result;
+}
+
+nsPoint
+ViewportFrame::AdjustReflowInputForScrollbars(ReflowInput* aReflowInput) const
+{
+ // Get our prinicpal child frame and see if we're scrollable
+ nsIFrame* kidFrame = mFrames.FirstChild();
+ nsIScrollableFrame* scrollingFrame = do_QueryFrame(kidFrame);
+
+ if (scrollingFrame) {
+ WritingMode wm = aReflowInput->GetWritingMode();
+ LogicalMargin scrollbars(wm, scrollingFrame->GetActualScrollbarSizes());
+ aReflowInput->SetComputedISize(aReflowInput->ComputedISize() -
+ scrollbars.IStartEnd(wm));
+ aReflowInput->AvailableISize() -= scrollbars.IStartEnd(wm);
+ aReflowInput->SetComputedBSizeWithoutResettingResizeFlags(
+ aReflowInput->ComputedBSize() - scrollbars.BStartEnd(wm));
+ return nsPoint(scrollbars.Left(wm), scrollbars.Top(wm));
+ }
+ return nsPoint(0, 0);
+}
+
+nsRect
+ViewportFrame::AdjustReflowInputAsContainingBlock(ReflowInput* aReflowInput) const
+{
+#ifdef DEBUG
+ nsPoint offset =
+#endif
+ AdjustReflowInputForScrollbars(aReflowInput);
+
+ NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() ||
+ (offset.x == 0 && offset.y == 0),
+ "We don't handle correct positioning of fixed frames with "
+ "scrollbars in odd positions");
+
+ // If a scroll position clamping scroll-port size has been set, layout
+ // fixed position elements to this size instead of the computed size.
+ nsRect rect(0, 0, aReflowInput->ComputedWidth(), aReflowInput->ComputedHeight());
+ nsIPresShell* ps = PresContext()->PresShell();
+ if (ps->IsScrollPositionClampingScrollPortSizeSet()) {
+ rect.SizeTo(ps->GetScrollPositionClampingScrollPortSize());
+ }
+
+ return rect;
+}
+
+void
+ViewportFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("ViewportFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");
+
+ // Initialize OUT parameters
+ aStatus = NS_FRAME_COMPLETE;
+
+ // Because |Reflow| sets ComputedBSize() on the child to our
+ // ComputedBSize().
+ AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+
+ // Set our size up front, since some parts of reflow depend on it
+ // being already set. Note that the computed height may be
+ // unconstrained; that's ok. Consumers should watch out for that.
+ SetSize(nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()));
+
+ // Reflow the main content first so that the placeholders of the
+ // fixed-position frames will be in the right places on an initial
+ // reflow.
+ nscoord kidBSize = 0;
+ WritingMode wm = aReflowInput.GetWritingMode();
+
+ if (mFrames.NotEmpty()) {
+ // Deal with a non-incremental reflow or an incremental reflow
+ // targeted at our one-and-only principal child frame.
+ if (aReflowInput.ShouldReflowAllKids() ||
+ aReflowInput.IsBResize() ||
+ NS_SUBTREE_DIRTY(mFrames.FirstChild())) {
+ // Reflow our one-and-only principal child frame
+ nsIFrame* kidFrame = mFrames.FirstChild();
+ ReflowOutput kidDesiredSize(aReflowInput);
+ WritingMode wm = kidFrame->GetWritingMode();
+ LogicalSize availableSpace = aReflowInput.AvailableSize(wm);
+ ReflowInput kidReflowInput(aPresContext, aReflowInput,
+ kidFrame, availableSpace);
+
+ // Reflow the frame
+ kidReflowInput.SetComputedBSize(aReflowInput.ComputedBSize());
+ ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput,
+ 0, 0, 0, aStatus);
+ kidBSize = kidDesiredSize.BSize(wm);
+
+ FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, nullptr, 0, 0, 0);
+ } else {
+ kidBSize = LogicalSize(wm, mFrames.FirstChild()->GetSize()).BSize(wm);
+ }
+ }
+
+ NS_ASSERTION(aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE,
+ "shouldn't happen anymore");
+
+ // Return the max size as our desired size
+ LogicalSize maxSize(wm, aReflowInput.AvailableISize(),
+ // Being flowed initially at an unconstrained block size
+ // means we should return our child's intrinsic size.
+ aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE
+ ? aReflowInput.ComputedBSize()
+ : kidBSize);
+ aDesiredSize.SetSize(wm, maxSize);
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+
+ if (HasAbsolutelyPositionedChildren()) {
+ // Make a copy of the reflow state and change the computed width and height
+ // to reflect the available space for the fixed items
+ ReflowInput reflowInput(aReflowInput);
+
+ if (reflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
+ // We have an intrinsic-height document with abs-pos/fixed-pos children.
+ // Set the available height and mComputedHeight to our chosen height.
+ reflowInput.AvailableBSize() = maxSize.BSize(wm);
+ // Not having border/padding simplifies things
+ NS_ASSERTION(reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0,0,0,0),
+ "Viewports can't have border/padding");
+ reflowInput.SetComputedBSize(maxSize.BSize(wm));
+ }
+
+ nsRect rect = AdjustReflowInputAsContainingBlock(&reflowInput);
+ nsOverflowAreas* overflowAreas = &aDesiredSize.mOverflowAreas;
+ nsIScrollableFrame* rootScrollFrame =
+ aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
+ if (rootScrollFrame && !rootScrollFrame->IsIgnoringViewportClipping()) {
+ overflowAreas = nullptr;
+ }
+ AbsPosReflowFlags flags =
+ AbsPosReflowFlags::eCBWidthAndHeightChanged; // XXX could be optimized
+ GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowInput, aStatus,
+ rect, flags, overflowAreas);
+ }
+
+ if (mFrames.NotEmpty()) {
+ ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mFrames.FirstChild());
+ }
+
+ // If we were dirty then do a repaint
+ if (GetStateBits() & NS_FRAME_IS_DIRTY) {
+ InvalidateFrame();
+ }
+
+ // Clipping is handled by the document container (e.g., nsSubDocumentFrame),
+ // so we don't need to change our overflow areas.
+ bool overflowChanged = FinishAndStoreOverflow(&aDesiredSize);
+ if (overflowChanged) {
+ // We may need to alert our container to get it to pick up the
+ // overflow change.
+ nsSubDocumentFrame* container = static_cast<nsSubDocumentFrame*>
+ (nsLayoutUtils::GetCrossDocParentFrame(this));
+ if (container && !container->ShouldClipSubdocument()) {
+ container->PresContext()->PresShell()->
+ FrameNeedsReflow(container, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
+ }
+
+ NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus);
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+bool
+ViewportFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
+{
+ nsIScrollableFrame* rootScrollFrame =
+ PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
+ if (rootScrollFrame && !rootScrollFrame->IsIgnoringViewportClipping()) {
+ return false;
+ }
+
+ return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
+}
+
+nsIAtom*
+ViewportFrame::GetType() const
+{
+ return nsGkAtoms::viewportFrame;
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult
+ViewportFrame::GetFrameName(nsAString& aResult) const
+{
+ return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult);
+}
+#endif
diff --git a/layout/generic/nsViewportFrame.h b/layout/generic/nsViewportFrame.h
new file mode 100644
index 000000000..062de4054
--- /dev/null
+++ b/layout/generic/nsViewportFrame.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * rendering object that is the root of the frame tree, which contains
+ * the document's scrollbars and contains fixed-positioned elements
+ */
+
+#ifndef nsViewportFrame_h___
+#define nsViewportFrame_h___
+
+#include "mozilla/Attributes.h"
+#include "nsContainerFrame.h"
+
+class nsPresContext;
+
+/**
+ * ViewportFrame is the parent of a single child - the doc root frame or a scroll frame
+ * containing the doc root frame. ViewportFrame stores this child in its primary child
+ * list.
+ */
+class ViewportFrame : public nsContainerFrame {
+public:
+ NS_DECL_QUERYFRAME_TARGET(ViewportFrame)
+ NS_DECL_QUERYFRAME
+ NS_DECL_FRAMEARENA_HELPERS
+
+ explicit ViewportFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext)
+ {}
+ virtual ~ViewportFrame() { } // useful for debugging
+
+ virtual void Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ virtual mozilla::WritingMode GetWritingMode() const override
+ {
+ nsIFrame* firstChild = mFrames.FirstChild();
+ if (firstChild) {
+ return firstChild->GetWritingMode();
+ }
+ return nsIFrame::GetWritingMode();
+ }
+
+#ifdef DEBUG
+ virtual void AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList) override;
+ virtual void InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList) override;
+ virtual void RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame) override;
+#endif
+
+ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists) override;
+
+ void BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
+ nsDisplayList* aList);
+
+ virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+ virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+ virtual void Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) override;
+
+ /**
+ * Get the "type" of the frame
+ *
+ * @see nsGkAtoms::viewportFrame
+ */
+ virtual nsIAtom* GetType() const override;
+
+ virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
+
+ /**
+ * Adjust aReflowInput to account for scrollbars and pres shell
+ * GetScrollPositionClampingScrollPortSizeSet and
+ * GetContentDocumentFixedPositionMargins adjustments.
+ * @return the rect to use as containing block rect
+ */
+ nsRect AdjustReflowInputAsContainingBlock(ReflowInput* aReflowInput) const;
+
+#ifdef DEBUG_FRAME_DUMP
+ virtual nsresult GetFrameName(nsAString& aResult) const override;
+#endif
+
+private:
+ virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const override { return kFixedList; }
+
+protected:
+ /**
+ * Calculate how much room is available for fixed frames. That means
+ * determining if the viewport is scrollable and whether the vertical and/or
+ * horizontal scrollbars are visible. Adjust the computed width/height and
+ * available width for aReflowInput accordingly.
+ * @return the current scroll position, or 0,0 if not scrollable
+ */
+ nsPoint AdjustReflowInputForScrollbars(ReflowInput* aReflowInput) const;
+};
+
+
+#endif // nsViewportFrame_h___
diff --git a/layout/generic/test/bug1174521.html b/layout/generic/test/bug1174521.html
new file mode 100644
index 000000000..a62558c36
--- /dev/null
+++ b/layout/generic/test/bug1174521.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <div>
+ <div style="float: left; padding-top: 20px">
+ <a href="#" onclick="parent.postMessage({msg: 'DONE'}, '*'); return false;">test</a>
+ </div>
+ </div>
+ <div style="position: absolute">
+ A
+ </div>
+ B
+ </body>
+</html>
diff --git a/layout/generic/test/bug344830_testembed.svg b/layout/generic/test/bug344830_testembed.svg
new file mode 100644
index 000000000..5dd98abe6
--- /dev/null
+++ b/layout/generic/test/bug344830_testembed.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 200 200">
+<g id="g1" transform="translate(100, 100)">
+<circle cx="0" cy="0" r="50" fill="green" />
+<text x="0" y="10" font-size="24" text-anchor="middle" fill="yellow">Kibology</text>
+</g>
+</svg>
diff --git a/layout/generic/test/bug421839-2-page.html b/layout/generic/test/bug421839-2-page.html
new file mode 100644
index 000000000..75402ca53
--- /dev/null
+++ b/layout/generic/test/bug421839-2-page.html
@@ -0,0 +1,55 @@
+<html>
+<head>
+</head>
+<body style="position: absolute;">
+<iframe id="a"></iframe>
+<iframe></iframe>
+<script>
+function tripleclick(){
+var wu = SpecialPowers.getDOMWindowUtils(window);
+wu.sendMouseEvent('mousedown', 100, 100, 0, 1, 0);
+setTimeout(tripleclick,20);
+}
+setTimeout(tripleclick,200,0, 0);
+
+function doe2() {
+document.body.setAttribute('style', 'position: absolute;');
+document.body.offsetHeight;
+document.getElementById('a').setAttribute('style', 'position: absolute; direction: rtl; ');
+setTimeout(doe3,200);
+}
+
+function doe3() {
+document.getElementsByTagName('*')[2].setAttribute('style', 'unicode-bidi: inherit; ime-mode: disabled; font-family: Al Bayan; ');
+}
+setTimeout(doe2,500,0);
+
+setTimeout(function(){window.location.reload()}, 1000);
+
+
+function designmodes(i){
+if (i>=0)
+ {
+try {
+window.frames[i].document.designMode='on';
+window.frames[i].document.execCommand('inserthtml', false, 'tesxt ');
+window.frames[i].document.designMode='off';
+}
+catch(e) {}
+}
+else {
+i = window.frames.length-1;
+ }
+ i--;
+setTimeout(designmodes,50,i);
+}
+setTimeout(designmodes,500,window.frames.length-1);
+
+function doe2(i) {
+document.body.style.position == 'absolute' ? document.body.style.position = '' : document.body.style.position = 'absolute';
+setTimeout(doe2,200,i);
+}
+setTimeout(doe2,500,0);
+</script>
+</body>
+</html>
diff --git a/layout/generic/test/bug633762_iframe.html b/layout/generic/test/bug633762_iframe.html
new file mode 100644
index 000000000..185d9af65
--- /dev/null
+++ b/layout/generic/test/bug633762_iframe.html
@@ -0,0 +1,8 @@
+<html>
+<body>
+<div style="background: red; height: 4000px;">hi</div>
+<div id="b" style="background: blue; height: 10000px;"></div>
+<div id="a" style="background: yellow; height: 100px;"></div>
+<div style="background: green; height: 4000px;"></div>
+</body>
+</html>
diff --git a/layout/generic/test/chrome.ini b/layout/generic/test/chrome.ini
new file mode 100644
index 000000000..173d542a6
--- /dev/null
+++ b/layout/generic/test/chrome.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ file_bug514732_window.xul
+ frame_selection_underline-ref.xhtml
+ frame_selection_underline.css
+ frame_selection_underline.xhtml
+
+[test_backspace_delete.xul]
+skip-if = true # Bug 1163311
+[test_bug469613.xul]
+[test_bug469774.xul]
+[test_bug508115.xul]
+[test_bug514732-2.xul]
+[test_bug632379.xul]
+skip-if = os == 'linux' # Bug 1207914
+[test_selection_underline.html]
diff --git a/layout/generic/test/file_BrokenImageReference.png b/layout/generic/test/file_BrokenImageReference.png
new file mode 100644
index 000000000..2a1e0dc9e
--- /dev/null
+++ b/layout/generic/test/file_BrokenImageReference.png
Binary files differ
diff --git a/layout/generic/test/file_Dolske.png b/layout/generic/test/file_Dolske.png
new file mode 100644
index 000000000..cf5c35e99
--- /dev/null
+++ b/layout/generic/test/file_Dolske.png
Binary files differ
diff --git a/layout/generic/test/file_IconTestServer.sjs b/layout/generic/test/file_IconTestServer.sjs
new file mode 100644
index 000000000..4aea92333
--- /dev/null
+++ b/layout/generic/test/file_IconTestServer.sjs
@@ -0,0 +1,94 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const TIMEOUT_INTERVAL_MS = 100;
+
+function handleRequest(request, response) {
+
+ // Allow us to asynchronously construct the response with timeouts
+ // rather than forcing us to make the whole thing in one call. See
+ // bug 396226.
+ response.processAsync();
+
+ // Figure out whether the client wants to load the image, or just
+ // to tell us to finish the previous load
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+ if (query["continue"] == "true") {
+
+ // Debugging information so we can figure out the hang
+ dump("file_IconTestServer.js DEBUG - Got continue command\n");
+
+ // Get the context structure and finish the old request
+ getObjectState("context", function(obj) {
+
+ // magic or goop, depending on how you look at it
+ savedCtx = obj.wrappedJSObject;
+
+ // Write the rest of the data
+ savedCtx.ostream.writeFrom(savedCtx.istream, savedCtx.istream.available());
+
+ // Close the streams
+ savedCtx.ostream.close();
+ savedCtx.istream.close();
+
+ // Finish off 'the old response'
+ savedCtx.response.finish();
+ });
+
+ // Finish off 'the current response'
+ response.finish();
+ return;
+ }
+
+ // Debugging information so we can figure out the hang
+ dump("file_IconTestServer.js DEBUG - Got initial request\n");
+
+ // Context structure - we need to set this up properly to pass to setObjectState
+ var ctx = {
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ };
+ ctx.wrappedJSObject = ctx;
+
+ // Save the response
+ ctx.response = response;
+
+ // We're serving up a png
+ response.setHeader("Content-Type", "image/png", false);
+
+ // Get the output stream
+ ctx.ostream = response.bodyOutputStream;
+
+ // Ugly hack, but effective - copied from dom/media/test/contentDuration1.sjs
+ var pngFile = Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+ var paths = "tests/layout/generic/test/file_Dolske.png";
+ var split = paths.split("/");
+ for(var i = 0; i < split.length; ++i) {
+ pngFile.append(split[i]);
+ }
+
+ // Get an input stream for the png data
+ ctx.istream = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ ctx.istream.init(pngFile, -1, 0, 0);
+
+ // Write the first 10 bytes, which is just boilerplate/magic bytes
+ ctx.ostream.writeFrom(ctx.istream, 10);
+
+ // Save the context structure for retrieval when we get pinged
+ setObjectState("context", ctx);
+
+ // Now we play the waiting game...
+
+ // Debugging information so we can figure out the hang
+ dump("file_IconTestServer.js DEBUG - Playing the waiting game\n");
+}
+
diff --git a/layout/generic/test/file_LoadingImageReference.png b/layout/generic/test/file_LoadingImageReference.png
new file mode 100644
index 000000000..5641cf4f5
--- /dev/null
+++ b/layout/generic/test/file_LoadingImageReference.png
Binary files differ
diff --git a/layout/generic/test/file_SlowImage.sjs b/layout/generic/test/file_SlowImage.sjs
new file mode 100644
index 000000000..cf95eeeea
--- /dev/null
+++ b/layout/generic/test/file_SlowImage.sjs
@@ -0,0 +1,45 @@
+"use strict";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const IMG_BYTES = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA" +
+ "DUlEQVQImWNgY2P7DwABOgESJhRQtgAAAABJRU5ErkJggg==");
+
+function handleRequest(request, response) {
+ response.processAsync();
+ getObjectState("context", function(obj) {
+ let ctx;
+ if (obj == null) {
+ ctx = {
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ };
+ ctx.wrappedJSObject = ctx;
+
+ ctx.promise = new Promise(resolve => {
+ ctx.resolve = resolve;
+ });
+
+ setObjectState("context", ctx);
+ } else {
+ ctx = obj.wrappedJSObject;
+ }
+ Promise.resolve(ctx).then(next);
+ });
+
+ function next(ctx) {
+ if (request.queryString.indexOf("continue") >= 0) {
+ ctx.resolve();
+ }
+
+ ctx.promise.then(() => {
+ response.setHeader("Content-Type", "image/png");
+ response.write(IMG_BYTES);
+ response.finish();
+ });
+ }
+}
diff --git a/layout/generic/test/file_bug1307853.html b/layout/generic/test/file_bug1307853.html
new file mode 100644
index 000000000..132dc65db
--- /dev/null
+++ b/layout/generic/test/file_bug1307853.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<title>Iframe for test for Mozilla bug 1307853</title>
+<meta charset="UTF-8">
+<style>
+
+html, body { overflow: hidden; margin: 0; padding: 0; border: none; }
+
+.wrapper {
+ box-sizing: border-box;
+ width: 100px;
+ padding-right: 20%;
+}
+
+#inner {
+ height: 1em;
+ background: aqua;
+}
+
+</style>
+
+<div class="wrapper">
+ <div id="inner"></div>
+</div>
diff --git a/layout/generic/test/file_bug448987.html b/layout/generic/test/file_bug448987.html
new file mode 100644
index 000000000..917e5878c
--- /dev/null
+++ b/layout/generic/test/file_bug448987.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body onload="focus_area()">
+<pre tabindex="1" id="pre">
+STEPS TO REPRODUCE:
+1. TAB to the image map area below
+
+EXPECTED RESULT:
+a focus border is painted just inside the image edge
+<pre>
+
+<div style="background:lime;padding:1em;float:left"><img src='data:image/gif;base64,R0lGODlhFAFuAPcAAPf39//7/+fn59bT1u/r787Lzq0UAN7b3hhFrRhJtRA0hBA8lMYYALWytffz94wQAMa+vb26vRhNxufj5+/v78bDxvfz772+vcbHxghRCM7PzggkYyFZ1tYkCNbX1hhRzpyenO+6AN7f3gBlANauAGOW7zFl1kp95wg8pbW2tZyanHOi797f5zlx3msMAAB9CP/PAL22va2mraWipedJMSlRtf91Y72WAFqK76WmpRBFta2qrcaeAK2urfdpUuc8If39/e9ZQmPTY94wGFrLWrUkEISq9/n5+ZR5ABCWGLWOAIRpAGNRAClJlK2GAL0sGGssITG2OaWCAK3H9848KaW+762qpcbX/73jvWN5pGvTc/eGc//vCFppfIwgEJRBMYSGlPeWhJSSjHN5jMaSjKWmrb3P787V1ilBa4R9c9nXy+rq6ntNQu/y95Su3svO1kphlISa1t7b1vf7/3uGraWGexdAmoySlDm6QrWyraWqrRiiIWtpa0LDStrl/1JtrcbHzpxlWntlUklVcztZk3vbhISStfffWufr786upVmB1ta2taW21tLS0ve6rd7j5zlhtfemnJSetefn78LCw7W2vdnZ2bVFMf/3//f3/8Z1a9bb59bb3eHh4a2WjM7HxoR1OSGqKbW6xvT09JyGQrXD3v/r5+fk3s7rzufb3q2ytffXKbWiY6inp1p1WrWmjP/LxnOOzsC/v+/n56qSMffXzr2eEP/3hPHx8f//987T57m4ubVZShk5f97LjO/HKQ03ks7DpdZlWv/vMd7LxqWuvefr/86uKd66QgwyhcnJyf/ztbKxsrWqpe/37//z7///75zTpf/397Szsvfn1ozLjO/jlN7v3qWko62OEO/r5//73tbX3vf37wotd1qqYxlNvYyujN7X1qKgoHuWe5KUmkKOQhZKvildMRh5IZqZmcXEwZ2amaajoNfU0QMaTAsiV5STlSo+bZibpSti2puZl5u28pGs5pqbn9Hd8/fr94WFi7i1sBguYfP09v///ywAAAAAFAFuAAAI/wD/CRxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bNmzhz6tzJs6fPn0CDCh1KtKjRo0iDYinEtJAWLUSIfJv6DVFShnOMXTEzpYrXKlPMXDGW6apZhli0CHmqtk+oJC9ejDDH6azBTGaMKDLBQUKCvzoC/5XAwcSJElV0UbB71lk0PFH6CGmqxS3cuZgyAwhw1tiUEiY+CEZBunTpwR9SfyisiNGmzYyNYqlWLZrtak6JRLmcoQKBCQQswEZ6ZcUJDn0RoFiwoAkhOH/+wCHURDkKHRIII0e+uoWbSbGRYv/SFy0qnj1yM6ArUEDDAQEOOBu9UqIF3xrMCWWRBGiAnP8eqKEBBpVIkgUhyg22nWocxKJLeEMBYIEAqVSDBx5vjTCCeinEgIEH8Mk3lB/G2YffAlmIcoAIcgxYAQQwXiAjjC+WkUV1qHH3QXaKbAIhUAAQwMIBWkCG3obq9dBABQcER1QmVeBwQgv4KZAFICIMgEGMEXTp5ZcNhJmCjQlmp5pfKCBgyHA/6hQkC6lUdl56Gbgiw5IiUCAiUCRKaQICChBSSYsydhiDly9ioOiiMKYQZgMqEJLmYH4pp4ACTRzQ5k4AOEABAYUQ4RaddqYwAHxDmVGClJAssMEYA8b/IGaHELQ3QIAriiDCAQcM4OIFYVqRRZkJILCAAhtsAMcEbG5q02YOFCLEqEiWCuKePn1WXw3IgqFBBEoqeQEG7uka3KfCWYCuABP0qgEEjt5RQ2DGIrvBIAOw4KSzOWEiLbUZZJAGnnoGVUUJ2yJ7BwYN3NlABBX4J0BwAAzH2cWcefqbBwXAq0INy9k7SB7jNmkBvzj5O20odA4MwQHCAaXtCdy+eoEMOVjRQK3vFYzxQp0KyfHNTYgMwgw7XHAqbAFgi7JLAPzLMpIu5/nzTjPX3EUEOSC98wDANftQ0FlGUPS9KqSd9KkOPG2TygBnAEYDGLAQn09+IIyDCa6i/9HDDEenUECTd1sUdBbIdgFC2jPQbbLbNUW7Mp13OF74TpmssOqfyd7RdQ6C9+w0RaXUQAgdeQDeOJOoQk6TBW0dGXDlBTDrkxEI04zsIDIEXve+Gm2yySkDRLCDzqxf7npMsE8rewZihC72TXmvsLerG4hxdA/JX50RAQPACzHhoy/fEgCx0xl97dPbZITmVO7euxVKt95RkAMUQC755tPkTPpIUoHgbLeT4liPb8kCg+/sVr6LAIAC7VoR8Ponk/85j04CZF8DYzIHI+AufhtAw+K4x7+PBIkAE6RgBdUypwDGQAP2wwmJjHC9xB2tATBs30YqtsGIPKMWjgjDFv+GuIUwRAIWpiAJNLaxjFsc4olPvMUytvETC7YwYBmMoUOo4YhICLGIkXAENbxXED6Y0QMs4MNmRKBGhFThfTTDXhqQxiSf8cQUkfABDYYwhA50YAg/CCQNaBAEYYQhFR6BhhN/EYJGOjIEMIgkDH7hC218pA1+uMIUNsnJrnzFgx6MgyhjEQtGFOQaULliBrKoQ4PUIgxBCCQgfzDIWhpyFghRFx9iYIED8AEC/8AAHyZwEGN4sD6ACqEKGqcBAu6EGlvYYwcYYIAiPOGaRfgjLWnwAz4KA5EZ2cYhYBACEvDgBjewhTp5QAISNDKSjUTGKTKSiSvQ8AT4PAEOEMb/TynZpy9oQoECFiAJO/4DlURQJSsbUgsb/MCPDCiCRLVZy4cOgQzt48MF/jGBHKQhABAY5kHyQsM/IUuEMqifxW5iijBIs5qaSMQniKGBRSRCE0/QpiwZ0AFNGDQi0BhnCHighGywIhhqENAngsEKW7TzkeX0hfIgYszjZEc7htlnCTTnp+SEbAF0IOZKEapKELxQiwYxRTSHINFLkCERNiUDL3I6yz4y4BLgLCMw28WHBhCADxo4yButx4Fj3StndezhS2Dhgx9QoQheIMMB2oCIR/DqDJi9QCCy+Ue7MoAK4qDIMoYBAxLcIBuvyNMkhjSAW50BAqxg5yPbeYx5/0ZkDlWwKgL+IAlGMCIOfyhMC/JpHwlAgg5ZSO6VJjQB+AyHrLxZZQSWphDGOrYIX0gEIig7JE6coRmX4Gkfp8mAJxDDIHzIQTAJALh/DMwgDvCgcaq0gS7ciX06iUQQaEAFA3zhE23olf629KIZeaIIPIUoAyK6CIncgguldQIt1EAAAbOnwDN6hVPdWU5z2kINECGRfXb7BnURYBbEKwYkCGMCvlRKEi1qbf40ADb7oSKV0TUrdRHiiP32NxCPQATHLkwjUQQiweQ1wBPyOpCV/iMTBDDIDOuTgEu9am0EcPJM8hiE/n5hRQOmEYExfOAFK9gABmgwGQ/y4AiTgv9XYU4Uo7qUgw2305znBHFD6HOCP9FhQu4acAEu8AfsXLVedADWocZVADT67MYJzfF00SoQR/igywYA8gHijGFZ8QLJ1DTAJWImETOs4ICAQtYYSLgYnHCZCl6AQqO5FDEaB8hXHYOAJxbM61Ab4BMOaTMJpAAKGtPaVrf+1SvYeWcenDMbelYIfXDQgt1qw8IVmHMKCg2YeilgbrRqT54KB2k8SHrHBWGsHrEboGPTWAP6AxYV/DhNAzzgARjV8kKmcOq9pXoDc0vsTRxhg3U/YAZvMFStaswuFBIgS+zRRK8XjOYlN+0fFzfIg7kQAicsoRkJDzfDHT6BIXWMFXf/buc5lUALZ0pZc3F8WYHJ9e5496AGVy3WsRZQBgzYKmwiKve50WqKggfhCQf/lsjNxS4Bk4GP9b63Fw4wVYZU4dTH+XfAGWiThtrg6A9gA8MakAKeOVc+QdsYIMLbazQ/IBA8RMgyuADhGywBFIAQEwTcM7HhVMwCQoPAMVK+cimwour/6ODmFgAHpTN6Rc01eQEkcdVKHavxBwD6nlAhhEhjcNJODkPBqRD2vD8sYvoSTsU0doAKvPQBLoh9HdqA+IRcvQTHOVa3lsR1mmBiC6P3ggvqEIMdPGxp7evUBDjRjIm7/QGJqBhChgHhYTMhesaHAPJHp3wPJCLl5lSC/xOkEIyqZ+3b4kM9xRygMY5l4Uw6V0AxZkGB6XFeN0NfKSxsYAMaPMEFaSAK2XcqqlcQb0IDHVAE9xZ7LgAFJdQQuENthWVlYHBWrcQSBPd1SAcFObM2vZcQDiAAnPBpZ2ZvX/AIpEYQGxcCdscEHThpKZhLIogMjaRyNzB+Lac8bXBqoOEqlcM920cQQXIKosABqhF/y1J/B3F/u/F56CYQwOcDsOYCYiAuT2gQ4jAE1AR7DCgItRODC2EcfSYB9WIz+EUTagV8NFAELsAGXWM5ivUPnpII9FZv9vYAzZBlewIN1BdhSyAIZQA6XxiHExIMjmRONygFSBABeohxAf/Ab9ZTM8sEhwfxJopwhPWCBmdYEEyYfwSxfxr4AFAAAshjNQjxDJFAA1sYe1AgCFWoUg9RH8VVhluziTJBcFvQZcLnhikFNhdIEL00bxD1fEDWiBhnDXQHAzyABEwAiPQzAFn2EBB0DDVoWuOHBG8Wjf8AAPKlO9mDZToUJG6wGjvibXqQJwBgEJ2IQTHwhJHAf2soioEIegfRUgjIALAHBWyQBmkziTn0EIpQGMlhWHAAgzUxRKHoAs74O3EoEBLydOO1hQ/gBb5xMgNBfRx3A8w4RwTTkNtIAL5wiESliEuAAQTEAivwQclEOzDTQBVzBnwxkAqTL2KDBZ3XhEj/AgL0KBDPwH9SyIYcyGrYAgtb8FAdYIJ1ECb9qAK9SGkIEZDc4W1oMEC1txKpYANqqIAKqQem4pQJAQDEIEtRB3ueoC/p+A/UkIwkoARLwAQc+Y8QMQdqIJLihwRLkFrRqAv9ZlIARzcuV4kEsBd8UYaw0pIFYZP454Ta+A+14ANf11/y6Jd+Z2l2ZQBeoAmLMCTFdzQ54yEPuBCxsBpGmADMoQB+M4gzgYs/CXuC0AOwGJcCEEvdJJEuEAhnQEDIWH0e55bgGBEAMAm/QE7ldIN2WQe3mY6Q6G+qZoEMIQ1x0AItwAFVpmrat5gCgZg4iUU7+Q+L4JhHZ28ceFYC/2GPduhWTcICHdMwxuchfFeVBhEHoika3rYwpigToveY9qaQqvAyP8UQADAHmjBIfXSHbTg4SngIEOaHvMmfHtlkviCc1miXeNckuXAwuMcByVSLXkkQjIBPfGmGWoSdnigQ76iB4FkGF8ACbaAGRVlvRYCZiCBk8YYo4tZc7mkQ48gd8acAY2Cga6YSFgB8Jsqa+zlZDToQiTBID0WgUECPq5CgPLCbVWOREvGgNUicd6dSFhqdqTYytpgQb+AnydSXqDkQIqqYw3Gf/mdvXsCVcsVroiZZlOUrMAIBFwAxjSYCfXek/2AGC0KGpUkI1cmnIAEA/DekbVikVBoRxP8QBPvFVgsIBQ2AX09KTlEqoYpKEWoATxG6BEswqcxSCvjEpbqnB1+KELqAA8rZOXlQptd5k9GlAtO1mKKnR0/ApoLwBUVwCbygCTDaBiyQPwXwIrXSaD1TgBMxCaFhhJZ3KWUAjb9oEhZwqGsKe2yQqRNxCpe2RwqYjzKAAZPVDasAT5d6d9gqEdoQnI1Uri4IrtJgBtAZGqR5Kd5SnwqxCWKaLCLUkQRxpi4ErQMRBj5gq3c4MNQFADJKZLVCYzYarQxxicyqc8dSmDcaEZm0FVeQsRqrsVyxSa4hEATgmATrreBasQdBAUb3A93agDnAnxYwrpbqBJhaNyYLX8H/GbNt2a5UJwAxKZ/HQgg+yhCpGkfJYl/c85f+qp3oJrAj+3bqt3z5oygQUAHtkXkUQ6gHkaM7AqjH0gQClxEkMiXQqU9bxYMSSIaB8gggK7IIeIccSLM/qhBBemkqG6m9SAEvS65KYJdL0LL2ChEAALNDJQU5e7fScImpwbUKoAdNolilQG01kz3MxCx7krSr1I7GyLRte29sIAqDoyubpii2kno1+xC6AH8SqwCG0LgYIWIthhwmMFwlAErGYQJoCwdRBi0pe5QLKAZwSxFbMLB1y4o5AIM3O5wkqQK/KxGZME4424zgWAqVh4QU9otuEJ3Yozgz0JVnaaaw6oQC/9C9+rVfvDuR3+ofvXIrHiACmmdCLYC69QK0G8oQxrACRsgBNZC/NQAJfxALp/Y+EggomTIhemKojrqkCxgIWKKEE8G0w5uoFogMnIqldfC1vum8LGiX0AuDFsBt3cYchkB/G+QAe1Mzg6A2DHqY3/uvi7kIB3yUd1jBEsMue6pvGzGOiWt5xxLC/ekQmVAKjFAKb4BZZwBvtuIG+yQl0ukqXdAiEjQHLsVNMHxvXzCoFOHAWqmQPSCeVoq8EmrFE4HBGskECwqtACC9gmEdKOC1YFgQVQBCg7A4ZuWqr5qYATSr3VsLSjqWVQw2eBsfDosRB8CsOWcszVEA1lkRz/8ggjMKAc85JbZrWFtjdnOwCGIpkV4AqoFcEALbZVnsMtdiDXRJkg3ANhMxB867ljn7ih5Qf0EiCWqsxjXwBrmQEFeQe/fSO8sExv26wkq7mAQgm3Z4b2U5vyYUB9NbLPWSBSJsEZ7SLh2jaBfwnP/0b11weu8BAKnQTZUZw8dJqMGLaaxIiimqJ6cwynbpCcc5EQCQyoRLxuRMdZwhJHRgGsyxAChQA6YEX7llUmMQA6pDld5jubIKrfIRAJoAdRRngt+cEgAwAABVyIZcUJtsgCEIcYxSAG7goRhqZV1gfPg1B8JAb6FGxW9gpMxbcP7HhQtppJlAgx12A3VJCif/bQF8GriWyoxMkF47IHAhqAaGsHP3jM860BqbMAmT4Aef0WeAIgkF8DeCaJgGQdB4fNCL8EdJdoeeMAmLahISEgeG5hcf3AQlhrVyCEGg2ysrwgirwqUeLQM7SYcleG/NkM0SQQAqncXao8mcYYgddk4yiwR5QKESIa7kxJZkvNeo+ZsH8AllAAeXcinMkQAf0AI4cGowZwLcogfiMCbI4wGJ7L12HDA6Zp0CIIxwytAMjBIAcACQENbdphyQIAI27J8ag0IUkNsWMDMdnTgPw7r/gAhU0HZ3aJvG7EqXNoVtGDim/A+TMHg2WJeswA3HXRBzF2FkLAjMHb5oBz4Q/5AHdwAHaBDZKCABLTC7AKzZCkAHcgAuSgKuXknVANtkZMBTqW1vgbBdZk0RQSIKlBLbyvEHoe0QsDFVpnah1syviZcIzsemEfCXDaFfKx172mO4DhkMzVZ4iwjhDIGMLLjK8bzacigAagAvVgACdzAGKk4HkhAHKbktC4AGhYLNAy7a2ZkBpb1SckBe920A+aYSEGQIggEY3YYCzOwRB551Hr0kB1AwH/lp9/0AvKAvDYqyuhh7fLA9ZRokyIBnhWd4R92gAEBa1ueWk0jHATAh4ZMHUN2ZDaBVSt4EjnJ8dq0QlquTT+gAAFDfDY5mP87a4JMFQ07kf4ECAt4RSf/e2/XVqvXZKZ+AYAvtdp5Ae3FbED0Wj8u9y+jm6LbgbM4m0+OnXaXr4Yit3ZqOVmnOyCaOMw1gCMPVZ1WCBp0ZtAtx59spEA7AAmzX5xilEs7wCAXwB5NC6IABCdzAEYlOi4y+qBPyCmj27M9eBJ8gfQ1BDbYqfKN45vaa6omATt4O6tmgATW7DaQ1xmzAOAoOXxAUuuJjCLHLF/P6bSRzLQ3hryOADjlugASwCJA+cbwmDLg0NrDwDBZxBLgwAcoACcNO7NjhBrWcEcm+5BWw7b9RB9AO7XiVcQohDQ71BNjej03ZvQ5JAJvwCt/+7bRwAHNQ6QJBWsvIBOc+Qq//mRDqMgG7MgAHcL3/xLVNYKofaOei8hZyoSH5XhAQRAYX7+9P0OtA4wjCoGYFTwFysA7CniYIcPX/XdTHjhGqsjcSYFiLbsEB8NMWf/FodgnntRBF9wMe34Zdo+1dLRAQdAavoAR2f/d2n/IMEVSDywSgoMuTG/cI4Sm6fQb+lBxXn8+VIBxxiAVGMvQaQg7w7TRBwgllD+3+TgVkQAxT9QywEAaPBfUXYfAHQAlZcM92gPX/LQFxoLYWYQZTUgNjGvbb3imbVgde8ABmXwRMfxA9NgRIV5ubuTqEre6b9grZMH5OcPfpJFUJsQyrMFR2GYC9Q/w1jhD/2SlxMCUA/9VtCeDwDqEFewAXkD8C6RAOL4MJJzskl5/0vdYBPyAMmoCQQaCF1XReFVPRBwEEFAAQB9aBaaJgwQI7CBQmYChBB4dYugD8o1jR4kWLAIyYqIFAwQaQG7rkqSDCQoCLAAR4AJTjywOYBmTKvESmFgUAAUzVihRkSIciD77keNNjRo4GEDwIyImRIgACLK2QklLViRMlN3gc80Xt5D9o226tCkFCCRJQzQAZnZF06USncS/qamGCg0OGeXXEgiv3H5Y9SV6MGJHBMOERL9KZM/fNQ0YCB9548gLzwcyZDDR30NxZplBx/yyMburX9D8gQHAd+NQKzgKDBxUiyCvhw/+HFnHMEEBpeo6fKjg4Iui1od9xkSRN9rboQMCACpXqvLScmQEVKjR+DNEs84snD28ayJDhVoAD5k6dQ68khhQS+FWz8tiK7NevsjxuSKH1akCB8cqrYIDz0jsNIzc+wEsvhlDIggC/rtnjhcEMs/AwxNKJpq9/AlBJqkAqsywmzDCDyYs6mJpgAgEI+OpAv1IbpRNl+LmDEAVyjG2h2m7j4IRY3KjCjCuKvMKMKYwo4QS7JPBoA282kGeQLsAAYYeSXkxpAvb0qIMNEUks0YAvAklkAhYwSKGHHlIYsMCKDKzIAi6j08M9UOBDQr4b+lTiT1pYCQZNNdl08y05T2v/ww8zSnEjjo5moy2vBFCAZBO5UKkmnHDIScMVUEN1hRxywhEAE/TSWy86FcB8wIURY4UJijo0mOSAAQbwQASmOIQxLiBGmcCdCGQAA0cdd+SxIQlsu+3ZZ5utdIEmCOnijnLG0VbbHjBgQUvI6lxTDxDqCISNL9JNlxdNyFiEBUS4hKABeiPAYCn0DgSATpbGVUGMOkgRmBRaAmUlkU9YZEGD6Oi9oAB8A0gUo0yuqGIFHOq6SwcUDvJ4WR0qrYEbv6DSIAYZQFBZ5R4ePuDlFkujiF87QUiDDShy1jlnNgLxZMACKhC6gAF4lflXuYA4goIDlGGmFTHGGASNZHP0/xgFSWdDYeuOq/3DEFE0YHiXHbTFZxwZYihggnydAsCBqAq4II8dyNNDlUoggJiTR/r+DwMIIhB8wANcnNipTOA+QG66cyjj7hQePoMbFvxmOPDBNSj86LgAuMIIJhVsEOsmSi9dWQZRqIGFziPDYLyjYrciqaEPgLOit5+rAGXHVRbjdzFAkKFNeuuV3Pa2kT4t2AkakWWaVqAeo4upqa66FzuorRaOLAyRJGwRTjnFgwJk2UWGHdLfIYUGIDa8ZAK4lHvcuhuI4IKhARc6cAw0Z9tXGLUhfv+ZVwPqZz/8YUCBGNif3vyHk9MAYAol0FilEECI7hUDEGcYgJpyQP8Q2UyKIQiwFG8wApXFza14DsvcAGwHLorAbQIM410ZZrAyHDquXoS7nfIOBAQAUEAA4igf2VoxAxX8Kx71KAe2QPC4XciiAsrQgDs0oAxATLEC65CFLCKwCzDKQhkFsAQneugXC8QvPBDAXANiMLgGFoBocmAR55TnHBasEXNvhOP+5Fi0/x2uIlfAGJNqQC06AOIU2pjF+NbIOxW8BmupUwAduuGr3OGqAGLjZCc9UDicJAoqE5BMw8aTg7qlsl4YgNjmBOlDjKTmCKOInyUaUYEvMkOXzKhbK6ywA2ZMowHTYBMz8pAHZvAjBRGAACU+oYFGuLATE1jDGnBhR7n/7ItOEwiPHL35RxfaLmYSg2VKCBA/EXTzm5sU2yfhFcoIVqEEODgBJA5Ch6XIgWEKrIACLxCDU+4gCwuYZF4QcJA3cE6bLCJAiwTg0Ic2FJ6dc44ANMnACrCRf388QB0BWM4fKm0UFFhDJw7gAQ0ErQJdFNo6KKHFT1CiANE8QCc6IYBqEoAC13QAAIBATou88h9zAABU0IkmESSVBQy1QKpAms3RsAipS6Wqi3oKIwooaUk1UEATLiACuWEOAvirQAEWeIF/5oEgBVUIbB7k1DgV1UMW2FdR6VrUXy1UAFN92QGUalVsPnV5qZElAGi5BgF0QgQH4AQLOmGJpT60/0UuGsURLGtZwsoSCBQRauc8tC8HUIA0oO2sYOta1NDStamlHeoKSrBVBaChrHxEKwT6186+4oqGIOiIQWHTBFsFVrAWIeppKXDc0X50uMtlbnOd+1wfahUH9txAGea1g3rt7a86Pe4A5VaGGujAIQfNUTG+xVrople962Vve596hRK4tgVNEEmxymMvQBLgbUcrKgHACgFJSIu8G7gDotx7YAQnWMELNsIKVnACrhI4ZS0rACiF+48gPucTdBAdbDYAhgpPdMEjJnGJTXwaXaxASS3wCBqS2IMIaE7EfiGqRS9gAgV5eAxvUu6JffxjIKv3YhgzwQI24OK2hBi9ov8hgBwUcRcPg+Fhpwpyla185eG6wcEQNrKLsYQvHxKVAHGA8kfukIK1XRjLa2bzlTUi30PGFgTtYxtQOasvabjBBBLo8pzTvOQ2B1rQCI7DPPcMGwWoIMa9AqmeIzyI8qR50JOmtI/jcIK6OAk2OyZQjw8UBxM8CQxIkXSlTX3q9qJEz00ir1cNDMs2nIADRh6EytRGZVTnWtfMRYkZOHCb8R4kC3JgCkgZwZGPqOAoF3j1rp39bOV1QxHSGiEJDWFCWPqhBYf8cCq9BUNoh1vcTikFgyQVC2wjzQ8n4PMGxmDfGBAoeeOmN70dYAFDYE1SeYFEKXKBtCm0gM+VLID/Udp3gBnXW+HQpoAIssC1fYu3BW54RDf84oAp4ADKTdCDGgDaMnkDeuEjX/N66OCxEDbkAxxoQZAYQaQjVcEIGVNQx7LgwgLG+Lwk57mzUfgJECALdc6CFrSk1TE4iEIbHhCrknv+dF2rBDoNAMNrht4srBu0a4nca1hjUFaEixzqY/cxzWJghTuMAQ6lW5YFEVCDGnxNFPm8XOaMJnay553EZreC4/51BzDQQfCG8F4xRNE/D/ynAGLF31ISrnfIs3k9jNtBDmYgO+LR9p+CQ+vXiWY7vEZe9IJWifwCN55U2q089VpmBIY2ADo+fvSzt7JR0US+xac1BbunbVlzUwUz/eKd9sM/cH/jx83E50r5ufqkX2PmaeJHP8j7Om53J3DOyeo0uXGSfvfbnJOiQt/74yd/+c3vF5T0Jv3nZ//s199++Mdf/rR///vnf3/8HyggADs=' usemap="#bug" border="0"></div>
+
+<map name="bug" id="bug"><area id="area" shape="default" href="#area_1" onclick="alert( 'click' ); return false;"></map>
+
+<script>
+var timer;
+var shiftKeyOn = false;
+var counter = 0;
+function focus_area() {
+ document.getElementById("pre").onfocus = function() {
+ document.getElementById("pre").onfocus = null;
+ document.getElementById("area").onfocus = function() {
+ clearInterval(timer);
+ parent.SimpleTest.executeSoon(parent.firstIframeLoaded, 0);
+ }
+
+ //XXX This code tries to shift focus to the image map for some reason. This is not
+ // working directly after page load, hence it is retried 10 times, see bug bug 922524
+ timer = setInterval(function() {
+ if (counter > 10) {
+ clearInterval(timer);
+ parent.ok(false, "Too often tried to focus image map, giving up");
+ parent.finish();
+ return;
+ }
+ synthesizeKey("VK_TAB", { shiftKey: shiftKeyOn }, window);
+ shiftKeyOn = !shiftKeyOn;
+ counter++;
+ }, 100);
+ };
+ document.getElementById("pre").focus();
+}
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/file_bug448987_notref.html b/layout/generic/test/file_bug448987_notref.html
new file mode 100644
index 000000000..1f106f886
--- /dev/null
+++ b/layout/generic/test/file_bug448987_notref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body onload="parent.thirdIframeLoaded();">
+<pre tabindex="1" id="pre">
+STEPS TO REPRODUCE:
+1. TAB to the image map area below
+
+EXPECTED RESULT:
+a focus border is painted just inside the image edge
+<pre>
+
+<div style="background:lime;padding:1em;float:left"><img src='data:image/gif;base64,R0lGODlhFAFuAPcAAPf39//7/+fn59bT1u/r787Lzq0UAN7b3hhFrRhJtRA0hBA8lMYYALWytffz94wQAMa+vb26vRhNxufj5+/v78bDxvfz772+vcbHxghRCM7PzggkYyFZ1tYkCNbX1hhRzpyenO+6AN7f3gBlANauAGOW7zFl1kp95wg8pbW2tZyanHOi797f5zlx3msMAAB9CP/PAL22va2mraWipedJMSlRtf91Y72WAFqK76WmpRBFta2qrcaeAK2urfdpUuc8If39/e9ZQmPTY94wGFrLWrUkEISq9/n5+ZR5ABCWGLWOAIRpAGNRAClJlK2GAL0sGGssITG2OaWCAK3H9848KaW+762qpcbX/73jvWN5pGvTc/eGc//vCFppfIwgEJRBMYSGlPeWhJSSjHN5jMaSjKWmrb3P787V1ilBa4R9c9nXy+rq6ntNQu/y95Su3svO1kphlISa1t7b1vf7/3uGraWGexdAmoySlDm6QrWyraWqrRiiIWtpa0LDStrl/1JtrcbHzpxlWntlUklVcztZk3vbhISStfffWufr786upVmB1ta2taW21tLS0ve6rd7j5zlhtfemnJSetefn78LCw7W2vdnZ2bVFMf/3//f3/8Z1a9bb59bb3eHh4a2WjM7HxoR1OSGqKbW6xvT09JyGQrXD3v/r5+fk3s7rzufb3q2ytffXKbWiY6inp1p1WrWmjP/LxnOOzsC/v+/n56qSMffXzr2eEP/3hPHx8f//987T57m4ubVZShk5f97LjO/HKQ03ks7DpdZlWv/vMd7LxqWuvefr/86uKd66QgwyhcnJyf/ztbKxsrWqpe/37//z7///75zTpf/397Szsvfn1ozLjO/jlN7v3qWko62OEO/r5//73tbX3vf37wotd1qqYxlNvYyujN7X1qKgoHuWe5KUmkKOQhZKvildMRh5IZqZmcXEwZ2amaajoNfU0QMaTAsiV5STlSo+bZibpSti2puZl5u28pGs5pqbn9Hd8/fr94WFi7i1sBguYfP09v///ywAAAAAFAFuAAAI/wD/CRxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bNmzhz6tzJs6fPn0CDCh1KtKjRo0iDYinEtJAWLUSIfJv6DVFShnOMXTEzpYrXKlPMXDGW6apZhli0CHmqtk+oJC9ejDDH6azBTGaMKDLBQUKCvzoC/5XAwcSJElV0UbB71lk0PFH6CGmqxS3cuZgyAwhw1tiUEiY+CEZBunTpwR9SfyisiNGmzYyNYqlWLZrtak6JRLmcoQKBCQQswEZ6ZcUJDn0RoFiwoAkhOH/+wCHURDkKHRIII0e+uoWbSbGRYv/SFy0qnj1yM6ArUEDDAQEOOBu9UqIF3xrMCWWRBGiAnP8eqKEBBpVIkgUhyg22nWocxKJLeEMBYIEAqVSDBx5vjTCCeinEgIEH8Mk3lB/G2YffAlmIcoAIcgxYAQQwXiAjjC+WkUV1qHH3QXaKbAIhUAAQwMIBWkCG3obq9dBABQcER1QmVeBwQgv4KZAFICIMgEGMEXTp5ZcNhJmCjQlmp5pfKCBgyHA/6hQkC6lUdl56Gbgiw5IiUCAiUCRKaQICChBSSYsydhiDly9ioOiiMKYQZgMqEJLmYH4pp4ACTRzQ5k4AOEABAYUQ4RaddqYwAHxDmVGClJAssMEYA8b/IGaHELQ3QIAriiDCAQcM4OIFYVqRRZkJILCAAhtsAMcEbG5q02YOFCLEqEiWCuKePn1WXw3IgqFBBEoqeQEG7uka3KfCWYCuABP0qgEEjt5RQ2DGIrvBIAOw4KSzOWEiLbUZZJAGnnoGVUUJ2yJ7BwYN3NlABBX4J0BwAAzH2cWcefqbBwXAq0INy9k7SB7jNmkBvzj5O20odA4MwQHCAaXtCdy+eoEMOVjRQK3vFYzxQp0KyfHNTYgMwgw7XHAqbAFgi7JLAPzLMpIu5/nzTjPX3EUEOSC98wDANftQ0FlGUPS9KqSd9KkOPG2TygBnAEYDGLAQn09+IIyDCa6i/9HDDEenUECTd1sUdBbIdgFC2jPQbbLbNUW7Mp13OF74TpmssOqfyd7RdQ6C9+w0RaXUQAgdeQDeOJOoQk6TBW0dGXDlBTDrkxEI04zsIDIEXve+Gm2yySkDRLCDzqxf7npMsE8rewZihC72TXmvsLerG4hxdA/JX50RAQPACzHhoy/fEgCx0xl97dPbZITmVO7euxVKt95RkAMUQC755tPkTPpIUoHgbLeT4liPb8kCg+/sVr6LAIAC7VoR8Ponk/85j04CZF8DYzIHI+AufhtAw+K4x7+PBIkAE6RgBdUypwDGQAP2wwmJjHC9xB2tATBs30YqtsGIPKMWjgjDFv+GuIUwRAIWpiAJNLaxjFsc4olPvMUytvETC7YwYBmMoUOo4YhICLGIkXAENbxXED6Y0QMs4MNmRKBGhFThfTTDXhqQxiSf8cQUkfABDYYwhA50YAg/CCQNaBAEYYQhFR6BhhN/EYJGOjIEMIgkDH7hC218pA1+uMIUNsnJrnzFgx6MgyhjEQtGFOQaULliBrKoQ4PUIgxBCCQgfzDIWhpyFghRFx9iYIED8AEC/8AAHyZwEGN4sD6ACqEKGqcBAu6EGlvYYwcYYIAiPOGaRfgjLWnwAz4KA5EZ2cYhYBACEvDgBjewhTp5QAISNDKSjUTGKTKSiSvQ8AT4PAEOEMb/TynZpy9oQoECFiAJO/4DlURQJSsbUgsb/MCPDCiCRLVZy4cOgQzt48MF/jGBHKQhABAY5kHyQsM/IUuEMqifxW5iijBIs5qaSMQniKGBRSRCE0/QpiwZ0AFNGDQi0BhnCHighGywIhhqENAngsEKW7TzkeX0hfIgYszjZEc7htlnCTTnp+SEbAF0IOZKEapKELxQiwYxRTSHINFLkCERNiUDL3I6yz4y4BLgLCMw28WHBhCADxo4yButx4Fj3StndezhS2Dhgx9QoQheIMMB2oCIR/DqDJi9QCCy+Ue7MoAK4qDIMoYBAxLcIBuvyNMkhjSAW50BAqxg5yPbeYx5/0ZkDlWwKgL+IAlGMCIOfyhMC/JpHwlAgg5ZSO6VJjQB+AyHrLxZZQSWphDGOrYIX0gEIig7JE6coRmX4Gkfp8mAJxDDIHzIQTAJALh/DMwgDvCgcaq0gS7ciX06iUQQaEAFA3zhE23olf629KIZeaIIPIUoAyK6CIncgguldQIt1EAAAbOnwDN6hVPdWU5z2kINECGRfXb7BnURYBbEKwYkCGMCvlRKEi1qbf40ADb7oSKV0TUrdRHiiP32NxCPQATHLkwjUQQiweQ1wBPyOpCV/iMTBDDIDOuTgEu9am0EcPJM8hiE/n5hRQOmEYExfOAFK9gABmgwGQ/y4AiTgv9XYU4Uo7qUgw2305znBHFD6HOCP9FhQu4acAEu8AfsXLVedADWocZVADT67MYJzfF00SoQR/igywYA8gHijGFZ8QLJ1DTAJWImETOs4ICAQtYYSLgYnHCZCl6AQqO5FDEaB8hXHYOAJxbM61Ab4BMOaTMJpAAKGtPaVrf+1SvYeWcenDMbelYIfXDQgt1qw8IVmHMKCg2YeilgbrRqT54KB2k8SHrHBWGsHrEboGPTWAP6AxYV/DhNAzzgARjV8kKmcOq9pXoDc0vsTRxhg3U/YAZvMFStaswuFBIgS+zRRK8XjOYlN+0fFzfIg7kQAicsoRkJDzfDHT6BIXWMFXf/buc5lUALZ0pZc3F8WYHJ9e5496AGVy3WsRZQBgzYKmwiKve50WqKggfhCQf/lsjNxS4Bk4GP9b63Fw4wVYZU4dTH+XfAGWiThtrg6A9gA8MakAKeOVc+QdsYIMLbazQ/IBA8RMgyuADhGywBFIAQEwTcM7HhVMwCQoPAMVK+cimwour/6ODmFgAHpTN6Rc01eQEkcdVKHavxBwD6nlAhhEhjcNJODkPBqRD2vD8sYvoSTsU0doAKvPQBLoh9HdqA+IRcvQTHOVa3lsR1mmBiC6P3ggvqEIMdPGxp7evUBDjRjIm7/QGJqBhChgHhYTMhesaHAPJHp3wPJCLl5lSC/xOkEIyqZ+3b4kM9xRygMY5l4Uw6V0AxZkGB6XFeN0NfKSxsYAMaPMEFaSAK2XcqqlcQb0IDHVAE9xZ7LgAFJdQQuENthWVlYHBWrcQSBPd1SAcFObM2vZcQDiAAnPBpZ2ZvX/AIpEYQGxcCdscEHThpKZhLIogMjaRyNzB+Lac8bXBqoOEqlcM920cQQXIKosABqhF/y1J/B3F/u/F56CYQwOcDsOYCYiAuT2gQ4jAE1AR7DCgItRODC2EcfSYB9WIz+EUTagV8NFAELsAGXWM5ivUPnpII9FZv9vYAzZBlewIN1BdhSyAIZQA6XxiHExIMjmRONygFSBABeohxAf/Ab9ZTM8sEhwfxJopwhPWCBmdYEEyYfwSxfxr4AFAAAshjNQjxDJFAA1sYe1AgCFWoUg9RH8VVhluziTJBcFvQZcLnhikFNhdIEL00bxD1fEDWiBhnDXQHAzyABEwAiPQzAFn2EBB0DDVoWuOHBG8Wjf8AAPKlO9mDZToUJG6wGjvibXqQJwBgEJ2IQTHwhJHAf2soioEIegfRUgjIALAHBWyQBmkziTn0EIpQGMlhWHAAgzUxRKHoAs74O3EoEBLydOO1hQ/gBb5xMgNBfRx3A8w4RwTTkNtIAL5wiESliEuAAQTEAivwQclEOzDTQBVzBnwxkAqTL2KDBZ3XhEj/AgL0KBDPwH9SyIYcyGrYAgtb8FAdYIJ1ECb9qAK9SGkIEZDc4W1oMEC1txKpYANqqIAKqQem4pQJAQDEIEtRB3ueoC/p+A/UkIwkoARLwAQc+Y8QMQdqIJLihwRLkFrRqAv9ZlIARzcuV4kEsBd8UYaw0pIFYZP454Ta+A+14ANf11/y6Jd+Z2l2ZQBeoAmLMCTFdzQ54yEPuBCxsBpGmADMoQB+M4gzgYs/CXuC0AOwGJcCEEvdJJEuEAhnQEDIWH0e55bgGBEAMAm/QE7ldIN2WQe3mY6Q6G+qZoEMIQ1x0AItwAFVpmrat5gCgZg4iUU7+Q+L4JhHZ28ceFYC/2GPduhWTcICHdMwxuchfFeVBhEHoika3rYwpigToveY9qaQqvAyP8UQADAHmjBIfXSHbTg4SngIEOaHvMmfHtlkviCc1miXeNckuXAwuMcByVSLXkkQjIBPfGmGWoSdnigQ76iB4FkGF8ACbaAGRVlvRYCZiCBk8YYo4tZc7mkQ48gd8acAY2Cga6YSFgB8Jsqa+zlZDToQiTBID0WgUECPq5CgPLCbVWOREvGgNUicd6dSFhqdqTYytpgQb+AnydSXqDkQIqqYw3Gf/mdvXsCVcsVroiZZlOUrMAIBFwAxjSYCfXek/2AGC0KGpUkI1cmnIAEA/DekbVikVBoRxP8QBPvFVgsIBQ2AX09KTlEqoYpKEWoATxG6BEswqcxSCvjEpbqnB1+KELqAA8rZOXlQptd5k9GlAtO1mKKnR0/ApoLwBUVwCbygCTDaBiyQPwXwIrXSaD1TgBMxCaFhhJZ3KWUAjb9oEhZwqGsKe2yQqRNxCpe2RwqYjzKAAZPVDasAT5d6d9gqEdoQnI1Uri4IrtJgBtAZGqR5Kd5SnwqxCWKaLCLUkQRxpi4ErQMRBj5gq3c4MNQFADJKZLVCYzYarQxxicyqc8dSmDcaEZm0FVeQsRqrsVyxSa4hEATgmATrreBasQdBAUb3A93agDnAnxYwrpbqBJhaNyYLX8H/GbNt2a5UJwAxKZ/HQgg+yhCpGkfJYl/c85f+qp3oJrAj+3bqt3z5oygQUAHtkXkUQ6gHkaM7AqjH0gQClxEkMiXQqU9bxYMSSIaB8gggK7IIeIccSLM/qhBBemkqG6m9SAEvS65KYJdL0LL2ChEAALNDJQU5e7fScImpwbUKoAdNolilQG01kz3MxCx7krSr1I7GyLRte29sIAqDoyubpii2kno1+xC6AH8SqwCG0LgYIWIthhwmMFwlAErGYQJoCwdRBi0pe5QLKAZwSxFbMLB1y4o5AIM3O5wkqQK/KxGZME4424zgWAqVh4QU9otuEJ3Yozgz0JVnaaaw6oQC/9C9+rVfvDuR3+ofvXIrHiACmmdCLYC69QK0G8oQxrACRsgBNZC/NQAJfxALp/Y+EggomTIhemKojrqkCxgIWKKEE8G0w5uoFogMnIqldfC1vum8LGiX0AuDFsBt3cYchkB/G+QAe1Mzg6A2DHqY3/uvi7kIB3yUd1jBEsMue6pvGzGOiWt5xxLC/ekQmVAKjFAKb4BZZwBvtuIG+yQl0ukqXdAiEjQHLsVNMHxvXzCoFOHAWqmQPSCeVoq8EmrFE4HBGskECwqtACC9gmEdKOC1YFgQVQBCg7A4ZuWqr5qYATSr3VsLSjqWVQw2eBsfDosRB8CsOWcszVEA1lkRz/8ggjMKAc85JbZrWFtjdnOwCGIpkV4AqoFcEALbZVnsMtdiDXRJkg3ANhMxB867ljn7ih5Qf0EiCWqsxjXwBrmQEFeQe/fSO8sExv26wkq7mAQgm3Z4b2U5vyYUB9NbLPWSBSJsEZ7SLh2jaBfwnP/0b11weu8BAKnQTZUZw8dJqMGLaaxIiimqJ6cwynbpCcc5EQCQyoRLxuRMdZwhJHRgGsyxAChQA6YEX7llUmMQA6pDld5jubIKrfIRAJoAdRRngt+cEgAwAABVyIZcUJtsgCEIcYxSAG7goRhqZV1gfPg1B8JAb6FGxW9gpMxbcP7HhQtppJlAgx12A3VJCif/bQF8GriWyoxMkF47IHAhqAaGsHP3jM860BqbMAmT4Aef0WeAIgkF8DeCaJgGQdB4fNCL8EdJdoeeMAmLahISEgeG5hcf3AQlhrVyCEGg2ysrwgirwqUeLQM7SYcleG/NkM0SQQAqncXao8mcYYgddk4yiwR5QKESIa7kxJZkvNeo+ZsH8AllAAeXcinMkQAf0AI4cGowZwLcogfiMCbI4wGJ7L12HDA6Zp0CIIxwytAMjBIAcACQENbdphyQIAI27J8ag0IUkNsWMDMdnTgPw7r/gAhU0HZ3aJvG7EqXNoVtGDim/A+TMHg2WJeswA3HXRBzF2FkLAjMHb5oBz4Q/5AHdwAHaBDZKCABLTC7AKzZCkAHcgAuSgKuXknVANtkZMBTqW1vgbBdZk0RQSIKlBLbyvEHoe0QsDFVpnah1syviZcIzsemEfCXDaFfKx172mO4DhkMzVZ4iwjhDIGMLLjK8bzacigAagAvVgACdzAGKk4HkhAHKbktC4AGhYLNAy7a2ZkBpb1SckBe920A+aYSEGQIggEY3YYCzOwRB551Hr0kB1AwH/lp9/0AvKAvDYqyuhh7fLA9ZRokyIBnhWd4R92gAEBa1ueWk0jHATAh4ZMHUN2ZDaBVSt4EjnJ8dq0QlquTT+gAAFDfDY5mP87a4JMFQ07kf4ECAt4RSf/e2/XVqvXZKZ+AYAvtdp5Ae3FbED0Wj8u9y+jm6LbgbM4m0+OnXaXr4Yit3ZqOVmnOyCaOMw1gCMPVZ1WCBp0ZtAtx59spEA7AAmzX5xilEs7wCAXwB5NC6IABCdzAEYlOi4y+qBPyCmj27M9eBJ8gfQ1BDbYqfKN45vaa6omATt4O6tmgATW7DaQ1xmzAOAoOXxAUuuJjCLHLF/P6bSRzLQ3hryOADjlugASwCJA+cbwmDLg0NrDwDBZxBLgwAcoACcNO7NjhBrWcEcm+5BWw7b9RB9AO7XiVcQohDQ71BNjej03ZvQ5JAJvwCt/+7bRwAHNQ6QJBWsvIBOc+Qq//mRDqMgG7MgAHcL3/xLVNYKofaOei8hZyoSH5XhAQRAYX7+9P0OtA4wjCoGYFTwFysA7CniYIcPX/XdTHjhGqsjcSYFiLbsEB8NMWf/FodgnntRBF9wMe34Zdo+1dLRAQdAavoAR2f/d2n/IMEVSDywSgoMuTG/cI4Sm6fQb+lBxXn8+VIBxxiAVGMvQaQg7w7TRBwgllD+3+TgVkQAxT9QywEAaPBfUXYfAHQAlZcM92gPX/LQFxoLYWYQZTUgNjGvbb3imbVgde8ABmXwRMfxA9NgRIV5ubuTqEre6b9grZMH5OcPfpJFUJsQyrMFR2GYC9Q/w1jhD/2SlxMCUA/9VtCeDwDqEFewAXkD8C6RAOL4MJJzskl5/0vdYBPyAMmoCQQaCF1XReFVPRBwEEFAAQB9aBaaJgwQI7CBQmYChBB4dYugD8o1jR4kWLAIyYqIFAwQaQG7rkqSDCQoCLAAR4AJTjywOYBmTKvESmFgUAAUzVihRkSIciD77keNNjRo4GEDwIyImRIgACLK2QklLViRMlN3gc80Xt5D9o226tCkFCCRJQzQAZnZF06USncS/qamGCg0OGeXXEgiv3H5Y9SV6MGJHBMOERL9KZM/fNQ0YCB9548gLzwcyZDDR30NxZplBx/yyMburX9D8gQHAd+NQKzgKDBxUiyCvhw/+HFnHMEEBpeo6fKjg4Iui1od9xkSRN9rboQMCACpXqvLScmQEVKjR+DNEs84snD28ayJDhVoAD5k6dQ68khhQS+FWz8tiK7NevsjxuSKH1akCB8cqrYIDz0jsNIzc+wEsvhlDIggC/rtnjhcEMs/AwxNKJpq9/AlBJqkAqsywmzDCDyYs6mJpgAgEI+OpAv1IbpRNl+LmDEAVyjG2h2m7j4IRY3KjCjCuKvMKMKYwo4QS7JPBoA282kGeQLsAAYYeSXkxpAvb0qIMNEUks0YAvAklkAhYwSKGHHlIYsMCKDKzIAi6j08M9UOBDQr4b+lTiT1pYCQZNNdl08y05T2v/ww8zSnEjjo5moy2vBFCAZBO5UKkmnHDIScMVUEN1hRxywhEAE/TSWy86FcB8wIURY4UJijo0mOSAAQbwQASmOIQxLiBGmcCdCGQAA0cdd+SxIQlsu+3ZZ5utdIEmCOnijnLG0VbbHjBgQUvI6lxTDxDqCISNL9JNlxdNyFiEBUS4hKABeiPAYCn0DgSATpbGVUGMOkgRmBRaAmUlkU9YZEGD6Oi9oAB8A0gUo0yuqGIFHOq6SwcUDvJ4WR0qrYEbv6DSIAYZQFBZ5R4ePuDlFkujiF87QUiDDShy1jlnNgLxZMACKhC6gAF4lflXuYA4goIDlGGmFTHGGASNZHP0/xgFSWdDYeuOq/3DEFE0YHiXHbTFZxwZYihggnydAsCBqAq4II8dyNNDlUoggJiTR/r+DwMIIhB8wANcnNipTOA+QG66cyjj7hQePoMbFvxmOPDBNSj86LgAuMIIJhVsEOsmSi9dWQZRqIGFziPDYLyjYrciqaEPgLOit5+rAGXHVRbjdzFAkKFNeuuV3Pa2kT4t2AkakWWaVqAeo4upqa66FzuorRaOLAyRJGwRTjnFgwJk2UWGHdLfIYUGIDa8ZAK4lHvcuhuI4IKhARc6cAw0Z9tXGLUhfv+ZVwPqZz/8YUCBGNif3vyHk9MAYAol0FilEECI7hUDEGcYgJpyQP8Q2UyKIQiwFG8wApXFza14DsvcAGwHLorAbQIM410ZZrAyHDquXoS7nfIOBAQAUEAA4igf2VoxAxX8Kx71KAe2QPC4XciiAsrQgDs0oAxATLEC65CFLCKwCzDKQhkFsAQneugXC8QvPBDAXANiMLgGFoBocmAR55TnHBasEXNvhOP+5Fi0/x2uIlfAGJNqQC06AOIU2pjF+NbIOxW8BmupUwAduuGr3OGqAGLjZCc9UDicJAoqE5BMw8aTg7qlsl4YgNjmBOlDjKTmCKOInyUaUYEvMkOXzKhbK6ywA2ZMowHTYBMz8pAHZvAjBRGAACU+oYFGuLATE1jDGnBhR7n/7ItOEwiPHL35RxfaLmYSg2VKCBA/EXTzm5sU2yfhFcoIVqEEODgBJA5Ch6XIgWEKrIACLxCDU+4gCwuYZF4QcJA3cE6bLCJAiwTg0Ic2FJ6dc44ANMnACrCRf388QB0BWM4fKm0UFFhDJw7gAQ0ErQJdFNo6KKHFT1CiANE8QCc6IYBqEoAC13QAAIBATou88h9zAABU0IkmESSVBQy1QKpAms3RsAipS6Wqi3oKIwooaUk1UEATLiACuWEOAvirQAEWeIF/5oEgBVUIbB7k1DgV1UMW2FdR6VrUXy1UAFN92QGUalVsPnV5qZElAGi5BgF0QgQH4AQLOmGJpT60/0UuGsURLGtZwsoSCBQRauc8tC8HUIA0oO2sYOta1NDStamlHeoKSrBVBaChrHxEKwT6186+4oqGIOiIQWHTBFsFVrAWIeppKXDc0X50uMtlbnOd+1wfahUH9txAGea1g3rt7a86Pe4A5VaGGujAIQfNUTG+xVrople962Vve596hRK4tgVNEEmxymMvQBLgbUcrKgHACgFJSIu8G7gDotx7YAQnWMELNsIKVnACrhI4ZS0rACiF+48gPucTdBAdbDYAhgpPdMEjJnGJTXwaXaxASS3wCBqS2IMIaE7EfiGqRS9gAgV5eAxvUu6JffxjIKv3YhgzwQI24OK2hBi9ov8hgBwUcRcPg+Fhpwpyla185eG6wcEQNrKLsYQvHxKVAHGA8kfukIK1XRjLa2bzlTUi30PGFgTtYxtQOasvabjBBBLo8pzTvOQ2B1rQCI7DPPcMGwWoIMa9AqmeIzyI8qR50JOmtI/jcIK6OAk2OyZQjw8UBxM8CQxIkXSlTX3q9qJEz00ir1cNDMs2nIADRh6EytRGZVTnWtfMRYkZOHCb8R4kC3JgCkgZwZGPqOAoF3j1rp39bOV1QxHSGiEJDWFCWPqhBYf8cCq9BUNoh1vcTikFgyQVC2wjzQ8n4PMGxmDfGBAoeeOmN70dYAFDYE1SeYFEKXKBtCm0gM+VLID/Udp3gBnXW+HQpoAIssC1fYu3BW54RDf84oAp4ADKTdCDGgDaMnkDeuEjX/N66OCxEDbkAxxoQZAYQaQjVcEIGVNQx7LgwgLG+Lwk57mzUfgJECALdc6CFrSk1TE4iEIbHhCrknv+dF2rBDoNAMNrht4srBu0a4nca1hjUFaEixzqY/cxzWJghTuMAQ6lW5YFEVCDGnxNFPm8XOaMJnay553EZreC4/51BzDQQfCG8F4xRNE/D/ynAGLF31ISrnfIs3k9jNtBDmYgO+LR9p+CQ+vXiWY7vEZe9IJWifwCN55U2q089VpmBIY2ADo+fvSzt7JR0US+xac1BbunbVlzUwUz/eKd9sM/cH/jx83E50r5ufqkX2PmaeJHP8j7Om53J3DOyeo0uXGSfvfbnJOiQt/74yd/+c3vF5T0Jv3nZ//s199++Mdf/rR///vnf3/8HyggADs=' usemap="#bug" border="0"></div>
+
+<map name="bug" id="bug"><area id="area" shape="rect" coords="0,0,275,109" href="#area_1" onclick="alert( 'click' ); return false;"></map>
+
+</body>
+</html>
diff --git a/layout/generic/test/file_bug448987_ref.html b/layout/generic/test/file_bug448987_ref.html
new file mode 100644
index 000000000..02ec91a10
--- /dev/null
+++ b/layout/generic/test/file_bug448987_ref.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body onload="focus_area()">
+<pre tabindex="1" id="pre">
+STEPS TO REPRODUCE:
+1. TAB to the image map area below
+
+EXPECTED RESULT:
+a focus border is painted just inside the image edge
+<pre>
+
+<div style="background:lime;padding:1em;float:left"><img src='data:image/gif;base64,R0lGODlhFAFuAPcAAPf39//7/+fn59bT1u/r787Lzq0UAN7b3hhFrRhJtRA0hBA8lMYYALWytffz94wQAMa+vb26vRhNxufj5+/v78bDxvfz772+vcbHxghRCM7PzggkYyFZ1tYkCNbX1hhRzpyenO+6AN7f3gBlANauAGOW7zFl1kp95wg8pbW2tZyanHOi797f5zlx3msMAAB9CP/PAL22va2mraWipedJMSlRtf91Y72WAFqK76WmpRBFta2qrcaeAK2urfdpUuc8If39/e9ZQmPTY94wGFrLWrUkEISq9/n5+ZR5ABCWGLWOAIRpAGNRAClJlK2GAL0sGGssITG2OaWCAK3H9848KaW+762qpcbX/73jvWN5pGvTc/eGc//vCFppfIwgEJRBMYSGlPeWhJSSjHN5jMaSjKWmrb3P787V1ilBa4R9c9nXy+rq6ntNQu/y95Su3svO1kphlISa1t7b1vf7/3uGraWGexdAmoySlDm6QrWyraWqrRiiIWtpa0LDStrl/1JtrcbHzpxlWntlUklVcztZk3vbhISStfffWufr786upVmB1ta2taW21tLS0ve6rd7j5zlhtfemnJSetefn78LCw7W2vdnZ2bVFMf/3//f3/8Z1a9bb59bb3eHh4a2WjM7HxoR1OSGqKbW6xvT09JyGQrXD3v/r5+fk3s7rzufb3q2ytffXKbWiY6inp1p1WrWmjP/LxnOOzsC/v+/n56qSMffXzr2eEP/3hPHx8f//987T57m4ubVZShk5f97LjO/HKQ03ks7DpdZlWv/vMd7LxqWuvefr/86uKd66QgwyhcnJyf/ztbKxsrWqpe/37//z7///75zTpf/397Szsvfn1ozLjO/jlN7v3qWko62OEO/r5//73tbX3vf37wotd1qqYxlNvYyujN7X1qKgoHuWe5KUmkKOQhZKvildMRh5IZqZmcXEwZ2amaajoNfU0QMaTAsiV5STlSo+bZibpSti2puZl5u28pGs5pqbn9Hd8/fr94WFi7i1sBguYfP09v///ywAAAAAFAFuAAAI/wD/CRxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bNmzhz6tzJs6fPn0CDCh1KtKjRo0iDYinEtJAWLUSIfJv6DVFShnOMXTEzpYrXKlPMXDGW6apZhli0CHmqtk+oJC9ejDDH6azBTGaMKDLBQUKCvzoC/5XAwcSJElV0UbB71lk0PFH6CGmqxS3cuZgyAwhw1tiUEiY+CEZBunTpwR9SfyisiNGmzYyNYqlWLZrtak6JRLmcoQKBCQQswEZ6ZcUJDn0RoFiwoAkhOH/+wCHURDkKHRIII0e+uoWbSbGRYv/SFy0qnj1yM6ArUEDDAQEOOBu9UqIF3xrMCWWRBGiAnP8eqKEBBpVIkgUhyg22nWocxKJLeEMBYIEAqVSDBx5vjTCCeinEgIEH8Mk3lB/G2YffAlmIcoAIcgxYAQQwXiAjjC+WkUV1qHH3QXaKbAIhUAAQwMIBWkCG3obq9dBABQcER1QmVeBwQgv4KZAFICIMgEGMEXTp5ZcNhJmCjQlmp5pfKCBgyHA/6hQkC6lUdl56Gbgiw5IiUCAiUCRKaQICChBSSYsydhiDly9ioOiiMKYQZgMqEJLmYH4pp4ACTRzQ5k4AOEABAYUQ4RaddqYwAHxDmVGClJAssMEYA8b/IGaHELQ3QIAriiDCAQcM4OIFYVqRRZkJILCAAhtsAMcEbG5q02YOFCLEqEiWCuKePn1WXw3IgqFBBEoqeQEG7uka3KfCWYCuABP0qgEEjt5RQ2DGIrvBIAOw4KSzOWEiLbUZZJAGnnoGVUUJ2yJ7BwYN3NlABBX4J0BwAAzH2cWcefqbBwXAq0INy9k7SB7jNmkBvzj5O20odA4MwQHCAaXtCdy+eoEMOVjRQK3vFYzxQp0KyfHNTYgMwgw7XHAqbAFgi7JLAPzLMpIu5/nzTjPX3EUEOSC98wDANftQ0FlGUPS9KqSd9KkOPG2TygBnAEYDGLAQn09+IIyDCa6i/9HDDEenUECTd1sUdBbIdgFC2jPQbbLbNUW7Mp13OF74TpmssOqfyd7RdQ6C9+w0RaXUQAgdeQDeOJOoQk6TBW0dGXDlBTDrkxEI04zsIDIEXve+Gm2yySkDRLCDzqxf7npMsE8rewZihC72TXmvsLerG4hxdA/JX50RAQPACzHhoy/fEgCx0xl97dPbZITmVO7euxVKt95RkAMUQC755tPkTPpIUoHgbLeT4liPb8kCg+/sVr6LAIAC7VoR8Ponk/85j04CZF8DYzIHI+AufhtAw+K4x7+PBIkAE6RgBdUypwDGQAP2wwmJjHC9xB2tATBs30YqtsGIPKMWjgjDFv+GuIUwRAIWpiAJNLaxjFsc4olPvMUytvETC7YwYBmMoUOo4YhICLGIkXAENbxXED6Y0QMs4MNmRKBGhFThfTTDXhqQxiSf8cQUkfABDYYwhA50YAg/CCQNaBAEYYQhFR6BhhN/EYJGOjIEMIgkDH7hC218pA1+uMIUNsnJrnzFgx6MgyhjEQtGFOQaULliBrKoQ4PUIgxBCCQgfzDIWhpyFghRFx9iYIED8AEC/8AAHyZwEGN4sD6ACqEKGqcBAu6EGlvYYwcYYIAiPOGaRfgjLWnwAz4KA5EZ2cYhYBACEvDgBjewhTp5QAISNDKSjUTGKTKSiSvQ8AT4PAEOEMb/TynZpy9oQoECFiAJO/4DlURQJSsbUgsb/MCPDCiCRLVZy4cOgQzt48MF/jGBHKQhABAY5kHyQsM/IUuEMqifxW5iijBIs5qaSMQniKGBRSRCE0/QpiwZ0AFNGDQi0BhnCHighGywIhhqENAngsEKW7TzkeX0hfIgYszjZEc7htlnCTTnp+SEbAF0IOZKEapKELxQiwYxRTSHINFLkCERNiUDL3I6yz4y4BLgLCMw28WHBhCADxo4yButx4Fj3StndezhS2Dhgx9QoQheIMMB2oCIR/DqDJi9QCCy+Ue7MoAK4qDIMoYBAxLcIBuvyNMkhjSAW50BAqxg5yPbeYx5/0ZkDlWwKgL+IAlGMCIOfyhMC/JpHwlAgg5ZSO6VJjQB+AyHrLxZZQSWphDGOrYIX0gEIig7JE6coRmX4Gkfp8mAJxDDIHzIQTAJALh/DMwgDvCgcaq0gS7ciX06iUQQaEAFA3zhE23olf629KIZeaIIPIUoAyK6CIncgguldQIt1EAAAbOnwDN6hVPdWU5z2kINECGRfXb7BnURYBbEKwYkCGMCvlRKEi1qbf40ADb7oSKV0TUrdRHiiP32NxCPQATHLkwjUQQiweQ1wBPyOpCV/iMTBDDIDOuTgEu9am0EcPJM8hiE/n5hRQOmEYExfOAFK9gABmgwGQ/y4AiTgv9XYU4Uo7qUgw2305znBHFD6HOCP9FhQu4acAEu8AfsXLVedADWocZVADT67MYJzfF00SoQR/igywYA8gHijGFZ8QLJ1DTAJWImETOs4ICAQtYYSLgYnHCZCl6AQqO5FDEaB8hXHYOAJxbM61Ab4BMOaTMJpAAKGtPaVrf+1SvYeWcenDMbelYIfXDQgt1qw8IVmHMKCg2YeilgbrRqT54KB2k8SHrHBWGsHrEboGPTWAP6AxYV/DhNAzzgARjV8kKmcOq9pXoDc0vsTRxhg3U/YAZvMFStaswuFBIgS+zRRK8XjOYlN+0fFzfIg7kQAicsoRkJDzfDHT6BIXWMFXf/buc5lUALZ0pZc3F8WYHJ9e5496AGVy3WsRZQBgzYKmwiKve50WqKggfhCQf/lsjNxS4Bk4GP9b63Fw4wVYZU4dTH+XfAGWiThtrg6A9gA8MakAKeOVc+QdsYIMLbazQ/IBA8RMgyuADhGywBFIAQEwTcM7HhVMwCQoPAMVK+cimwour/6ODmFgAHpTN6Rc01eQEkcdVKHavxBwD6nlAhhEhjcNJODkPBqRD2vD8sYvoSTsU0doAKvPQBLoh9HdqA+IRcvQTHOVa3lsR1mmBiC6P3ggvqEIMdPGxp7evUBDjRjIm7/QGJqBhChgHhYTMhesaHAPJHp3wPJCLl5lSC/xOkEIyqZ+3b4kM9xRygMY5l4Uw6V0AxZkGB6XFeN0NfKSxsYAMaPMEFaSAK2XcqqlcQb0IDHVAE9xZ7LgAFJdQQuENthWVlYHBWrcQSBPd1SAcFObM2vZcQDiAAnPBpZ2ZvX/AIpEYQGxcCdscEHThpKZhLIogMjaRyNzB+Lac8bXBqoOEqlcM920cQQXIKosABqhF/y1J/B3F/u/F56CYQwOcDsOYCYiAuT2gQ4jAE1AR7DCgItRODC2EcfSYB9WIz+EUTagV8NFAELsAGXWM5ivUPnpII9FZv9vYAzZBlewIN1BdhSyAIZQA6XxiHExIMjmRONygFSBABeohxAf/Ab9ZTM8sEhwfxJopwhPWCBmdYEEyYfwSxfxr4AFAAAshjNQjxDJFAA1sYe1AgCFWoUg9RH8VVhluziTJBcFvQZcLnhikFNhdIEL00bxD1fEDWiBhnDXQHAzyABEwAiPQzAFn2EBB0DDVoWuOHBG8Wjf8AAPKlO9mDZToUJG6wGjvibXqQJwBgEJ2IQTHwhJHAf2soioEIegfRUgjIALAHBWyQBmkziTn0EIpQGMlhWHAAgzUxRKHoAs74O3EoEBLydOO1hQ/gBb5xMgNBfRx3A8w4RwTTkNtIAL5wiESliEuAAQTEAivwQclEOzDTQBVzBnwxkAqTL2KDBZ3XhEj/AgL0KBDPwH9SyIYcyGrYAgtb8FAdYIJ1ECb9qAK9SGkIEZDc4W1oMEC1txKpYANqqIAKqQem4pQJAQDEIEtRB3ueoC/p+A/UkIwkoARLwAQc+Y8QMQdqIJLihwRLkFrRqAv9ZlIARzcuV4kEsBd8UYaw0pIFYZP454Ta+A+14ANf11/y6Jd+Z2l2ZQBeoAmLMCTFdzQ54yEPuBCxsBpGmADMoQB+M4gzgYs/CXuC0AOwGJcCEEvdJJEuEAhnQEDIWH0e55bgGBEAMAm/QE7ldIN2WQe3mY6Q6G+qZoEMIQ1x0AItwAFVpmrat5gCgZg4iUU7+Q+L4JhHZ28ceFYC/2GPduhWTcICHdMwxuchfFeVBhEHoika3rYwpigToveY9qaQqvAyP8UQADAHmjBIfXSHbTg4SngIEOaHvMmfHtlkviCc1miXeNckuXAwuMcByVSLXkkQjIBPfGmGWoSdnigQ76iB4FkGF8ACbaAGRVlvRYCZiCBk8YYo4tZc7mkQ48gd8acAY2Cga6YSFgB8Jsqa+zlZDToQiTBID0WgUECPq5CgPLCbVWOREvGgNUicd6dSFhqdqTYytpgQb+AnydSXqDkQIqqYw3Gf/mdvXsCVcsVroiZZlOUrMAIBFwAxjSYCfXek/2AGC0KGpUkI1cmnIAEA/DekbVikVBoRxP8QBPvFVgsIBQ2AX09KTlEqoYpKEWoATxG6BEswqcxSCvjEpbqnB1+KELqAA8rZOXlQptd5k9GlAtO1mKKnR0/ApoLwBUVwCbygCTDaBiyQPwXwIrXSaD1TgBMxCaFhhJZ3KWUAjb9oEhZwqGsKe2yQqRNxCpe2RwqYjzKAAZPVDasAT5d6d9gqEdoQnI1Uri4IrtJgBtAZGqR5Kd5SnwqxCWKaLCLUkQRxpi4ErQMRBj5gq3c4MNQFADJKZLVCYzYarQxxicyqc8dSmDcaEZm0FVeQsRqrsVyxSa4hEATgmATrreBasQdBAUb3A93agDnAnxYwrpbqBJhaNyYLX8H/GbNt2a5UJwAxKZ/HQgg+yhCpGkfJYl/c85f+qp3oJrAj+3bqt3z5oygQUAHtkXkUQ6gHkaM7AqjH0gQClxEkMiXQqU9bxYMSSIaB8gggK7IIeIccSLM/qhBBemkqG6m9SAEvS65KYJdL0LL2ChEAALNDJQU5e7fScImpwbUKoAdNolilQG01kz3MxCx7krSr1I7GyLRte29sIAqDoyubpii2kno1+xC6AH8SqwCG0LgYIWIthhwmMFwlAErGYQJoCwdRBi0pe5QLKAZwSxFbMLB1y4o5AIM3O5wkqQK/KxGZME4424zgWAqVh4QU9otuEJ3Yozgz0JVnaaaw6oQC/9C9+rVfvDuR3+ofvXIrHiACmmdCLYC69QK0G8oQxrACRsgBNZC/NQAJfxALp/Y+EggomTIhemKojrqkCxgIWKKEE8G0w5uoFogMnIqldfC1vum8LGiX0AuDFsBt3cYchkB/G+QAe1Mzg6A2DHqY3/uvi7kIB3yUd1jBEsMue6pvGzGOiWt5xxLC/ekQmVAKjFAKb4BZZwBvtuIG+yQl0ukqXdAiEjQHLsVNMHxvXzCoFOHAWqmQPSCeVoq8EmrFE4HBGskECwqtACC9gmEdKOC1YFgQVQBCg7A4ZuWqr5qYATSr3VsLSjqWVQw2eBsfDosRB8CsOWcszVEA1lkRz/8ggjMKAc85JbZrWFtjdnOwCGIpkV4AqoFcEALbZVnsMtdiDXRJkg3ANhMxB867ljn7ih5Qf0EiCWqsxjXwBrmQEFeQe/fSO8sExv26wkq7mAQgm3Z4b2U5vyYUB9NbLPWSBSJsEZ7SLh2jaBfwnP/0b11weu8BAKnQTZUZw8dJqMGLaaxIiimqJ6cwynbpCcc5EQCQyoRLxuRMdZwhJHRgGsyxAChQA6YEX7llUmMQA6pDld5jubIKrfIRAJoAdRRngt+cEgAwAABVyIZcUJtsgCEIcYxSAG7goRhqZV1gfPg1B8JAb6FGxW9gpMxbcP7HhQtppJlAgx12A3VJCif/bQF8GriWyoxMkF47IHAhqAaGsHP3jM860BqbMAmT4Aef0WeAIgkF8DeCaJgGQdB4fNCL8EdJdoeeMAmLahISEgeG5hcf3AQlhrVyCEGg2ysrwgirwqUeLQM7SYcleG/NkM0SQQAqncXao8mcYYgddk4yiwR5QKESIa7kxJZkvNeo+ZsH8AllAAeXcinMkQAf0AI4cGowZwLcogfiMCbI4wGJ7L12HDA6Zp0CIIxwytAMjBIAcACQENbdphyQIAI27J8ag0IUkNsWMDMdnTgPw7r/gAhU0HZ3aJvG7EqXNoVtGDim/A+TMHg2WJeswA3HXRBzF2FkLAjMHb5oBz4Q/5AHdwAHaBDZKCABLTC7AKzZCkAHcgAuSgKuXknVANtkZMBTqW1vgbBdZk0RQSIKlBLbyvEHoe0QsDFVpnah1syviZcIzsemEfCXDaFfKx172mO4DhkMzVZ4iwjhDIGMLLjK8bzacigAagAvVgACdzAGKk4HkhAHKbktC4AGhYLNAy7a2ZkBpb1SckBe920A+aYSEGQIggEY3YYCzOwRB551Hr0kB1AwH/lp9/0AvKAvDYqyuhh7fLA9ZRokyIBnhWd4R92gAEBa1ueWk0jHATAh4ZMHUN2ZDaBVSt4EjnJ8dq0QlquTT+gAAFDfDY5mP87a4JMFQ07kf4ECAt4RSf/e2/XVqvXZKZ+AYAvtdp5Ae3FbED0Wj8u9y+jm6LbgbM4m0+OnXaXr4Yit3ZqOVmnOyCaOMw1gCMPVZ1WCBp0ZtAtx59spEA7AAmzX5xilEs7wCAXwB5NC6IABCdzAEYlOi4y+qBPyCmj27M9eBJ8gfQ1BDbYqfKN45vaa6omATt4O6tmgATW7DaQ1xmzAOAoOXxAUuuJjCLHLF/P6bSRzLQ3hryOADjlugASwCJA+cbwmDLg0NrDwDBZxBLgwAcoACcNO7NjhBrWcEcm+5BWw7b9RB9AO7XiVcQohDQ71BNjej03ZvQ5JAJvwCt/+7bRwAHNQ6QJBWsvIBOc+Qq//mRDqMgG7MgAHcL3/xLVNYKofaOei8hZyoSH5XhAQRAYX7+9P0OtA4wjCoGYFTwFysA7CniYIcPX/XdTHjhGqsjcSYFiLbsEB8NMWf/FodgnntRBF9wMe34Zdo+1dLRAQdAavoAR2f/d2n/IMEVSDywSgoMuTG/cI4Sm6fQb+lBxXn8+VIBxxiAVGMvQaQg7w7TRBwgllD+3+TgVkQAxT9QywEAaPBfUXYfAHQAlZcM92gPX/LQFxoLYWYQZTUgNjGvbb3imbVgde8ABmXwRMfxA9NgRIV5ubuTqEre6b9grZMH5OcPfpJFUJsQyrMFR2GYC9Q/w1jhD/2SlxMCUA/9VtCeDwDqEFewAXkD8C6RAOL4MJJzskl5/0vdYBPyAMmoCQQaCF1XReFVPRBwEEFAAQB9aBaaJgwQI7CBQmYChBB4dYugD8o1jR4kWLAIyYqIFAwQaQG7rkqSDCQoCLAAR4AJTjywOYBmTKvESmFgUAAUzVihRkSIciD77keNNjRo4GEDwIyImRIgACLK2QklLViRMlN3gc80Xt5D9o226tCkFCCRJQzQAZnZF06USncS/qamGCg0OGeXXEgiv3H5Y9SV6MGJHBMOERL9KZM/fNQ0YCB9548gLzwcyZDDR30NxZplBx/yyMburX9D8gQHAd+NQKzgKDBxUiyCvhw/+HFnHMEEBpeo6fKjg4Iui1od9xkSRN9rboQMCACpXqvLScmQEVKjR+DNEs84snD28ayJDhVoAD5k6dQ68khhQS+FWz8tiK7NevsjxuSKH1akCB8cqrYIDz0jsNIzc+wEsvhlDIggC/rtnjhcEMs/AwxNKJpq9/AlBJqkAqsywmzDCDyYs6mJpgAgEI+OpAv1IbpRNl+LmDEAVyjG2h2m7j4IRY3KjCjCuKvMKMKYwo4QS7JPBoA282kGeQLsAAYYeSXkxpAvb0qIMNEUks0YAvAklkAhYwSKGHHlIYsMCKDKzIAi6j08M9UOBDQr4b+lTiT1pYCQZNNdl08y05T2v/ww8zSnEjjo5moy2vBFCAZBO5UKkmnHDIScMVUEN1hRxywhEAE/TSWy86FcB8wIURY4UJijo0mOSAAQbwQASmOIQxLiBGmcCdCGQAA0cdd+SxIQlsu+3ZZ5utdIEmCOnijnLG0VbbHjBgQUvI6lxTDxDqCISNL9JNlxdNyFiEBUS4hKABeiPAYCn0DgSATpbGVUGMOkgRmBRaAmUlkU9YZEGD6Oi9oAB8A0gUo0yuqGIFHOq6SwcUDvJ4WR0qrYEbv6DSIAYZQFBZ5R4ePuDlFkujiF87QUiDDShy1jlnNgLxZMACKhC6gAF4lflXuYA4goIDlGGmFTHGGASNZHP0/xgFSWdDYeuOq/3DEFE0YHiXHbTFZxwZYihggnydAsCBqAq4II8dyNNDlUoggJiTR/r+DwMIIhB8wANcnNipTOA+QG66cyjj7hQePoMbFvxmOPDBNSj86LgAuMIIJhVsEOsmSi9dWQZRqIGFziPDYLyjYrciqaEPgLOit5+rAGXHVRbjdzFAkKFNeuuV3Pa2kT4t2AkakWWaVqAeo4upqa66FzuorRaOLAyRJGwRTjnFgwJk2UWGHdLfIYUGIDa8ZAK4lHvcuhuI4IKhARc6cAw0Z9tXGLUhfv+ZVwPqZz/8YUCBGNif3vyHk9MAYAol0FilEECI7hUDEGcYgJpyQP8Q2UyKIQiwFG8wApXFza14DsvcAGwHLorAbQIM410ZZrAyHDquXoS7nfIOBAQAUEAA4igf2VoxAxX8Kx71KAe2QPC4XciiAsrQgDs0oAxATLEC65CFLCKwCzDKQhkFsAQneugXC8QvPBDAXANiMLgGFoBocmAR55TnHBasEXNvhOP+5Fi0/x2uIlfAGJNqQC06AOIU2pjF+NbIOxW8BmupUwAduuGr3OGqAGLjZCc9UDicJAoqE5BMw8aTg7qlsl4YgNjmBOlDjKTmCKOInyUaUYEvMkOXzKhbK6ywA2ZMowHTYBMz8pAHZvAjBRGAACU+oYFGuLATE1jDGnBhR7n/7ItOEwiPHL35RxfaLmYSg2VKCBA/EXTzm5sU2yfhFcoIVqEEODgBJA5Ch6XIgWEKrIACLxCDU+4gCwuYZF4QcJA3cE6bLCJAiwTg0Ic2FJ6dc44ANMnACrCRf388QB0BWM4fKm0UFFhDJw7gAQ0ErQJdFNo6KKHFT1CiANE8QCc6IYBqEoAC13QAAIBATou88h9zAABU0IkmESSVBQy1QKpAms3RsAipS6Wqi3oKIwooaUk1UEATLiACuWEOAvirQAEWeIF/5oEgBVUIbB7k1DgV1UMW2FdR6VrUXy1UAFN92QGUalVsPnV5qZElAGi5BgF0QgQH4AQLOmGJpT60/0UuGsURLGtZwsoSCBQRauc8tC8HUIA0oO2sYOta1NDStamlHeoKSrBVBaChrHxEKwT6186+4oqGIOiIQWHTBFsFVrAWIeppKXDc0X50uMtlbnOd+1wfahUH9txAGea1g3rt7a86Pe4A5VaGGujAIQfNUTG+xVrople962Vve596hRK4tgVNEEmxymMvQBLgbUcrKgHACgFJSIu8G7gDotx7YAQnWMELNsIKVnACrhI4ZS0rACiF+48gPucTdBAdbDYAhgpPdMEjJnGJTXwaXaxASS3wCBqS2IMIaE7EfiGqRS9gAgV5eAxvUu6JffxjIKv3YhgzwQI24OK2hBi9ov8hgBwUcRcPg+Fhpwpyla185eG6wcEQNrKLsYQvHxKVAHGA8kfukIK1XRjLa2bzlTUi30PGFgTtYxtQOasvabjBBBLo8pzTvOQ2B1rQCI7DPPcMGwWoIMa9AqmeIzyI8qR50JOmtI/jcIK6OAk2OyZQjw8UBxM8CQxIkXSlTX3q9qJEz00ir1cNDMs2nIADRh6EytRGZVTnWtfMRYkZOHCb8R4kC3JgCkgZwZGPqOAoF3j1rp39bOV1QxHSGiEJDWFCWPqhBYf8cCq9BUNoh1vcTikFgyQVC2wjzQ8n4PMGxmDfGBAoeeOmN70dYAFDYE1SeYFEKXKBtCm0gM+VLID/Udp3gBnXW+HQpoAIssC1fYu3BW54RDf84oAp4ADKTdCDGgDaMnkDeuEjX/N66OCxEDbkAxxoQZAYQaQjVcEIGVNQx7LgwgLG+Lwk57mzUfgJECALdc6CFrSk1TE4iEIbHhCrknv+dF2rBDoNAMNrht4srBu0a4nca1hjUFaEixzqY/cxzWJghTuMAQ6lW5YFEVCDGnxNFPm8XOaMJnay553EZreC4/51BzDQQfCG8F4xRNE/D/ynAGLF31ISrnfIs3k9jNtBDmYgO+LR9p+CQ+vXiWY7vEZe9IJWifwCN55U2q089VpmBIY2ADo+fvSzt7JR0US+xac1BbunbVlzUwUz/eKd9sM/cH/jx83E50r5ufqkX2PmaeJHP8j7Om53J3DOyeo0uXGSfvfbnJOiQt/74yd/+c3vF5T0Jv3nZ//s199++Mdf/rR///vnf3/8HyggADs=' usemap="#bug" border="0"></div>
+
+<map name="bug" id="bug"><area id="area" shape="rect" coords="0,0,275,109" href="#area_1" onclick="alert( 'click' ); return false;"></map>
+
+<script>
+var timer;
+var shiftKeyOn = false;
+var counter = 0;
+function focus_area() {
+ document.getElementById("pre").onfocus = function() {
+ document.getElementById("pre").onfocus = null;
+ document.getElementById("area").onfocus = function() {
+ clearInterval(timer);
+ parent.SimpleTest.executeSoon(parent.secondIframeLoaded, 0);
+ }
+
+ //XXX This code tries to shift focus to the image map for some reason. This is not
+ // working directly after page load, hence it is retried 10 times, see bug bug 922524
+ timer = setInterval(function() {
+ if (counter > 10) {
+ clearInterval(timer);
+ parent.ok(false, "Too often tried to focus image map, giving up");
+ parent.finish();
+ return;
+ }
+ synthesizeKey("VK_TAB", { shiftKey: shiftKeyOn }, window);
+ shiftKeyOn = !shiftKeyOn;
+ counter++;
+ }, 100);
+ };
+ document.getElementById("pre").focus();
+}
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/file_bug449653_1.html b/layout/generic/test/file_bug449653_1.html
new file mode 100644
index 000000000..8a70cb05f
--- /dev/null
+++ b/layout/generic/test/file_bug449653_1.html
@@ -0,0 +1,18 @@
+<html><head>
+<title>Bug 449653 - drawWindow on canvas fails on load, draws white instead of specified region</title>
+<style>
+html, body {
+margin: 0px;
+padding: 0px;
+}
+</style>
+</head><body>
+<div style="width: 100px; height: 100px; background-color: lime;"></div>
+<br>
+<canvas id="canvas" width="100" height="100" style="background-color: red;"></canvas>
+<script>
+ var canvas = document.getElementById("canvas");
+ var ctx = canvas.getContext("2d");
+ SpecialPowers.wrap(ctx).drawWindow(window, 0, 0, 100, 100, "white");
+</script>
+</body></html>
diff --git a/layout/generic/test/file_bug449653_1_ref.html b/layout/generic/test/file_bug449653_1_ref.html
new file mode 100644
index 000000000..b1d78e0aa
--- /dev/null
+++ b/layout/generic/test/file_bug449653_1_ref.html
@@ -0,0 +1,13 @@
+<html><head>
+<title>Bug 449653 - drawWindow on canvas fails on load, draws white instead of specified region</title>
+<style>
+html, body {
+margin: 0px;
+padding: 0px;
+}
+</style>
+</head><body>
+<div style="width: 100px; height: 100px; background-color: lime;"></div>
+<br>
+<div style="width: 100px; height: 100px; background-color: lime;"></div>
+</body></html>
diff --git a/layout/generic/test/file_bug514732_1.html b/layout/generic/test/file_bug514732_1.html
new file mode 100644
index 000000000..55b1c7df5
--- /dev/null
+++ b/layout/generic/test/file_bug514732_1.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+</head>
+<body>
+<h1>hi</h1>
+<div id="imgs" style="whitespace: nowrap; overflow: visible; height: 10px;">feh</div>
+
+</body>
+</html>
diff --git a/layout/generic/test/file_bug514732_helper.html b/layout/generic/test/file_bug514732_helper.html
new file mode 100644
index 000000000..0ef110609
--- /dev/null
+++ b/layout/generic/test/file_bug514732_helper.html
@@ -0,0 +1,300 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514732
+-->
+<head>
+ <title>Test for Bug 514732</title>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug?id=514732">Mozilla Bug 514732</a>
+
+<div id="imgs" style="whitespace: nowrap; overflow: visible; height: 10px;"></div>
+
+<iframe id="testframe" src="./file_bug514732_1.html"></iframe>
+
+<br/><br/>
+<div id="log">log:<br/></div>
+<br/><br/>
+
+<script class="testbody" style="width: 300; height: 150;" type="text/javascript;version=1.8">
+// Import test API
+var is = window.opener.is;
+var ok = window.opener.ok;
+var todo = window.opener.todo;
+var finish = window.opener.SimpleTest.finish;
+
+function log(msg) {
+ document.getElementById("log").innerHTML += msg;
+}
+
+function logln(msg) {
+ if (arguments.length == 0) msg = "";
+ document.getElementById("log").innerHTML += msg + "<br/>";
+}
+
+function logev(ev) {
+ logln([ev.x, ev.y, ev.width, ev.height].join(' '));
+}
+
+/* Absolutely position a 500x500 div at x, y on doc */
+function phoom(doc, x, y) {
+ var d = doc.createElement('div');
+ d.id = "phoomer";
+ d.style.position = "absolute";
+ d.style.left = x + 'px';
+ d.style.top = y + 'px';
+ d.style.backgroundColor = "#00FFF0";
+ d.style.width = 500 + 'px';
+ d.style.height = 500 + 'px';
+ doc.getElementById('imgs').appendChild(d);
+}
+
+function unphoom(doc) {
+ var phoomer = doc.getElementById('phoomer');
+ if (phoomer)
+ doc.getElementById('imgs').removeChild(phoomer);
+}
+
+function getDocBodyDimensions(doc) {
+ return [doc.documentElement.scrollWidth, doc.documentElement.scrollHeight];
+}
+
+function makeResult() {
+ return {
+ success: false,
+ event: null
+ };
+}
+
+function makeListener(result, eventGen) {
+ return function(ev) {
+ result.success = true;
+ result.event = ev;
+ setTimeout(function() { eventGen.next(); }, 0);
+ };
+}
+
+function waitInterrupt(result, gen) {
+ result.event = null;
+ result.success = false;
+ setTimeout(function() { if (!result.success) gen.next(); }, 3500);
+}
+
+function testPhoom(isCapturing, x, y, expectEvent) {
+
+ var eventGen = (function() {
+ var innerdoc = document.getElementById('testframe').contentDocument;
+
+ for (var doc of [document, innerdoc]) {
+ var inner = (doc == innerdoc);
+ var w, h, result, listener;
+
+ /* --- EXPANSION --- */
+
+ result = makeResult();
+ listener = makeListener(result, eventGen);
+
+ doc.addEventListener("MozScrolledAreaChanged", listener, isCapturing);
+
+ if (!expectEvent) waitInterrupt(result, eventGen);
+ phoom(doc, x, y);
+ yield;
+
+ doc.removeEventListener("MozScrolledAreaChanged", listener, isCapturing);
+
+ /* If we're expecting an event, better check that we got one. If we are not expecting one,
+ one can still arrive, but we don't complain if it did not. In either case, any event
+ that arrives will have its data checked below. */
+ if (expectEvent) {
+ ok(result.success, "Received expected " + (inner ? "inner" : "") + " expansion event");
+ }
+
+ if (result.success) {
+ is(result.event.x, 0, "Expansion event x is 0");
+ is(result.event.y, 0, "Expansion event y is 0");
+ if (inner) {
+ /* In iframe-land, the values of |myiframe.contentDocument.documentElement.scrollWidth| and
+ |.scrollHeight| appear to be not as expected (they stay very small). This is fine, since
+ we know exactly where we're positioning our fixed-dimensions div, and we know our iframe's
+ dimensions, so we can do our own math. NB: The inner iframe is styled at 300x500. */
+ is(Math.round(result.event.width),
+ Math.max(x + 500, 300),
+ "Inner expansion event width matches body width");
+ is(Math.round(result.event.height),
+ Math.max(y + 500, 150),
+ "Inner expansion event height matches body height");
+ } else {
+ [w, h] = getDocBodyDimensions(doc);
+ is(Math.round(result.event.width), w, "Expansion event width matches body width");
+ is(Math.round(result.event.height), h, "Expansion event height matches body height");
+ }
+ }
+
+ /* --- CONTRACTION --- */
+
+ result = makeResult();
+ listener = makeListener(result, eventGen);
+
+ doc.addEventListener("MozScrolledAreaChanged", listener, isCapturing);
+
+ if (!expectEvent) waitInterrupt(result, eventGen);
+ unphoom(doc);
+ yield;
+
+ doc.removeEventListener("MozScrolledAreaChanged", listener, isCapturing);
+
+ if (expectEvent) {
+ ok(result.success, "Received expected " + (inner ? "inner" : "") + " contraction event");
+ }
+
+ if (result.success) {
+ is(result.event.x, 0, "Contraction event x is 0");
+ is(result.event.y, 0, "Contraction event y is 0");
+ if (inner) {
+ is(Math.round(result.event.width), 300, "Inner contraction event width matches body width");
+ is(Math.round(result.event.height), 150, "Inner contraction event height matches body height");
+ } else {
+ [w, h] = getDocBodyDimensions(doc);
+ is(Math.round(result.event.width), w, "Contraction event width matches body width");
+ is(Math.round(result.event.height), h, "Contraction event height matches body height");
+ }
+ }
+ }
+
+ setTimeout(nextTest, 0);
+
+ yield;
+ })();
+
+ setTimeout(function() { eventGen.next(); }, 0);
+}
+
+function testPreliminaries() {
+ /* Do any setup here */
+ nextTest();
+}
+
+function testEventCapturingHorz() {
+ testPhoom(true, 10000, 0, true); // calls nextTest() when finished
+}
+
+function testEventCapturingVert() {
+ testPhoom(true, 0, 10000, true); // calls nextTest() when finished
+}
+
+function testEventCapturingBoth() {
+ testPhoom(true, 10000, 10000, true); // calls nextTest() when finished
+}
+
+function testEventCapturingNegv() {
+ testPhoom(true, -10000, -10000, false); // calls nextTest() when finished
+}
+
+function testEventBubblingHorz() {
+ testPhoom(false, 10000, 0, true); // calls nextTest() when finished
+}
+
+function testEventBubblingVert() {
+ testPhoom(false, 0, 10000, true); // calls nextTest() when finished
+}
+
+function testEventBubblingBoth() {
+ testPhoom(false, 10000, 10000, true); // calls nextTest() when finished
+}
+
+function testEventBubblingNegv() {
+ testPhoom(false, -10000, -10000, false); // calls nextTest() when finished
+}
+
+function testCustomCreationHelper(creatingDoc, listeningDoc, x, y, w, h, expectEvent) {
+ var result = makeResult();
+
+ var listener = function(evt) {
+ result.success = true;
+ result.event = evt;
+ };
+
+ var ev = creatingDoc.createEvent("ScrollAreaEvent");
+ ev.initScrollAreaEvent("MozScrolledAreaChanged",
+ true, true, window, 0,
+ x, y, w, h);
+
+ listeningDoc.addEventListener("MozScrolledAreaChanged", listener, true);
+
+ result.success = false;
+ creatingDoc.dispatchEvent(ev);
+
+ listeningDoc.removeEventListener("MozScrolledAreaChanged", listener, true);
+
+ is(result.success, expectEvent, "Received custom event iff expected");
+ if (result.success) {
+ is(result.event.x, x, "Custom event data matches for x");
+ is(result.event.y, y, "Custom event data matches for y");
+ is(result.event.width, w, "Custom event data matches for width");
+ is(result.event.height, h, "Custom event data matches for height");
+ }
+}
+
+function testCustomCreation() {
+ var innerdoc = document.getElementById('testframe').contentDocument;
+ testCustomCreationHelper(document, document, 2, 7, 1, 8, true); // e = 2.718 ...
+ testCustomCreationHelper(innerdoc, innerdoc, 2, 8, 1, 8, true); // ... 2818 ...
+ testCustomCreationHelper(innerdoc, document, 2, 8, 4, 5, false); // ... 2845 ...
+ nextTest();
+}
+
+var TESTS = [testPreliminaries,
+ testEventCapturingHorz,
+ testEventCapturingVert,
+ testEventCapturingBoth,
+ testEventCapturingNegv,
+ testEventBubblingHorz,
+ testEventBubblingVert,
+ testEventBubblingBoth,
+ testEventBubblingNegv,
+ testCustomCreation,
+ __end];
+
+var PC = 0;
+
+function nextTest() {
+ TESTS[PC++]();
+}
+
+function __end() {
+ log("done!");
+ finish();
+ window.close();
+}
+
+/*
+ * We need both the parent document and all iframe documents to load
+ * before starting anything
+ */
+var loaded = 0;
+
+window
+.addEventListener('load', function(ev) {
+ logln(++loaded);
+ if (loaded >= 2) {
+ logln("go...");
+ nextTest();
+ }
+}, false);
+
+document.getElementById("testframe").contentWindow
+.addEventListener('load', function(ev) {
+ logln(++loaded);
+ if (loaded >= 2) {
+ logln("go...");
+ nextTest();
+ }
+}, false);
+
+// window.opener says: SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/file_bug514732_window.xul b/layout/generic/test/file_bug514732_window.xul
new file mode 100644
index 000000000..37d5e464b
--- /dev/null
+++ b/layout/generic/test/file_bug514732_window.xul
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="514732Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(nextTest,0);"
+ title="bug 514732 test">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/docshell_helpers.js">
+ </script>
+
+ <script type="application/javascript"><![CDATA[
+
+ // Define the generator-iterator for the tests.
+ var tests = testIterator();
+
+ ////
+ // Execute the next test in the generator function.
+ //
+ function nextTest() {
+ tests.next();
+ }
+
+ ////
+ // Generator function for test steps for bug 514732. The MozScrolledAreaChanged
+ // should be fired when a page is restored from the bfcache as though it had
+ // reloaded.
+ //
+ function testIterator() {
+ // Make sure bfcache is on.
+ enableBFCache(true);
+
+
+ // Load a wide and tall page, and then another.
+ for (var i = 0; i < 2; ++i) {
+ doPageNavigation( {
+ uri: "data:text/html,<!DOCTYPE html><html>" +
+ "<head><title>bug 514732 bfcache test page " + i + "</title></head>" +
+ "<body>" +
+ '<div style="position: absolute; left: 10000px; top: 10000px; width: 500px; height: 500px;">' +
+ "</body></html>",
+ eventsToListenFor: ["MozScrolledAreaChanged"],
+ expectedEvents: [ { type: "MozScrolledAreaChanged" } ],
+ onNavComplete: nextTest
+ } );
+ yield;
+ }
+
+ // Navigate back to the first page. Don't test for width and height
+ // yet, just make sure we get an event.
+ doPageNavigation( {
+ back: true,
+ eventsToListenFor: ["MozScrolledAreaChanged"],
+ expectedEvents: [ { type: "MozScrolledAreaChanged" } ],
+ onNavComplete: nextTest
+ } );
+ yield;
+
+ // Navigate forth to our wide and tall page, this time testing for
+ // width and height on the event.
+ doPageNavigation( {
+ forward: true,
+ eventsToListenFor: ["MozScrolledAreaChanged"],
+ expectedEvents: [ { type: "MozScrolledAreaChanged" } ],
+ onNavComplete: nextTest
+ } );
+ yield;
+
+ // Tell the framework the test is finished. Include the final 'yield'
+ // statement to prevent a StopIteration exception from being thrown.
+ finish();
+ yield;
+ }
+
+ ]]></script>
+
+ <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/layout/generic/test/file_bug579767_1.html b/layout/generic/test/file_bug579767_1.html
new file mode 100644
index 000000000..7d4c93077
--- /dev/null
+++ b/layout/generic/test/file_bug579767_1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<HTML style="background-color: yellow">
+ <FRAMESET cols="10%,90%" border="6">
+ <FRAMESET rows="90,210" border="6">
+ <FRAME src="data:text/html,<body bgcolor=blue>">
+ <FRAME src="data:text/html,<body bgcolor=green>">
+ </FRAMESET>
+ <FRAME src="data:text/html,<body bgcolor=red>">
+ </FRAMESET>
+</HTML>
diff --git a/layout/generic/test/file_bug579767_2.html b/layout/generic/test/file_bug579767_2.html
new file mode 100644
index 000000000..a6a0927b9
--- /dev/null
+++ b/layout/generic/test/file_bug579767_2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<HTML style="background-color: yellow">
+ <FRAMESET cols="11%,89%" border="6">
+ <FRAMESET rows="100,200" border="6">
+ <FRAME src="data:text/html,<body bgcolor=blue>">
+ <FRAME src="data:text/html,<body bgcolor=green>">
+ </FRAMESET>
+ <FRAME src="data:text/html,<body bgcolor=red>">
+ </FRAMESET>
+</HTML>
diff --git a/layout/generic/test/file_scroll_position_restore.html b/layout/generic/test/file_scroll_position_restore.html
new file mode 100644
index 000000000..7bd79537f
--- /dev/null
+++ b/layout/generic/test/file_scroll_position_restore.html
@@ -0,0 +1,111186 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <meta charset="UTF-8">
+ <title>Directory Listing: /pub/firefox/tinderbox-builds/mozilla-central-win32/</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ </head>
+ <body onload="window.opener.handleLoad()">
+ <h1>Index of /pub/firefox/tinderbox-builds/mozilla-inbound-win32/</h1>
+ <table>
+ <tr>
+ <th>Type</th>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Last Modified</th>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/">..</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442444362/">1442444362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442445380/">1442445380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442446344/">1442446344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442447004/">1442447004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442448336/">1442448336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442448442/">1442448442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442558151/">1442558151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442558812/">1442558812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442871815/">1442871815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442873979/">1442873979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442874157/">1442874157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442875117/">1442875117/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442875296/">1442875296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442876558/">1442876558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442876880/">1442876880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442877182/">1442877182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442877362/">1442877362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442878623/">1442878623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442878741/">1442878741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442880422/">1442880422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442880901/">1442880901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442881382/">1442881382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442882281/">1442882281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442882641/">1442882641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442883602/">1442883602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442883724/">1442883724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442888042/">1442888042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442890741/">1442890741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442891143/">1442891143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442891586/">1442891586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442896142/">1442896142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442899442/">1442899442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442901542/">1442901542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442901602/">1442901602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442905322/">1442905322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442905686/">1442905686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442906282/">1442906282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442906704/">1442906704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442907061/">1442907061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442910655/">1442910655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442916223/">1442916223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442918223/">1442918223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442919086/">1442919086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442919309/">1442919309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442923021/">1442923021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442925721/">1442925721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442926261/">1442926261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442926921/">1442926921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442927044/">1442927044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442929501/">1442929501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442932681/">1442932681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442933703/">1442933703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442934002/">1442934002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442934157/">1442934157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442934485/">1442934485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442934722/">1442934722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442934962/">1442934962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442935272/">1442935272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442935681/">1442935681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442935922/">1442935922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442936407/">1442936407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442938321/">1442938321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442938381/">1442938381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442939222/">1442939222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442940603/">1442940603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442941623/">1442941623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442941747/">1442941747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442942042/">1442942042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442945617/">1442945617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442946155/">1442946155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442946455/">1442946455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442946874/">1442946874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442946935/">1442946935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442946997/">1442946997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442948194/">1442948194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442949034/">1442949034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442949396/">1442949396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442949754/">1442949754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442950476/">1442950476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442951198/">1442951198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442957615/">1442957615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442958334/">1442958334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442958394/">1442958394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442958638/">1442958638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442961035/">1442961035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442961515/">1442961515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442962114/">1442962114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442962294/">1442962294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442962353/">1442962353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442963014/">1442963014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442963435/">1442963435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442964395/">1442964395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442965660/">1442965660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442967035/">1442967035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442967875/">1442967875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442968354/">1442968354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442968474/">1442968474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442968595/">1442968595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442968775/">1442968775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442969375/">1442969375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442970876/">1442970876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442971361/">1442971361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442971598/">1442971598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442975194/">1442975194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442976154/">1442976154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442977294/">1442977294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442977544/">1442977544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442978800/">1442978800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442979216/">1442979216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442983475/">1442983475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442985816/">1442985816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442986241/">1442986241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442987075/">1442987075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442987195/">1442987195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442987735/">1442987735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442989474/">1442989474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442989654/">1442989654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442990014/">1442990014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442990193/">1442990193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442990974/">1442990974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442991394/">1442991394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442992457/">1442992457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442992475/">1442992475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442992655/">1442992655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442993014/">1442993014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442994216/">1442994216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1442996316/">1442996316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443005195/">1443005195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443005255/">1443005255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443006556/">1443006556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443008116/">1443008116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443009679/">1443009679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443013516/">1443013516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443013517/">1443013517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443014296/">1443014296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443015498/">1443015498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443016035/">1443016035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443016816/">1443016816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443018915/">1443018915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443020176/">1443020176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443021376/">1443021376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443021686/">1443021686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443023364/">1443023364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443025756/">1443025756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443026475/">1443026475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443026536/">1443026536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443028156/">1443028156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443028816/">1443028816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443030077/">1443030077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443031758/">1443031758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443031816/">1443031816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443031875/">1443031875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443033380/">1443033380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443033677/">1443033677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443034096/">1443034096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443036736/">1443036736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443039858/">1443039858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443040037/">1443040037/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443041300/">1443041300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443041716/">1443041716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443044240/">1443044240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443046816/">1443046816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443047422/">1443047422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443051976/">1443051976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443053956/">1443053956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443054857/">1443054857/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443056296/">1443056296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443057856/">1443057856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443058889/">1443058889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443060398/">1443060398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443065296/">1443065296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443066016/">1443066016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443066916/">1443066916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443067456/">1443067456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443067640/">1443067640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443067816/">1443067816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443067987/">1443067987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443071357/">1443071357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443072676/">1443072676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443075795/">1443075795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443076395/">1443076395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443076875/">1443076875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443077055/">1443077055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443077238/">1443077238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443077355/">1443077355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443078136/">1443078136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443078495/">1443078495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443086775/">1443086775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443087916/">1443087916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443089240/">1443089240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443090195/">1443090195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443092355/">1443092355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443093016/">1443093016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443094339/">1443094339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443094815/">1443094815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443095955/">1443095955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443100276/">1443100276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443101116/">1443101116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443103820/">1443103820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443103896/">1443103896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443105038/">1443105038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443105193/">1443105193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443105856/">1443105856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443107668/">1443107668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443108735/">1443108735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443108976/">1443108976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443110956/">1443110956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443114736/">1443114736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443117436/">1443117436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443118815/">1443118815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443121641/">1443121641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443128475/">1443128475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443128542/">1443128542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443128630/">1443128630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443128631/">1443128631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443128840/">1443128840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443128895/">1443128895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443129135/">1443129135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443129195/">1443129195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443129917/">1443129917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443132016/">1443132016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443133335/">1443133335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443135196/">1443135196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443135316/">1443135316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443135856/">1443135856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443136638/">1443136638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443137055/">1443137055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443140116/">1443140116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443140655/">1443140655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443141075/">1443141075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443145757/">1443145757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443145996/">1443145996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443146296/">1443146296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443147189/">1443147189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443147676/">1443147676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443149116/">1443149116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443159915/">1443159915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443161172/">1443161172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443161416/">1443161416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443161898/">1443161898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443162858/">1443162858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443163155/">1443163155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443166639/">1443166639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443168916/">1443168916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443174375/">1443174375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443174916/">1443174916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443175215/">1443175215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443177075/">1443177075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443179715/">1443179715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443180015/">1443180015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443182235/">1443182235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443183015/">1443183015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443184936/">1443184936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443185178/">1443185178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443188055/">1443188055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443188357/">1443188357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443190840/">1443190840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443191616/">1443191616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443192998/">1443192998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443194975/">1443194975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443195216/">1443195216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443195454/">1443195454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443195640/">1443195640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443196415/">1443196415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443196655/">1443196655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443197736/">1443197736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443203078/">1443203078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443203079/">1443203079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443203674/">1443203674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443203735/">1443203735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443205476/">1443205476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443205716/">1443205716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443205895/">1443205895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443206732/">1443206732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443207157/">1443207157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443207877/">1443207877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443207993/">1443207993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443208296/">1443208296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443209796/">1443209796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443210335/">1443210335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443210395/">1443210395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443210575/">1443210575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443210995/">1443210995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443211354/">1443211354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443211772/">1443211772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443211835/">1443211835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443212136/">1443212136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443212551/">1443212551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443214055/">1443214055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443214474/">1443214474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443215612/">1443215612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443216337/">1443216337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443217595/">1443217595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443225275/">1443225275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443229444/">1443229444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443230194/">1443230194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443238891/">1443238891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443252755/">1443252755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443253415/">1443253415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443256655/">1443256655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443272634/">1443272634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443285335/">1443285335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443291755/">1443291755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443306035/">1443306035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443331295/">1443331295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443332675/">1443332675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443335375/">1443335375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443340295/">1443340295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443345755/">1443345755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443353375/">1443353375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443365135/">1443365135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443367895/">1443367895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443382295/">1443382295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443387807/">1443387807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443390395/">1443390395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443390575/">1443390575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443396875/">1443396875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443397055/">1443397055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443397175/">1443397175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443400235/">1443400235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443401735/">1443401735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443402313/">1443402313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443403415/">1443403415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443403655/">1443403655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443406655/">1443406655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443407913/">1443407913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443411575/">1443411575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443413555/">1443413555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443413737/">1443413737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443415777/">1443415777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443416195/">1443416195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443416255/">1443416255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443416436/">1443416436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443419975/">1443419975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443423275/">1443423275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443430779/">1443430779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443430895/">1443430895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443431141/">1443431141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443434675/">1443434675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443435275/">1443435275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443439895/">1443439895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443441035/">1443441035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443441824/">1443441824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443442743/">1443442743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443444239/">1443444239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443445557/">1443445557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443445739/">1443445739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443446280/">1443446280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443447098/">1443447098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443448625/">1443448625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443448696/">1443448696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443448799/">1443448799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443449082/">1443449082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443449376/">1443449376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443451053/">1443451053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443452136/">1443452136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443453335/">1443453335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443456815/">1443456815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443467701/">1443467701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443467761/">1443467761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443467881/">1443467881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443467940/">1443467940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443468001/">1443468001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443468061/">1443468061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443468481/">1443468481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443468781/">1443468781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443469381/">1443469381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443469740/">1443469740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443469984/">1443469984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443470161/">1443470161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443470461/">1443470461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443470701/">1443470701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443471781/">1443471781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443476101/">1443476101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443476225/">1443476225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443476521/">1443476521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443476820/">1443476820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443477481/">1443477481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443477864/">1443477864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443479724/">1443479724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443480681/">1443480681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443481044/">1443481044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443481221/">1443481221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443481466/">1443481466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443481582/">1443481582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443481764/">1443481764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443481941/">1443481941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443482002/">1443482002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443482121/">1443482121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443482964/">1443482964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443483145/">1443483145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443484881/">1443484881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443485305/">1443485305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443485421/">1443485421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443486742/">1443486742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443489625/">1443489625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443490284/">1443490284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443490884/">1443490884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443491545/">1443491545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443492444/">1443492444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443492930/">1443492930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443493224/">1443493224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443493944/">1443493944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443494482/">1443494482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443499284/">1443499284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443499644/">1443499644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443500606/">1443500606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443501144/">1443501144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443502344/">1443502344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443505585/">1443505585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443505944/">1443505944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443507324/">1443507324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443508884/">1443508884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443509424/">1443509424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443510324/">1443510324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443511464/">1443511464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443512361/">1443512361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443514166/">1443514166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443519264/">1443519264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443519442/">1443519442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443520224/">1443520224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443522084/">1443522084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443523285/">1443523285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443524484/">1443524484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443527784/">1443527784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443527964/">1443527964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443531974/">1443531974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443532464/">1443532464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443533605/">1443533605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443535044/">1443535044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443537292/">1443537292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443537625/">1443537625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443537744/">1443537744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443538224/">1443538224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443539072/">1443539072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443539909/">1443539909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443540452/">1443540452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443540872/">1443540872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443541051/">1443541051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443541289/">1443541289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443542308/">1443542308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443542673/">1443542673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443543514/">1443543514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443544716/">1443544716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443545554/">1443545554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443546572/">1443546572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443548911/">1443548911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443549453/">1443549453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443550894/">1443550894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443551853/">1443551853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443552639/">1443552639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443554314/">1443554314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443556833/">1443556833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558639/">1443558639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558706/">1443558706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558707/">1443558707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558708/">1443558708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558709/">1443558709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558711/">1443558711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558712/">1443558712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558713/">1443558713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558718/">1443558718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558719/">1443558719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558720/">1443558720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558725/">1443558725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558726/">1443558726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558727/">1443558727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558729/">1443558729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558740/">1443558740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558741/">1443558741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558744/">1443558744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558746/">1443558746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558747/">1443558747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558748/">1443558748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558749/">1443558749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558988/">1443558988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558989/">1443558989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558991/">1443558991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558992/">1443558992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558993/">1443558993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558995/">1443558995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558997/">1443558997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558998/">1443558998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443558999/">1443558999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559004/">1443559004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559005/">1443559005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559007/">1443559007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559008/">1443559008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559019/">1443559019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559020/">1443559020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559023/">1443559023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559025/">1443559025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559026/">1443559026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559027/">1443559027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559028/">1443559028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443559292/">1443559292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443562267/">1443562267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443563346/">1443563346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443566586/">1443566586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443567969/">1443567969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443568327/">1443568327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443568508/">1443568508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443570128/">1443570128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443570247/">1443570247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443570607/">1443570607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443571507/">1443571507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443572352/">1443572352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443573847/">1443573847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443574208/">1443574208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443577269/">1443577269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443577465/">1443577465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443579619/">1443579619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443580507/">1443580507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443580629/">1443580629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443580747/">1443580747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443580867/">1443580867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443581107/">1443581107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443581108/">1443581108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443581227/">1443581227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443581588/">1443581588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443582428/">1443582428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443582787/">1443582787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443584169/">1443584169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443587647/">1443587647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443591247/">1443591247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443593347/">1443593347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443595926/">1443595926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443596107/">1443596107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443596286/">1443596286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443596897/">1443596897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443597007/">1443597007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443597967/">1443597967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443599048/">1443599048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443599167/">1443599167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443602468/">1443602468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607770/">1443607770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607771/">1443607771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607772/">1443607772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607774/">1443607774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607775/">1443607775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607776/">1443607776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607777/">1443607777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607780/">1443607780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607781/">1443607781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607782/">1443607782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607783/">1443607783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607787/">1443607787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607788/">1443607788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607789/">1443607789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607790/">1443607790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607802/">1443607802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607803/">1443607803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607805/">1443607805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607808/">1443607808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443607810/">1443607810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608129/">1443608129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608130/">1443608130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608131/">1443608131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608134/">1443608134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608135/">1443608135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608137/">1443608137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608140/">1443608140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608142/">1443608142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608147/">1443608147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608148/">1443608148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608149/">1443608149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608162/">1443608162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608163/">1443608163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608164/">1443608164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608169/">1443608169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608170/">1443608170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608172/">1443608172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608444/">1443608444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608445/">1443608445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608446/">1443608446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608449/">1443608449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608450/">1443608450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608452/">1443608452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608455/">1443608455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608456/">1443608456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608457/">1443608457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608458/">1443608458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608462/">1443608462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608463/">1443608463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608464/">1443608464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608476/">1443608476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608477/">1443608477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608478/">1443608478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608479/">1443608479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608484/">1443608484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608485/">1443608485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608487/">1443608487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608763/">1443608763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608764/">1443608764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608765/">1443608765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608766/">1443608766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608768/">1443608768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608770/">1443608770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608771/">1443608771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608773/">1443608773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608774/">1443608774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608775/">1443608775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608776/">1443608776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608780/">1443608780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608781/">1443608781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608782/">1443608782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608784/">1443608784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608796/">1443608796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608799/">1443608799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608801/">1443608801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608802/">1443608802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608803/">1443608803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443608804/">1443608804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443609368/">1443609368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443610867/">1443610867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443614048/">1443614048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443615786/">1443615786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443618368/">1443618368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443619747/">1443619747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443620524/">1443620524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443621367/">1443621367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443622526/">1443622526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443622647/">1443622647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443623008/">1443623008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443624331/">1443624331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443625528/">1443625528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443625672/">1443625672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443627213/">1443627213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443627445/">1443627445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443627927/">1443627927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443628108/">1443628108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443628588/">1443628588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443628767/">1443628767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443629906/">1443629906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443630387/">1443630387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443630687/">1443630687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443630989/">1443630989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443631467/">1443631467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443631767/">1443631767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443632546/">1443632546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443632794/">1443632794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443633688/">1443633688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443636747/">1443636747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443644908/">1443644908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443645508/">1443645508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443645747/">1443645747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443645806/">1443645806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443646166/">1443646166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443646768/">1443646768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443647367/">1443647367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443647488/">1443647488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443647549/">1443647549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443648088/">1443648088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443649167/">1443649167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443651448/">1443651448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443651927/">1443651927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443652168/">1443652168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443652290/">1443652290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443653008/">1443653008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443653966/">1443653966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443654266/">1443654266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443654688/">1443654688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443655048/">1443655048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443655049/">1443655049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443655527/">1443655527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443655833/">1443655833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443655885/">1443655885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443655949/">1443655949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443656368/">1443656368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443656668/">1443656668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443657745/">1443657745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443667587/">1443667587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443669148/">1443669148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443671907/">1443671907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443671967/">1443671967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443673107/">1443673107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443677847/">1443677847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443678207/">1443678207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443679107/">1443679107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443680547/">1443680547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443681028/">1443681028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443681328/">1443681328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443683252/">1443683252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443683906/">1443683906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443684991/">1443684991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443687508/">1443687508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443687868/">1443687868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443689608/">1443689608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443690211/">1443690211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443690625/">1443690625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443692306/">1443692306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443692487/">1443692487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443693568/">1443693568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443696148/">1443696148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443697468/">1443697468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443698188/">1443698188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443698488/">1443698488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443699207/">1443699207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443699867/">1443699867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443700408/">1443700408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443701189/">1443701189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443701788/">1443701788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443702747/">1443702747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443705628/">1443705628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443705808/">1443705808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443710068/">1443710068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712222/">1443712222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712223/">1443712223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712224/">1443712224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712225/">1443712225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712292/">1443712292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712293/">1443712293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712294/">1443712294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712297/">1443712297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712298/">1443712298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712299/">1443712299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712300/">1443712300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712301/">1443712301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712306/">1443712306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712307/">1443712307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712309/">1443712309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712310/">1443712310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712312/">1443712312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712315/">1443712315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712316/">1443712316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712317/">1443712317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712319/">1443712319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712323/">1443712323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712324/">1443712324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712325/">1443712325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712326/">1443712326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712328/">1443712328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712332/">1443712332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712334/">1443712334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712335/">1443712335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712336/">1443712336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712343/">1443712343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712344/">1443712344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712345/">1443712345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712348/">1443712348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712605/">1443712605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712606/">1443712606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712607/">1443712607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712660/">1443712660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712661/">1443712661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712664/">1443712664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712665/">1443712665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712667/">1443712667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712670/">1443712670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712671/">1443712671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712672/">1443712672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712673/">1443712673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712677/">1443712677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712678/">1443712678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712683/">1443712683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712684/">1443712684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712687/">1443712687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712690/">1443712690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712691/">1443712691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712692/">1443712692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712697/">1443712697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712698/">1443712698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443712701/">1443712701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443713309/">1443713309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443714568/">1443714568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443716246/">1443716246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443719366/">1443719366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443719488/">1443719488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443719548/">1443719548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443720928/">1443720928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443724408/">1443724408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443724468/">1443724468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443725428/">1443725428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443728128/">1443728128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443728188/">1443728188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443728368/">1443728368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443728966/">1443728966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443731788/">1443731788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443734619/">1443734619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443735278/">1443735278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443739718/">1443739718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443740137/">1443740137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443743137/">1443743137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443747759/">1443747759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443750458/">1443750458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443750765/">1443750765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443751058/">1443751058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443753758/">1443753758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443753878/">1443753878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443755258/">1443755258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443759578/">1443759578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443766358/">1443766358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443767738/">1443767738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443768938/">1443768938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443769178/">1443769178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443770078/">1443770078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443770558/">1443770558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443773438/">1443773438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443774518/">1443774518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443775598/">1443775598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443777938/">1443777938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443778779/">1443778779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443781598/">1443781598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443784060/">1443784060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443785919/">1443785919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443792772/">1443792772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443794558/">1443794558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443795594/">1443795594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443796358/">1443796358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443796957/">1443796957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443798363/">1443798363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443798639/">1443798639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443799420/">1443799420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443800440/">1443800440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443800919/">1443800919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443801877/">1443801877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443802538/">1443802538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443802897/">1443802897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443804696/">1443804696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443806678/">1443806678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443807339/">1443807339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443807518/">1443807518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443807759/">1443807759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443808238/">1443808238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443810338/">1443810338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443810518/">1443810518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443812677/">1443812677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443816818/">1443816818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443817961/">1443817961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443818498/">1443818498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443818619/">1443818619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443819851/">1443819851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443820418/">1443820418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443820419/">1443820419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443822039/">1443822039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443822518/">1443822518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443823359/">1443823359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443823780/">1443823780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443825159/">1443825159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443827320/">1443827320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443827860/">1443827860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828261/">1443828261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828262/">1443828262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828263/">1443828263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828318/">1443828318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828319/">1443828319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828320/">1443828320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828322/">1443828322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828323/">1443828323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828324/">1443828324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828329/">1443828329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828330/">1443828330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828331/">1443828331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828335/">1443828335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828337/">1443828337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828338/">1443828338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828342/">1443828342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828343/">1443828343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828344/">1443828344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828346/">1443828346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828349/">1443828349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828350/">1443828350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828351/">1443828351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828353/">1443828353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828356/">1443828356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828357/">1443828357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828358/">1443828358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828359/">1443828359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828486/">1443828486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828487/">1443828487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828490/">1443828490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828541/">1443828541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828542/">1443828542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828543/">1443828543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828545/">1443828545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828546/">1443828546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828548/">1443828548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828550/">1443828550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828551/">1443828551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828552/">1443828552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828553/">1443828553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828557/">1443828557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828558/">1443828558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828559/">1443828559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828563/">1443828563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828564/">1443828564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828565/">1443828565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828570/">1443828570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828571/">1443828571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828573/">1443828573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828577/">1443828577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828578/">1443828578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828579/">1443828579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828580/">1443828580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828795/">1443828795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828796/">1443828796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828797/">1443828797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828851/">1443828851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828852/">1443828852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828853/">1443828853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828855/">1443828855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828856/">1443828856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828857/">1443828857/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828858/">1443828858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828861/">1443828861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828862/">1443828862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828863/">1443828863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828865/">1443828865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828867/">1443828867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828868/">1443828868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828869/">1443828869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828870/">1443828870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828872/">1443828872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828873/">1443828873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828874/">1443828874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828875/">1443828875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828879/">1443828879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828880/">1443828880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828881/">1443828881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828885/">1443828885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828886/">1443828886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443828888/">1443828888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829108/">1443829108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829109/">1443829109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829110/">1443829110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829170/">1443829170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829171/">1443829171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829172/">1443829172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829174/">1443829174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829175/">1443829175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829176/">1443829176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829181/">1443829181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829182/">1443829182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829183/">1443829183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829187/">1443829187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829188/">1443829188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829189/">1443829189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829190/">1443829190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829194/">1443829194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829195/">1443829195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829198/">1443829198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829201/">1443829201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829202/">1443829202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829204/">1443829204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829207/">1443829207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829208/">1443829208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829209/">1443829209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829210/">1443829210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443829780/">1443829780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443834758/">1443834758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443841794/">1443841794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443843577/">1443843577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443849098/">1443849098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443885938/">1443885938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443886058/">1443886058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443886899/">1443886899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443888218/">1443888218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443897098/">1443897098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443909637/">1443909637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443912939/">1443912939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443917200/">1443917200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443969458/">1443969458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443978039/">1443978039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443979838/">1443979838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1443999878/">1443999878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444008999/">1444008999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444010758/">1444010758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444014698/">1444014698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444015598/">1444015598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444015658/">1444015658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444015718/">1444015718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444020458/">1444020458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444023998/">1444023998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444024057/">1444024057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444024898/">1444024898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444025258/">1444025258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444025918/">1444025918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444026897/">1444026897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444027358/">1444027358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444027538/">1444027538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444027838/">1444027838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444028138/">1444028138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444029098/">1444029098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444030238/">1444030238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444030501/">1444030501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444032579/">1444032579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444035218/">1444035218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444035698/">1444035698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444037860/">1444037860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444037978/">1444037978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444038818/">1444038818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444039899/">1444039899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444044698/">1444044698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444046020/">1444046020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444048536/">1444048536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444052858/">1444052858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444055099/">1444055099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444055511/">1444055511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444057179/">1444057179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444057298/">1444057298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444057552/">1444057552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444058921/">1444058921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444059339/">1444059339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444059578/">1444059578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444059878/">1444059878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444060240/">1444060240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444060482/">1444060482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444060898/">1444060898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444061138/">1444061138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444061798/">1444061798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444061919/">1444061919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444062161/">1444062161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444062399/">1444062399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444063121/">1444063121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444066655/">1444066655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444075959/">1444075959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444076078/">1444076078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444076138/">1444076138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444076380/">1444076380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444076381/">1444076381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444076678/">1444076678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444077012/">1444077012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444078778/">1444078778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444080518/">1444080518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444080623/">1444080623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444080774/">1444080774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444081075/">1444081075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444082035/">1444082035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444083713/">1444083713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444084016/">1444084016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444084436/">1444084436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444085216/">1444085216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444085816/">1444085816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444087139/">1444087139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444087827/">1444087827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444095715/">1444095715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444096256/">1444096256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444097101/">1444097101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444097335/">1444097335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444097755/">1444097755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444097935/">1444097935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444098534/">1444098534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444098711/">1444098711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444101596/">1444101596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444103633/">1444103633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444105075/">1444105075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444109425/">1444109425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444110894/">1444110894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444113356/">1444113356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444113715/">1444113715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444115815/">1444115815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444116415/">1444116415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444116596/">1444116596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444120216/">1444120216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444120555/">1444120555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444121336/">1444121336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444121814/">1444121814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444122223/">1444122223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444122536/">1444122536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444123375/">1444123375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444125654/">1444125654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444125955/">1444125955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444126973/">1444126973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444131040/">1444131040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444132436/">1444132436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444133996/">1444133996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444137357/">1444137357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444137536/">1444137536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444138674/">1444138674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444139227/">1444139227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444139589/">1444139589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444139639/">1444139639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444139753/">1444139753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444140574/">1444140574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141629/">1444141629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141630/">1444141630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141631/">1444141631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141632/">1444141632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141716/">1444141716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141717/">1444141717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444141973/">1444141973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444143033/">1444143033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444145374/">1444145374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444146576/">1444146576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444152611/">1444152611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444156714/">1444156714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444156892/">1444156892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444157074/">1444157074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444157196/">1444157196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444157314/">1444157314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444158874/">1444158874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444159052/">1444159052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444159239/">1444159239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444159475/">1444159475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444160613/">1444160613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444162891/">1444162891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163010/">1444163010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163011/">1444163011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163012/">1444163012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163091/">1444163091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163092/">1444163092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163093/">1444163093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163376/">1444163376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163377/">1444163377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163378/">1444163378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163379/">1444163379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163455/">1444163455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163456/">1444163456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444163457/">1444163457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444164936/">1444164936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444167152/">1444167152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444167692/">1444167692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444168834/">1444168834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444170212/">1444170212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444170993/">1444170993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444171172/">1444171172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444172135/">1444172135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444174204/">1444174204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444174952/">1444174952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444177650/">1444177650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444179092/">1444179092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444182452/">1444182452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444187195/">1444187195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444190132/">1444190132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444190312/">1444190312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444195814/">1444195814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444195952/">1444195952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444196555/">1444196555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444197032/">1444197032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444197393/">1444197393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444197632/">1444197632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444198530/">1444198530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444199852/">1444199852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444200091/">1444200091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444201509/">1444201509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444202792/">1444202792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444205672/">1444205672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444211492/">1444211492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444211792/">1444211792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444211972/">1444211972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444212391/">1444212391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444212392/">1444212392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444212691/">1444212691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444212991/">1444212991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213113/">1444213113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213198/">1444213198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213411/">1444213411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213592/">1444213592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213891/">1444213891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213996/">1444213996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213997/">1444213997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444213998/">1444213998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214001/">1444214001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214079/">1444214079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214080/">1444214080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214083/">1444214083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214252/">1444214252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214278/">1444214278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214400/">1444214400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214403/">1444214403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214406/">1444214406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214481/">1444214481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214483/">1444214483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214486/">1444214486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214492/">1444214492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214506/">1444214506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214510/">1444214510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444214513/">1444214513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444215271/">1444215271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444216350/">1444216350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444217423/">1444217423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444217912/">1444217912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444219473/">1444219473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444222592/">1444222592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444222832/">1444222832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444223552/">1444223552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444224751/">1444224751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444224870/">1444224870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444224931/">1444224931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444226135/">1444226135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444226372/">1444226372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444227994/">1444227994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444228246/">1444228246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444229972/">1444229972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444231112/">1444231112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444233931/">1444233931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444237112/">1444237112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444237292/">1444237292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444238611/">1444238611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444239932/">1444239932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444240714/">1444240714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444241073/">1444241073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444241507/">1444241507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444241801/">1444241801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444242511/">1444242511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444243592/">1444243592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444244432/">1444244432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444244915/">1444244915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444245093/">1444245093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444246414/">1444246414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444246532/">1444246532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444247132/">1444247132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444247432/">1444247432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444248692/">1444248692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444250130/">1444250130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444251833/">1444251833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444253372/">1444253372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444253671/">1444253671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444254872/">1444254872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444255349/">1444255349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444255650/">1444255650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444256911/">1444256911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444257212/">1444257212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444257453/">1444257453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444257872/">1444257872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444260032/">1444260032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444260635/">1444260635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444260932/">1444260932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444261233/">1444261233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444261652/">1444261652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444262627/">1444262627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444264170/">1444264170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444264312/">1444264312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444274671/">1444274671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444277972/">1444277972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444281212/">1444281212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444281392/">1444281392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444281452/">1444281452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444282171/">1444282171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444283612/">1444283612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444284394/">1444284394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444284512/">1444284512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444284932/">1444284932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444286970/">1444286970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444288591/">1444288591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444290213/">1444290213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444291712/">1444291712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444292072/">1444292072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444292972/">1444292972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444296272/">1444296272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444296392/">1444296392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444296452/">1444296452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444296512/">1444296512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444296898/">1444296898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444296993/">1444296993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444297352/">1444297352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444297470/">1444297470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444297711/">1444297711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444297892/">1444297892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444298311/">1444298311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444300171/">1444300171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444300232/">1444300232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444303805/">1444303805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444305151/">1444305151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444305835/">1444305835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444308216/">1444308216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444309056/">1444309056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444312896/">1444312896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444313557/">1444313557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444314154/">1444314154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444314743/">1444314743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444318055/">1444318055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444319673/">1444319673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444321537/">1444321537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444322793/">1444322793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444323337/">1444323337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444324293/">1444324293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444324589/">1444324589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444324954/">1444324954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444325413/">1444325413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444325733/">1444325733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444326035/">1444326035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444330757/">1444330757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444330947/">1444330947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444331719/">1444331719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444332197/">1444332197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444332621/">1444332621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444334764/">1444334764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444335200/">1444335200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444336220/">1444336220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444336699/">1444336699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444337839/">1444337839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444338154/">1444338154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444339158/">1444339158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444340179/">1444340179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444340658/">1444340658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444341137/">1444341137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444341558/">1444341558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444342038/">1444342038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444342699/">1444342699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444342998/">1444342998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444343359/">1444343359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444344320/">1444344320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444347015/">1444347015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444348851/">1444348851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444349035/">1444349035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444349270/">1444349270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444350947/">1444350947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444354727/">1444354727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444356108/">1444356108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444357881/">1444357881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444359948/">1444359948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444363728/">1444363728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444383767/">1444383767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444384727/">1444384727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444385012/">1444385012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444388088/">1444388088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444393608/">1444393608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444395048/">1444395048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444402437/">1444402437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444402612/">1444402612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444403268/">1444403268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444404467/">1444404467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444407827/">1444407827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444408488/">1444408488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444408716/">1444408716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444409751/">1444409751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444411308/">1444411308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444411489/">1444411489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444412267/">1444412267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444416167/">1444416167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444417248/">1444417248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444417249/">1444417249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444417573/">1444417573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444421087/">1444421087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444422169/">1444422169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444422649/">1444422649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444422828/">1444422828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444422890/">1444422890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444423248/">1444423248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444423788/">1444423788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444424148/">1444424148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444425348/">1444425348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444425408/">1444425408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444434229/">1444434229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444434348/">1444434348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444439747/">1444439747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444440048/">1444440048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444440114/">1444440114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444460749/">1444460749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444462187/">1444462187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444465067/">1444465067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444477367/">1444477367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444495547/">1444495547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444509947/">1444509947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444518396/">1444518396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444537727/">1444537727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444538987/">1444538987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444550867/">1444550867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444565327/">1444565327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444567607/">1444567607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444574568/">1444574568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444579068/">1444579068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444579127/">1444579127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444579187/">1444579187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444580149/">1444580149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444592568/">1444592568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444604773/">1444604773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444609513/">1444609513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444611970/">1444611970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444612573/">1444612573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444616952/">1444616952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444621812/">1444621812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444622294/">1444622294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444623253/">1444623253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444630873/">1444630873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444634233/">1444634233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444634413/">1444634413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444634533/">1444634533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444635672/">1444635672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444639633/">1444639633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444642152/">1444642152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444643954/">1444643954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444644862/">1444644862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444646053/">1444646053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444647192/">1444647192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444650850/">1444650850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444652598/">1444652598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444653254/">1444653254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444656013/">1444656013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444663332/">1444663332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444667053/">1444667053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444667533/">1444667533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444668974/">1444668974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444669692/">1444669692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444670893/">1444670893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444671793/">1444671793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444672154/">1444672154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444672573/">1444672573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444673235/">1444673235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444674133/">1444674133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444674373/">1444674373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444674793/">1444674793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444675812/">1444675812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444677013/">1444677013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678622/">1444678622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678623/">1444678623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678624/">1444678624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678625/">1444678625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678626/">1444678626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678627/">1444678627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678628/">1444678628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678629/">1444678629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678630/">1444678630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678631/">1444678631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678632/">1444678632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678634/">1444678634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678635/">1444678635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678637/">1444678637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444678638/">1444678638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444680492/">1444680492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681358/">1444681358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681652/">1444681652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681653/">1444681653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681654/">1444681654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681655/">1444681655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681656/">1444681656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681657/">1444681657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681658/">1444681658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681659/">1444681659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681661/">1444681661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681662/">1444681662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681663/">1444681663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681665/">1444681665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681666/">1444681666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681668/">1444681668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444681898/">1444681898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683229/">1444683229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683230/">1444683230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683233/">1444683233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683310/">1444683310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683311/">1444683311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683312/">1444683312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683313/">1444683313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683518/">1444683518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683519/">1444683519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683520/">1444683520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683521/">1444683521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683525/">1444683525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683607/">1444683607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683608/">1444683608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683609/">1444683609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444683612/">1444683612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444687479/">1444687479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444692114/">1444692114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444693735/">1444693735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444697275/">1444697275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444699017/">1444699017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444702375/">1444702375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444705560/">1444705560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444714555/">1444714555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444716295/">1444716295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444719895/">1444719895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444720079/">1444720079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444720194/">1444720194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444720675/">1444720675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444732917/">1444732917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444733036/">1444733036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444734383/">1444734383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444734534/">1444734534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444735315/">1444735315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444736274/">1444736274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444737657/">1444737657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444739456/">1444739456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444740596/">1444740596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444746704/">1444746704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444746750/">1444746750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444747349/">1444747349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444747769/">1444747769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444748429/">1444748429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444749328/">1444749328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444753588/">1444753588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444754609/">1444754609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444756109/">1444756109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444756468/">1444756468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444764089/">1444764089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444765948/">1444765948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444766813/">1444766813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444769131/">1444769131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444769188/">1444769188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444769249/">1444769249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444770029/">1444770029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444770629/">1444770629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444771289/">1444771289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444771949/">1444771949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444772669/">1444772669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444772788/">1444772788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444773449/">1444773449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444777109/">1444777109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444777429/">1444777429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444778369/">1444778369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444779509/">1444779509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444779988/">1444779988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444780409/">1444780409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444780949/">1444780949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444782029/">1444782029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444783109/">1444783109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444783771/">1444783771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444786532/">1444786532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444788929/">1444788929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444790476/">1444790476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444791689/">1444791689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444794020/">1444794020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444797440/">1444797440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444799899/">1444799899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444801939/">1444801939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444802752/">1444802752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444803140/">1444803140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444803799/">1444803799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444804641/">1444804641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444804879/">1444804879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444804999/">1444804999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444805419/">1444805419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444806269/">1444806269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444807640/">1444807640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444807819/">1444807819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444807939/">1444807939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444808178/">1444808178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444808599/">1444808599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444809620/">1444809620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444810462/">1444810462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444812379/">1444812379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444815322/">1444815322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444815440/">1444815440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444815560/">1444815560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444818680/">1444818680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444819159/">1444819159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444819702/">1444819702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444824861/">1444824861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444825818/">1444825818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444825941/">1444825941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444825996/">1444825996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444827200/">1444827200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444828817/">1444828817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444832538/">1444832538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444832960/">1444832960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444834895/">1444834895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444835262/">1444835262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444835610/">1444835610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444836510/">1444836510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444837171/">1444837171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444838768/">1444838768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444839208/">1444839208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444841551/">1444841551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444845331/">1444845331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444845691/">1444845691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444845828/">1444845828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444845871/">1444845871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444845991/">1444845991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444846230/">1444846230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444847011/">1444847011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444847491/">1444847491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444847671/">1444847671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444847730/">1444847730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444849287/">1444849287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444849591/">1444849591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444853251/">1444853251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444853371/">1444853371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444853727/">1444853727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444854871/">1444854871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444855347/">1444855347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444855768/">1444855768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444855769/">1444855769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444855892/">1444855892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444856628/">1444856628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444857529/">1444857529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444857649/">1444857649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444857708/">1444857708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444858186/">1444858186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444860109/">1444860109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444860963/">1444860963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444861966/">1444861966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444865031/">1444865031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444865274/">1444865274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444866757/">1444866757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444867248/">1444867248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444867249/">1444867249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444870034/">1444870034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444873969/">1444873969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444875349/">1444875349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444877027/">1444877027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444877544/">1444877544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444879368/">1444879368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444880989/">1444880989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444881166/">1444881166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444881167/">1444881167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444882790/">1444882790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444888431/">1444888431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444889329/">1444889329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444889809/">1444889809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444889989/">1444889989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444890169/">1444890169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444890410/">1444890410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444893356/">1444893356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444894010/">1444894010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444894369/">1444894369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444894370/">1444894370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444895938/">1444895938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444896648/">1444896648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444899827/">1444899827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444900170/">1444900170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444901618/">1444901618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444903184/">1444903184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444909546/">1444909546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444910929/">1444910929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444914889/">1444914889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444917025/">1444917025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444917108/">1444917108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444917229/">1444917229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444917912/">1444917912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444918787/">1444918787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444918914/">1444918914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444922556/">1444922556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444923714/">1444923714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444924553/">1444924553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444924790/">1444924790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444925388/">1444925388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444925448/">1444925448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444925630/">1444925630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444929950/">1444929950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444934259/">1444934259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444935876/">1444935876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444936115/">1444936115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444936370/">1444936370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444936921/">1444936921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444938355/">1444938355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444938639/">1444938639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444939955/">1444939955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444941817/">1444941817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444942536/">1444942536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444943259/">1444943259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444943323/">1444943323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444944815/">1444944815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444945058/">1444945058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444948890/">1444948890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444949490/">1444949490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444952731/">1444952731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444953690/">1444953690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444956878/">1444956878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444961105/">1444961105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444973010/">1444973010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444973191/">1444973191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444973310/">1444973310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444974810/">1444974810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444975471/">1444975471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444975830/">1444975830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444976610/">1444976610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444977090/">1444977090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444977210/">1444977210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444977269/">1444977269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444977810/">1444977810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444977931/">1444977931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444978110/">1444978110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444978650/">1444978650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444981350/">1444981350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444984228/">1444984228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444986031/">1444986031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444989870/">1444989870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444993053/">1444993053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444995012/">1444995012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444999591/">1444999591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1444999710/">1444999710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445001751/">1445001751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445002246/">1445002246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445002651/">1445002651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445002833/">1445002833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445003286/">1445003286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445003437/">1445003437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445004098/">1445004098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445004595/">1445004595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445005911/">1445005911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445008836/">1445008836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445009313/">1445009313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445009492/">1445009492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445011170/">1445011170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445011583/">1445011583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445013031/">1445013031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445013871/">1445013871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445014170/">1445014170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445014725/">1445014725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445015845/">1445015845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445015910/">1445015910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445015911/">1445015911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445016510/">1445016510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445016612/">1445016612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445018855/">1445018855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445020594/">1445020594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445020651/">1445020651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445021009/">1445021009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445021731/">1445021731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445021920/">1445021920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445022606/">1445022606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445023891/">1445023891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445024501/">1445024501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445025358/">1445025358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445026560/">1445026560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445026797/">1445026797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445027337/">1445027337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445027525/">1445027525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445028993/">1445028993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445031631/">1445031631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445032050/">1445032050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445032231/">1445032231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445034105/">1445034105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445034692/">1445034692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445038215/">1445038215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445038591/">1445038591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445043031/">1445043031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445045490/">1445045490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445046211/">1445046211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445049051/">1445049051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445052150/">1445052150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445052451/">1445052451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445052813/">1445052813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445064630/">1445064630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445067750/">1445067750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445068468/">1445068468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445070624/">1445070624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445096228/">1445096228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445102989/">1445102989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445106330/">1445106330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445117195/">1445117195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445117475/">1445117475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445124603/">1445124603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445141972/">1445141972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445143291/">1445143291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445145390/">1445145390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445146206/">1445146206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445166990/">1445166990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445174431/">1445174431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445192610/">1445192610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445194296/">1445194296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445199570/">1445199570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445208030/">1445208030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445214810/">1445214810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445216190/">1445216190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445217932/">1445217932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445219070/">1445219070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445221110/">1445221110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445221230/">1445221230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445221350/">1445221350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445222610/">1445222610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445224290/">1445224290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445224830/">1445224830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445224890/">1445224890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445234250/">1445234250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445234370/">1445234370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445235150/">1445235150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445235570/">1445235570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445243440/">1445243440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445244990/">1445244990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445245770/">1445245770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445247691/">1445247691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445248230/">1445248230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445248830/">1445248830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445250872/">1445250872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445252011/">1445252011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445252555/">1445252555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445253811/">1445253811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445254207/">1445254207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445254890/">1445254890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445260231/">1445260231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445261971/">1445261971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445262658/">1445262658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445262755/">1445262755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445264014/">1445264014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445264542/">1445264542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445265235/">1445265235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445265249/">1445265249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445265529/">1445265529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445267337/">1445267337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445267711/">1445267711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445267888/">1445267888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445269176/">1445269176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445269889/">1445269889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445270791/">1445270791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445273972/">1445273972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445274100/">1445274100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445274272/">1445274272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445276223/">1445276223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445276729/">1445276729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445277229/">1445277229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445277394/">1445277394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445277395/">1445277395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445278189/">1445278189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445278490/">1445278490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445279009/">1445279009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445279369/">1445279369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445279851/">1445279851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445280597/">1445280597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445282190/">1445282190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445283679/">1445283679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445284123/">1445284123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445284529/">1445284529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445284709/">1445284709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445284765/">1445284765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445285792/">1445285792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445286269/">1445286269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445286614/">1445286614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445287829/">1445287829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445288250/">1445288250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445289272/">1445289272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445289453/">1445289453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445292232/">1445292232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445293051/">1445293051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445293289/">1445293289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445293470/">1445293470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445293650/">1445293650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445294265/">1445294265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445295750/">1445295750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445296006/">1445296006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445296247/">1445296247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445296632/">1445296632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445296930/">1445296930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445297647/">1445297647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445300347/">1445300347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445302586/">1445302586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445302760/">1445302760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445304626/">1445304626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445305699/">1445305699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445306146/">1445306146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445306171/">1445306171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445307671/">1445307671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445309032/">1445309032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445309300/">1445309300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445310462/">1445310462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445311749/">1445311749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445312695/">1445312695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445314989/">1445314989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445315107/">1445315107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445318168/">1445318168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445318482/">1445318482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445318609/">1445318609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445319367/">1445319367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445323207/">1445323207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445329387/">1445329387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445329567/">1445329567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445330167/">1445330167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445330707/">1445330707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445330766/">1445330766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445331189/">1445331189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445332086/">1445332086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445332807/">1445332807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445333647/">1445333647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445334007/">1445334007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445334386/">1445334386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445334488/">1445334488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445336419/">1445336419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445337188/">1445337188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445337308/">1445337308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445337486/">1445337486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445337668/">1445337668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445338874/">1445338874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445340667/">1445340667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445340907/">1445340907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445341517/">1445341517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445341567/">1445341567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445342891/">1445342891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445343254/">1445343254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445344929/">1445344929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445345955/">1445345955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445347632/">1445347632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445348887/">1445348887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445350268/">1445350268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445350695/">1445350695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445351056/">1445351056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445351126/">1445351126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445352014/">1445352014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445357527/">1445357527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445359327/">1445359327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445359449/">1445359449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445359574/">1445359574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445361488/">1445361488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445362628/">1445362628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445362749/">1445362749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445364009/">1445364009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445364151/">1445364151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445365874/">1445365874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445366709/">1445366709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445368210/">1445368210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445369264/">1445369264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445369599/">1445369599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445370608/">1445370608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445370912/">1445370912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445371648/">1445371648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445371987/">1445371987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445373737/">1445373737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445373897/">1445373897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445373952/">1445373952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445374431/">1445374431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445379019/">1445379019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445380241/">1445380241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445386501/">1445386501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445400721/">1445400721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445406422/">1445406422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445406423/">1445406423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445406481/">1445406481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445406540/">1445406540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445407020/">1445407020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445407080/">1445407080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445407501/">1445407501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445407560/">1445407560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445408101/">1445408101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445408884/">1445408884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445409420/">1445409420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445410209/">1445410209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445411793/">1445411793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413648/">1445413648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413709/">1445413709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413710/">1445413710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413853/">1445413853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413854/">1445413854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413855/">1445413855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445413942/">1445413942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445415302/">1445415302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445415615/">1445415615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445415847/">1445415847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445418538/">1445418538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445418975/">1445418975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445423402/">1445423402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445434502/">1445434502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445435731/">1445435731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445435732/">1445435732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445435758/">1445435758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445436081/">1445436081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445436649/">1445436649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445436781/">1445436781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445436945/">1445436945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445437158/">1445437158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445437621/">1445437621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445442564/">1445442564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445444015/">1445444015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445444016/">1445444016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445444688/">1445444688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445446501/">1445446501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445448238/">1445448238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445451750/">1445451750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445454474/">1445454474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445457312/">1445457312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445458080/">1445458080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445459649/">1445459649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445460183/">1445460183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445463061/">1445463061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445463122/">1445463122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445463458/">1445463458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445466067/">1445466067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445470866/">1445470866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445471398/">1445471398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445472017/">1445472017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445472860/">1445472860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445472865/">1445472865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445473640/">1445473640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445475256/">1445475256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445477296/">1445477296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445478658/">1445478658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445478917/">1445478917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445480280/">1445480280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445480462/">1445480462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445481490/">1445481490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445484239/">1445484239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445486458/">1445486458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445489040/">1445489040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445494698/">1445494698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445495841/">1445495841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445496002/">1445496002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445496240/">1445496240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445497082/">1445497082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445501655/">1445501655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445501777/">1445501777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445502081/">1445502081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445502211/">1445502211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445506240/">1445506240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445507821/">1445507821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445516703/">1445516703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445517551/">1445517551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445520298/">1445520298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445521082/">1445521082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445521387/">1445521387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445521719/">1445521719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445522709/">1445522709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445523189/">1445523189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445527581/">1445527581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445530642/">1445530642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445531151/">1445531151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445531648/">1445531648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445531827/">1445531827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445533214/">1445533214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445534906/">1445534906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445537313/">1445537313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445537314/">1445537314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445538232/">1445538232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445538239/">1445538239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445546341/">1445546341/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445546578/">1445546578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445546808/">1445546808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445549580/">1445549580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445549640/">1445549640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445550003/">1445550003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445550485/">1445550485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445554220/">1445554220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445554339/">1445554339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445555300/">1445555300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445556919/">1445556919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445557158/">1445557158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445558254/">1445558254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445558334/">1445558334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445558553/">1445558553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445562766/">1445562766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445563863/">1445563863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445565996/">1445565996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445569017/">1445569017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445571885/">1445571885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445572591/">1445572591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445572772/">1445572772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445574875/">1445574875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445578588/">1445578588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445579504/">1445579504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445580569/">1445580569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445581232/">1445581232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445581833/">1445581833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445582072/">1445582072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445584708/">1445584708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445584899/">1445584899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445585074/">1445585074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445585128/">1445585128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445585247/">1445585247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445590352/">1445590352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445593773/">1445593773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445594681/">1445594681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445599234/">1445599234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445603790/">1445603790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445605203/">1445605203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445605374/">1445605374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445608978/">1445608978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445610222/">1445610222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445612011/">1445612011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445613808/">1445613808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445620112/">1445620112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445626474/">1445626474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445626955/">1445626955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445627435/">1445627435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445629001/">1445629001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445629175/">1445629175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445629236/">1445629236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445629549/">1445629549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445632833/">1445632833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445633261/">1445633261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445635536/">1445635536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445637215/">1445637215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445637876/">1445637876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445640938/">1445640938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445641973/">1445641973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445648735/">1445648735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445649095/">1445649095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445655635/">1445655635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445661635/">1445661635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445661755/">1445661755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445665475/">1445665475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445679398/">1445679398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445679574/">1445679574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445679694/">1445679694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445689116/">1445689116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445694756/">1445694756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445695175/">1445695175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445695497/">1445695497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445699495/">1445699495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445707355/">1445707355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445713297/">1445713297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445713671/">1445713671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445715515/">1445715515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445717223/">1445717223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445775517/">1445775517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445785297/">1445785297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445804134/">1445804134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445805934/">1445805934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445811093/">1445811093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445816314/">1445816314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445819492/">1445819492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445835860/">1445835860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445838160/">1445838160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445839487/">1445839487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445848414/">1445848414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445852613/">1445852613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445853641/">1445853641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445853866/">1445853866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445853867/">1445853867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445853876/">1445853876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445854295/">1445854295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445854894/">1445854894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445855386/">1445855386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445856516/">1445856516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445856709/">1445856709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445856932/">1445856932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445857054/">1445857054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445858617/">1445858617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445858736/">1445858736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445859514/">1445859514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445864674/">1445864674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445866824/">1445866824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445867182/">1445867182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445873218/">1445873218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445873553/">1445873553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445873665/">1445873665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445873789/">1445873789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445874895/">1445874895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445876570/">1445876570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445877501/">1445877501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445878342/">1445878342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445880487/">1445880487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445880625/">1445880625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445880866/">1445880866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445880867/">1445880867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445881357/">1445881357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445882871/">1445882871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445883022/">1445883022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445883144/">1445883144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445883500/">1445883500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445883802/">1445883802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445885606/">1445885606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445888379/">1445888379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445888813/">1445888813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445889621/">1445889621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445893787/">1445893787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445893789/">1445893789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445895570/">1445895570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445898204/">1445898204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445898482/">1445898482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445899503/">1445899503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445900518/">1445900518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445900522/">1445900522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445904925/">1445904925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445906926/">1445906926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445908384/">1445908384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445912542/">1445912542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445913680/">1445913680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445914882/">1445914882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445915089/">1445915089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445918002/">1445918002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445920761/">1445920761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445924659/">1445924659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445929101/">1445929101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445933361/">1445933361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445933541/">1445933541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445933962/">1445933962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445934082/">1445934082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445934562/">1445934562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445935881/">1445935881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445936001/">1445936001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445939903/">1445939903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445946624/">1445946624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445950402/">1445950402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445950524/">1445950524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445950525/">1445950525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445950761/">1445950761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445951122/">1445951122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445951779/">1445951779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445952982/">1445952982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445955145/">1445955145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445956827/">1445956827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445957485/">1445957485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445960182/">1445960182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445961575/">1445961575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445963080/">1445963080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445964278/">1445964278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445966193/">1445966193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445966550/">1445966550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445967873/">1445967873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445968352/">1445968352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445968591/">1445968591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445970151/">1445970151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445970750/">1445970750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445971051/">1445971051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445972010/">1445972010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445977533/">1445977533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445977534/">1445977534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445979761/">1445979761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445980412/">1445980412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445980591/">1445980591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445982148/">1445982148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445982451/">1445982451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445983170/">1445983170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445984311/">1445984311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445989651/">1445989651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1445998892/">1445998892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446000637/">1446000637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446001759/">1446001759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446004472/">1446004472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446005088/">1446005088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446007712/">1446007712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446008434/">1446008434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446009332/">1446009332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446010352/">1446010352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446011852/">1446011852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446023431/">1446023431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446025953/">1446025953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446027632/">1446027632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446029252/">1446029252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446030340/">1446030340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446031834/">1446031834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446032617/">1446032617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446032912/">1446032912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446035454/">1446035454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446036453/">1446036453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446036636/">1446036636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446038972/">1446038972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446039216/">1446039216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446039453/">1446039453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446040986/">1446040986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446041254/">1446041254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446041492/">1446041492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446043059/">1446043059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446044315/">1446044315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446047311/">1446047311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446047372/">1446047372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446048360/">1446048360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446051277/">1446051277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446053013/">1446053013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446054692/">1446054692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446055498/">1446055498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446056193/">1446056193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446056792/">1446056792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446058113/">1446058113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446058353/">1446058353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446058772/">1446058772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446059493/">1446059493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446059613/">1446059613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446061114/">1446061114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446061233/">1446061233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446062559/">1446062559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446065012/">1446065012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446065073/">1446065073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446068012/">1446068012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446068254/">1446068254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446068793/">1446068793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446069274/">1446069274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446069332/">1446069332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446070178/">1446070178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446070653/">1446070653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446071436/">1446071436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446073475/">1446073475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446074435/">1446074435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446074793/">1446074793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446075394/">1446075394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446075753/">1446075753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446075932/">1446075932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446077135/">1446077135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446077279/">1446077279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446077672/">1446077672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446078934/">1446078934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446080027/">1446080027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446080563/">1446080563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446080711/">1446080711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446082125/">1446082125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446084409/">1446084409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446085665/">1446085665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446086090/">1446086090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446098325/">1446098325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446098927/">1446098927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446099465/">1446099465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446099644/">1446099644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446099704/">1446099704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446099764/">1446099764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446100904/">1446100904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446103007/">1446103007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446103425/">1446103425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446103726/">1446103726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446103844/">1446103844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446104024/">1446104024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446104167/">1446104167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446108524/">1446108524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446109007/">1446109007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446109666/">1446109666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446110745/">1446110745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446112001/">1446112001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446112301/">1446112301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446117344/">1446117344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446120688/">1446120688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446123088/">1446123088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446123204/">1446123204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446124408/">1446124408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446157812/">1446157812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446158173/">1446158173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446158412/">1446158412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446160512/">1446160512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446160573/">1446160573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446160815/">1446160815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446161776/">1446161776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446161952/">1446161952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446161953/">1446161953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446162673/">1446162673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446163092/">1446163092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446164233/">1446164233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446166333/">1446166333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446166981/">1446166981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446167775/">1446167775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446168135/">1446168135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446171617/">1446171617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446172634/">1446172634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446173659/">1446173659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446175649/">1446175649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446177093/">1446177093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446177901/">1446177901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446178712/">1446178712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446179021/">1446179021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446179070/">1446179070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446185551/">1446185551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446185790/">1446185790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446186390/">1446186390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446190231/">1446190231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446192570/">1446192570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446192810/">1446192810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446192990/">1446192990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446194011/">1446194011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446194668/">1446194668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446195890/">1446195890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446199051/">1446199051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446199493/">1446199493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446199661/">1446199661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446200371/">1446200371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446200491/">1446200491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446202349/">1446202349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446203971/">1446203971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446207992/">1446207992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446208831/">1446208831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446209490/">1446209490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446211290/">1446211290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446211665/">1446211665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446212910/">1446212910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446214438/">1446214438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446215732/">1446215732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446215970/">1446215970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446216966/">1446216966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446217053/">1446217053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446217832/">1446217832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446219763/">1446219763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446223163/">1446223163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446223401/">1446223401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446223526/">1446223526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446224062/">1446224062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446224306/">1446224306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446224444/">1446224444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446225335/">1446225335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446225993/">1446225993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446227072/">1446227072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446231335/">1446231335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446233251/">1446233251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446234273/">1446234273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446234570/">1446234570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446235366/">1446235366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446235506/">1446235506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446235771/">1446235771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446235891/">1446235891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446236432/">1446236432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446237507/">1446237507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446245610/">1446245610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446246297/">1446246297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446247710/">1446247710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446247949/">1446247949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446248070/">1446248070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446249510/">1446249510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446252870/">1446252870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446256470/">1446256470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446258210/">1446258210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446261652/">1446261652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446262613/">1446262613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446269093/">1446269093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446273833/">1446273833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446281153/">1446281153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446288473/">1446288473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446292561/">1446292561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446294593/">1446294593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446296753/">1446296753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446297114/">1446297114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446298133/">1446298133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446304973/">1446304973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446305872/">1446305872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446310313/">1446310313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446310492/">1446310492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446312173/">1446312173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446312473/">1446312473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446312892/">1446312892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446312952/">1446312952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446317933/">1446317933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446318951/">1446318951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446323692/">1446323692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446325072/">1446325072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446326092/">1446326092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446328913/">1446328913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446332992/">1446332992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446334069/">1446334069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446337553/">1446337553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446343064/">1446343064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446376357/">1446376357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446394477/">1446394477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446399998/">1446399998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446400717/">1446400717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446411698/">1446411698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446420870/">1446420870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446422130/">1446422130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446423391/">1446423391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446427291/">1446427291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446431311/">1446431311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446431909/">1446431909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446433708/">1446433708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446434311/">1446434311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446434880/">1446434880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446439712/">1446439712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446443671/">1446443671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446449611/">1446449611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446449792/">1446449792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446449971/">1446449971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446450091/">1446450091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446450751/">1446450751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446451772/">1446451772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446453449/">1446453449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446456638/">1446456638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446456721/">1446456721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446456807/">1446456807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446457122/">1446457122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446461106/">1446461106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446462307/">1446462307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446467886/">1446467886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446469027/">1446469027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446470886/">1446470886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446473105/">1446473105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446476424/">1446476424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446476474/">1446476474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446477127/">1446477127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446477307/">1446477307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446477965/">1446477965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446480009/">1446480009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446480561/">1446480561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446481085/">1446481085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446482288/">1446482288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446483395/">1446483395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446484329/">1446484329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446486247/">1446486247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446486615/">1446486615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446486729/">1446486729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446486849/">1446486849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446487809/">1446487809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446488227/">1446488227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446488407/">1446488407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446489307/">1446489307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446489428/">1446489428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446491527/">1446491527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446491528/">1446491528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446491886/">1446491886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446494707/">1446494707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446496806/">1446496806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446498308/">1446498308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446499207/">1446499207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446499327/">1446499327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446499810/">1446499810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446499866/">1446499866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446500228/">1446500228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446501187/">1446501187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446501426/">1446501426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446504366/">1446504366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446507007/">1446507007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446507368/">1446507368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446507907/">1446507907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446508687/">1446508687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446510078/">1446510078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446511327/">1446511327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446520104/">1446520104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446524227/">1446524227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446524406/">1446524406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446525368/">1446525368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446526148/">1446526148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446531007/">1446531007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446531186/">1446531186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446531366/">1446531366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446531548/">1446531548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446532387/">1446532387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446535086/">1446535086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446536347/">1446536347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446537727/">1446537727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446537955/">1446537955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446539047/">1446539047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446539285/">1446539285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446539466/">1446539466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446543185/">1446543185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446543849/">1446543849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446545796/">1446545796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446549210/">1446549210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446549932/">1446549932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446550172/">1446550172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446556232/">1446556232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446558751/">1446558751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446559481/">1446559481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446560312/">1446560312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446563852/">1446563852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446565172/">1446565172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446566716/">1446566716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446567214/">1446567214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446569612/">1446569612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446569672/">1446569672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446571651/">1446571651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446574174/">1446574174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446575010/">1446575010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446575192/">1446575192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446576751/">1446576751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446578492/">1446578492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446578552/">1446578552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446579152/">1446579152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446583591/">1446583591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446585152/">1446585152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446586472/">1446586472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446587731/">1446587731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446588696/">1446588696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446592776/">1446592776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446595660/">1446595660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446597333/">1446597333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446603215/">1446603215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446603936/">1446603936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446604895/">1446604895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446605634/">1446605634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446605736/">1446605736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446606996/">1446606996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446607055/">1446607055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446609096/">1446609096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446610775/">1446610775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446611914/">1446611914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446615814/">1446615814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446619236/">1446619236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446620678/">1446620678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446620913/">1446620913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446624375/">1446624375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446631115/">1446631115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446631775/">1446631775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446634836/">1446634836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446635975/">1446635975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446636696/">1446636696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446637895/">1446637895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446641255/">1446641255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446643475/">1446643475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446643655/">1446643655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446645035/">1446645035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446646655/">1446646655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446649823/">1446649823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446652293/">1446652293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446654455/">1446654455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446656558/">1446656558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446660755/">1446660755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446661896/">1446661896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446662197/">1446662197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446663454/">1446663454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446665075/">1446665075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446665914/">1446665914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446666155/">1446666155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446671555/">1446671555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446672214/">1446672214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446673114/">1446673114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446673235/">1446673235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446674855/">1446674855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446675035/">1446675035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446676355/">1446676355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446676836/">1446676836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446678437/">1446678437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679471/">1446679471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679475/">1446679475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679479/">1446679479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679481/">1446679481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679486/">1446679486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679487/">1446679487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679490/">1446679490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446679536/">1446679536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446680435/">1446680435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446680615/">1446680615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446682655/">1446682655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683461/">1446683461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683467/">1446683467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683469/">1446683469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683470/">1446683470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683474/">1446683474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683524/">1446683524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683622/">1446683622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683627/">1446683627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683629/">1446683629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446683632/">1446683632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446685295/">1446685295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446685472/">1446685472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446688295/">1446688295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446688835/">1446688835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446690155/">1446690155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446692315/">1446692315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446692835/">1446692835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446704315/">1446704315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446705214/">1446705214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446708335/">1446708335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446710555/">1446710555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446710860/">1446710860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446711095/">1446711095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446712055/">1446712055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446712056/">1446712056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446712850/">1446712850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446712896/">1446712896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446713021/">1446713021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446713134/">1446713134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446713374/">1446713374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446713432/">1446713432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446715655/">1446715655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446717575/">1446717575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446718300/">1446718300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446718357/">1446718357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446718955/">1446718955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446719374/">1446719374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446720156/">1446720156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446721894/">1446721894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446722196/">1446722196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446726641/">1446726641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446727415/">1446727415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446728255/">1446728255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446728494/">1446728494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446731917/">1446731917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446733954/">1446733954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446734075/">1446734075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446735002/">1446735002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446737195/">1446737195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446737514/">1446737514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446737522/">1446737522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446737976/">1446737976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446744874/">1446744874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446747936/">1446747936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446748245/">1446748245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446754295/">1446754295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446757836/">1446757836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446757955/">1446757955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446758255/">1446758255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446759008/">1446759008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446759335/">1446759335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446759755/">1446759755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446760595/">1446760595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446760896/">1446760896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446763832/">1446763832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446766115/">1446766115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446766175/">1446766175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446767738/">1446767738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446768816/">1446768816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446769715/">1446769715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446770555/">1446770555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446770975/">1446770975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446771455/">1446771455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446771635/">1446771635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446772297/">1446772297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446773135/">1446773135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446774574/">1446774574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446775664/">1446775664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446777999/">1446777999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446780516/">1446780516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446780807/">1446780807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446781056/">1446781056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446781294/">1446781294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446782320/">1446782320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446783754/">1446783754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446784356/">1446784356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446787174/">1446787174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446787773/">1446787773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446788322/">1446788322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446794495/">1446794495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446798935/">1446798935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446799174/">1446799174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446802257/">1446802257/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446809674/">1446809674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446809854/">1446809854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446809914/">1446809914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446809974/">1446809974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446810035/">1446810035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446810154/">1446810154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446810454/">1446810454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446814774/">1446814774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446815735/">1446815735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446815914/">1446815914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446817237/">1446817237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446817655/">1446817655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446817954/">1446817954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446818435/">1446818435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446821799/">1446821799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446821877/">1446821877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446821914/">1446821914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446822704/">1446822704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446823721/">1446823721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446823935/">1446823935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446825454/">1446825454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446826176/">1446826176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446826715/">1446826715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446826895/">1446826895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446828948/">1446828948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446831154/">1446831154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446831277/">1446831277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446831459/">1446831459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446832529/">1446832529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446833246/">1446833246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446833254/">1446833254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446834671/">1446834671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446836195/">1446836195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446836376/">1446836376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446836561/">1446836561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446837216/">1446837216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446837336/">1446837336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446838118/">1446838118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446841594/">1446841594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446842434/">1446842434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446844413/">1446844413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446844414/">1446844414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446845426/">1446845426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446846635/">1446846635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446847716/">1446847716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446848557/">1446848557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446848855/">1446848855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446849156/">1446849156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446850295/">1446850295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446850835/">1446850835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446851676/">1446851676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446852034/">1446852034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446852755/">1446852755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446854376/">1446854376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446854666/">1446854666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446855333/">1446855333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446855875/">1446855875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446856225/">1446856225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446860615/">1446860615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446867935/">1446867935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446870712/">1446870712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446873755/">1446873755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446888035/">1446888035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446888215/">1446888215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446899914/">1446899914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446904595/">1446904595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446907115/">1446907115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446907294/">1446907294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446925175/">1446925175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1446941266/">1446941266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447007195/">1447007195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447007315/">1447007315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447014335/">1447014335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447014514/">1447014514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447030295/">1447030295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447030535/">1447030535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447030775/">1447030775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447038696/">1447038696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447042355/">1447042355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447045551/">1447045551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447046613/">1447046613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447046735/">1447046735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447050611/">1447050611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447054955/">1447054955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447055194/">1447055194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447055374/">1447055374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447055674/">1447055674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447055974/">1447055974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447056172/">1447056172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447058751/">1447058751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447077578/">1447077578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447078896/">1447078896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447079797/">1447079797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447081174/">1447081174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447083325/">1447083325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447090952/">1447090952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447092458/">1447092458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447092690/">1447092690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447093292/">1447093292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447095634/">1447095634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447096417/">1447096417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447099650/">1447099650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447101034/">1447101034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447104093/">1447104093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447104615/">1447104615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447104933/">1447104933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447105411/">1447105411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447105833/">1447105833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447106855/">1447106855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447107454/">1447107454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447114831/">1447114831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447114951/">1447114951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447115793/">1447115793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447116331/">1447116331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447118791/">1447118791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447119211/">1447119211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447119271/">1447119271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447119331/">1447119331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447120052/">1447120052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447121431/">1447121431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447123785/">1447123785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447124011/">1447124011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447124372/">1447124372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447125090/">1447125090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447126305/">1447126305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447131092/">1447131092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447133917/">1447133917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447134515/">1447134515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447137016/">1447137016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447142763/">1447142763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447143036/">1447143036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447144655/">1447144655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447145855/">1447145855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447146515/">1447146515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447146815/">1447146815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447147355/">1447147355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447149335/">1447149335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447150535/">1447150535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447151675/">1447151675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447154013/">1447154013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447156715/">1447156715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447158035/">1447158035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447158605/">1447158605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447159475/">1447159475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447163015/">1447163015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447164403/">1447164403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447169082/">1447169082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447169581/">1447169581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447170995/">1447170995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447171657/">1447171657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447172382/">1447172382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447173214/">1447173214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447173397/">1447173397/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447173514/">1447173514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447174953/">1447174953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447175194/">1447175194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447175317/">1447175317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447175675/">1447175675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447176396/">1447176396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447177717/">1447177717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447178373/">1447178373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447180259/">1447180259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447181555/">1447181555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447181974/">1447181974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447183114/">1447183114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447184914/">1447184914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447188094/">1447188094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447188275/">1447188275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447188995/">1447188995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447189115/">1447189115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447189892/">1447189892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447192349/">1447192349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447193069/">1447193069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447193911/">1447193911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447195052/">1447195052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447195951/">1447195951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447198051/">1447198051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447208492/">1447208492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447209811/">1447209811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447212570/">1447212570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447213117/">1447213117/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447214880/">1447214880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447215272/">1447215272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447216169/">1447216169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447216535/">1447216535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447218641/">1447218641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447219051/">1447219051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447219591/">1447219591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447219831/">1447219831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447220731/">1447220731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447221211/">1447221211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447225712/">1447225712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447225895/">1447225895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447228651/">1447228651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447228954/">1447228954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447234245/">1447234245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447237111/">1447237111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447237832/">1447237832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447238072/">1447238072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447238851/">1447238851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447239152/">1447239152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447241552/">1447241552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447241670/">1447241670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447241911/">1447241911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447242164/">1447242164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447242334/">1447242334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447242634/">1447242634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447244252/">1447244252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447245015/">1447245015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447251151/">1447251151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447252052/">1447252052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447253311/">1447253311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447254508/">1447254508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447254752/">1447254752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447254991/">1447254991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447255109/">1447255109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447255173/">1447255173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447255291/">1447255291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447255878/">1447255878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447257153/">1447257153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447258235/">1447258235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447260632/">1447260632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447260992/">1447260992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447261232/">1447261232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447261349/">1447261349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447261712/">1447261712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447263454/">1447263454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447266152/">1447266152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447266654/">1447266654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447275451/">1447275451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447277372/">1447277372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447277373/">1447277373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447277492/">1447277492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447277551/">1447277551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447277793/">1447277793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447278391/">1447278391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447278870/">1447278870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447279891/">1447279891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447280371/">1447280371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447280913/">1447280913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447281571/">1447281571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447281811/">1447281811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447285307/">1447285307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447287887/">1447287887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447287950/">1447287950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447288206/">1447288206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447288908/">1447288908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447290768/">1447290768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447293888/">1447293888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447295027/">1447295027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447298267/">1447298267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447299227/">1447299227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447299828/">1447299828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447305707/">1447305707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447315007/">1447315007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447320370/">1447320370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447322858/">1447322858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447322925/">1447322925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447325868/">1447325868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447328268/">1447328268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447339248/">1447339248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447339967/">1447339967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447340864/">1447340864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447340865/">1447340865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447342965/">1447342965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447344108/">1447344108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447344585/">1447344585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447344746/">1447344746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447345253/">1447345253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447347285/">1447347285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447348310/">1447348310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447350348/">1447350348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447351480/">1447351480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447351661/">1447351661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447352988/">1447352988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447353063/">1447353063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447358254/">1447358254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447374397/">1447374397/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447376079/">1447376079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447376139/">1447376139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447376197/">1447376197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447376256/">1447376256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447377097/">1447377097/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447377880/">1447377880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447381776/">1447381776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447383770/">1447383770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447388376/">1447388376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447389999/">1447389999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447394976/">1447394976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447395278/">1447395278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447400265/">1447400265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447407036/">1447407036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447407042/">1447407042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447407511/">1447407511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447408173/">1447408173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447408354/">1447408354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447408711/">1447408711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447409132/">1447409132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447409433/">1447409433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447416332/">1447416332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447417808/">1447417808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447421851/">1447421851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447424373/">1447424373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447424793/">1447424793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447425395/">1447425395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447425696/">1447425696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447426259/">1447426259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447426774/">1447426774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447427689/">1447427689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447428109/">1447428109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447428212/">1447428212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447428450/">1447428450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447429130/">1447429130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447431346/">1447431346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447432958/">1447432958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447433192/">1447433192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447433372/">1447433372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447434302/">1447434302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447435776/">1447435776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447437032/">1447437032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447439194/">1447439194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447440511/">1447440511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447440815/">1447440815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447442013/">1447442013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447444710/">1447444710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447445491/">1447445491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447445552/">1447445552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447446211/">1447446211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447448132/">1447448132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447450713/">1447450713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447451313/">1447451313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447452869/">1447452869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447454372/">1447454372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447456113/">1447456113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447456531/">1447456531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447457852/">1447457852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447458152/">1447458152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447460132/">1447460132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447465352/">1447465352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447466552/">1447466552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447467812/">1447467812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447467991/">1447467991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447472033/">1447472033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447477451/">1447477451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447477893/">1447477893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447490911/">1447490911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447507833/">1447507833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447515173/">1447515173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447516772/">1447516772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447524633/">1447524633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447536992/">1447536992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447542276/">1447542276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447547409/">1447547409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447554033/">1447554033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447554632/">1447554632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447559253/">1447559253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447576052/">1447576052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447576936/">1447576936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447588173/">1447588173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447588713/">1447588713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447595734/">1447595734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447599633/">1447599633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447606472/">1447606472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447610253/">1447610253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447610373/">1447610373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447612052/">1447612052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447612412/">1447612412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447622552/">1447622552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447627292/">1447627292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447627293/">1447627293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447628253/">1447628253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447631613/">1447631613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447638092/">1447638092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447638453/">1447638453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447643854/">1447643854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447646861/">1447646861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447650547/">1447650547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447652079/">1447652079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447657896/">1447657896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447658496/">1447658496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447661797/">1447661797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447665517/">1447665517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447670076/">1447670076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447677004/">1447677004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447679616/">1447679616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447682014/">1447682014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447685988/">1447685988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447686043/">1447686043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447686306/">1447686306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447686515/">1447686515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447686875/">1447686875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447687922/">1447687922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447688198/">1447688198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447688681/">1447688681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447688736/">1447688736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447688977/">1447688977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447689873/">1447689873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447690426/">1447690426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447691613/">1447691613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447692154/">1447692154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447693446/">1447693446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447695400/">1447695400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447696417/">1447696417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447696699/">1447696699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447696700/">1447696700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447696704/">1447696704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447696708/">1447696708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447697194/">1447697194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447697736/">1447697736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447698697/">1447698697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447698714/">1447698714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447700499/">1447700499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447701276/">1447701276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447710996/">1447710996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447711237/">1447711237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447711296/">1447711296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447711297/">1447711297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447711475/">1447711475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447711656/">1447711656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447712136/">1447712136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447713636/">1447713636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447713758/">1447713758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447714295/">1447714295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447714537/">1447714537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447714655/">1447714655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447715079/">1447715079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447715442/">1447715442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447718885/">1447718885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447719724/">1447719724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447719843/">1447719843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447719905/">1447719905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447720023/">1447720023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447720231/">1447720231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447720503/">1447720503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447721523/">1447721523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447722149/">1447722149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447722545/">1447722545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447723687/">1447723687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447723923/">1447723923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447724523/">1447724523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447728064/">1447728064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447731302/">1447731302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447733286/">1447733286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447736284/">1447736284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447738384/">1447738384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447738744/">1447738744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447740165/">1447740165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447740342/">1447740342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447745283/">1447745283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447746483/">1447746483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447747864/">1447747864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447750264/">1447750264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447750948/">1447750948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447751464/">1447751464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447752658/">1447752658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447753383/">1447753383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447754826/">1447754826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447756444/">1447756444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447760104/">1447760104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447762507/">1447762507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447763413/">1447763413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447764065/">1447764065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447766644/">1447766644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447767843/">1447767843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447768872/">1447768872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447772808/">1447772808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447773024/">1447773024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447774083/">1447774083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447774411/">1447774411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447775344/">1447775344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447776311/">1447776311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447779544/">1447779544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447781402/">1447781402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447781945/">1447781945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447782184/">1447782184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447782912/">1447782912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447783503/">1447783503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447783702/">1447783702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447784704/">1447784704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447785029/">1447785029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447785240/">1447785240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447788664/">1447788664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447788904/">1447788904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447789863/">1447789863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447790403/">1447790403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447790825/">1447790825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447795449/">1447795449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447796523/">1447796523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447797484/">1447797484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447797743/">1447797743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447800123/">1447800123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447800783/">1447800783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447801024/">1447801024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447802193/">1447802193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447803576/">1447803576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447803814/">1447803814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447805734/">1447805734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447806095/">1447806095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447808645/">1447808645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447809274/">1447809274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447810294/">1447810294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447811974/">1447811974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447813354/">1447813354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447813475/">1447813475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447817481/">1447817481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447818035/">1447818035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447818394/">1447818394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447821214/">1447821214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447823269/">1447823269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447854565/">1447854565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447854745/">1447854745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447854984/">1447854984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447856961/">1447856961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447858406/">1447858406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447861765/">1447861765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447862006/">1447862006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447862431/">1447862431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447862906/">1447862906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447863087/">1447863087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447863159/">1447863159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447863206/">1447863206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447863622/">1447863622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447864881/">1447864881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447865245/">1447865245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447865487/">1447865487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447866865/">1447866865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447867347/">1447867347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447867582/">1447867582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447869205/">1447869205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447870960/">1447870960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447872762/">1447872762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447872818/">1447872818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447872819/">1447872819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447873720/">1447873720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447874678/">1447874678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447877561/">1447877561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447877562/">1447877562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447877629/">1447877629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447878276/">1447878276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447880195/">1447880195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447884459/">1447884459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447884700/">1447884700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447884815/">1447884815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447884938/">1447884938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447886019/">1447886019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447886379/">1447886379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447887881/">1447887881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447888059/">1447888059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447888060/">1447888060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447889199/">1447889199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447889619/">1447889619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447889916/">1447889916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890099/">1447890099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890162/">1447890162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890457/">1447890457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890519/">1447890519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890639/">1447890639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890823/">1447890823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447890879/">1447890879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447891184/">1447891184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447891241/">1447891241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447892438/">1447892438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447893279/">1447893279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447893461/">1447893461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447893763/">1447893763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447894296/">1447894296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447895091/">1447895091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447895496/">1447895496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447901506/">1447901506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447902640/">1447902640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447903539/">1447903539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447909897/">1447909897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447912179/">1447912179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447912539/">1447912539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447917279/">1447917279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447920339/">1447920339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447921120/">1447921120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447923459/">1447923459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447924419/">1447924419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447925379/">1447925379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447925556/">1447925556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447926040/">1447926040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447926280/">1447926280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447926399/">1447926399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447926459/">1447926459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447926579/">1447926579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447926696/">1447926696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447935560/">1447935560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447939819/">1447939819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447940119/">1447940119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447941986/">1447941986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447942087/">1447942087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447942088/">1447942088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447943046/">1447943046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447944559/">1447944559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447945054/">1447945054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447945147/">1447945147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447945989/">1447945989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447948205/">1447948205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447949345/">1447949345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447949469/">1447949469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447951867/">1447951867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447954867/">1447954867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447955404/">1447955404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447957539/">1447957539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447958319/">1447958319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447960475/">1447960475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447960716/">1447960716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447961555/">1447961555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447961735/">1447961735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447962879/">1447962879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447963235/">1447963235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447964195/">1447964195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447964798/">1447964798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447965218/">1447965218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447967771/">1447967771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447968430/">1447968430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447968550/">1447968550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447969753/">1447969753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447969993/">1447969993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447970348/">1447970348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447970852/">1447970852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447971035/">1447971035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447973374/">1447973374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447973433/">1447973433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447974399/">1447974399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447983755/">1447983755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447985435/">1447985435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447985916/">1447985916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447987236/">1447987236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447990408/">1447990408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447992095/">1447992095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447993362/">1447993362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447993921/">1447993921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447994196/">1447994196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447994674/">1447994674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447996175/">1447996175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447998514/">1447998514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1447999714/">1447999714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448000434/">1448000434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448000554/">1448000554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448000974/">1448000974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448003050/">1448003050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448004515/">1448004515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448005475/">1448005475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448005714/">1448005714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448010456/">1448010456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448010932/">1448010932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448011114/">1448011114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448013575/">1448013575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448019756/">1448019756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448019874/">1448019874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448020835/">1448020835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448024802/">1448024802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448025815/">1448025815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448025934/">1448025934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448026533/">1448026533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448028265/">1448028265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448028935/">1448028935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448029294/">1448029294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448029534/">1448029534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448030973/">1448030973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448034343/">1448034343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448034996/">1448034996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448035656/">1448035656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448039376/">1448039376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448039431/">1448039431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448039972/">1448039972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448040572/">1448040572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448042494/">1448042494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448043751/">1448043751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448044657/">1448044657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448047470/">1448047470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448048195/">1448048195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448049633/">1448049633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448053593/">1448053593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448055033/">1448055033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448055210/">1448055210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448056232/">1448056232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448056533/">1448056533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448057195/">1448057195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448059056/">1448059056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448059474/">1448059474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448059597/">1448059597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448060493/">1448060493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448068007/">1448068007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448068233/">1448068233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448079993/">1448079993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448080267/">1448080267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448087612/">1448087612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448090913/">1448090913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448112934/">1448112934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448135711/">1448135711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448156113/">1448156113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448185631/">1448185631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448200691/">1448200691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448205311/">1448205311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448205431/">1448205431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448220613/">1448220613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448226011/">1448226011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448230271/">1448230271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448233271/">1448233271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448244791/">1448244791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448248811/">1448248811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448251231/">1448251231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448251946/">1448251946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448255425/">1448255425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448256745/">1448256745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448256985/">1448256985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448258621/">1448258621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448262624/">1448262624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448269764/">1448269764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448278465/">1448278465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448278645/">1448278645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448279426/">1448279426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448279725/">1448279725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448280265/">1448280265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448283744/">1448283744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448285546/">1448285546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448286507/">1448286507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448287551/">1448287551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448287922/">1448287922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448288126/">1448288126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448291268/">1448291268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448291492/">1448291492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448292093/">1448292093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448292204/">1448292204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448292865/">1448292865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448293105/">1448293105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448293343/">1448293343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448293644/">1448293644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448293705/">1448293705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448293952/">1448293952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448294006/">1448294006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448294127/">1448294127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448294128/">1448294128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448294424/">1448294424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448295505/">1448295505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448299045/">1448299045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448301267/">1448301267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448306366/">1448306366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448307559/">1448307559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448309184/">1448309184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448313019/">1448313019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448313080/">1448313080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448314279/">1448314279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448314459/">1448314459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448314579/">1448314579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448315419/">1448315419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448316224/">1448316224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448316560/">1448316560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448317519/">1448317519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448317759/">1448317759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448319499/">1448319499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448320173/">1448320173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448321012/">1448321012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448321372/">1448321372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448324072/">1448324072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448326413/">1448326413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448328452/">1448328452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448332060/">1448332060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448332173/">1448332173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448333254/">1448333254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448337576/">1448337576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448339855/">1448339855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448341534/">1448341534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448344592/">1448344592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448348643/">1448348643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448351132/">1448351132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448351252/">1448351252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448351313/">1448351313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448358692/">1448358692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448359234/">1448359234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448361573/">1448361573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448364093/">1448364093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448369013/">1448369013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448374232/">1448374232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448374892/">1448374892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448375974/">1448375974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448378672/">1448378672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448380233/">1448380233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448380593/">1448380593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448380654/">1448380654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448381152/">1448381152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448381493/">1448381493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448382814/">1448382814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448383359/">1448383359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448383777/">1448383777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448384675/">1448384675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448386003/">1448386003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448386172/">1448386172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448387013/">1448387013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448387252/">1448387252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448387433/">1448387433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448388452/">1448388452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448390314/">1448390314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448390372/">1448390372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448391094/">1448391094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448391872/">1448391872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448392532/">1448392532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448392772/">1448392772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448394514/">1448394514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448394752/">1448394752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448395893/">1448395893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448396253/">1448396253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448399733/">1448399733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448400333/">1448400333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448401593/">1448401593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448405673/">1448405673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448406390/">1448406390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448407593/">1448407593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448409213/">1448409213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448409333/">1448409333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448411672/">1448411672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448413234/">1448413234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448414012/">1448414012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448415992/">1448415992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448422001/">1448422001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448422653/">1448422653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448424942/">1448424942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448425296/">1448425296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448426193/">1448426193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448426913/">1448426913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448428233/">1448428233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448429312/">1448429312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448435255/">1448435255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448435856/">1448435856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448436105/">1448436105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448436654/">1448436654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448436752/">1448436752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448442813/">1448442813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448445092/">1448445092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448445971/">1448445971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448448814/">1448448814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448451572/">1448451572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448451933/">1448451933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448452951/">1448452951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448454391/">1448454391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448456313/">1448456313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448457333/">1448457333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448458498/">1448458498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448458499/">1448458499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448459553/">1448459553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448462431/">1448462431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448462616/">1448462616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448463275/">1448463275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448463640/">1448463640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448465588/">1448465588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448467610/">1448467610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448467835/">1448467835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448470655/">1448470655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448471793/">1448471793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448472574/">1448472574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448472694/">1448472694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448473652/">1448473652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448473773/">1448473773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448482893/">1448482893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448483672/">1448483672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448484033/">1448484033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448484516/">1448484516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448485533/">1448485533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448489470/">1448489470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448491090/">1448491090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448492950/">1448492950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448494813/">1448494813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448496315/">1448496315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448496610/">1448496610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448496853/">1448496853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448499130/">1448499130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448499131/">1448499131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448511190/">1448511190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448514260/">1448514260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448524509/">1448524509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448528350/">1448528350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448529250/">1448529250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448529371/">1448529371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448529849/">1448529849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448530453/">1448530453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448530570/">1448530570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448530750/">1448530750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448533211/">1448533211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448535069/">1448535069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448535130/">1448535130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448535670/">1448535670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448536210/">1448536210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448538469/">1448538469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448539349/">1448539349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448540229/">1448540229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448547850/">1448547850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448549531/">1448549531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448551450/">1448551450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448553250/">1448553250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448553431/">1448553431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448555110/">1448555110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448555530/">1448555530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448555590/">1448555590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448555840/">1448555840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448557451/">1448557451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448557813/">1448557813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448557931/">1448557931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448564896/">1448564896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448566390/">1448566390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448566571/">1448566571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448567832/">1448567832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448568972/">1448568972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448570234/">1448570234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448570292/">1448570292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448571492/">1448571492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448572034/">1448572034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448574852/">1448574852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448577072/">1448577072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448578212/">1448578212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448578512/">1448578512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448580492/">1448580492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448581572/">1448581572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448583672/">1448583672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448588832/">1448588832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448589968/">1448589968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448591236/">1448591236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448591772/">1448591772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448592732/">1448592732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448597173/">1448597173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448597713/">1448597713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448607132/">1448607132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448609292/">1448609292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448617092/">1448617092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448619132/">1448619132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448628252/">1448628252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448628312/">1448628312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448629093/">1448629093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448630292/">1448630292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448631972/">1448631972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448635092/">1448635092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448635452/">1448635452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448639832/">1448639832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448641234/">1448641234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448643272/">1448643272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448647892/">1448647892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448650653/">1448650653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448652453/">1448652453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448655453/">1448655453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448659952/">1448659952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448664932/">1448664932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448666493/">1448666493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448667635/">1448667635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448669673/">1448669673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448672310/">1448672310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448683355/">1448683355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448684732/">1448684732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448693612/">1448693612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448693973/">1448693973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448707772/">1448707772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448743112/">1448743112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448743232/">1448743232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448759312/">1448759312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448759672/">1448759672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448816372/">1448816372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448824292/">1448824292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448829585/">1448829585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448831610/">1448831610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448835752/">1448835752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448836832/">1448836832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448839592/">1448839592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448842054/">1448842054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448844572/">1448844572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448859811/">1448859811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448860056/">1448860056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448860173/">1448860173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448861312/">1448861312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448867853/">1448867853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448868334/">1448868334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448874093/">1448874093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448875112/">1448875112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448876492/">1448876492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448876735/">1448876735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448882312/">1448882312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448885252/">1448885252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448886273/">1448886273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448886813/">1448886813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448886933/">1448886933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448887608/">1448887608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448894853/">1448894853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448898633/">1448898633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448903972/">1448903972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448904393/">1448904393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448908211/">1448908211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448912793/">1448912793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448912913/">1448912913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448913121/">1448913121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448913159/">1448913159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448914592/">1448914592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448916334/">1448916334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448916453/">1448916453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448917558/">1448917558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448917595/">1448917595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448918193/">1448918193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448918313/">1448918313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448918854/">1448918854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448919633/">1448919633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448921026/">1448921026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448921171/">1448921171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448921553/">1448921553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448921734/">1448921734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448922936/">1448922936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448923233/">1448923233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448925093/">1448925093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448925392/">1448925392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448925992/">1448925992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448926053/">1448926053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448926054/">1448926054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448926354/">1448926354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448926712/">1448926712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448927253/">1448927253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448927312/">1448927312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448928933/">1448928933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448928993/">1448928993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448929053/">1448929053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448929472/">1448929472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448929773/">1448929773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448931963/">1448931963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448933073/">1448933073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448933734/">1448933734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448933911/">1448933911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448934455/">1448934455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448934516/">1448934516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448936490/">1448936490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448938773/">1448938773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448939147/">1448939147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448939148/">1448939148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448944414/">1448944414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448944596/">1448944596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448945973/">1448945973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448946154/">1448946154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448946936/">1448946936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448947412/">1448947412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448947593/">1448947593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448948432/">1448948432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448949212/">1448949212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448954435/">1448954435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448956113/">1448956113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448959593/">1448959593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448959951/">1448959951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448960552/">1448960552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448960771/">1448960771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448962352/">1448962352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448962953/">1448962953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448963552/">1448963552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448963974/">1448963974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448964154/">1448964154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448964225/">1448964225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448964392/">1448964392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448964512/">1448964512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448964573/">1448964573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448964692/">1448964692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448965533/">1448965533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448967333/">1448967333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448973933/">1448973933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448974592/">1448974592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448976092/">1448976092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448976694/">1448976694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448978373/">1448978373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448978628/">1448978628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448978912/">1448978912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448980113/">1448980113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448981022/">1448981022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448981314/">1448981314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448981673/">1448981673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448983298/">1448983298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448984226/">1448984226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448984553/">1448984553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448989441/">1448989441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448990392/">1448990392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448994208/">1448994208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448995522/">1448995522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448996065/">1448996065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448996248/">1448996248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448997685/">1448997685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448997801/">1448997801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448999006/">1448999006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448999246/">1448999246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1448999545/">1448999545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449002785/">1449002785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449003145/">1449003145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449003324/">1449003324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449005910/">1449005910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449006630/">1449006630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449006810/">1449006810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449008491/">1449008491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449008790/">1449008790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449009210/">1449009210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449010230/">1449010230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449012420/">1449012420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449012960/">1449012960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449013259/">1449013259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449013320/">1449013320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449014160/">1449014160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449015061/">1449015061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449021240/">1449021240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449021984/">1449021984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449024060/">1449024060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449026581/">1449026581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449030061/">1449030061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449031441/">1449031441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449032667/">1449032667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449039120/">1449039120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449041581/">1449041581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449042181/">1449042181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449042360/">1449042360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449043201/">1449043201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449044700/">1449044700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449045660/">1449045660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449048180/">1449048180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449050636/">1449050636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449051120/">1449051120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449061140/">1449061140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449061500/">1449061500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449061622/">1449061622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449061981/">1449061981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449064381/">1449064381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449066206/">1449066206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449066981/">1449066981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449069385/">1449069385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449069804/">1449069804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449071981/">1449071981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449073539/">1449073539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449074126/">1449074126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449074541/">1449074541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449076347/">1449076347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449076702/">1449076702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449078206/">1449078206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449082042/">1449082042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449082466/">1449082466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449083121/">1449083121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449084385/">1449084385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449084866/">1449084866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449086301/">1449086301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449086421/">1449086421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449089961/">1449089961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449090921/">1449090921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449092301/">1449092301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449092721/">1449092721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449093381/">1449093381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449093561/">1449093561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449094641/">1449094641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449094881/">1449094881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449094941/">1449094941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449095425/">1449095425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449095841/">1449095841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449096202/">1449096202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449096381/">1449096381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449096501/">1449096501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449097281/">1449097281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449100465/">1449100465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449104661/">1449104661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449104783/">1449104783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449105026/">1449105026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449105381/">1449105381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449105802/">1449105802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449106701/">1449106701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449106761/">1449106761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449108981/">1449108981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449109102/">1449109102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449113592/">1449113592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449114563/">1449114563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449116905/">1449116905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449117321/">1449117321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449123745/">1449123745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449124405/">1449124405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449125245/">1449125245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449125601/">1449125601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449128723/">1449128723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449131002/">1449131002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449131061/">1449131061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449131301/">1449131301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449132141/">1449132141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449134841/">1449134841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449136526/">1449136526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449136645/">1449136645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449136761/">1449136761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449136822/">1449136822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449137191/">1449137191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449138141/">1449138141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449140903/">1449140903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449141445/">1449141445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449141622/">1449141622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449141801/">1449141801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449143781/">1449143781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449149122/">1449149122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449150506/">1449150506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449151822/">1449151822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449151941/">1449151941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449156265/">1449156265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449158122/">1449158122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449158242/">1449158242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449159759/">1449159759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449163344/">1449163344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449167241/">1449167241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449171024/">1449171024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449171444/">1449171444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449171921/">1449171921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449173962/">1449173962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449174381/">1449174381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449177638/">1449177638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449178902/">1449178902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449180281/">1449180281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449180822/">1449180822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449183881/">1449183881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449184004/">1449184004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449185442/">1449185442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449185742/">1449185742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449185921/">1449185921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449185981/">1449185981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449187122/">1449187122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449187938/">1449187938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449189614/">1449189614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449190815/">1449190815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449191175/">1449191175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449191475/">1449191475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449191775/">1449191775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449192555/">1449192555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449195493/">1449195493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449197295/">1449197295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449198015/">1449198015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449199277/">1449199277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449199767/">1449199767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449200716/">1449200716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449202695/">1449202695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449203055/">1449203055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449203234/">1449203234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449203895/">1449203895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449205037/">1449205037/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449206956/">1449206956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449209595/">1449209595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449211038/">1449211038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449224055/">1449224055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449224655/">1449224655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449225437/">1449225437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449227012/">1449227012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449237015/">1449237015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449238755/">1449238755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449238996/">1449238996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449239596/">1449239596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449240740/">1449240740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449240915/">1449240915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449241356/">1449241356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449241560/">1449241560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449241570/">1449241570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449248851/">1449248851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449249316/">1449249316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449251656/">1449251656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449253275/">1449253275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449254288/">1449254288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449255069/">1449255069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449256035/">1449256035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449256209/">1449256209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449259433/">1449259433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449263848/">1449263848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449263968/">1449263968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449264808/">1449264808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449264869/">1449264869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449265289/">1449265289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449266129/">1449266129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449271346/">1449271346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449273566/">1449273566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449280890/">1449280890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449281071/">1449281071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449281206/">1449281206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449283587/">1449283587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449287190/">1449287190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449292047/">1449292047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449296971/">1449296971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449299607/">1449299607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449305008/">1449305008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449308371/">1449308371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449309151/">1449309151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449320910/">1449320910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449329910/">1449329910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449331961/">1449331961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449346591/">1449346591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449347371/">1449347371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449348031/">1449348031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449351268/">1449351268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449393691/">1449393691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449420691/">1449420691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449428549/">1449428549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449436170/">1449436170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449444871/">1449444871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449453690/">1449453690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449454588/">1449454588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449454770/">1449454770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449463950/">1449463950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449466235/">1449466235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449467490/">1449467490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449483690/">1449483690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449487470/">1449487470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449491610/">1449491610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449496295/">1449496295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449497252/">1449497252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449500723/">1449500723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449504810/">1449504810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449506139/">1449506139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449516031/">1449516031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449527847/">1449527847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449529647/">1449529647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449530122/">1449530122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449531626/">1449531626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449531805/">1449531805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449534206/">1449534206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449560242/">1449560242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449562105/">1449562105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449564684/">1449564684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449564744/">1449564744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449570385/">1449570385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449570505/">1449570505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449593961/">1449593961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449597342/">1449597342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449597942/">1449597942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449599422/">1449599422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449604062/">1449604062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449604662/">1449604662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449605443/">1449605443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449606520/">1449606520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449606700/">1449606700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449613004/">1449613004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449614261/">1449614261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449614982/">1449614982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449615161/">1449615161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449626601/">1449626601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630141/">1449630141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630163/">1449630163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630165/">1449630165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630362/">1449630362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630373/">1449630373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630374/">1449630374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630377/">1449630377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630452/">1449630452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630455/">1449630455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630538/">1449630538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630586/">1449630586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630608/">1449630608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630681/">1449630681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630697/">1449630697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449630857/">1449630857/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449635864/">1449635864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449654342/">1449654342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449654457/">1449654457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449656742/">1449656742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449660222/">1449660222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449660523/">1449660523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449668443/">1449668443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449673002/">1449673002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449673522/">1449673522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449674142/">1449674142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449676422/">1449676422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449676901/">1449676901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449677080/">1449677080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449677440/">1449677440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449678042/">1449678042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449678939/">1449678939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449679064/">1449679064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449679303/">1449679303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449680383/">1449680383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449684896/">1449684896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449685363/">1449685363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449695922/">1449695922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449696582/">1449696582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449697480/">1449697480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449697660/">1449697660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449700182/">1449700182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449700299/">1449700299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449700663/">1449700663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449701258/">1449701258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449703303/">1449703303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449714103/">1449714103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449715959/">1449715959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449719985/">1449719985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449737982/">1449737982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449738357/">1449738357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449738643/">1449738643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449742483/">1449742483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449747283/">1449747283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449750605/">1449750605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449752615/">1449752615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449761022/">1449761022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449761872/">1449761872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449763253/">1449763253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449766793/">1449766793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449767182/">1449767182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449767330/">1449767330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449767509/">1449767509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449767873/">1449767873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449771769/">1449771769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449772213/">1449772213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449772913/">1449772913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449773332/">1449773332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449774592/">1449774592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449775073/">1449775073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449779152/">1449779152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449779332/">1449779332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449779393/">1449779393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449779573/">1449779573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449780589/">1449780589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449780712/">1449780712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449782136/">1449782136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449782320/">1449782320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449782797/">1449782797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449783032/">1449783032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449783877/">1449783877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449784415/">1449784415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449788918/">1449788918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449793805/">1449793805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449797078/">1449797078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449802268/">1449802268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449804738/">1449804738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449814597/">1449814597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449815419/">1449815419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449818557/">1449818557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449821618/">1449821618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449821678/">1449821678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449821978/">1449821978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449822157/">1449822157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449822277/">1449822277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449824438/">1449824438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449824499/">1449824499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449826207/">1449826207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449830558/">1449830558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449837002/">1449837002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449845498/">1449845498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449847293/">1449847293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449847652/">1449847652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449847986/">1449847986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449848493/">1449848493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449850710/">1449850710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449852152/">1449852152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449852812/">1449852812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449853710/">1449853710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449855394/">1449855394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449857733/">1449857733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449858617/">1449858617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449858812/">1449858812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449861511/">1449861511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449862292/">1449862292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449863611/">1449863611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449869406/">1449869406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449898351/">1449898351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449901810/">1449901810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449930032/">1449930032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449934446/">1449934446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449946351/">1449946351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449947611/">1449947611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449948931/">1449948931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449954394/">1449954394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449955806/">1449955806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449968432/">1449968432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449976232/">1449976232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1449977511/">1449977511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450045471/">1450045471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450045651/">1450045651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450053002/">1450053002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450054711/">1450054711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450056092/">1450056092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450056751/">1450056751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450056811/">1450056811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450060651/">1450060651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450061241/">1450061241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450074993/">1450074993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450079851/">1450079851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450080201/">1450080201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450080512/">1450080512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450081532/">1450081532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450082132/">1450082132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450085403/">1450085403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450087055/">1450087055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450091251/">1450091251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450096212/">1450096212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450103371/">1450103371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450103731/">1450103731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450103853/">1450103853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450104751/">1450104751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450105764/">1450105764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450107217/">1450107217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450109209/">1450109209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450112013/">1450112013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450112611/">1450112611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450116332/">1450116332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450117471/">1450117471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450117808/">1450117808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450119332/">1450119332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450122151/">1450122151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450122513/">1450122513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450122744/">1450122744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450123165/">1450123165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450123464/">1450123464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450123824/">1450123824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450125866/">1450125866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450126464/">1450126464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450128667/">1450128667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450128805/">1450128805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450129285/">1450129285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450129945/">1450129945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450130061/">1450130061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450132232/">1450132232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450133121/">1450133121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450133305/">1450133305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450134741/">1450134741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450137140/">1450137140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450137556/">1450137556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450137684/">1450137684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450138391/">1450138391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450138509/">1450138509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450139430/">1450139430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450144211/">1450144211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450144329/">1450144329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450150364/">1450150364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450150931/">1450150931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450151710/">1450151710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450153331/">1450153331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450157650/">1450157650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450160590/">1450160590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450173807/">1450173807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450182606/">1450182606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450186691/">1450186691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450187591/">1450187591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450187830/">1450187830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450188790/">1450188790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450188850/">1450188850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450189090/">1450189090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450190848/">1450190848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450191030/">1450191030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450192109/">1450192109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450192227/">1450192227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450195852/">1450195852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450201769/">1450201769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450204595/">1450204595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450205309/">1450205309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450205369/">1450205369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450207409/">1450207409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450214913/">1450214913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450217998/">1450217998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450218298/">1450218298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450218477/">1450218477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450219377/">1450219377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450219858/">1450219858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450220875/">1450220875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450221115/">1450221115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450223638/">1450223638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450223998/">1450223998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450224968/">1450224968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450226709/">1450226709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450228746/">1450228746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450230366/">1450230366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450230846/">1450230846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450236486/">1450236486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450239846/">1450239846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450245486/">1450245486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450254546/">1450254546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450256608/">1450256608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450257426/">1450257426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450259766/">1450259766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450259948/">1450259948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450260967/">1450260967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450262706/">1450262706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450263246/">1450263246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450263846/">1450263846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450263847/">1450263847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450264624/">1450264624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450264748/">1450264748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450265346/">1450265346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450266966/">1450266966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450269484/">1450269484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450270999/">1450270999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450273394/">1450273394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450276266/">1450276266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450276567/">1450276567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450278919/">1450278919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450280646/">1450280646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450282000/">1450282000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450282117/">1450282117/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450282539/">1450282539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450286917/">1450286917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450287877/">1450287877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450298460/">1450298460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450298519/">1450298519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450298699/">1450298699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450298939/">1450298939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450298940/">1450298940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450299120/">1450299120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450299360/">1450299360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450300260/">1450300260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450300319/">1450300319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450300619/">1450300619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450301160/">1450301160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450301698/">1450301698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450301939/">1450301939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450302360/">1450302360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450302420/">1450302420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450303258/">1450303258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450304159/">1450304159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450305360/">1450305360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450306140/">1450306140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450306261/">1450306261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450306620/">1450306620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450306739/">1450306739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450307017/">1450307017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450310039/">1450310039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450310339/">1450310339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450311360/">1450311360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450312379/">1450312379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450313156/">1450313156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450314237/">1450314237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450317153/">1450317153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450317995/">1450317995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450320932/">1450320932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450321952/">1450321952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450322072/">1450322072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450322193/">1450322193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450322312/">1450322312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450323149/">1450323149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450324712/">1450324712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450328192/">1450328192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450337431/">1450337431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450337911/">1450337911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450338272/">1450338272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450338392/">1450338392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450338632/">1450338632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450339433/">1450339433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450344572/">1450344572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450346312/">1450346312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450350271/">1450350271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450353690/">1450353690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450358070/">1450358070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450364806/">1450364806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450365150/">1450365150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450366831/">1450366831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450369590/">1450369590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450370012/">1450370012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450370248/">1450370248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450370435/">1450370435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450373731/">1450373731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450373911/">1450373911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450377952/">1450377952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450379752/">1450379752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450380113/">1450380113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450381618/">1450381618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450381972/">1450381972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450383235/">1450383235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450383355/">1450383355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450386413/">1450386413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450387912/">1450387912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450390791/">1450390791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450390792/">1450390792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450391519/">1450391519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450391631/">1450391631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450392175/">1450392175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450393672/">1450393672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450397933/">1450397933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450399191/">1450399191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450400617/">1450400617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450404053/">1450404053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450404218/">1450404218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450405552/">1450405552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450408974/">1450408974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450410368/">1450410368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450410711/">1450410711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450421752/">1450421752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450421876/">1450421876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450426132/">1450426132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450426312/">1450426312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450427272/">1450427272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450428292/">1450428292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450429132/">1450429132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450429852/">1450429852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450433636/">1450433636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450434591/">1450434591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450440833/">1450440833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450443833/">1450443833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450445571/">1450445571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450447311/">1450447311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450448812/">1450448812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450448936/">1450448936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450450194/">1450450194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450451874/">1450451874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450452358/">1450452358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450454391/">1450454391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450454769/">1450454769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450455472/">1450455472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450456374/">1450456374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450457516/">1450457516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450457875/">1450457875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450457991/">1450457991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450464330/">1450464330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450464510/">1450464510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450465054/">1450465054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450465470/">1450465470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450466250/">1450466250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450467693/">1450467693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450468350/">1450468350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450469191/">1450469191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450471470/">1450471470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450473570/">1450473570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450473810/">1450473810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450475250/">1450475250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450476351/">1450476351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450476810/">1450476810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450477650/">1450477650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450478610/">1450478610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450480412/">1450480412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450481190/">1450481190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450481609/">1450481609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450487850/">1450487850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450488030/">1450488030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450489231/">1450489231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450495470/">1450495470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450536090/">1450536090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450559850/">1450559850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450572990/">1450572990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450622970/">1450622970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450665509/">1450665509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450674689/">1450674689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450685429/">1450685429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450687287/">1450687287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450688489/">1450688489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450690109/">1450690109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450690289/">1450690289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450690409/">1450690409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450690949/">1450690949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450691129/">1450691129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450692629/">1450692629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450693829/">1450693829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450694370/">1450694370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450695510/">1450695510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450697126/">1450697126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450699765/">1450699765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450708648/">1450708648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450708769/">1450708769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450710871/">1450710871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450711290/">1450711290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450711348/">1450711348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450715069/">1450715069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450716689/">1450716689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450718606/">1450718606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450721486/">1450721486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450721546/">1450721546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450722509/">1450722509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450723589/">1450723589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450724549/">1450724549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450727429/">1450727429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450729769/">1450729769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450730609/">1450730609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450732271/">1450732271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450732512/">1450732512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450733121/">1450733121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450733589/">1450733589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450734550/">1450734550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450737224/">1450737224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450737284/">1450737284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450737645/">1450737645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450737823/">1450737823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450738631/">1450738631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450738688/">1450738688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450743738/">1450743738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450744398/">1450744398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450744518/">1450744518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450745655/">1450745655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450745898/">1450745898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450746557/">1450746557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450748898/">1450748898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450753567/">1450753567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450753938/">1450753938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450760898/">1450760898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450763959/">1450763959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450765278/">1450765278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450766178/">1450766178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450766958/">1450766958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450767378/">1450767378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450767678/">1450767678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450773737/">1450773737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450776378/">1450776378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450779136/">1450779136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450781301/">1450781301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450784839/">1450784839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450786636/">1450786636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450789938/">1450789938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450790599/">1450790599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450791258/">1450791258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450792699/">1450792699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450793727/">1450793727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450793787/">1450793787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450795347/">1450795347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450797327/">1450797327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450798320/">1450798320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450800928/">1450800928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450801647/">1450801647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450802127/">1450802127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450802309/">1450802309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450803208/">1450803208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450804107/">1450804107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450805126/">1450805126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450808847/">1450808847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450809016/">1450809016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450814247/">1450814247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450814738/">1450814738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450817067/">1450817067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450818214/">1450818214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450819527/">1450819527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450820067/">1450820067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450821267/">1450821267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450821867/">1450821867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450822646/">1450822646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450826667/">1450826667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450827267/">1450827267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450830613/">1450830613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450831527/">1450831527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450832733/">1450832733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450836388/">1450836388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450837887/">1450837887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450838307/">1450838307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450843947/">1450843947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450846827/">1450846827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450852212/">1450852212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450853368/">1450853368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450855288/">1450855288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450860627/">1450860627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450863005/">1450863005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450863507/">1450863507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450867048/">1450867048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450871308/">1450871308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450873803/">1450873803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450879887/">1450879887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450880428/">1450880428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450881867/">1450881867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450882286/">1450882286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450882527/">1450882527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450882955/">1450882955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450883271/">1450883271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450886490/">1450886490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450886804/">1450886804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450887869/">1450887869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450888046/">1450888046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450889732/">1450889732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450889909/">1450889909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450892372/">1450892372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450895450/">1450895450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450897769/">1450897769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450900651/">1450900651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450901129/">1450901129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450901549/">1450901549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450902089/">1450902089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450902628/">1450902628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450903949/">1450903949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450904430/">1450904430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450904743/">1450904743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450904909/">1450904909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450906215/">1450906215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450906587/">1450906587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450907112/">1450907112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450908371/">1450908371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450908433/">1450908433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450914432/">1450914432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450916772/">1450916772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450917032/">1450917032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450918392/">1450918392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450918941/">1450918941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450922538/">1450922538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450923851/">1450923851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450925712/">1450925712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450926491/">1450926491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450927903/">1450927903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450928111/">1450928111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450928173/">1450928173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450930451/">1450930451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450933151/">1450933151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450933812/">1450933812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450937291/">1450937291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450938613/">1450938613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450949403/">1450949403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450953191/">1450953191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450956990/">1450956990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450960204/">1450960204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450975427/">1450975427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450978847/">1450978847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450979207/">1450979207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450979807/">1450979807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450981127/">1450981127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450981832/">1450981832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450984065/">1450984065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450984547/">1450984547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450984847/">1450984847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450986287/">1450986287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450991387/">1450991387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1450993727/">1450993727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451003407/">1451003407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451011667/">1451011667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451014304/">1451014304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451082167/">1451082167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451089804/">1451089804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451094708/">1451094708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451128247/">1451128247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451133004/">1451133004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451155367/">1451155367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451165405/">1451165405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451172047/">1451172047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451176547/">1451176547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451187131/">1451187131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451212967/">1451212967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451219425/">1451219425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451229107/">1451229107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451229707/">1451229707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451230127/">1451230127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451230479/">1451230479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451245427/">1451245427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451251787/">1451251787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451251805/">1451251805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451253107/">1451253107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451255627/">1451255627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451260547/">1451260547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451295887/">1451295887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451296067/">1451296067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451297267/">1451297267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451300867/">1451300867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451304767/">1451304767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451305804/">1451305804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451311392/">1451311392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451315406/">1451315406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451316527/">1451316527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451328452/">1451328452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451332352/">1451332352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451333242/">1451333242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451338101/">1451338101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451342375/">1451342375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451342558/">1451342558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451342978/">1451342978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451343910/">1451343910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451345558/">1451345558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451346758/">1451346758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451349038/">1451349038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451349576/">1451349576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451349644/">1451349644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451349758/">1451349758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451354258/">1451354258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451354377/">1451354377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451357378/">1451357378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451364878/">1451364878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451377478/">1451377478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451378200/">1451378200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451380178/">1451380178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451380238/">1451380238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451380358/">1451380358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451382163/">1451382163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451383178/">1451383178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451386538/">1451386538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451387678/">1451387678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451388038/">1451388038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451389298/">1451389298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451390800/">1451390800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451395778/">1451395778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451397638/">1451397638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451397910/">1451397910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451399078/">1451399078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451399738/">1451399738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451400515/">1451400515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451403338/">1451403338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451403997/">1451403997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451404176/">1451404176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451405199/">1451405199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451407422/">1451407422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451412218/">1451412218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451412219/">1451412219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451412641/">1451412641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451412815/">1451412815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451414630/">1451414630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451421399/">1451421399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451421818/">1451421818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451424038/">1451424038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451428298/">1451428298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451429078/">1451429078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451431238/">1451431238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451432976/">1451432976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451435498/">1451435498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451445460/">1451445460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451446418/">1451446418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451454880/">1451454880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451463038/">1451463038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451465678/">1451465678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451469338/">1451469338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451469398/">1451469398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451471918/">1451471918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451474199/">1451474199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451476598/">1451476598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451478218/">1451478218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451478698/">1451478698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451482478/">1451482478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451484095/">1451484095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451484998/">1451484998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451495918/">1451495918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451497418/">1451497418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451500778/">1451500778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451501798/">1451501798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451502098/">1451502098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451503298/">1451503298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451505964/">1451505964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451506718/">1451506718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451506837/">1451506837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451507678/">1451507678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451510136/">1451510136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451511338/">1451511338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451511818/">1451511818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451513125/">1451513125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451513319/">1451513319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451514757/">1451514757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451515718/">1451515718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451515775/">1451515775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451515958/">1451515958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451516815/">1451516815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451517098/">1451517098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451517518/">1451517518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451518476/">1451518476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451519018/">1451519018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451519498/">1451519498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451519738/">1451519738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451521118/">1451521118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451521238/">1451521238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451521779/">1451521779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451522138/">1451522138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451526158/">1451526158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451528499/">1451528499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451531558/">1451531558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451532578/">1451532578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451533958/">1451533958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451536658/">1451536658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451538098/">1451538098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451540797/">1451540797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451547518/">1451547518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451549618/">1451549618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451557959/">1451557959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451565173/">1451565173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451565234/">1451565234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451565237/">1451565237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451565240/">1451565240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451565243/">1451565243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451565246/">1451565246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451568278/">1451568278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451569718/">1451569718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451585378/">1451585378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451587239/">1451587239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451590418/">1451590418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451591440/">1451591440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451594378/">1451594378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451597918/">1451597918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451598458/">1451598458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451601998/">1451601998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451602298/">1451602298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451604278/">1451604278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451604758/">1451604758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451606018/">1451606018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451606594/">1451606594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451606799/">1451606799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451607219/">1451607219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451617718/">1451617718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451642196/">1451642196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451645499/">1451645499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451657018/">1451657018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451665720/">1451665720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451681018/">1451681018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451686718/">1451686718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451694700/">1451694700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451699018/">1451699018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451786198/">1451786198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451791118/">1451791118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451858618/">1451858618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451858918/">1451858918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451859638/">1451859638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451859998/">1451859998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451863538/">1451863538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451864856/">1451864856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451872598/">1451872598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451876316/">1451876316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451877578/">1451877578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451887478/">1451887478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451887718/">1451887718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451894582/">1451894582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451894703/">1451894703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451894856/">1451894856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451895098/">1451895098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451895218/">1451895218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451895398/">1451895398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451908776/">1451908776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451910605/">1451910605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451911418/">1451911418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451911838/">1451911838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451915498/">1451915498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451918378/">1451918378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451919218/">1451919218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451920835/">1451920835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451921589/">1451921589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451923010/">1451923010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451929598/">1451929598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451932217/">1451932217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451932479/">1451932479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451934211/">1451934211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451934331/">1451934331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451934570/">1451934570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451935231/">1451935231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451936129/">1451936129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451936791/">1451936791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451937090/">1451937090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451937448/">1451937448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451937810/">1451937810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451940028/">1451940028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451941950/">1451941950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451942071/">1451942071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451943040/">1451943040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451943051/">1451943051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451943450/">1451943450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451943873/">1451943873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451944291/">1451944291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451945111/">1451945111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451945610/">1451945610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451945852/">1451945852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451946570/">1451946570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451947291/">1451947291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451947708/">1451947708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451948479/">1451948479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451949019/">1451949019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451949978/">1451949978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451953538/">1451953538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451953826/">1451953826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451954860/">1451954860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451954981/">1451954981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451955460/">1451955460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451956360/">1451956360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451959396/">1451959396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451962903/">1451962903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451963861/">1451963861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451964521/">1451964521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451964686/">1451964686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451966389/">1451966389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451966440/">1451966440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451966741/">1451966741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451973396/">1451973396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451978138/">1451978138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451985340/">1451985340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451986228/">1451986228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451986840/">1451986840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451987141/">1451987141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451987559/">1451987559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451988100/">1451988100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451988581/">1451988581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451991802/">1451991802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451991924/">1451991924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451992901/">1451992901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451997029/">1451997029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451998300/">1451998300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1451999108/">1451999108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452003581/">1452003581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452005377/">1452005377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452007004/">1452007004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452008007/">1452008007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452008010/">1452008010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452008445/">1452008445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452010120/">1452010120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452010419/">1452010419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452012041/">1452012041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452012697/">1452012697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452012764/">1452012764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452012881/">1452012881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452013964/">1452013964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452014261/">1452014261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452014560/">1452014560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452014750/">1452014750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452017849/">1452017849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452018651/">1452018651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452021210/">1452021210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452021270/">1452021270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452024149/">1452024149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452024317/">1452024317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452024332/">1452024332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452024388/">1452024388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452025501/">1452025501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452025561/">1452025561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452025683/">1452025683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452026161/">1452026161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452026405/">1452026405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452027300/">1452027300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452028052/">1452028052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452028647/">1452028647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452029310/">1452029310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452029967/">1452029967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452032852/">1452032852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452035731/">1452035731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452040232/">1452040232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452041406/">1452041406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452041588/">1452041588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452045367/">1452045367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452045609/">1452045609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452045745/">1452045745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452045814/">1452045814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452045928/">1452045928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452046148/">1452046148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452047168/">1452047168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452047588/">1452047588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452048128/">1452048128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452048186/">1452048186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452048904/">1452048904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452049148/">1452049148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452049350/">1452049350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452051084/">1452051084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452053015/">1452053015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452053348/">1452053348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452055267/">1452055267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452055509/">1452055509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452055868/">1452055868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452056944/">1452056944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452060010/">1452060010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452060667/">1452060667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452062285/">1452062285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452065468/">1452065468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452069247/">1452069247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452069429/">1452069429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452072643/">1452072643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452074605/">1452074605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452075067/">1452075067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452082147/">1452082147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452082208/">1452082208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452085027/">1452085027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452088447/">1452088447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452089647/">1452089647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452091148/">1452091148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452091813/">1452091813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452091870/">1452091870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452092168/">1452092168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452094407/">1452094407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452094866/">1452094866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452095064/">1452095064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452097211/">1452097211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452099184/">1452099184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452099366/">1452099366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452100035/">1452100035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452100165/">1452100165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452104226/">1452104226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452104646/">1452104646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452104708/">1452104708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452107647/">1452107647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452108968/">1452108968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452109987/">1452109987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452110227/">1452110227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452110644/">1452110644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452110768/">1452110768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452111365/">1452111365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452111606/">1452111606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452113887/">1452113887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452114205/">1452114205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452114546/">1452114546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452115149/">1452115149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452115812/">1452115812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452116288/">1452116288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452117808/">1452117808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452118987/">1452118987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452119227/">1452119227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452119527/">1452119527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452119947/">1452119947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452121808/">1452121808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452121866/">1452121866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452123187/">1452123187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452123368/">1452123368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452123727/">1452123727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452124746/">1452124746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452126007/">1452126007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452126609/">1452126609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452127024/">1452127024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452130146/">1452130146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452131827/">1452131827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452135124/">1452135124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452136268/">1452136268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452136508/">1452136508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452137530/">1452137530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452137534/">1452137534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452142507/">1452142507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452143167/">1452143167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452143407/">1452143407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452143767/">1452143767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452146106/">1452146106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452147730/">1452147730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452148362/">1452148362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452150550/">1452150550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452151627/">1452151627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452153188/">1452153188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452158467/">1452158467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452159027/">1452159027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452159424/">1452159424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452160507/">1452160507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452161887/">1452161887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452165607/">1452165607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452165670/">1452165670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452167885/">1452167885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452169747/">1452169747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452169804/">1452169804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452170107/">1452170107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452171668/">1452171668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452172508/">1452172508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452173764/">1452173764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452176708/">1452176708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452180798/">1452180798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452181227/">1452181227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452182067/">1452182067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452182127/">1452182127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452182365/">1452182365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452182545/">1452182545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452184302/">1452184302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452184707/">1452184707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452185365/">1452185365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452188005/">1452188005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452189266/">1452189266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452189745/">1452189745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452190528/">1452190528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452190707/">1452190707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452190885/">1452190885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452191479/">1452191479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452191496/">1452191496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452192387/">1452192387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452194247/">1452194247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452194727/">1452194727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452195088/">1452195088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452197788/">1452197788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452198626/">1452198626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452199050/">1452199050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452202227/">1452202227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452204505/">1452204505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452205046/">1452205046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452207805/">1452207805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452209873/">1452209873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452210591/">1452210591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452211548/">1452211548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452212931/">1452212931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452215812/">1452215812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452215872/">1452215872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452218091/">1452218091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452219291/">1452219291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452221031/">1452221031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452223984/">1452223984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452224091/">1452224091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452224811/">1452224811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225231/">1452225231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225516/">1452225516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225568/">1452225568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225823/">1452225823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225832/">1452225832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225833/">1452225833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225846/">1452225846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452225990/">1452225990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452226194/">1452226194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452226195/">1452226195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452226272/">1452226272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452226286/">1452226286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452226772/">1452226772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452226860/">1452226860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227001/">1452227001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227134/">1452227134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227136/">1452227136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227138/">1452227138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227141/">1452227141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227143/">1452227143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227144/">1452227144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227145/">1452227145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227188/">1452227188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227255/">1452227255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227363/">1452227363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227391/">1452227391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227433/">1452227433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227652/">1452227652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227767/">1452227767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452227769/">1452227769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452228007/">1452228007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452228028/">1452228028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452228158/">1452228158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452228159/">1452228159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452228161/">1452228161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452228164/">1452228164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452229131/">1452229131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452231113/">1452231113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452234616/">1452234616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452234831/">1452234831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452239691/">1452239691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452241011/">1452241011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452242031/">1452242031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452242271/">1452242271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452245391/">1452245391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452245407/">1452245407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452247070/">1452247070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452247554/">1452247554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452248271/">1452248271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452249111/">1452249111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452256214/">1452256214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452257812/">1452257812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452263035/">1452263035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452264353/">1452264353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452266451/">1452266451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452267293/">1452267293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452269331/">1452269331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452269514/">1452269514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452269750/">1452269750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452272620/">1452272620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452277891/">1452277891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452279152/">1452279152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452279213/">1452279213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452280653/">1452280653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452280712/">1452280712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452280889/">1452280889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452284012/">1452284012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452285333/">1452285333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452285873/">1452285873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452286174/">1452286174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452287070/">1452287070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452288091/">1452288091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452288619/">1452288619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452289173/">1452289173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452290732/">1452290732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452292293/">1452292293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452299407/">1452299407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452307713/">1452307713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452310772/">1452310772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311234/">1452311234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311238/">1452311238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311266/">1452311266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311269/">1452311269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311270/">1452311270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311273/">1452311273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311274/">1452311274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311276/">1452311276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311280/">1452311280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311435/">1452311435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311437/">1452311437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311438/">1452311438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311440/">1452311440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311442/">1452311442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311445/">1452311445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311446/">1452311446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311471/">1452311471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311481/">1452311481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311596/">1452311596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311601/">1452311601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311602/">1452311602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311605/">1452311605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311636/">1452311636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311726/">1452311726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311752/">1452311752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311772/">1452311772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311775/">1452311775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311817/">1452311817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311834/">1452311834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311838/">1452311838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311857/">1452311857/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452311922/">1452311922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312016/">1452312016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312036/">1452312036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312125/">1452312125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312131/">1452312131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312137/">1452312137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312196/">1452312196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312302/">1452312302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312303/">1452312303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312304/">1452312304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312305/">1452312305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312309/">1452312309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312312/">1452312312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312588/">1452312588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312596/">1452312596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312602/">1452312602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312605/">1452312605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312608/">1452312608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312614/">1452312614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312650/">1452312650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312672/">1452312672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312674/">1452312674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312800/">1452312800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312885/">1452312885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312952/">1452312952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312972/">1452312972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312979/">1452312979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312982/">1452312982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312985/">1452312985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452312986/">1452312986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313183/">1452313183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313283/">1452313283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313365/">1452313365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313370/">1452313370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313409/">1452313409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313413/">1452313413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313511/">1452313511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313529/">1452313529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313771/">1452313771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313779/">1452313779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313824/">1452313824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313835/">1452313835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313855/">1452313855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313943/">1452313943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313946/">1452313946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313948/">1452313948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452313966/">1452313966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452314013/">1452314013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452314050/">1452314050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452314058/">1452314058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452314063/">1452314063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452319413/">1452319413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452320792/">1452320792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452321046/">1452321046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452326630/">1452326630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452338012/">1452338012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452342606/">1452342606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452369393/">1452369393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452369634/">1452369634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452372932/">1452372932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452378754/">1452378754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452380852/">1452380852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452382893/">1452382893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452383972/">1452383972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452385811/">1452385811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452401253/">1452401253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452403593/">1452403593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452434613/">1452434613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452435212/">1452435212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452439962/">1452439962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452462993/">1452462993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452463233/">1452463233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452465632/">1452465632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452468512/">1452468512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452469352/">1452469352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452469772/">1452469772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452472207/">1452472207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452478660/">1452478660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452480634/">1452480634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452483321/">1452483321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452486896/">1452486896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452489569/">1452489569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452492232/">1452492232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452493652/">1452493652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452493834/">1452493834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452496353/">1452496353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452500794/">1452500794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452502233/">1452502233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452504607/">1452504607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452507093/">1452507093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452509913/">1452509913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452511052/">1452511052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452511173/">1452511173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452515406/">1452515406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452515612/">1452515612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452521133/">1452521133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452521613/">1452521613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452524179/">1452524179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452525330/">1452525330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452525447/">1452525447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452526463/">1452526463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452526465/">1452526465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452529524/">1452529524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452532529/">1452532529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452534928/">1452534928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452536068/">1452536068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452536548/">1452536548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452537079/">1452537079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452537394/">1452537394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452537690/">1452537690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452537994/">1452537994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452538048/">1452538048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452538468/">1452538468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452540092/">1452540092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452542496/">1452542496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452543509/">1452543509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452544412/">1452544412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452545250/">1452545250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452545852/">1452545852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452547712/">1452547712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452547819/">1452547819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452547829/">1452547829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452549452/">1452549452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452550114/">1452550114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452550228/">1452550228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452550892/">1452550892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452556877/">1452556877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452557180/">1452557180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452557241/">1452557241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452557299/">1452557299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452557359/">1452557359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452558440/">1452558440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452558664/">1452558664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452560360/">1452560360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452561502/">1452561502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452563899/">1452563899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452564619/">1452564619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452566121/">1452566121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452568557/">1452568557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452568675/">1452568675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452568793/">1452568793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452569573/">1452569573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452571559/">1452571559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452572093/">1452572093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452572395/">1452572395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452575335/">1452575335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452577974/">1452577974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452579355/">1452579355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452579536/">1452579536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452580214/">1452580214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452582955/">1452582955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452583316/">1452583316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452585056/">1452585056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452585175/">1452585175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452585295/">1452585295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452585475/">1452585475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452588474/">1452588474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452589315/">1452589315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452590456/">1452590456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452591534/">1452591534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452592555/">1452592555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452594295/">1452594295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452595491/">1452595491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452596217/">1452596217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452597115/">1452597115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452597896/">1452597896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452598974/">1452598974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452601736/">1452601736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452601814/">1452601814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452604916/">1452604916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452605215/">1452605215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452607546/">1452607546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452608146/">1452608146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452608387/">1452608387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452609045/">1452609045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452610183/">1452610183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452612977/">1452612977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617026/">1452617026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617146/">1452617146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617276/">1452617276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617332/">1452617332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617333/">1452617333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617385/">1452617385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617445/">1452617445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452617684/">1452617684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452618235/">1452618235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452618405/">1452618405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452618465/">1452618465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452618587/">1452618587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452619125/">1452619125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452619246/">1452619246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452619845/">1452619845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452619965/">1452619965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452620084/">1452620084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452621465/">1452621465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452622653/">1452622653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452623070/">1452623070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452623498/">1452623498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452625596/">1452625596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452625709/">1452625709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452625832/">1452625832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452626130/">1452626130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452626370/">1452626370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452629730/">1452629730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452630390/">1452630390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452630451/">1452630451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452632745/">1452632745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452633751/">1452633751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452634214/">1452634214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452634349/">1452634349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452634473/">1452634473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452634651/">1452634651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452635070/">1452635070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452636031/">1452636031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452637650/">1452637650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452637711/">1452637711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452638130/">1452638130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452638910/">1452638910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452639391/">1452639391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452639750/">1452639750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452641373/">1452641373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452641491/">1452641491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452641969/">1452641969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452642031/">1452642031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452643520/">1452643520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452643531/">1452643531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452643532/">1452643532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452644311/">1452644311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452645008/">1452645008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452645930/">1452645930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452646291/">1452646291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452646532/">1452646532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452647193/">1452647193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452647430/">1452647430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452647491/">1452647491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452649831/">1452649831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452650310/">1452650310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452651690/">1452651690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452653791/">1452653791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452656730/">1452656730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452659970/">1452659970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452666708/">1452666708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452669210/">1452669210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452674611/">1452674611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452676410/">1452676410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452676950/">1452676950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452677251/">1452677251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452677314/">1452677314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452677451/">1452677451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452680311/">1452680311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452680850/">1452680850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452682771/">1452682771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452684510/">1452684510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452686070/">1452686070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452688221/">1452688221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452690090/">1452690090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452690991/">1452690991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452693921/">1452693921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452696454/">1452696454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452697172/">1452697172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452699113/">1452699113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452699451/">1452699451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452699990/">1452699990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452700833/">1452700833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452704911/">1452704911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452705930/">1452705930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452706231/">1452706231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452709531/">1452709531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452709875/">1452709875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452711511/">1452711511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452711631/">1452711631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452712291/">1452712291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452712891/">1452712891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452714031/">1452714031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452714271/">1452714271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452714454/">1452714454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452715292/">1452715292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452716311/">1452716311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452717930/">1452717930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452717991/">1452717991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452718051/">1452718051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452718232/">1452718232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452718711/">1452718711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452718831/">1452718831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452719072/">1452719072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452719970/">1452719970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452720626/">1452720626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452722970/">1452722970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452724471/">1452724471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452726223/">1452726223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452726870/">1452726870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452727050/">1452727050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452728551/">1452728551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452728611/">1452728611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452728791/">1452728791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452729093/">1452729093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452730535/">1452730535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452731370/">1452731370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452732752/">1452732752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452734792/">1452734792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452734912/">1452734912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452735271/">1452735271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452738935/">1452738935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452739469/">1452739469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452740672/">1452740672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452742482/">1452742482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452742778/">1452742778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452744092/">1452744092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452745030/">1452745030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452746796/">1452746796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452748834/">1452748834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452750751/">1452750751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452751231/">1452751231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452751599/">1452751599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452753511/">1452753511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452754833/">1452754833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452755253/">1452755253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452755371/">1452755371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452755611/">1452755611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452756992/">1452756992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452758848/">1452758848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452761071/">1452761071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452762991/">1452762991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452763111/">1452763111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452763233/">1452763233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452763651/">1452763651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452763769/">1452763769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452763841/">1452763841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452764071/">1452764071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452764732/">1452764732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452768391/">1452768391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452769112/">1452769112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452770191/">1452770191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452774618/">1452774618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452774930/">1452774930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452777272/">1452777272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452780150/">1452780150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452780931/">1452780931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452781111/">1452781111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452781352/">1452781352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452781471/">1452781471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452781530/">1452781530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452781951/">1452781951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452785653/">1452785653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452787591/">1452787591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452788006/">1452788006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452788009/">1452788009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452788013/">1452788013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452788016/">1452788016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452789718/">1452789718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452793498/">1452793498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452794936/">1452794936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452796281/">1452796281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452796796/">1452796796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452797036/">1452797036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452797396/">1452797396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452798718/">1452798718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452799436/">1452799436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452800456/">1452800456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452800996/">1452800996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452801056/">1452801056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452803817/">1452803817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452803876/">1452803876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452803936/">1452803936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452804956/">1452804956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452805437/">1452805437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452806457/">1452806457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452807012/">1452807012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452807116/">1452807116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452807956/">1452807956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452808017/">1452808017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452808617/">1452808617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452809276/">1452809276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452809576/">1452809576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452811494/">1452811494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452811619/">1452811619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452812217/">1452812217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452812516/">1452812516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452812635/">1452812635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452813837/">1452813837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452817853/">1452817853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452818759/">1452818759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452820902/">1452820902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452825462/">1452825462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452826782/">1452826782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452828697/">1452828697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452839201/">1452839201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452842022/">1452842022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452842200/">1452842200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452843341/">1452843341/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452844962/">1452844962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452845441/">1452845441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452846401/">1452846401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452846821/">1452846821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452847601/">1452847601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452848621/">1452848621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452850231/">1452850231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452851322/">1452851322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452853721/">1452853721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452854985/">1452854985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452861581/">1452861581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452866381/">1452866381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452868000/">1452868000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452870502/">1452870502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452872060/">1452872060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452872062/">1452872062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452872451/">1452872451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452874903/">1452874903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452875564/">1452875564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452876821/">1452876821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452876941/">1452876941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452878730/">1452878730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452879561/">1452879561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452880342/">1452880342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452881145/">1452881145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452881482/">1452881482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452881601/">1452881601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452881722/">1452881722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452881901/">1452881901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452882084/">1452882084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452882659/">1452882659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452882676/">1452882676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452882867/">1452882867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452883044/">1452883044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452883163/">1452883163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452884784/">1452884784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452886101/">1452886101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452887962/">1452887962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452888021/">1452888021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452888562/">1452888562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452888865/">1452888865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452889161/">1452889161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452891142/">1452891142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452891204/">1452891204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452892102/">1452892102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452892881/">1452892881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452893122/">1452893122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452896001/">1452896001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452896481/">1452896481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452899663/">1452899663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452902121/">1452902121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452902181/">1452902181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452904161/">1452904161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452904209/">1452904209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452905722/">1452905722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452908001/">1452908001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452915169/">1452915169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452918441/">1452918441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452918781/">1452918781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452926962/">1452926962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452927321/">1452927321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452927621/">1452927621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452936637/">1452936637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452938546/">1452938546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452947410/">1452947410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452961101/">1452961101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452969084/">1452969084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452982461/">1452982461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452983602/">1452983602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452984260/">1452984260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1452996861/">1452996861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453000021/">1453000021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453001784/">1453001784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453050211/">1453050211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453051401/">1453051401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453053321/">1453053321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453055490/">1453055490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453055601/">1453055601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453056322/">1453056322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453058381/">1453058381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453061242/">1453061242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453066220/">1453066220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453068147/">1453068147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453071021/">1453071021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453077001/">1453077001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453079061/">1453079061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453086025/">1453086025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453086981/">1453086981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453087221/">1453087221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453087893/">1453087893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453092082/">1453092082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453094840/">1453094840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453098141/">1453098141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453102042/">1453102042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453102161/">1453102161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453103724/">1453103724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453105486/">1453105486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453108161/">1453108161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453108401/">1453108401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453109842/">1453109842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453115063/">1453115063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453117162/">1453117162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453119202/">1453119202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453120205/">1453120205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453123462/">1453123462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453125083/">1453125083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453133791/">1453133791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453133910/">1453133910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453137270/">1453137270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453138050/">1453138050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453138172/">1453138172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453138533/">1453138533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453138592/">1453138592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453139611/">1453139611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453140271/">1453140271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453141865/">1453141865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453141951/">1453141951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453142188/">1453142188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453147471/">1453147471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453148675/">1453148675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453148732/">1453148732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453152614/">1453152614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453153230/">1453153230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453155331/">1453155331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453157013/">1453157013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453157196/">1453157196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453157255/">1453157255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453158396/">1453158396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453158635/">1453158635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453160257/">1453160257/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453167276/">1453167276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453171656/">1453171656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453179216/">1453179216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453180834/">1453180834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453182517/">1453182517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453185222/">1453185222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453185399/">1453185399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453189657/">1453189657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453190136/">1453190136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453190378/">1453190378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453191876/">1453191876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453193376/">1453193376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453193436/">1453193436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453198427/">1453198427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453199442/">1453199442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453201536/">1453201536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453205135/">1453205135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453207956/">1453207956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453212696/">1453212696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453212756/">1453212756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453213835/">1453213835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453213896/">1453213896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453214136/">1453214136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453216372/">1453216372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453216906/">1453216906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453217815/">1453217815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453219437/">1453219437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453220711/">1453220711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453221872/">1453221872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453223792/">1453223792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453225055/">1453225055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453225892/">1453225892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453226553/">1453226553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453226672/">1453226672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453228172/">1453228172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453228535/">1453228535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453230250/">1453230250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453230813/">1453230813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453230932/">1453230932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453231294/">1453231294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453232256/">1453232256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453232676/">1453232676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453235496/">1453235496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453236577/">1453236577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453237595/">1453237595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453239576/">1453239576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453240476/">1453240476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453240536/">1453240536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453240655/">1453240655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453242152/">1453242152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453242632/">1453242632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453243237/">1453243237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453243595/">1453243595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453243895/">1453243895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453244072/">1453244072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453244195/">1453244195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453244255/">1453244255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453244315/">1453244315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453244737/">1453244737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453247297/">1453247297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453248377/">1453248377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453248438/">1453248438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453248439/">1453248439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453249757/">1453249757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453250057/">1453250057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453251742/">1453251742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453251977/">1453251977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453252277/">1453252277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453256897/">1453256897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261349/">1453261349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261425/">1453261425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261428/">1453261428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261584/">1453261584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261586/">1453261586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261620/">1453261620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453261921/">1453261921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262008/">1453262008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262011/">1453262011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262013/">1453262013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262157/">1453262157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262159/">1453262159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262162/">1453262162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262165/">1453262165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262179/">1453262179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262195/">1453262195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262245/">1453262245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262249/">1453262249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262251/">1453262251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262252/">1453262252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262365/">1453262365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262443/">1453262443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262444/">1453262444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262447/">1453262447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262463/">1453262463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262680/">1453262680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262865/">1453262865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262879/">1453262879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453262936/">1453262936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263144/">1453263144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263425/">1453263425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263495/">1453263495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263497/">1453263497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263591/">1453263591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263695/">1453263695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263702/">1453263702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263705/">1453263705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263706/">1453263706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263771/">1453263771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263783/">1453263783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453263832/">1453263832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264024/">1453264024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264047/">1453264047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264222/">1453264222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264486/">1453264486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264676/">1453264676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264683/">1453264683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264751/">1453264751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264784/">1453264784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264832/">1453264832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453264867/">1453264867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453279457/">1453279457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453279877/">1453279877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453280057/">1453280057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453280345/">1453280345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453280356/">1453280356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453280357/">1453280357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453280926/">1453280926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453280927/">1453280927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453281197/">1453281197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453281437/">1453281437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453281497/">1453281497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453282098/">1453282098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453282337/">1453282337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453282937/">1453282937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453283057/">1453283057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453283298/">1453283298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453284917/">1453284917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453286240/">1453286240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453286777/">1453286777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453286897/">1453286897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453287437/">1453287437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453287681/">1453287681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453289479/">1453289479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453294577/">1453294577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453295357/">1453295357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453295717/">1453295717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453296137/">1453296137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453297397/">1453297397/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453297638/">1453297638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453297699/">1453297699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453298838/">1453298838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453299737/">1453299737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453300639/">1453300639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453300817/">1453300817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453300937/">1453300937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453302087/">1453302087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453302744/">1453302744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453303165/">1453303165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453304130/">1453304130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453305565/">1453305565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453310726/">1453310726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453312466/">1453312466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453312825/">1453312825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453313016/">1453313016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453313666/">1453313666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453314446/">1453314446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453314687/">1453314687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453314749/">1453314749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453314803/">1453314803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453314923/">1453314923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453315227/">1453315227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453315282/">1453315282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453315794/">1453315794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453316760/">1453316760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453317357/">1453317357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453317717/">1453317717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453322066/">1453322066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453322906/">1453322906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453323026/">1453323026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453323265/">1453323265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453323682/">1453323682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453323683/">1453323683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453325245/">1453325245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453326505/">1453326505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335621/">1453335621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335681/">1453335681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335739/">1453335739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335740/">1453335740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335860/">1453335860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335861/">1453335861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453335918/">1453335918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453336101/">1453336101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453336520/">1453336520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453344200/">1453344200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453344288/">1453344288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453347708/">1453347708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453352002/">1453352002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453352120/">1453352120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453352183/">1453352183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453352241/">1453352241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453353505/">1453353505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453354641/">1453354641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453357460/">1453357460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453358780/">1453358780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453358960/">1453358960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453359082/">1453359082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453362500/">1453362500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453363220/">1453363220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453363354/">1453363354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453364550/">1453364550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453364720/">1453364720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453364780/">1453364780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453364900/">1453364900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453371020/">1453371020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453374500/">1453374500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453375520/">1453375520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453375821/">1453375821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453377205/">1453377205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453378400/">1453378400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453380083/">1453380083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453381886/">1453381886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453382000/">1453382000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453382480/">1453382480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453382660/">1453382660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453382780/">1453382780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453382842/">1453382842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453382960/">1453382960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453383860/">1453383860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453384520/">1453384520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453385720/">1453385720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453388000/">1453388000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453388122/">1453388122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453390558/">1453390558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453390782/">1453390782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453391486/">1453391486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453395202/">1453395202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453395861/">1453395861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453396101/">1453396101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453396410/">1453396410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453396525/">1453396525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453399100/">1453399100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453399944/">1453399944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453400299/">1453400299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453400423/">1453400423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453403481/">1453403481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453403683/">1453403683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453405221/">1453405221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453406902/">1453406902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453408040/">1453408040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453408160/">1453408160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453412489/">1453412489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453413330/">1453413330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453413630/">1453413630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453413689/">1453413689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453414231/">1453414231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453414769/">1453414769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453414949/">1453414949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453415550/">1453415550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453416269/">1453416269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453418430/">1453418430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453418431/">1453418431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453418491/">1453418491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453418730/">1453418730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453419690/">1453419690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453420829/">1453420829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453421789/">1453421789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453422210/">1453422210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453424190/">1453424190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453424969/">1453424969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426028/">1453426028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426170/">1453426170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426467/">1453426467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426471/">1453426471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426475/">1453426475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426479/">1453426479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426483/">1453426483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453426649/">1453426649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453427249/">1453427249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453427250/">1453427250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453428870/">1453428870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453430808/">1453430808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453430885/">1453430885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453431091/">1453431091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453432769/">1453432769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453432953/">1453432953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453433669/">1453433669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453435586/">1453435586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453435710/">1453435710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453444356/">1453444356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453445069/">1453445069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453445973/">1453445973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453446569/">1453446569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453447049/">1453447049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453448429/">1453448429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453448669/">1453448669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453448968/">1453448968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453449389/">1453449389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453450169/">1453450169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453453109/">1453453109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453453888/">1453453888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453454069/">1453454069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453454551/">1453454551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453454729/">1453454729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453455206/">1453455206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453457729/">1453457729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453457907/">1453457907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453459649/">1453459649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453463849/">1453463849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453465649/">1453465649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453465839/">1453465839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453465949/">1453465949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453466549/">1453466549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453467869/">1453467869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453468169/">1453468169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453468349/">1453468349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453470389/">1453470389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453472069/">1453472069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453473449/">1453473449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453474289/">1453474289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453474352/">1453474352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453477892/">1453477892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453477949/">1453477949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453478189/">1453478189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453478325/">1453478325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453478909/">1453478909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453479208/">1453479208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453480288/">1453480288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453480530/">1453480530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453481250/">1453481250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453482929/">1453482929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453483470/">1453483470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453483833/">1453483833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453484432/">1453484432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453485870/">1453485870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453485953/">1453485953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453486827/">1453486827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453487792/">1453487792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453487967/">1453487967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453488153/">1453488153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453488267/">1453488267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453488389/">1453488389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453489650/">1453489650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453490250/">1453490250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453494390/">1453494390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453494449/">1453494449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453494569/">1453494569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453494629/">1453494629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453494869/">1453494869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453496849/">1453496849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453498229/">1453498229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453498829/">1453498829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453499249/">1453499249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453499730/">1453499730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453500509/">1453500509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453502550/">1453502550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453502609/">1453502609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453505009/">1453505009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453506269/">1453506269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453507470/">1453507470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453508909/">1453508909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453511670/">1453511670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453512509/">1453512509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453513349/">1453513349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453513409/">1453513409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453513950/">1453513950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453514787/">1453514787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453518749/">1453518749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453520247/">1453520247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453527509/">1453527509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453527749/">1453527749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453532669/">1453532669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453533211/">1453533211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453534049/">1453534049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453535849/">1453535849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453552049/">1453552049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453563989/">1453563989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453564229/">1453564229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453565969/">1453565969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453568069/">1453568069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453569269/">1453569269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453578269/">1453578269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453583249/">1453583249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453589820/">1453589820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453589962/">1453589962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453589965/">1453589965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453589976/">1453589976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453589978/">1453589978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590006/">1453590006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590014/">1453590014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590018/">1453590018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590144/">1453590144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590260/">1453590260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590265/">1453590265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590266/">1453590266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590269/">1453590269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590271/">1453590271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590293/">1453590293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590501/">1453590501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590571/">1453590571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590640/">1453590640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590713/">1453590713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590764/">1453590764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590766/">1453590766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590849/">1453590849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590852/">1453590852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590856/">1453590856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590880/">1453590880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590998/">1453590998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453590999/">1453590999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591010/">1453591010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591015/">1453591015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591016/">1453591016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591019/">1453591019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591022/">1453591022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591023/">1453591023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591148/">1453591148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591237/">1453591237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591340/">1453591340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591496/">1453591496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591562/">1453591562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591566/">1453591566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591724/">1453591724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591745/">1453591745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591759/">1453591759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591836/">1453591836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591838/">1453591838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591862/">1453591862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591893/">1453591893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591898/">1453591898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453591989/">1453591989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592013/">1453592013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592093/">1453592093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592211/">1453592211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592215/">1453592215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592217/">1453592217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592243/">1453592243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592267/">1453592267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592270/">1453592270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592273/">1453592273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592274/">1453592274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592278/">1453592278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592280/">1453592280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592283/">1453592283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592286/">1453592286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592341/">1453592341/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592342/">1453592342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592607/">1453592607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592637/">1453592637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592697/">1453592697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592753/">1453592753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592830/">1453592830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592867/">1453592867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453592964/">1453592964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593000/">1453593000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593003/">1453593003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593005/">1453593005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593008/">1453593008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593010/">1453593010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593133/">1453593133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593228/">1453593228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593253/">1453593253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593256/">1453593256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593258/">1453593258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593263/">1453593263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593285/">1453593285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593291/">1453593291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593295/">1453593295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593320/">1453593320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593325/">1453593325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593327/">1453593327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593338/">1453593338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593417/">1453593417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593471/">1453593471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593493/">1453593493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453593653/">1453593653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453598407/">1453598407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453600508/">1453600508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453600687/">1453600687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453602008/">1453602008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453602487/">1453602487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453603746/">1453603746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453623787/">1453623787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453629826/">1453629826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453647608/">1453647608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453670107/">1453670107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453670287/">1453670287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453672808/">1453672808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453675627/">1453675627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453677427/">1453677427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453680068/">1453680068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453682407/">1453682407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453682707/">1453682707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453683547/">1453683547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453683826/">1453683826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453685602/">1453685602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453690267/">1453690267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453691707/">1453691707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453693747/">1453693747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453693987/">1453693987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453695247/">1453695247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453695307/">1453695307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453696569/">1453696569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453697826/">1453697826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453704427/">1453704427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453705208/">1453705208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453705687/">1453705687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453711566/">1453711566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453721105/">1453721105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453722309/">1453722309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453732464/">1453732464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453733904/">1453733904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453734931/">1453734931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453734985/">1453734985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453735105/">1453735105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453737926/">1453737926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453738105/">1453738105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453739305/">1453739305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453739907/">1453739907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453743922/">1453743922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453747585/">1453747585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453748066/">1453748066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453748707/">1453748707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453748837/">1453748837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453752266/">1453752266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453752267/">1453752267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453752805/">1453752805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453753225/">1453753225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453756466/">1453756466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453757604/">1453757604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453759164/">1453759164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453759642/">1453759642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453760065/">1453760065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453762345/">1453762345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453763667/">1453763667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453763788/">1453763788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453768709/">1453768709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453769129/">1453769129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453771709/">1453771709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453772067/">1453772067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453775725/">1453775725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453779264/">1453779264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453787549/">1453787549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453790191/">1453790191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453793968/">1453793968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453794568/">1453794568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453795108/">1453795108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453795480/">1453795480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453800750/">1453800750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453802189/">1453802189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453802550/">1453802550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453802667/">1453802667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453802968/">1453802968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453803628/">1453803628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453804348/">1453804348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453804529/">1453804529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453805428/">1453805428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453806389/">1453806389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453807589/">1453807589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453807645/">1453807645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453808789/">1453808789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453809089/">1453809089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453809333/">1453809333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453811067/">1453811067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453812326/">1453812326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453812627/">1453812627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453813109/">1453813109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453813768/">1453813768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453814247/">1453814247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453815148/">1453815148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453816526/">1453816526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453818029/">1453818029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453818989/">1453818989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453821147/">1453821147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453822090/">1453822090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453824088/">1453824088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453824290/">1453824290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453826001/">1453826001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453826671/">1453826671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453827275/">1453827275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453828648/">1453828648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453829369/">1453829369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453830685/">1453830685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453830989/">1453830989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453831355/">1453831355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453831958/">1453831958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453833154/">1453833154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453833205/">1453833205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453833569/">1453833569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453834469/">1453834469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453835370/">1453835370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453835547/">1453835547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453835969/">1453835969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453838188/">1453838188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453838487/">1453838487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453838646/">1453838646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453840290/">1453840290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453841248/">1453841248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453841365/">1453841365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453841789/">1453841789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453842158/">1453842158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453842248/">1453842248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453842808/">1453842808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453843470/">1453843470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453844488/">1453844488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453844851/">1453844851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453848621/">1453848621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453850541/">1453850541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453851321/">1453851321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453857985/">1453857985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453862002/">1453862002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453862299/">1453862299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453862960/">1453862960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453865646/">1453865646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453867500/">1453867500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453867522/">1453867522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453873289/">1453873289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453873370/">1453873370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453873829/">1453873829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453874249/">1453874249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453874369/">1453874369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453874972/">1453874972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875389/">1453875389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875485/">1453875485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875626/">1453875626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875630/">1453875630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875631/">1453875631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875633/">1453875633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875634/">1453875634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875637/">1453875637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875642/">1453875642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875702/">1453875702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875879/">1453875879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453875918/">1453875918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876207/">1453876207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876212/">1453876212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876213/">1453876213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876291/">1453876291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876490/">1453876490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876503/">1453876503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876720/">1453876720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876815/">1453876815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876818/">1453876818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876925/">1453876925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876928/">1453876928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876929/">1453876929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453876934/">1453876934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877198/">1453877198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877283/">1453877283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877392/">1453877392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877851/">1453877851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877855/">1453877855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877856/">1453877856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453877864/">1453877864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453878299/">1453878299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453878509/">1453878509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453878552/">1453878552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453879769/">1453879769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453881929/">1453881929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453883009/">1453883009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453883549/">1453883549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453884209/">1453884209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453889129/">1453889129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453889261/">1453889261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453889549/">1453889549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890029/">1453890029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890328/">1453890328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890449/">1453890449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890631/">1453890631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890749/">1453890749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890808/">1453890808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890929/">1453890929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453890990/">1453890990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453891051/">1453891051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453891169/">1453891169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453891409/">1453891409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453892069/">1453892069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453892370/">1453892370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453892371/">1453892371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453892550/">1453892550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453893089/">1453893089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453897409/">1453897409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453898189/">1453898189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453898489/">1453898489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453899509/">1453899509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453905029/">1453905029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453905660/">1453905660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453906740/">1453906740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453907640/">1453907640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453907880/">1453907880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453908702/">1453908702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453908703/">1453908703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453909587/">1453909587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453910651/">1453910651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453910707/">1453910707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453911660/">1453911660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453915020/">1453915020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453915218/">1453915218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453915861/">1453915861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453916217/">1453916217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453916476/">1453916476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453916519/">1453916519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453916820/">1453916820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453917063/">1453917063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453917960/">1453917960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453919340/">1453919340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453922820/">1453922820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453922880/">1453922880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453926480/">1453926480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453927560/">1453927560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453927620/">1453927620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453927860/">1453927860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453929360/">1453929360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453929600/">1453929600/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453930320/">1453930320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453933199/">1453933199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453933560/">1453933560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453934699/">1453934699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453935240/">1453935240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453935420/">1453935420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453935599/">1453935599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453936199/">1453936199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453936320/">1453936320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453937519/">1453937519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453938000/">1453938000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453939920/">1453939920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453942437/">1453942437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453944239/">1453944239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453944360/">1453944360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453945320/">1453945320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453945618/">1453945618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453947001/">1453947001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453948620/">1453948620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453949820/">1453949820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453951200/">1453951200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453951970/">1453951970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453955439/">1453955439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453955879/">1453955879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453959540/">1453959540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453960740/">1453960740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453967221/">1453967221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453967699/">1453967699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453968480/">1453968480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453968960/">1453968960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453969440/">1453969440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453969859/">1453969859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453969980/">1453969980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453971238/">1453971238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453973457/">1453973457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453973520/">1453973520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453976937/">1453976937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453977599/">1453977599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453977780/">1453977780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453978560/">1453978560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453980540/">1453980540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453988520/">1453988520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453989480/">1453989480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453994640/">1453994640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453994940/">1453994940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453995280/">1453995280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453995659/">1453995659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453995840/">1453995840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453997100/">1453997100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1453999440/">1453999440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454001806/">1454001806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454003426/">1454003426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454003667/">1454003667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454004086/">1454004086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454006426/">1454006426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454011230/">1454011230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454011706/">1454011706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454012366/">1454012366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454014706/">1454014706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454014885/">1454014885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454015908/">1454015908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454018065/">1454018065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454021005/">1454021005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454021486/">1454021486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454021545/">1454021545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454022505/">1454022505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454022984/">1454022984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454023825/">1454023825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454024125/">1454024125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454027005/">1454027005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454029285/">1454029285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454031565/">1454031565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454032287/">1454032287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454032465/">1454032465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454033163/">1454033163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454033164/">1454033164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454036365/">1454036365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454037326/">1454037326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454037874/">1454037874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454039665/">1454039665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454042965/">1454042965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454043025/">1454043025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454043445/">1454043445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454044347/">1454044347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454049924/">1454049924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454053705/">1454053705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454054426/">1454054426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454056166/">1454056166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454057908/">1454057908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454059106/">1454059106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454064565/">1454064565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454064805/">1454064805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454064926/">1454064926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454065047/">1454065047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454065445/">1454065445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454066185/">1454066185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454071944/">1454071944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454072365/">1454072365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454075605/">1454075605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454077945/">1454077945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454078787/">1454078787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454081129/">1454081129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454081725/">1454081725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454082025/">1454082025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454086707/">1454086707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454091386/">1454091386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454091756/">1454091756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454092310/">1454092310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100274/">1454100274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100334/">1454100334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100373/">1454100373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100389/">1454100389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100393/">1454100393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100400/">1454100400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100428/">1454100428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100444/">1454100444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100448/">1454100448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100526/">1454100526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100556/">1454100556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100616/">1454100616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100630/">1454100630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100635/">1454100635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100637/">1454100637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100640/">1454100640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100665/">1454100665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100675/">1454100675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100733/">1454100733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100739/">1454100739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100829/">1454100829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100849/">1454100849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100855/">1454100855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100870/">1454100870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100871/">1454100871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100911/">1454100911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100964/">1454100964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100966/">1454100966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100967/">1454100967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100970/">1454100970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100972/">1454100972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454100981/">1454100981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101016/">1454101016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101091/">1454101091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101125/">1454101125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101234/">1454101234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101467/">1454101467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101527/">1454101527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101641/">1454101641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101715/">1454101715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101833/">1454101833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101854/">1454101854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454101986/">1454101986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102162/">1454102162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102165/">1454102165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102504/">1454102504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102571/">1454102571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102581/">1454102581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102685/">1454102685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102687/">1454102687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454102765/">1454102765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454103805/">1454103805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454104105/">1454104105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454104705/">1454104705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454105246/">1454105246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454106085/">1454106085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454106325/">1454106325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454106626/">1454106626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454106985/">1454106985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454107886/">1454107886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454108305/">1454108305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454108769/">1454108769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454108937/">1454108937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454109089/">1454109089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454109505/">1454109505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454109587/">1454109587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454109795/">1454109795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110105/">1454110105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110108/">1454110108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110112/">1454110112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110115/">1454110115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110118/">1454110118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110123/">1454110123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110464/">1454110464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110656/">1454110656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110667/">1454110667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110672/">1454110672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110677/">1454110677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454110885/">1454110885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454111159/">1454111159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454111171/">1454111171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454113849/">1454113849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454115385/">1454115385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454118806/">1454118806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454125765/">1454125765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454126185/">1454126185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454126366/">1454126366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454142085/">1454142085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454146232/">1454146232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454171616/">1454171616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454173646/">1454173646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454175265/">1454175265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454175866/">1454175866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454177905/">1454177905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454178505/">1454178505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185346/">1454185346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185349/">1454185349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185403/">1454185403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185413/">1454185413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185475/">1454185475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185478/">1454185478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185524/">1454185524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185591/">1454185591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185593/">1454185593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185596/">1454185596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185604/">1454185604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185609/">1454185609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185693/">1454185693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185702/">1454185702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185704/">1454185704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185757/">1454185757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185760/">1454185760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185794/">1454185794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185797/">1454185797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185800/">1454185800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185804/">1454185804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185805/">1454185805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185807/">1454185807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185810/">1454185810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185814/">1454185814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185943/">1454185943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454185957/">1454185957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186058/">1454186058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186071/">1454186071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186109/">1454186109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186182/">1454186182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186184/">1454186184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186187/">1454186187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186189/">1454186189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186257/">1454186257/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186277/">1454186277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186284/">1454186284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186285/">1454186285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186317/">1454186317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186444/">1454186444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186447/">1454186447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186593/">1454186593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186670/">1454186670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186758/">1454186758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186763/">1454186763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186766/">1454186766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186768/">1454186768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186787/">1454186787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186823/">1454186823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186827/">1454186827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186829/">1454186829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186930/">1454186930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186963/">1454186963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186969/">1454186969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186986/">1454186986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454186991/">1454186991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187038/">1454187038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187067/">1454187067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187113/">1454187113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187123/">1454187123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187198/">1454187198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187259/">1454187259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187312/">1454187312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187432/">1454187432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187454/">1454187454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187524/">1454187524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187576/">1454187576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187589/">1454187589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187626/">1454187626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187629/">1454187629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187636/">1454187636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187706/">1454187706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187740/">1454187740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187741/">1454187741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187742/">1454187742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187745/">1454187745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187748/">1454187748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187837/">1454187837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187839/">1454187839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187843/">1454187843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187845/">1454187845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187846/">1454187846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187855/">1454187855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187858/">1454187858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187860/">1454187860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187862/">1454187862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187863/">1454187863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454187961/">1454187961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454202745/">1454202745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454228906/">1454228906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454230165/">1454230165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454245286/">1454245286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454252065/">1454252065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454253206/">1454253206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454263886/">1454263886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454269586/">1454269586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454270366/">1454270366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454281586/">1454281586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454281765/">1454281765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454285485/">1454285485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454286685/">1454286685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454289625/">1454289625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454290045/">1454290045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454291968/">1454291968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454295026/">1454295026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454295446/">1454295446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454297305/">1454297305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454301324/">1454301324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454309607/">1454309607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454313827/">1454313827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454314225/">1454314225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454314465/">1454314465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454314945/">1454314945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454315065/">1454315065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454315245/">1454315245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454316025/">1454316025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454316085/">1454316085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454317228/">1454317228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454317765/">1454317765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454317886/">1454317886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454329526/">1454329526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454337028/">1454337028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454337204/">1454337204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454339484/">1454339484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454339906/">1454339906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454339964/">1454339964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454340805/">1454340805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454342366/">1454342366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454343629/">1454343629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454345666/">1454345666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454345726/">1454345726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454345786/">1454345786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454346685/">1454346685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454348785/">1454348785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454351906/">1454351906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454354186/">1454354186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454356165/">1454356165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454358627/">1454358627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454360253/">1454360253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454360431/">1454360431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454361751/">1454361751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454362775/">1454362775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454363370/">1454363370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454364030/">1454364030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454364272/">1454364272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454364396/">1454364396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454366119/">1454366119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454366899/">1454366899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454367199/">1454367199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454367319/">1454367319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454367975/">1454367975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454369838/">1454369838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454371579/">1454371579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454371580/">1454371580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454371698/">1454371698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454371879/">1454371879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454372479/">1454372479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454372719/">1454372719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454373015/">1454373015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454374095/">1454374095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454374817/">1454374817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380574/">1454380574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380587/">1454380587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380588/">1454380588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380590/">1454380590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380591/">1454380591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380602/">1454380602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380606/">1454380606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380610/">1454380610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380630/">1454380630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380641/">1454380641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380642/">1454380642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380654/">1454380654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380655/">1454380655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380658/">1454380658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380659/">1454380659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380666/">1454380666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380668/">1454380668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380674/">1454380674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380680/">1454380680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380681/">1454380681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380685/">1454380685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380686/">1454380686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380693/">1454380693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380694/">1454380694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380700/">1454380700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380701/">1454380701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380702/">1454380702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380713/">1454380713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380715/">1454380715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380716/">1454380716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380725/">1454380725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380726/">1454380726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380727/">1454380727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380728/">1454380728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380729/">1454380729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380730/">1454380730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380745/">1454380745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380746/">1454380746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380748/">1454380748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380749/">1454380749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380755/">1454380755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380757/">1454380757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380761/">1454380761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380769/">1454380769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380782/">1454380782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380785/">1454380785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380789/">1454380789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380798/">1454380798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380818/">1454380818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380819/">1454380819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380820/">1454380820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380821/">1454380821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380822/">1454380822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380823/">1454380823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380824/">1454380824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380831/">1454380831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380834/">1454380834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380858/">1454380858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380861/">1454380861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380864/">1454380864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380867/">1454380867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380870/">1454380870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380876/">1454380876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380884/">1454380884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380885/">1454380885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380886/">1454380886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380896/">1454380896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380899/">1454380899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380930/">1454380930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380938/">1454380938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380939/">1454380939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380945/">1454380945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380951/">1454380951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380957/">1454380957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380971/">1454380971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380981/">1454380981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454380982/">1454380982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454381007/">1454381007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454381030/">1454381030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454381032/">1454381032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454381045/">1454381045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454381064/">1454381064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454382020/">1454382020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454382379/">1454382379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454385259/">1454385259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454388319/">1454388319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454390958/">1454390958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454392699/">1454392699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454400145/">1454400145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454400435/">1454400435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454403016/">1454403016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454403199/">1454403199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454404875/">1454404875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454405659/">1454405659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454406379/">1454406379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454406859/">1454406859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454408055/">1454408055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454409743/">1454409743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454410278/">1454410278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454410460/">1454410460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454410461/">1454410461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454411360/">1454411360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454411555/">1454411555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454418859/">1454418859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454418979/">1454418979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454419519/">1454419519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454423359/">1454423359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454426773/">1454426773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454427435/">1454427435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454427792/">1454427792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454431933/">1454431933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454436290/">1454436290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454436291/">1454436291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454436331/">1454436331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454436611/">1454436611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454437149/">1454437149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454437270/">1454437270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454440392/">1454440392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454440512/">1454440512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454443692/">1454443692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454445434/">1454445434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454447050/">1454447050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454447712/">1454447712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454449514/">1454449514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454450113/">1454450113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454450752/">1454450752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454451190/">1454451190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454451852/">1454451852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454453383/">1454453383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454453564/">1454453564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454454103/">1454454103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454454883/">1454454883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454456684/">1454456684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454457224/">1454457224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454458543/">1454458543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454460223/">1454460223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454460582/">1454460582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454461723/">1454461723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454464423/">1454464423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467286/">1454467286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467290/">1454467290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467357/">1454467357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467375/">1454467375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467381/">1454467381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467430/">1454467430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467431/">1454467431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467454/">1454467454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467455/">1454467455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467456/">1454467456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467495/">1454467495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467496/">1454467496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467502/">1454467502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467506/">1454467506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467518/">1454467518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467536/">1454467536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467540/">1454467540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467551/">1454467551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467559/">1454467559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467563/">1454467563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467568/">1454467568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467582/">1454467582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467584/">1454467584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467599/">1454467599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467654/">1454467654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467655/">1454467655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467743/">1454467743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467750/">1454467750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467757/">1454467757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467766/">1454467766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467768/">1454467768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467773/">1454467773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454467840/">1454467840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468324/">1454468324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468825/">1454468825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468834/">1454468834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468890/">1454468890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468912/">1454468912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468921/">1454468921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468930/">1454468930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468944/">1454468944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468963/">1454468963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468964/">1454468964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468977/">1454468977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468978/">1454468978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468980/">1454468980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468997/">1454468997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454468998/">1454468998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469011/">1454469011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469013/">1454469013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469021/">1454469021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469025/">1454469025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469031/">1454469031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469036/">1454469036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469041/">1454469041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469053/">1454469053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469054/">1454469054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469060/">1454469060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469064/">1454469064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469068/">1454469068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469075/">1454469075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469090/">1454469090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469145/">1454469145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469147/">1454469147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469149/">1454469149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454469403/">1454469403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454470003/">1454470003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454474924/">1454474924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454481099/">1454481099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454491852/">1454491852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454493103/">1454493103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454493583/">1454493583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454496403/">1454496403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454496464/">1454496464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454496583/">1454496583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454498682/">1454498682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454498864/">1454498864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454499283/">1454499283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454499763/">1454499763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454499889/">1454499889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454499890/">1454499890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454500003/">1454500003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454500124/">1454500124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454500243/">1454500243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454507684/">1454507684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454508367/">1454508367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454509067/">1454509067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454509183/">1454509183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454509963/">1454509963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454510864/">1454510864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454513141/">1454513141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454513338/">1454513338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454513731/">1454513731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454514463/">1454514463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454516547/">1454516547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454517154/">1454517154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454517628/">1454517628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454521527/">1454521527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454522488/">1454522488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454522778/">1454522778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454524245/">1454524245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454525128/">1454525128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454526330/">1454526330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454526510/">1454526510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454526566/">1454526566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454526868/">1454526868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454526985/">1454526985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454527533/">1454527533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454530646/">1454530646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454531309/">1454531309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454531667/">1454531667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454531907/">1454531907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454532028/">1454532028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454532384/">1454532384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454535050/">1454535050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454535066/">1454535066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454535088/">1454535088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454535327/">1454535327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454536288/">1454536288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454536839/">1454536839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454536842/">1454536842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454536853/">1454536853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454537907/">1454537907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454539405/">1454539405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454539467/">1454539467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454541388/">1454541388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454543487/">1454543487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454544687/">1454544687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454544867/">1454544867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454545407/">1454545407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454545764/">1454545764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454545832/">1454545832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454546124/">1454546124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454546427/">1454546427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454546667/">1454546667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454547627/">1454547627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454548647/">1454548647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454549906/">1454549906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454551947/">1454551947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454553086/">1454553086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454556767/">1454556767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454556986/">1454556986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454557889/">1454557889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454558007/">1454558007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454559387/">1454559387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454559567/">1454559567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454560452/">1454560452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454564788/">1454564788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454565868/">1454565868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454566470/">1454566470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454567464/">1454567464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454570667/">1454570667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454570788/">1454570788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454571208/">1454571208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454572228/">1454572228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454573307/">1454573307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454573727/">1454573727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454573847/">1454573847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454575228/">1454575228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454575767/">1454575767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454578229/">1454578229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454580630/">1454580630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454580744/">1454580744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454583993/">1454583993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454585784/">1454585784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454587709/">1454587709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454589063/">1454589063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454594429/">1454594429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454595748/">1454595748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454595868/">1454595868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454596708/">1454596708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454598273/">1454598273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454599168/">1454599168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454599768/">1454599768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454600184/">1454600184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454600484/">1454600484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454601627/">1454601627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454606437/">1454606437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454606788/">1454606788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609620/">1454609620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609621/">1454609621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609622/">1454609622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609625/">1454609625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609626/">1454609626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609627/">1454609627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609628/">1454609628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609629/">1454609629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609630/">1454609630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609631/">1454609631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609632/">1454609632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609633/">1454609633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609634/">1454609634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609635/">1454609635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609636/">1454609636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609637/">1454609637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609639/">1454609639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609640/">1454609640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609641/">1454609641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609643/">1454609643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609644/">1454609644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609646/">1454609646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609647/">1454609647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609648/">1454609648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609650/">1454609650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609651/">1454609651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609652/">1454609652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609654/">1454609654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609655/">1454609655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609656/">1454609656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609657/">1454609657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609658/">1454609658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609659/">1454609659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609660/">1454609660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609661/">1454609661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609662/">1454609662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609663/">1454609663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609665/">1454609665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609666/">1454609666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609667/">1454609667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609669/">1454609669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609670/">1454609670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609671/">1454609671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609673/">1454609673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609674/">1454609674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609676/">1454609676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609677/">1454609677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609678/">1454609678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609680/">1454609680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609711/">1454609711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609712/">1454609712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609713/">1454609713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609715/">1454609715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609716/">1454609716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609718/">1454609718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609719/">1454609719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609720/">1454609720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609721/">1454609721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609723/">1454609723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609724/">1454609724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609725/">1454609725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609729/">1454609729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609730/">1454609730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609731/">1454609731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609734/">1454609734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609735/">1454609735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609737/">1454609737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609738/">1454609738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609739/">1454609739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609740/">1454609740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609741/">1454609741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609742/">1454609742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609743/">1454609743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609744/">1454609744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609746/">1454609746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609747/">1454609747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609748/">1454609748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609750/">1454609750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609751/">1454609751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609752/">1454609752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609754/">1454609754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609755/">1454609755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609756/">1454609756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609757/">1454609757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609759/">1454609759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609761/">1454609761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609762/">1454609762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609814/">1454609814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609815/">1454609815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609817/">1454609817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609818/">1454609818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454609819/">1454609819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610199/">1454610199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610329/">1454610329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610380/">1454610380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610641/">1454610641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610718/">1454610718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610780/">1454610780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454610829/">1454610829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611262/">1454611262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611297/">1454611297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611314/">1454611314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611398/">1454611398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611401/">1454611401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611605/">1454611605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611781/">1454611781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611787/">1454611787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611792/">1454611792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454611835/">1454611835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612048/">1454612048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612050/">1454612050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612051/">1454612051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612052/">1454612052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612057/">1454612057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612062/">1454612062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612065/">1454612065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454612109/">1454612109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454615008/">1454615008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454621361/">1454621361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454621980/">1454621980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454621985/">1454621985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454622605/">1454622605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454622697/">1454622697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454635374/">1454635374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454641390/">1454641390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454641932/">1454641932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454642109/">1454642109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454642890/">1454642890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454643729/">1454643729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454643969/">1454643969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454644332/">1454644332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454644509/">1454644509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454647452/">1454647452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454649429/">1454649429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454652530/">1454652530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454653871/">1454653871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454653872/">1454653872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454653890/">1454653890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454656467/">1454656467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454658489/">1454658489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454662210/">1454662210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454664650/">1454664650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454667610/">1454667610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454668030/">1454668030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454668150/">1454668150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454668390/">1454668390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454668450/">1454668450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454669410/">1454669410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454670070/">1454670070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454671450/">1454671450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454672710/">1454672710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454675437/">1454675437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454676851/">1454676851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454677150/">1454677150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454677252/">1454677252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454677255/">1454677255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454677692/">1454677692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454678350/">1454678350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454679970/">1454679970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454681109/">1454681109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454682790/">1454682790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454683930/">1454683930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454685019/">1454685019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454686151/">1454686151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454686352/">1454686352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454686355/">1454686355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454686929/">1454686929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454687469/">1454687469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454687532/">1454687532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454689154/">1454689154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454689453/">1454689453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454695929/">1454695929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454696049/">1454696049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454696050/">1454696050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454697050/">1454697050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454697129/">1454697129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454697794/">1454697794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454701494/">1454701494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454704013/">1454704013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454707819/">1454707819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454707914/">1454707914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454708033/">1454708033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454708094/">1454708094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454708274/">1454708274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454708574/">1454708574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454708935/">1454708935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454709054/">1454709054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454710133/">1454710133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454710254/">1454710254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454712953/">1454712953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454713793/">1454713793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454714274/">1454714274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454715578/">1454715578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454715637/">1454715637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454716834/">1454716834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454719358/">1454719358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454719418/">1454719418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454720317/">1454720317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454720680/">1454720680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454724038/">1454724038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454728358/">1454728358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454729599/">1454729599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454729681/">1454729681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454730220/">1454730220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454732917/">1454732917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454733227/">1454733227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454734717/">1454734717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454736399/">1454736399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454740240/">1454740240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454740837/">1454740837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454741018/">1454741018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454742638/">1454742638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454751001/">1454751001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454767958/">1454767958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454780378/">1454780378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454781219/">1454781219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454787660/">1454787660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454794010/">1454794010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454794353/">1454794353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454795940/">1454795940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454805001/">1454805001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454812980/">1454812980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454814961/">1454814961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454818140/">1454818140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454820120/">1454820120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454821691/">1454821691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454827861/">1454827861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454840580/">1454840580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454841061/">1454841061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454842320/">1454842320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454847360/">1454847360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454856060/">1454856060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454863503/">1454863503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454865001/">1454865001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454872080/">1454872080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454872920/">1454872920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454874420/">1454874420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454874720/">1454874720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454890140/">1454890140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454892240/">1454892240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454893980/">1454893980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454894580/">1454894580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454897821/">1454897821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454899980/">1454899980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454902259/">1454902259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454910240/">1454910240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454911140/">1454911140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454912880/">1454912880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454913421/">1454913421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454921280/">1454921280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454921460/">1454921460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454925600/">1454925600/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454926920/">1454926920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454929080/">1454929080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454929438/">1454929438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454933176/">1454933176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454933939/">1454933939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454934541/">1454934541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454937060/">1454937060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454937424/">1454937424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454937722/">1454937722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454941081/">1454941081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454941141/">1454941141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454941501/">1454941501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454941621/">1454941621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454942702/">1454942702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454943182/">1454943182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454943244/">1454943244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454944634/">1454944634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454944811/">1454944811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454949252/">1454949252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454949360/">1454949360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454949963/">1454949963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454950582/">1454950582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454952604/">1454952604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454953212/">1454953212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454954044/">1454954044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454957641/">1454957641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454959286/">1454959286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454959527/">1454959527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454961395/">1454961395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454962167/">1454962167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454974284/">1454974284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454975184/">1454975184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454975364/">1454975364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454976564/">1454976564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454977044/">1454977044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454978724/">1454978724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454980047/">1454980047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454981064/">1454981064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454981964/">1454981964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454984665/">1454984665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454987135/">1454987135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454988176/">1454988176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1454994476/">1454994476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455000657/">1455000657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455002698/">1455002698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455003536/">1455003536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455004136/">1455004136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455006776/">1455006776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455011516/">1455011516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455011577/">1455011577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455014816/">1455014816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455015356/">1455015356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455016136/">1455016136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455019857/">1455019857/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455022136/">1455022136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455023222/">1455023222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455025196/">1455025196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455025316/">1455025316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455026696/">1455026696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455028676/">1455028676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455028794/">1455028794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455029276/">1455029276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455029396/">1455029396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455031435/">1455031435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455032217/">1455032217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455034739/">1455034739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455036298/">1455036298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455037640/">1455037640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455056357/">1455056357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455056717/">1455056717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455056778/">1455056778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455056895/">1455056895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455056958/">1455056958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455057158/">1455057158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455057639/">1455057639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455057698/">1455057698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455057817/">1455057817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455057878/">1455057878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455058356/">1455058356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455058597/">1455058597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455058658/">1455058658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455059030/">1455059030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455059617/">1455059617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455059858/">1455059858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455060097/">1455060097/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455060217/">1455060217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455060755/">1455060755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455060831/">1455060831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455061058/">1455061058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455062805/">1455062805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455063158/">1455063158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455065678/">1455065678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455065975/">1455065975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455066636/">1455066636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455067058/">1455067058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455067355/">1455067355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455067657/">1455067657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455068314/">1455068314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455072278/">1455072278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455072338/">1455072338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455072459/">1455072459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455072577/">1455072577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455073183/">1455073183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455075261/">1455075261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455076582/">1455076582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455077182/">1455077182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455080902/">1455080902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455081802/">1455081802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455085225/">1455085225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455088642/">1455088642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455088763/">1455088763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455088882/">1455088882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455089063/">1455089063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455089725/">1455089725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455090982/">1455090982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455091942/">1455091942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455095302/">1455095302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455095539/">1455095539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455097400/">1455097400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455097642/">1455097642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455098793/">1455098793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455099682/">1455099682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455100583/">1455100583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455100823/">1455100823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455103343/">1455103343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455105502/">1455105502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455105922/">1455105922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455106462/">1455106462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455106882/">1455106882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455107663/">1455107663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455108742/">1455108742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455110302/">1455110302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455110543/">1455110543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455111023/">1455111023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455114143/">1455114143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455114520/">1455114520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455115043/">1455115043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455115102/">1455115102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455116063/">1455116063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455116299/">1455116299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455117530/">1455117530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455117531/">1455117531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455117923/">1455117923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455118549/">1455118549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455119859/">1455119859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455119978/">1455119978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455120098/">1455120098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455121978/">1455121978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455121979/">1455121979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455122499/">1455122499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455123098/">1455123098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455123640/">1455123640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455124961/">1455124961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455125082/">1455125082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455125263/">1455125263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455125801/">1455125801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455126580/">1455126580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455127358/">1455127358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455128918/">1455128918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455128979/">1455128979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455129278/">1455129278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455130478/">1455130478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455130538/">1455130538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455130659/">1455130659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455132162/">1455132162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455134258/">1455134258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455137498/">1455137498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455137679/">1455137679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455137918/">1455137918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455139778/">1455139778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455139958/">1455139958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455140498/">1455140498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455143978/">1455143978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455143979/">1455143979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455144038/">1455144038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455144458/">1455144458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455144818/">1455144818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455144878/">1455144878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455145778/">1455145778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455146672/">1455146672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455148113/">1455148113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455156874/">1455156874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455157053/">1455157053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455163892/">1455163892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455165251/">1455165251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455165335/">1455165335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455172652/">1455172652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455178712/">1455178712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455179132/">1455179132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455179192/">1455179192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455179617/">1455179617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455180752/">1455180752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455180992/">1455180992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455185852/">1455185852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455186871/">1455186871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455188253/">1455188253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455188644/">1455188644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455189632/">1455189632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455191612/">1455191612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455191672/">1455191672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455197132/">1455197132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455198392/">1455198392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455200612/">1455200612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455203262/">1455203262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455204453/">1455204453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455204879/">1455204879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455205712/">1455205712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455206013/">1455206013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455206432/">1455206432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455206498/">1455206498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455206880/">1455206880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455210815/">1455210815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455212553/">1455212553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455212851/">1455212851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455213095/">1455213095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455213214/">1455213214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455214794/">1455214794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455215314/">1455215314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455216152/">1455216152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455216213/">1455216213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455217233/">1455217233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455217832/">1455217832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455218193/">1455218193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455218613/">1455218613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455219214/">1455219214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455219692/">1455219692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455219815/">1455219815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455219999/">1455219999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455220295/">1455220295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455221559/">1455221559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455222817/">1455222817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455222932/">1455222932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455224733/">1455224733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455225325/">1455225325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455225865/">1455225865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455226230/">1455226230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455226705/">1455226705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455227006/">1455227006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455227428/">1455227428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455228675/">1455228675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455228676/">1455228676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455228677/">1455228677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455229049/">1455229049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455229169/">1455229169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455229227/">1455229227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455229406/">1455229406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455230126/">1455230126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455230308/">1455230308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455230367/">1455230367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455230967/">1455230967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455231089/">1455231089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455231090/">1455231090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455233608/">1455233608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455234209/">1455234209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455236609/">1455236609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455237329/">1455237329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455237869/">1455237869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455238349/">1455238349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455239665/">1455239665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455250768/">1455250768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455251309/">1455251309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455252493/">1455252493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455252746/">1455252746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455252988/">1455252988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455255029/">1455255029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455255688/">1455255688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455259333/">1455259333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455262275/">1455262275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455262814/">1455262814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455262935/">1455262935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455263415/">1455263415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455263715/">1455263715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455264315/">1455264315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455265215/">1455265215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455265458/">1455265458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455265573/">1455265573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455270555/">1455270555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455270734/">1455270734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455271202/">1455271202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455271203/">1455271203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455275835/">1455275835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455279495/">1455279495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455281236/">1455281236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455283811/">1455283811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455283875/">1455283875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455284294/">1455284294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455284775/">1455284775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455285554/">1455285554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455286881/">1455286881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455287774/">1455287774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455291613/">1455291613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455293423/">1455293423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455296846/">1455296846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455297685/">1455297685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455298403/">1455298403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455317425/">1455317425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455317665/">1455317665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455317725/">1455317725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455317965/">1455317965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455320965/">1455320965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455322945/">1455322945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455323425/">1455323425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455327745/">1455327745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455330625/">1455330625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455334475/">1455334475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455342865/">1455342865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455346705/">1455346705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455365605/">1455365605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455370465/">1455370465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455375445/">1455375445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455379765/">1455379765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455385285/">1455385285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455395896/">1455395896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455400036/">1455400036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455413357/">1455413357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455420377/">1455420377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455456681/">1455456681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455460697/">1455460697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455482963/">1455482963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455489623/">1455489623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455496523/">1455496523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455500903/">1455500903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455520283/">1455520283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455523041/">1455523041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455525863/">1455525863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455527303/">1455527303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455531743/">1455531743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455533003/">1455533003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455535883/">1455535883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455537863/">1455537863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455539422/">1455539422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455542671/">1455542671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455545361/">1455545361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455546145/">1455546145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455546924/">1455546924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455550040/">1455550040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455551060/">1455551060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455556648/">1455556648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455560004/">1455560004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455561572/">1455561572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455562880/">1455562880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455564562/">1455564562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455564984/">1455564984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455582050/">1455582050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455595850/">1455595850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455603290/">1455603290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455603409/">1455603409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455603529/">1455603529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455603649/">1455603649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455606528/">1455606528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455608089/">1455608089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455609538/">1455609538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455609854/">1455609854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455611029/">1455611029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455613431/">1455613431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455616129/">1455616129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455616369/">1455616369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455619670/">1455619670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455621650/">1455621650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455623090/">1455623090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455623871/">1455623871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455629449/">1455629449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455633649/">1455633649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455634069/">1455634069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455637489/">1455637489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455637609/">1455637609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455637670/">1455637670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455637851/">1455637851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455638150/">1455638150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455638930/">1455638930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455639469/">1455639469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455640009/">1455640009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455640970/">1455640970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455642532/">1455642532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455643490/">1455643490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455645289/">1455645289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455646549/">1455646549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455646853/">1455646853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455649733/">1455649733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455650330/">1455650330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455651410/">1455651410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455651774/">1455651774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455652790/">1455652790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455653031/">1455653031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455653810/">1455653810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455654469/">1455654469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455655189/">1455655189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455656209/">1455656209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455658129/">1455658129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455658611/">1455658611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455658669/">1455658669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455659210/">1455659210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455659330/">1455659330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455659809/">1455659809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455661249/">1455661249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455661969/">1455661969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455663110/">1455663110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455663529/">1455663529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455664433/">1455664433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455669649/">1455669649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455669710/">1455669710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455670131/">1455670131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455671040/">1455671040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455674150/">1455674150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455676443/">1455676443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455677870/">1455677870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455678589/">1455678589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455678832/">1455678832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455681532/">1455681532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455682717/">1455682717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455683929/">1455683929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455684529/">1455684529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455685909/">1455685909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455686149/">1455686149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455689930/">1455689930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455697609/">1455697609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455697789/">1455697789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455698149/">1455698149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455699470/">1455699470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455701030/">1455701030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455701390/">1455701390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455702710/">1455702710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455703262/">1455703262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455704990/">1455704990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455705170/">1455705170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455706131/">1455706131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455707693/">1455707693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455707810/">1455707810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455708471/">1455708471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455710750/">1455710750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455716270/">1455716270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455717530/">1455717530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455717531/">1455717531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455721009/">1455721009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455722330/">1455722330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455723279/">1455723279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455723348/">1455723348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455723710/">1455723710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455723775/">1455723775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455724378/">1455724378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455729702/">1455729702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455730370/">1455730370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455731515/">1455731515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455731870/">1455731870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455732771/">1455732771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455732949/">1455732949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455733190/">1455733190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455733250/">1455733250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455733731/">1455733731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455736527/">1455736527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455737129/">1455737129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455737247/">1455737247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455738506/">1455738506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455739529/">1455739529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455739647/">1455739647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455741451/">1455741451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455743128/">1455743128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455743427/">1455743427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455744026/">1455744026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455745047/">1455745047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455745348/">1455745348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455746648/">1455746648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455749305/">1455749305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455753567/">1455753567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455755006/">1455755006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455755727/">1455755727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455756626/">1455756626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455758368/">1455758368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455759206/">1455759206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455759506/">1455759506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455764127/">1455764127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455764750/">1455764750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455765567/">1455765567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455767066/">1455767066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455767966/">1455767966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455769526/">1455769526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455772466/">1455772466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455772706/">1455772706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455776366/">1455776366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455778526/">1455778526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455780928/">1455780928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455782187/">1455782187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455783746/">1455783746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455784466/">1455784466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455784586/">1455784586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455788065/">1455788065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455788192/">1455788192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455788426/">1455788426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455788487/">1455788487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455788726/">1455788726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455788966/">1455788966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455789146/">1455789146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455791847/">1455791847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455791966/">1455791966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455792273/">1455792273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455792568/">1455792568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455794966/">1455794966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455795086/">1455795086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455799293/">1455799293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455801686/">1455801686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455803726/">1455803726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455810147/">1455810147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455810386/">1455810386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455810506/">1455810506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455810747/">1455810747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455810806/">1455810806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455811052/">1455811052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455811649/">1455811649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455812546/">1455812546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455812785/">1455812785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455813328/">1455813328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455813506/">1455813506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455813986/">1455813986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455815449/">1455815449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455815613/">1455815613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455817646/">1455817646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455818254/">1455818254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455821666/">1455821666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455821907/">1455821907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455822749/">1455822749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455823589/">1455823589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455824246/">1455824246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455825324/">1455825324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455825394/">1455825394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455826169/">1455826169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455826646/">1455826646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455826767/">1455826767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455828806/">1455828806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455831387/">1455831387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455831986/">1455831986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455832887/">1455832887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455833127/">1455833127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455833487/">1455833487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455835768/">1455835768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455836304/">1455836304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455836642/">1455836642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455837747/">1455837747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455838586/">1455838586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455838707/">1455838707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455843485/">1455843485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455844385/">1455844385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455844805/">1455844805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455845284/">1455845284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455845703/">1455845703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455847085/">1455847085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455847208/">1455847208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455860880/">1455860880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455862137/">1455862137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455863759/">1455863759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455866460/">1455866460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455867540/">1455867540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455868199/">1455868199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455869160/">1455869160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455869639/">1455869639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455870657/">1455870657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455871200/">1455871200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455871979/">1455871979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455873000/">1455873000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455875580/">1455875580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455878460/">1455878460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455878519/">1455878519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455878640/">1455878640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455878940/">1455878940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455879059/">1455879059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455880140/">1455880140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455881939/">1455881939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455882779/">1455882779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455888664/">1455888664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455889860/">1455889860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455891299/">1455891299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455892019/">1455892019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455896716/">1455896716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455898946/">1455898946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455899366/">1455899366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455899858/">1455899858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455900685/">1455900685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455905126/">1455905126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455905546/">1455905546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455905788/">1455905788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455906867/">1455906867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455907345/">1455907345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455912363/">1455912363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455912364/">1455912364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455912366/">1455912366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455912370/">1455912370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455912423/">1455912423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455912626/">1455912626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455914246/">1455914246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455914725/">1455914725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455915085/">1455915085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455915145/">1455915145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455916819/">1455916819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455916882/">1455916882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455917181/">1455917181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455917722/">1455917722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455918201/">1455918201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455922401/">1455922401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455922945/">1455922945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455923361/">1455923361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455923479/">1455923479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455934821/">1455934821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455934881/">1455934881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455936441/">1455936441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455937377/">1455937377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455937714/">1455937714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455938061/">1455938061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455945022/">1455945022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455964462/">1455964462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1455976042/">1455976042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456012211/">1456012211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456018572/">1456018572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456019832/">1456019832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456020433/">1456020433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456026090/">1456026090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456033572/">1456033572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456047671/">1456047671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456056433/">1456056433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456076832/">1456076832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456078634/">1456078634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456085771/">1456085771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456087570/">1456087570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456089551/">1456089551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456094577/">1456094577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456101957/">1456101957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456110697/">1456110697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456113358/">1456113358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456127097/">1456127097/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456131839/">1456131839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456136638/">1456136638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456139757/">1456139757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456145818/">1456145818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456148517/">1456148517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456152297/">1456152297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456153087/">1456153087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456156136/">1456156136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456156698/">1456156698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456156859/">1456156859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456157050/">1456157050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456158059/">1456158059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456158740/">1456158740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456159375/">1456159375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456160915/">1456160915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456161428/">1456161428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456161949/">1456161949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456162573/">1456162573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456163030/">1456163030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456164155/">1456164155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456164752/">1456164752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456167621/">1456167621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456173898/">1456173898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456174318/">1456174318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456175075/">1456175075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456175076/">1456175076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456175288/">1456175288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456175460/">1456175460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456176718/">1456176718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456178278/">1456178278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456178545/">1456178545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456179290/">1456179290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456182472/">1456182472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456183130/">1456183130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456185050/">1456185050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456186370/">1456186370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456186851/">1456186851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456186911/">1456186911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456189990/">1456189990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456195869/">1456195869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456209847/">1456209847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456237267/">1456237267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456237327/">1456237327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456237388/">1456237388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456237447/">1456237447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456238947/">1456238947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456239248/">1456239248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456239490/">1456239490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456239607/">1456239607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456239747/">1456239747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456239886/">1456239886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456240149/">1456240149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456240747/">1456240747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456241285/">1456241285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456241709/">1456241709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456243028/">1456243028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456243510/">1456243510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456246150/">1456246150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456246579/">1456246579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456249214/">1456249214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456249407/">1456249407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456250698/">1456250698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456251367/">1456251367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456251547/">1456251547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456251848/">1456251848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456252806/">1456252806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456253407/">1456253407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456254967/">1456254967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456255568/">1456255568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456256471/">1456256471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456256828/">1456256828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456257431/">1456257431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456257854/">1456257854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456257916/">1456257916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456261866/">1456261866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456262167/">1456262167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456262287/">1456262287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456262407/">1456262407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456262468/">1456262468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456262527/">1456262527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456262707/">1456262707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456265985/">1456265985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456268505/">1456268505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456270005/">1456270005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456270847/">1456270847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456271087/">1456271087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456271205/">1456271205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456272765/">1456272765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456273485/">1456273485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456273905/">1456273905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456274444/">1456274444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456276424/">1456276424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456276609/">1456276609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456277864/">1456277864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456285845/">1456285845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456291725/">1456291725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456294965/">1456294965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456297374/">1456297374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456301685/">1456301685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456303729/">1456303729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456304565/">1456304565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456305645/">1456305645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456307085/">1456307085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456307450/">1456307450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456308045/">1456308045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456310147/">1456310147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456312428/">1456312428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456312547/">1456312547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456312729/">1456312729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456312911/">1456312911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456313026/">1456313026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456314827/">1456314827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456314887/">1456314887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456315065/">1456315065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456315494/">1456315494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456315764/">1456315764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456316870/">1456316870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456318908/">1456318908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456321065/">1456321065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456322565/">1456322565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456323289/">1456323289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456323525/">1456323525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456324185/">1456324185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456328398/">1456328398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456329405/">1456329405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456330196/">1456330196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456331025/">1456331025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456331709/">1456331709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456333125/">1456333125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456337054/">1456337054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456337445/">1456337445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456338054/">1456338054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456338405/">1456338405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456339305/">1456339305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456340565/">1456340565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456340885/">1456340885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456340886/">1456340886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456342784/">1456342784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456343567/">1456343567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456343685/">1456343685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456343686/">1456343686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456344049/">1456344049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456344314/">1456344314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456345067/">1456345067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456345186/">1456345186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456345305/">1456345305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456345846/">1456345846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456347106/">1456347106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456347107/">1456347107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456347404/">1456347404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456347465/">1456347465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456347585/">1456347585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456347765/">1456347765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456348785/">1456348785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456349085/">1456349085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456350707/">1456350707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456351770/">1456351770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456352011/">1456352011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456352668/">1456352668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456352847/">1456352847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456355065/">1456355065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456357589/">1456357589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456358369/">1456358369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456358968/">1456358968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456358969/">1456358969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456359326/">1456359326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456359568/">1456359568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456361669/">1456361669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456362029/">1456362029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456364129/">1456364129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456365089/">1456365089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456366412/">1456366412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456366528/">1456366528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456366586/">1456366586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456366710/">1456366710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456367008/">1456367008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456368208/">1456368208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456369367/">1456369367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456369713/">1456369713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456370548/">1456370548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456371625/">1456371625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456372469/">1456372469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456377268/">1456377268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456381588/">1456381588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456381709/">1456381709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456386028/">1456386028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456386388/">1456386388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456386689/">1456386689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456386749/">1456386749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456386868/">1456386868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456388132/">1456388132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456388670/">1456388670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456388908/">1456388908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456389808/">1456389808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456393290/">1456393290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456393484/">1456393484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456397968/">1456397968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456402229/">1456402229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456402709/">1456402709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456402768/">1456402768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456403456/">1456403456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456404688/">1456404688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456406189/">1456406189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456408886/">1456408886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456409670/">1456409670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456410149/">1456410149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456410929/">1456410929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456414953/">1456414953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456415059/">1456415059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456415615/">1456415615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456416450/">1456416450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456416830/">1456416830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456417009/">1456417009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456417195/">1456417195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456417591/">1456417591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456418789/">1456418789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456419148/">1456419148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456419750/">1456419750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456422749/">1456422749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456424005/">1456424005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456424610/">1456424610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456424669/">1456424669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456425266/">1456425266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456425446/">1456425446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456425926/">1456425926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456426286/">1456426286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456427486/">1456427486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456427786/">1456427786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456428446/">1456428446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456429106/">1456429106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456429167/">1456429167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456429706/">1456429706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456431387/">1456431387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456432825/">1456432825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456434474/">1456434474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456434987/">1456434987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456435046/">1456435046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456436547/">1456436547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456437627/">1456437627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456438405/">1456438405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456439904/">1456439904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456440927/">1456440927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456442187/">1456442187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456442311/">1456442311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456444704/">1456444704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456446149/">1456446149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456446506/">1456446506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456447346/">1456447346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456447767/">1456447767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456451306/">1456451306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456451486/">1456451486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456453646/">1456453646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456453824/">1456453824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456453946/">1456453946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456455023/">1456455023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456455207/">1456455207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456455461/">1456455461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456456587/">1456456587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456458927/">1456458927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456459536/">1456459536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456461771/">1456461771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456462529/">1456462529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456462586/">1456462586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456468827/">1456468827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456472728/">1456472728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456475547/">1456475547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456476446/">1456476446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456478665/">1456478665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456481846/">1456481846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456482206/">1456482206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456484845/">1456484845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456489828/">1456489828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456491687/">1456491687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456494566/">1456494566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456494806/">1456494806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456496546/">1456496546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456497083/">1456497083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456497708/">1456497708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456498228/">1456498228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456498766/">1456498766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456498888/">1456498888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456500385/">1456500385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456500927/">1456500927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456502066/">1456502066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456502246/">1456502246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456502691/">1456502691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456502791/">1456502791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456503183/">1456503183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456504352/">1456504352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456504353/">1456504353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456504648/">1456504648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456504766/">1456504766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456505364/">1456505364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456505678/">1456505678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456506146/">1456506146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456508004/">1456508004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456508831/">1456508831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456509686/">1456509686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456509925/">1456509925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456510119/">1456510119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456510120/">1456510120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456511533/">1456511533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456511534/">1456511534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456511905/">1456511905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456512325/">1456512325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456515124/">1456515124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456515566/">1456515566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456516890/">1456516890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456517056/">1456517056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456517067/">1456517067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456518265/">1456518265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456518566/">1456518566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456519165/">1456519165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456519647/">1456519647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456519703/">1456519703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456519886/">1456519886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456519946/">1456519946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456520545/">1456520545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456521264/">1456521264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456521926/">1456521926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456523554/">1456523554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456524091/">1456524091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456526124/">1456526124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456526548/">1456526548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456527512/">1456527512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456528531/">1456528531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456529554/">1456529554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456530445/">1456530445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456535727/">1456535727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456537046/">1456537046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456537586/">1456537586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456539386/">1456539386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456541006/">1456541006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456549466/">1456549466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456550786/">1456550786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456557806/">1456557806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456563758/">1456563758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456565487/">1456565487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456569986/">1456569986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456570466/">1456570466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456577786/">1456577786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456583786/">1456583786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456590806/">1456590806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456599326/">1456599326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456604546/">1456604546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456609226/">1456609226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456613186/">1456613186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456613306/">1456613306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456613966/">1456613966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456614852/">1456614852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456623673/">1456623673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456631233/">1456631233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456633092/">1456633092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456647973/">1456647973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456650012/">1456650012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456677101/">1456677101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456690452/">1456690452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456696945/">1456696945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456699452/">1456699452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456699513/">1456699513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456700293/">1456700293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456702513/">1456702513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456703053/">1456703053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456705873/">1456705873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456706652/">1456706652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456717693/">1456717693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456722317/">1456722317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456722977/">1456722977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456728492/">1456728492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456729033/">1456729033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456729573/">1456729573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456729812/">1456729812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456731493/">1456731493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456733473/">1456733473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456734134/">1456734134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456737678/">1456737678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456740244/">1456740244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456742714/">1456742714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456744632/">1456744632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456750332/">1456750332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456750393/">1456750393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456751474/">1456751474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456751952/">1456751952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456752073/">1456752073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456752673/">1456752673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456753453/">1456753453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456753813/">1456753813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456755913/">1456755913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456756933/">1456756933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456757171/">1456757171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456757651/">1456757651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456758399/">1456758399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456758587/">1456758587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456758914/">1456758914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456759513/">1456759513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456760353/">1456760353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456760773/">1456760773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456761973/">1456761973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456763835/">1456763835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456764013/">1456764013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456764571/">1456764571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456764853/">1456764853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456765154/">1456765154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456765656/">1456765656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456766969/">1456766969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456767806/">1456767806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456767992/">1456767992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456769065/">1456769065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456769788/">1456769788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456770026/">1456770026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456770086/">1456770086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456770146/">1456770146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456770209/">1456770209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456770805/">1456770805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456771167/">1456771167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456772146/">1456772146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456772426/">1456772426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456773087/">1456773087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456773447/">1456773447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456773505/">1456773505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456774190/">1456774190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456774191/">1456774191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456774842/">1456774842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456775132/">1456775132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456775488/">1456775488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456775614/">1456775614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456777709/">1456777709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456778006/">1456778006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456778306/">1456778306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456779386/">1456779386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456779747/">1456779747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456781067/">1456781067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456781607/">1456781607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456781667/">1456781667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456781727/">1456781727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456782806/">1456782806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456783047/">1456783047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456783706/">1456783706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456784666/">1456784666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456784785/">1456784785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456787486/">1456787486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456790246/">1456790246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456790903/">1456790903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456792474/">1456792474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456794150/">1456794150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456801173/">1456801173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456810833/">1456810833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456811493/">1456811493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456814433/">1456814433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456815333/">1456815333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456817013/">1456817013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456820253/">1456820253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456820314/">1456820314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456821642/">1456821642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456822481/">1456822481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456823682/">1456823682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456823742/">1456823742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456825662/">1456825662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456826141/">1456826141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456832681/">1456832681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456832742/">1456832742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456833221/">1456833221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456834181/">1456834181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456838621/">1456838621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456841802/">1456841802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456841921/">1456841921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456842102/">1456842102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456842402/">1456842402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456842521/">1456842521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456842821/">1456842821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456842881/">1456842881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456843241/">1456843241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456844995/">1456844995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456845402/">1456845402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456847567/">1456847567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456847739/">1456847739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456847803/">1456847803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456848259/">1456848259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456848381/">1456848381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456853425/">1456853425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456855730/">1456855730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456856017/">1456856017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456856671/">1456856671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456857111/">1456857111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456858170/">1456858170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456858231/">1456858231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456859195/">1456859195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456860032/">1456860032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456860871/">1456860871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456860931/">1456860931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456861053/">1456861053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456862491/">1456862491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456862880/">1456862880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456867471/">1456867471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456867771/">1456867771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456869271/">1456869271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456869993/">1456869993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456873531/">1456873531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456874552/">1456874552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456875034/">1456875034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456875271/">1456875271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456875811/">1456875811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456878812/">1456878812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456882532/">1456882532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456883851/">1456883851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456884811/">1456884811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456888059/">1456888059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456889791/">1456889791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456891292/">1456891292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456892975/">1456892975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456893331/">1456893331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456902031/">1456902031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456903474/">1456903474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456904071/">1456904071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456907431/">1456907431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456910071/">1456910071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456910971/">1456910971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456914151/">1456914151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456916131/">1456916131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456916311/">1456916311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456916431/">1456916431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456916562/">1456916562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456916671/">1456916671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456917518/">1456917518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456918351/">1456918351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456920273/">1456920273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456920991/">1456920991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456929572/">1456929572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456930771/">1456930771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456930882/">1456930882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456931420/">1456931420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456931658/">1456931658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456932562/">1456932562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456934885/">1456934885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456935861/">1456935861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456936639/">1456936639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456937421/">1456937421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456937481/">1456937481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456938579/">1456938579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456940359/">1456940359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456941019/">1456941019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456941201/">1456941201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456941833/">1456941833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456942588/">1456942588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456943659/">1456943659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456944561/">1456944561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456947381/">1456947381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456947502/">1456947502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456949541/">1456949541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456949661/">1456949661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456949841/">1456949841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456950201/">1456950201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456950501/">1456950501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456951342/">1456951342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456951821/">1456951821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456952241/">1456952241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456953081/">1456953081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456953140/">1456953140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456954645/">1456954645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456954764/">1456954764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456954883/">1456954883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456955964/">1456955964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456956258/">1456956258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456956989/">1456956989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456957226/">1456957226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456958014/">1456958014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456958429/">1456958429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456959931/">1456959931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456960468/">1456960468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456960530/">1456960530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456960769/">1456960769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456961787/">1456961787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456964789/">1456964789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456965807/">1456965807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456967664/">1456967664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456970064/">1456970064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456973063/">1456973063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456973123/">1456973123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456976304/">1456976304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456977144/">1456977144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456977203/">1456977203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456978104/">1456978104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456979520/">1456979520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456980504/">1456980504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456983201/">1456983201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456986384/">1456986384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456986564/">1456986564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456992278/">1456992278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456993883/">1456993883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456994965/">1456994965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456995143/">1456995143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456995267/">1456995267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1456999111/">1456999111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457002711/">1457002711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457003272/">1457003272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457003431/">1457003431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457004575/">1457004575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457005351/">1457005351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457006311/">1457006311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457009191/">1457009191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457012558/">1457012558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457013571/">1457013571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457014651/">1457014651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457015792/">1457015792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457018195/">1457018195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457019452/">1457019452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457020065/">1457020065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457020594/">1457020594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457021291/">1457021291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457021335/">1457021335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457022067/">1457022067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457023171/">1457023171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457026291/">1457026291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457026362/">1457026362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457026414/">1457026414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457027433/">1457027433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457028346/">1457028346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457028698/">1457028698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457028995/">1457028995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457030013/">1457030013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457030311/">1457030311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457031859/">1457031859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457034095/">1457034095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457034151/">1457034151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457034272/">1457034272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457035565/">1457035565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457036252/">1457036252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457038712/">1457038712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457039043/">1457039043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457039851/">1457039851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457040031/">1457040031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457040755/">1457040755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457041314/">1457041314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457041494/">1457041494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457041615/">1457041615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457041798/">1457041798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457041974/">1457041974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457042158/">1457042158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457042275/">1457042275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457045633/">1457045633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457047494/">1457047494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457047613/">1457047613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457051938/">1457051938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457051939/">1457051939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457053708/">1457053708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457053947/">1457053947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457054187/">1457054187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457057486/">1457057486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457057727/">1457057727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457059408/">1457059408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457060225/">1457060225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457060226/">1457060226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457061928/">1457061928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457064413/">1457064413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457064414/">1457064414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457065285/">1457065285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457066306/">1457066306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457067266/">1457067266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457068947/">1457068947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457070031/">1457070031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457076387/">1457076387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457083839/">1457083839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457084547/">1457084547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457084968/">1457084968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457088988/">1457088988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457090486/">1457090486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457095044/">1457095044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457095229/">1457095229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457096550/">1457096550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457097508/">1457097508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457105849/">1457105849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457107105/">1457107105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457108304/">1457108304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457110584/">1457110584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457110879/">1457110879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457111180/">1457111180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457111674/">1457111674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457112564/">1457112564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457113763/">1457113763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457120879/">1457120879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457130987/">1457130987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131048/">1457131048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131106/">1457131106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131226/">1457131226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131286/">1457131286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131347/">1457131347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131407/">1457131407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131467/">1457131467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131526/">1457131526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131586/">1457131586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131706/">1457131706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131767/">1457131767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131889/">1457131889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457131890/">1457131890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457132427/">1457132427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457132559/">1457132559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457132907/">1457132907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457133028/">1457133028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457133087/">1457133087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457133985/">1457133985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457135367/">1457135367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457135431/">1457135431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457135908/">1457135908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457136339/">1457136339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457142987/">1457142987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457144247/">1457144247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457149406/">1457149406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457149707/">1457149707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457150071/">1457150071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457152373/">1457152373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457152520/">1457152520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457154860/">1457154860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457163320/">1457163320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457164640/">1457164640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457171420/">1457171420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457184973/">1457184973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457185018/">1457185018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457185072/">1457185072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457186691/">1457186691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457186732/">1457186732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457186808/">1457186808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457186842/">1457186842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457186892/">1457186892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457186935/">1457186935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457188634/">1457188634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457188743/">1457188743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457188853/">1457188853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457188959/">1457188959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457190177/">1457190177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457190314/">1457190314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457191280/">1457191280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457198600/">1457198600/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457198840/">1457198840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457200824/">1457200824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457200960/">1457200960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457201421/">1457201421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457202380/">1457202380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457205140/">1457205140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457211260/">1457211260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457211320/">1457211320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457212340/">1457212340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457222528/">1457222528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457222948/">1457222948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457223788/">1457223788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457223849/">1457223849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457224329/">1457224329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457236208/">1457236208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457236748/">1457236748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457248148/">1457248148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457262069/">1457262069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457271488/">1457271488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457289672/">1457289672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457294650/">1457294650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457295008/">1457295008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457295308/">1457295308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457296209/">1457296209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457298009/">1457298009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457301311/">1457301311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457303950/">1457303950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457308148/">1457308148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457308869/">1457308869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457311928/">1457311928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457321770/">1457321770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457322668/">1457322668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457325444/">1457325444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457329508/">1457329508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457329869/">1457329869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457334278/">1457334278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457337860/">1457337860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457341608/">1457341608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457342229/">1457342229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457343019/">1457343019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457344869/">1457344869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457347089/">1457347089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457349549/">1457349549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457351529/">1457351529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457355669/">1457355669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457358068/">1457358068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457359210/">1457359210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457359641/">1457359641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457362347/">1457362347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457363307/">1457363307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457364203/">1457364203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457364560/">1457364560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457364980/">1457364980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457365340/">1457365340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457365760/">1457365760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457366360/">1457366360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457367084/">1457367084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457368467/">1457368467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457369844/">1457369844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457370446/">1457370446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457370602/">1457370602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457371227/">1457371227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457374107/">1457374107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457374167/">1457374167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457374407/">1457374407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457374707/">1457374707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457375607/">1457375607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457380047/">1457380047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457385627/">1457385627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457385747/">1457385747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457385807/">1457385807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457385868/">1457385868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457385869/">1457385869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457385987/">1457385987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457386107/">1457386107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457386287/">1457386287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457386347/">1457386347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457386466/">1457386466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457386527/">1457386527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457387187/">1457387187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457387188/">1457387188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457388507/">1457388507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457388747/">1457388747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457389047/">1457389047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457389228/">1457389228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457389527/">1457389527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457389964/">1457389964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457399181/">1457399181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457400648/">1457400648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457401008/">1457401008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457401487/">1457401487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457401728/">1457401728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457402147/">1457402147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457402267/">1457402267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457402387/">1457402387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457405990/">1457405990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457407069/">1457407069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457407428/">1457407428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457418386/">1457418386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457419409/">1457419409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457419475/">1457419475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457419587/">1457419587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457421621/">1457421621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457422881/">1457422881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457423605/">1457423605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457423725/">1457423725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457423841/">1457423841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457424506/">1457424506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457424685/">1457424685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457424925/">1457424925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457425042/">1457425042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457425704/">1457425704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457428166/">1457428166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457430561/">1457430561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457431127/">1457431127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457431728/">1457431728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457431943/">1457431943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457432573/">1457432573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457432574/">1457432574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457436564/">1457436564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457437523/">1457437523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457437884/">1457437884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457438364/">1457438364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457438967/">1457438967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457440523/">1457440523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457441304/">1457441304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457441425/">1457441425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457441784/">1457441784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457442925/">1457442925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457443224/">1457443224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457443704/">1457443704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457444665/">1457444665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457444845/">1457444845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457446826/">1457446826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457447003/">1457447003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457447124/">1457447124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457447185/">1457447185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457448984/">1457448984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457449396/">1457449396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457449823/">1457449823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457449944/">1457449944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457450367/">1457450367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457451506/">1457451506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457451507/">1457451507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457452467/">1457452467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457453070/">1457453070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457453964/">1457453964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457453965/">1457453965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457454983/">1457454983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457455285/">1457455285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457457144/">1457457144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457458402/">1457458402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457459546/">1457459546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457461152/">1457461152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457461211/">1457461211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457462413/">1457462413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457464095/">1457464095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457464515/">1457464515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457465053/">1457465053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457465113/">1457465113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457465537/">1457465537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457465712/">1457465712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457466553/">1457466553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457468593/">1457468593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457470513/">1457470513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457470873/">1457470873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457472012/">1457472012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457472972/">1457472972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457474656/">1457474656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457474895/">1457474895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457475136/">1457475136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457476753/">1457476753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457480473/">1457480473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457482093/">1457482093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457482157/">1457482157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457482871/">1457482871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457482933/">1457482933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457482992/">1457482992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457483052/">1457483052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457483173/">1457483173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457483293/">1457483293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457485153/">1457485153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457488873/">1457488873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457490253/">1457490253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457490973/">1457490973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457491094/">1457491094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457491330/">1457491330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457492173/">1457492173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457493076/">1457493076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457494417/">1457494417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457495716/">1457495716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457495773/">1457495773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457496502/">1457496502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457496925/">1457496925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457498177/">1457498177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457498233/">1457498233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457498292/">1457498292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457500213/">1457500213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457501254/">1457501254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457502193/">1457502193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457507833/">1457507833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457509453/">1457509453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457509692/">1457509692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457512217/">1457512217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457514793/">1457514793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457516532/">1457516532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457518046/">1457518046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457518453/">1457518453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457518573/">1457518573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457520496/">1457520496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457521693/">1457521693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457522411/">1457522411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457522713/">1457522713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457528654/">1457528654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457530066/">1457530066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457530335/">1457530335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457530679/">1457530679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457531233/">1457531233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457531953/">1457531953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457532011/">1457532011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457532916/">1457532916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457538081/">1457538081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457538564/">1457538564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457538682/">1457538682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457539351/">1457539351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457539449/">1457539449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457543481/">1457543481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457543665/">1457543665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457547924/">1457547924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457548764/">1457548764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457549906/">1457549906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457549965/">1457549965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457550625/">1457550625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457551764/">1457551764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457552784/">1457552784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457552964/">1457552964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457553443/">1457553443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457554824/">1457554824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457555548/">1457555548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457556923/">1457556923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457558124/">1457558124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457558243/">1457558243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457559743/">1457559743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457559744/">1457559744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457560045/">1457560045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457560401/">1457560401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457560643/">1457560643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457561363/">1457561363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457562202/">1457562202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457562503/">1457562503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457562564/">1457562564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457562625/">1457562625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457562803/">1457562803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457563584/">1457563584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457563703/">1457563703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457563883/">1457563883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457564243/">1457564243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457565324/">1457565324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457566824/">1457566824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457566944/">1457566944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457567061/">1457567061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457567843/">1457567843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457567905/">1457567905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457568264/">1457568264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457569462/">1457569462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457571504/">1457571504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457573425/">1457573425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457575464/">1457575464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457579602/">1457579602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457580444/">1457580444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457580512/">1457580512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457582333/">1457582333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457583505/">1457583505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457583827/">1457583827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457585604/">1457585604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457585964/">1457585964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457587524/">1457587524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457588186/">1457588186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457588543/">1457588543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457591904/">1457591904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457597423/">1457597423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457597485/">1457597485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457597663/">1457597663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457600797/">1457600797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457601923/">1457601923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457603483/">1457603483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457603544/">1457603544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457603782/">1457603782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457603962/">1457603962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457604402/">1457604402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457606542/">1457606542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457607333/">1457607333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457608405/">1457608405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457611224/">1457611224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457612304/">1457612304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457612664/">1457612664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457613149/">1457613149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457614464/">1457614464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457616864/">1457616864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457617644/">1457617644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457621484/">1457621484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457621724/">1457621724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457622447/">1457622447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457623764/">1457623764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457627906/">1457627906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457629106/">1457629106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457630723/">1457630723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457633486/">1457633486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457634026/">1457634026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457634264/">1457634264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457637862/">1457637862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457642847/">1457642847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457647764/">1457647764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457648126/">1457648126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457648366/">1457648366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457648784/">1457648784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457648906/">1457648906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457649024/">1457649024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457649145/">1457649145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457649386/">1457649386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457649506/">1457649506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457650155/">1457650155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457650635/">1457650635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457650872/">1457650872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457650958/">1457650958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457651293/">1457651293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457651373/">1457651373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457652256/">1457652256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457652435/">1457652435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457653575/">1457653575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457654472/">1457654472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457655615/">1457655615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457656033/">1457656033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457656995/">1457656995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457657835/">1457657835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457658916/">1457658916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457659274/">1457659274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457662875/">1457662875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457662932/">1457662932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457663054/">1457663054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457663176/">1457663176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457663352/">1457663352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457663655/">1457663655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457664434/">1457664434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457665094/">1457665094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457666610/">1457666610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457667516/">1457667516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457669716/">1457669716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457672116/">1457672116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457672175/">1457672175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457674404/">1457674404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457677216/">1457677216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457678112/">1457678112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457680334/">1457680334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457683452/">1457683452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457683813/">1457683813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457685287/">1457685287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457688314/">1457688314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457690724/">1457690724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457690801/">1457690801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457691316/">1457691316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457692395/">1457692395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457694675/">1457694675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457696292/">1457696292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457697372/">1457697372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457697555/">1457697555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457699593/">1457699593/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457702234/">1457702234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457704275/">1457704275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457704276/">1457704276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457706255/">1457706255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457707333/">1457707333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457707692/">1457707692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457707812/">1457707812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457708823/">1457708823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457708847/">1457708847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457709257/">1457709257/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457711954/">1457711954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457712915/">1457712915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457714175/">1457714175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457714176/">1457714176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457714415/">1457714415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457715614/">1457715614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457715750/">1457715750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457716936/">1457716936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457719035/">1457719035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457719095/">1457719095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457721567/">1457721567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457721872/">1457721872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457722109/">1457722109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457722168/">1457722168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457723079/">1457723079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457723908/">1457723908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457724027/">1457724027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457724989/">1457724989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457725348/">1457725348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457726865/">1457726865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457728047/">1457728047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457728589/">1457728589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457730928/">1457730928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457732667/">1457732667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457733373/">1457733373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457733374/">1457733374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457733567/">1457733567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457733997/">1457733997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457736268/">1457736268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457736388/">1457736388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457737828/">1457737828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457738428/">1457738428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457739027/">1457739027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457739390/">1457739390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457739567/">1457739567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457741427/">1457741427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457741908/">1457741908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457742988/">1457742988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457743947/">1457743947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457745447/">1457745447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457748448/">1457748448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457748687/">1457748687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457749888/">1457749888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457750488/">1457750488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457758827/">1457758827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457760507/">1457760507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457761467/">1457761467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457769208/">1457769208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457772147/">1457772147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457792967/">1457792967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457798928/">1457798928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457799867/">1457799867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457803347/">1457803347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457811029/">1457811029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457812648/">1457812648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457814329/">1457814329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457821108/">1457821108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457825487/">1457825487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457826507/">1457826507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457832989/">1457832989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457842227/">1457842227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457876007/">1457876007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457876727/">1457876727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457890887/">1457890887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457896707/">1457896707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457910567/">1457910567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457923647/">1457923647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457924629/">1457924629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457933964/">1457933964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457938285/">1457938285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457939248/">1457939248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457945010/">1457945010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457949201/">1457949201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457951908/">1457951908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457953223/">1457953223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457954964/">1457954964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457955813/">1457955813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457962230/">1457962230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457963011/">1457963011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457963310/">1457963310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457963384/">1457963384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457966552/">1457966552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457969123/">1457969123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457970146/">1457970146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457970803/">1457970803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457970864/">1457970864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457971894/">1457971894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457972910/">1457972910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457973087/">1457973087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457974163/">1457974163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457976151/">1457976151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457976387/">1457976387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457976525/">1457976525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457977232/">1457977232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457977471/">1457977471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457977644/">1457977644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457978914/">1457978914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457979083/">1457979083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457979330/">1457979330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457980051/">1457980051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457980762/">1457980762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457980825/">1457980825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457982239/">1457982239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457982270/">1457982270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457982387/">1457982387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457983064/">1457983064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457985641/">1457985641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457985833/">1457985833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457985874/">1457985874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457986180/">1457986180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457986657/">1457986657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457986902/">1457986902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457987910/">1457987910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457988819/">1457988819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457988997/">1457988997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457989840/">1457989840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457990675/">1457990675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457990676/">1457990676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457990917/">1457990917/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457991034/">1457991034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457991880/">1457991880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457993080/">1457993080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457994555/">1457994555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457994621/">1457994621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457994973/">1457994973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457995450/">1457995450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457997762/">1457997762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1457999253/">1457999253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458000236/">1458000236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458001238/">1458001238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458002372/">1458002372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458002503/">1458002503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458002554/">1458002554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458002683/">1458002683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458003156/">1458003156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458003277/">1458003277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458006758/">1458006758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458007466/">1458007466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458007467/">1458007467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458007468/">1458007468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458007780/">1458007780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458008073/">1458008073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458008918/">1458008918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458009221/">1458009221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458009518/">1458009518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458010297/">1458010297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458012223/">1458012223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458012458/">1458012458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458014380/">1458014380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458014439/">1458014439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458014677/">1458014677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458014922/">1458014922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458016063/">1458016063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458017980/">1458017980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458018881/">1458018881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458019234/">1458019234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458019362/">1458019362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458021850/">1458021850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458025116/">1458025116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458027040/">1458027040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458027241/">1458027241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458027822/">1458027822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458027880/">1458027880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458028538/">1458028538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458030393/">1458030393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458033455/">1458033455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458035135/">1458035135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458036099/">1458036099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458036231/">1458036231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458037959/">1458037959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458038134/">1458038134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458040658/">1458040658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458044193/">1458044193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458047673/">1458047673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458049057/">1458049057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458050504/">1458050504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458050567/">1458050567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458051758/">1458051758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458051820/">1458051820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458052270/">1458052270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458052442/">1458052442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458054273/">1458054273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458054336/">1458054336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458054755/">1458054755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458056801/">1458056801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458057092/">1458057092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458058470/">1458058470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458058528/">1458058528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458058956/">1458058956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458060268/">1458060268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458060816/">1458060816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458060926/">1458060926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458060994/">1458060994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458061228/">1458061228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458061532/">1458061532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458062864/">1458062864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458063934/">1458063934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458064299/">1458064299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458064598/">1458064598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458064838/">1458064838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458064884/">1458064884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458065318/">1458065318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458067107/">1458067107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458067169/">1458067169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458067228/">1458067228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458067471/">1458067471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458068185/">1458068185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458068662/">1458068662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458069209/">1458069209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458069448/">1458069448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458069507/">1458069507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458073843/">1458073843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458076053/">1458076053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458077016/">1458077016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458077194/">1458077194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458077982/">1458077982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458079910/">1458079910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458080024/">1458080024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458080502/">1458080502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458081110/">1458081110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458081169/">1458081169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458081472/">1458081472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458081701/">1458081701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458082368/">1458082368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458083444/">1458083444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458084470/">1458084470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458085547/">1458085547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458086438/">1458086438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458087104/">1458087104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458087288/">1458087288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458088302/">1458088302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458088423/">1458088423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458090282/">1458090282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458092082/">1458092082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458093541/">1458093541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458093603/">1458093603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458095022/">1458095022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458099528/">1458099528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458099697/">1458099697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458103723/">1458103723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458103840/">1458103840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458107442/">1458107442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458107859/">1458107859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458108043/">1458108043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458108882/">1458108882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458109060/">1458109060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458111409/">1458111409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458114158/">1458114158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458114715/">1458114715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458115180/">1458115180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458115784/">1458115784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458116626/">1458116626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458117817/">1458117817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458120579/">1458120579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458120884/">1458120884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458121421/">1458121421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458121778/">1458121778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458122271/">1458122271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458122562/">1458122562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458123047/">1458123047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458123108/">1458123108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458123167/">1458123167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458124062/">1458124062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458128080/">1458128080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458129850/">1458129850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458132405/">1458132405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458132582/">1458132582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458134503/">1458134503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458135342/">1458135342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458135762/">1458135762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458136425/">1458136425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458136842/">1458136842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458137121/">1458137121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458137621/">1458137621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458138047/">1458138047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458138224/">1458138224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458138817/">1458138817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458140209/">1458140209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458140836/">1458140836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458141943/">1458141943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458142968/">1458142968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458143316/">1458143316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458143624/">1458143624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458144358/">1458144358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458145237/">1458145237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458145300/">1458145300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458145301/">1458145301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458145420/">1458145420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458146219/">1458146219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458146450/">1458146450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458146496/">1458146496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458147401/">1458147401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458147854/">1458147854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458149685/">1458149685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458152375/">1458152375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458152504/">1458152504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458152856/">1458152856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458153284/">1458153284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458153412/">1458153412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458154368/">1458154368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458155503/">1458155503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458156702/">1458156702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458158255/">1458158255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458158387/">1458158387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458158502/">1458158502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458160006/">1458160006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458160426/">1458160426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458160670/">1458160670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458160908/">1458160908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458161028/">1458161028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458161146/">1458161146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458161208/">1458161208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458161323/">1458161323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458163959/">1458163959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458165040/">1458165040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458165588/">1458165588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458166989/">1458166989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458167957/">1458167957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458168186/">1458168186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458168672/">1458168672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458169095/">1458169095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458169211/">1458169211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458169267/">1458169267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458169461/">1458169461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458172338/">1458172338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458176950/">1458176950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458178330/">1458178330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458179708/">1458179708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458181091/">1458181091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458181390/">1458181390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458181571/">1458181571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458183008/">1458183008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458183675/">1458183675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458184048/">1458184048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458185169/">1458185169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458185776/">1458185776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458190689/">1458190689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458191475/">1458191475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458195494/">1458195494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458196099/">1458196099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458199810/">1458199810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458200835/">1458200835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458204367/">1458204367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458204554/">1458204554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458204797/">1458204797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458205147/">1458205147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458205459/">1458205459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458206953/">1458206953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458207910/">1458207910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458208818/">1458208818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458209255/">1458209255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458209718/">1458209718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458213137/">1458213137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458215299/">1458215299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458216016/">1458216016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458216475/">1458216475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458216855/">1458216855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458218949/">1458218949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458221352/">1458221352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458223211/">1458223211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458223987/">1458223987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458226508/">1458226508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458227254/">1458227254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458227745/">1458227745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458230545/">1458230545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458230681/">1458230681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458232156/">1458232156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458232516/">1458232516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458232631/">1458232631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458234687/">1458234687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458236773/">1458236773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458240975/">1458240975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458242061/">1458242061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458243192/">1458243192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458243609/">1458243609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458243673/">1458243673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458243851/">1458243851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458244513/">1458244513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458246367/">1458246367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458246492/">1458246492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458247219/">1458247219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458249137/">1458249137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458249378/">1458249378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458251232/">1458251232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458252488/">1458252488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458254467/">1458254467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458254589/">1458254589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458255316/">1458255316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458257647/">1458257647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458258369/">1458258369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458259763/">1458259763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458259764/">1458259764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458259937/">1458259937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458260894/">1458260894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458262030/">1458262030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458262954/">1458262954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458263891/">1458263891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458265875/">1458265875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458271507/">1458271507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458272172/">1458272172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458273873/">1458273873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458275288/">1458275288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458275419/">1458275419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458276372/">1458276372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458280033/">1458280033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458280959/">1458280959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458284477/">1458284477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458284678/">1458284678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458285794/">1458285794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458289757/">1458289757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458290347/">1458290347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458291006/">1458291006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458291222/">1458291222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458291432/">1458291432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458291620/">1458291620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458294491/">1458294491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458294859/">1458294859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458296168/">1458296168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458296778/">1458296778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458297912/">1458297912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458300833/">1458300833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458301271/">1458301271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458305109/">1458305109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458306446/">1458306446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458308352/">1458308352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458308840/">1458308840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458311484/">1458311484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458312256/">1458312256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458313643/">1458313643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458313869/">1458313869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458314046/">1458314046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458314227/">1458314227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458315070/">1458315070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458315372/">1458315372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458317293/">1458317293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458317888/">1458317888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319394/">1458319394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319531/">1458319531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319709/">1458319709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319865/">1458319865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319868/">1458319868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319870/">1458319870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319877/">1458319877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319878/">1458319878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458319883/">1458319883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320043/">1458320043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320048/">1458320048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320052/">1458320052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320055/">1458320055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320057/">1458320057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320061/">1458320061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320205/">1458320205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320207/">1458320207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320210/">1458320210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320217/">1458320217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320360/">1458320360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320361/">1458320361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320364/">1458320364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320370/">1458320370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320372/">1458320372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320374/">1458320374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320508/">1458320508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320553/">1458320553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320557/">1458320557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320560/">1458320560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320565/">1458320565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320572/">1458320572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320580/">1458320580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320709/">1458320709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320793/">1458320793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320829/">1458320829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320831/">1458320831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320919/">1458320919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320922/">1458320922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458320928/">1458320928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458321063/">1458321063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458321065/">1458321065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458321068/">1458321068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458321076/">1458321076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458321080/">1458321080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458321088/">1458321088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458322258/">1458322258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458322575/">1458322575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458323115/">1458323115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458323241/">1458323241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458323545/">1458323545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458323720/">1458323720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458324500/">1458324500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458325333/">1458325333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458325512/">1458325512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458327188/">1458327188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458327491/">1458327491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458328525/">1458328525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458329660/">1458329660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458330434/">1458330434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458330616/">1458330616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458331293/">1458331293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458331772/">1458331772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458332087/">1458332087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458332502/">1458332502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458332561/">1458332561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458333058/">1458333058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458333159/">1458333159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458333349/">1458333349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458334416/">1458334416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458334772/">1458334772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458335863/">1458335863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458336758/">1458336758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458337423/">1458337423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458338733/">1458338733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458341322/">1458341322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458342105/">1458342105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458342333/">1458342333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458342490/">1458342490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458344676/">1458344676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458346361/">1458346361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458346475/">1458346475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458347499/">1458347499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458348344/">1458348344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458351097/">1458351097/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458351344/">1458351344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458351638/">1458351638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458354867/">1458354867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458356865/">1458356865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458357697/">1458357697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458360153/">1458360153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458361838/">1458361838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458372993/">1458372993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458382244/">1458382244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458385775/">1458385775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458387084/">1458387084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458394606/">1458394606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458401189/">1458401189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458402994/">1458402994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458404909/">1458404909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458408673/">1458408673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458409177/">1458409177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458415105/">1458415105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458419430/">1458419430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458423087/">1458423087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458430202/">1458430202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458436769/">1458436769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458437312/">1458437312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458440311/">1458440311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458441932/">1458441932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458447996/">1458447996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458451992/">1458451992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458461850/">1458461850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458466710/">1458466710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458468731/">1458468731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458471633/">1458471633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458473297/">1458473297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458473405/">1458473405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458490167/">1458490167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458519938/">1458519938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458520293/">1458520293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458520356/">1458520356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458527193/">1458527193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458527547/">1458527547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458528392/">1458528392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458531460/">1458531460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458534029/">1458534029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458534637/">1458534637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458538250/">1458538250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458538530/">1458538530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458538602/">1458538602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458538830/">1458538830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458539194/">1458539194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458539436/">1458539436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458539551/">1458539551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458540513/">1458540513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458543511/">1458543511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458544597/">1458544597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458545487/">1458545487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458548493/">1458548493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458549022/">1458549022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458552454/">1458552454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458557560/">1458557560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458560793/">1458560793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458562023/">1458562023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458562661/">1458562661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458562707/">1458562707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458562828/">1458562828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458562887/">1458562887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458565231/">1458565231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458565449/">1458565449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458566679/">1458566679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458566916/">1458566916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458569385/">1458569385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458569971/">1458569971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458570268/">1458570268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458570939/">1458570939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458571737/">1458571737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458571854/">1458571854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458572547/">1458572547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458574475/">1458574475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458575555/">1458575555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458575791/">1458575791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458576319/">1458576319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458576595/">1458576595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458576699/">1458576699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458576810/">1458576810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458577110/">1458577110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458577295/">1458577295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458577528/">1458577528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458577987/">1458577987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458579159/">1458579159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458580655/">1458580655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458581358/">1458581358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458581493/">1458581493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458581673/">1458581673/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458581850/">1458581850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458582693/">1458582693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458583070/">1458583070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458583236/">1458583236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458583447/">1458583447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458584195/">1458584195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458584373/">1458584373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458584548/">1458584548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458588167/">1458588167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458589416/">1458589416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458589594/">1458589594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458589989/">1458589989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458590667/">1458590667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458591148/">1458591148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458591538/">1458591538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458592307/">1458592307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458593439/">1458593439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458595023/">1458595023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458595472/">1458595472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458596021/">1458596021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458596456/">1458596456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458597753/">1458597753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458601276/">1458601276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458601651/">1458601651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458602247/">1458602247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458602899/">1458602899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458603041/">1458603041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458603805/">1458603805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458604346/">1458604346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458604395/">1458604395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458605417/">1458605417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458606444/">1458606444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458609049/">1458609049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458610783/">1458610783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458611872/">1458611872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458613728/">1458613728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458615160/">1458615160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458616357/">1458616357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458616849/">1458616849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458619836/">1458619836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458620379/">1458620379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458622846/">1458622846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458624679/">1458624679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458626740/">1458626740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458627345/">1458627345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458631723/">1458631723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458632855/">1458632855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458633276/">1458633276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458633459/">1458633459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458636936/">1458636936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458641261/">1458641261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458641324/">1458641324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458642199/">1458642199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458642530/">1458642530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458642645/">1458642645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458645661/">1458645661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458646252/">1458646252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458646357/">1458646357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458649365/">1458649365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458650076/">1458650076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458650373/">1458650373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458653014/">1458653014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458653078/">1458653078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458655126/">1458655126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458656076/">1458656076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458656978/">1458656978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458658306/">1458658306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458659465/">1458659465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458663145/">1458663145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458667893/">1458667893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458668726/">1458668726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458669560/">1458669560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458669924/">1458669924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458677543/">1458677543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458678026/">1458678026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458678083/">1458678083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458678139/">1458678139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458678140/">1458678140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458678198/">1458678198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458678320/">1458678320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458679217/">1458679217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458680360/">1458680360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458680426/">1458680426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458680712/">1458680712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458681220/">1458681220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458681405/">1458681405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458681876/">1458681876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458682314/">1458682314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458683445/">1458683445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458683449/">1458683449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458683570/">1458683570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458684766/">1458684766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458687632/">1458687632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458687870/">1458687870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458688039/">1458688039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458688401/">1458688401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458688702/">1458688702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458689301/">1458689301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458689431/">1458689431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458690320/">1458690320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458690979/">1458690979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458691096/">1458691096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458692482/">1458692482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458692899/">1458692899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458693082/">1458693082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458693685/">1458693685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458694106/">1458694106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458695612/">1458695612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458696978/">1458696978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458697722/">1458697722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458698870/">1458698870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458699654/">1458699654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458700398/">1458700398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458701258/">1458701258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458707241/">1458707241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458708201/">1458708201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458708976/">1458708976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458709158/">1458709158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458709505/">1458709505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458710068/">1458710068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458711083/">1458711083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458713428/">1458713428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458716944/">1458716944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458718876/">1458718876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458721858/">1458721858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458722252/">1458722252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458723449/">1458723449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458725848/">1458725848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458726267/">1458726267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458727025/">1458727025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458729559/">1458729559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458730198/">1458730198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458730470/">1458730470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458731319/">1458731319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458732708/">1458732708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458732724/">1458732724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458733769/">1458733769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458737079/">1458737079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458738987/">1458738987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458739350/">1458739350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458739544/">1458739544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458739720/">1458739720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458740451/">1458740451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458742958/">1458742958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458743535/">1458743535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458743553/">1458743553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458743977/">1458743977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458744040/">1458744040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458745527/">1458745527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458746278/">1458746278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458746969/">1458746969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458747655/">1458747655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458747656/">1458747656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458747940/">1458747940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458748185/">1458748185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458748515/">1458748515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458748767/">1458748767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458749142/">1458749142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458750687/">1458750687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458750936/">1458750936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458752564/">1458752564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458752863/">1458752863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458754001/">1458754001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458754262/">1458754262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458754477/">1458754477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458754596/">1458754596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458755938/">1458755938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458756239/">1458756239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458756451/">1458756451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458757060/">1458757060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458757309/">1458757309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458757541/">1458757541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458759883/">1458759883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458760650/">1458760650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458761259/">1458761259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458763183/">1458763183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458765022/">1458765022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458766246/">1458766246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458766866/">1458766866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458769176/">1458769176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458769177/">1458769177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458769370/">1458769370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458769483/">1458769483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458769668/">1458769668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458769956/">1458769956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458770198/">1458770198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458770650/">1458770650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458771589/">1458771589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458771641/">1458771641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458771830/">1458771830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458773738/">1458773738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458775960/">1458775960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458777186/">1458777186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458777189/">1458777189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458777192/">1458777192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458777317/">1458777317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458777407/">1458777407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458778070/">1458778070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458778118/">1458778118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458779558/">1458779558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458780442/">1458780442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458780810/">1458780810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458781487/">1458781487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458781750/">1458781750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458782051/">1458782051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458782499/">1458782499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458783585/">1458783585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458783703/">1458783703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458785746/">1458785746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458786432/">1458786432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458786863/">1458786863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458791567/">1458791567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458792528/">1458792528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458792529/">1458792529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458792726/">1458792726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458793209/">1458793209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458794674/">1458794674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458794684/">1458794684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458795283/">1458795283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458795797/">1458795797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458796101/">1458796101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458796419/">1458796419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458796880/">1458796880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458797390/">1458797390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458797737/">1458797737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458798343/">1458798343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458798715/">1458798715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458798893/">1458798893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458801518/">1458801518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458801949/">1458801949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458802372/">1458802372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458805012/">1458805012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458805540/">1458805540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458805705/">1458805705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458808007/">1458808007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458808187/">1458808187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458808283/">1458808283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458811666/">1458811666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458813224/">1458813224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458815578/">1458815578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458816562/">1458816562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819088/">1458819088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819427/">1458819427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819436/">1458819436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819562/">1458819562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819567/">1458819567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819714/">1458819714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819718/">1458819718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819720/">1458819720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819840/">1458819840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819846/">1458819846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819985/">1458819985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819989/">1458819989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458819990/">1458819990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820115/">1458820115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820120/">1458820120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820127/">1458820127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820247/">1458820247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820255/">1458820255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820380/">1458820380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820384/">1458820384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820520/">1458820520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820522/">1458820522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820525/">1458820525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820643/">1458820643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458820645/">1458820645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458825471/">1458825471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458826674/">1458826674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458827208/">1458827208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458827342/">1458827342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458827383/">1458827383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458827736/">1458827736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458828639/">1458828639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458829001/">1458829001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458829592/">1458829592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458830852/">1458830852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458831335/">1458831335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458832169/">1458832169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458832534/">1458832534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458832843/">1458832843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458833679/">1458833679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458833803/">1458833803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834158/">1458834158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834330/">1458834330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834399/">1458834399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834577/">1458834577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834632/">1458834632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834722/">1458834722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458834838/">1458834838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458835379/">1458835379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458835512/">1458835512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458836034/">1458836034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458839021/">1458839021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458839460/">1458839460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458839461/">1458839461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458839462/">1458839462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458840666/">1458840666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458841311/">1458841311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458841759/">1458841759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458845239/">1458845239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458845868/">1458845868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458846090/">1458846090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458846346/">1458846346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458847538/">1458847538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458848017/">1458848017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458848496/">1458848496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458849935/">1458849935/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458850084/">1458850084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458850262/">1458850262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458850669/">1458850669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458851437/">1458851437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458851737/">1458851737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458853539/">1458853539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458856291/">1458856291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458856536/">1458856536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458857976/">1458857976/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458858574/">1458858574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458859232/">1458859232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458860137/">1458860137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458860434/">1458860434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458860672/">1458860672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458862234/">1458862234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458863678/">1458863678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458864518/">1458864518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458873093/">1458873093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458877894/">1458877894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458884498/">1458884498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892328/">1458892328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892333/">1458892333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892334/">1458892334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892337/">1458892337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892344/">1458892344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892473/">1458892473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892475/">1458892475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892481/">1458892481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892484/">1458892484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892490/">1458892490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892626/">1458892626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892631/">1458892631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892632/">1458892632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892637/">1458892637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892638/">1458892638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458892646/">1458892646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458894647/">1458894647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458895267/">1458895267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458895536/">1458895536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458895915/">1458895915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458900940/">1458900940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458905254/">1458905254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458905406/">1458905406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458908848/">1458908848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458912270/">1458912270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458913230/">1458913230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458913289/">1458913289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458913349/">1458913349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458916474/">1458916474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458918637/">1458918637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458918759/">1458918759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458920309/">1458920309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458921468/">1458921468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458921471/">1458921471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458921472/">1458921472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458924819/">1458924819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458925183/">1458925183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458927070/">1458927070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458927582/">1458927582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458928076/">1458928076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458931061/">1458931061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458931666/">1458931666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458931868/">1458931868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458932252/">1458932252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458933170/">1458933170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458936327/">1458936327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458937719/">1458937719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458937827/">1458937827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458937840/">1458937840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458938375/">1458938375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458939208/">1458939208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458939450/">1458939450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458939938/">1458939938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458941008/">1458941008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458941249/">1458941249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458942006/">1458942006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458942633/">1458942633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458943121/">1458943121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458943774/">1458943774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458944380/">1458944380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458945093/">1458945093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458945535/">1458945535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458948668/">1458948668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458948738/">1458948738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458952972/">1458952972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458956203/">1458956203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458957037/">1458957037/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458957697/">1458957697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458958304/">1458958304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458958536/">1458958536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458970060/">1458970060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458970420/">1458970420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458970532/">1458970532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458972272/">1458972272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458974140/">1458974140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458980023/">1458980023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458995008/">1458995008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458995737/">1458995737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458995790/">1458995790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458995912/">1458995912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458995913/">1458995913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458996091/">1458996091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458996216/">1458996216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458996696/">1458996696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1458999150/">1458999150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459001727/">1459001727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459002710/">1459002710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459008489/">1459008489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459009660/">1459009660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459013497/">1459013497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459018530/">1459018530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459036764/">1459036764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459046092/">1459046092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459047090/">1459047090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459056206/">1459056206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459061676/">1459061676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459067413/">1459067413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459069231/">1459069231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459080027/">1459080027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459089227/">1459089227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459108052/">1459108052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459108585/">1459108585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459110387/">1459110387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459114407/">1459114407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459115553/">1459115553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459120049/">1459120049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459121401/">1459121401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459122693/">1459122693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459122748/">1459122748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459132303/">1459132303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459132984/">1459132984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459143066/">1459143066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459147230/">1459147230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459155204/">1459155204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459155272/">1459155272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459162115/">1459162115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459162191/">1459162191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459164606/">1459164606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459170417/">1459170417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459170931/">1459170931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459171172/">1459171172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459172844/">1459172844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459175501/">1459175501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459178967/">1459178967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459180822/">1459180822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459182142/">1459182142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459184071/">1459184071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459186172/">1459186172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459186315/">1459186315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459186897/">1459186897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459187374/">1459187374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459188563/">1459188563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459188811/">1459188811/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459192407/">1459192407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459192826/">1459192826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459194929/">1459194929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459195502/">1459195502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459195797/">1459195797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459196613/">1459196613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459197031/">1459197031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459197571/">1459197571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459202427/">1459202427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459202859/">1459202859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459203098/">1459203098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459203281/">1459203281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459203519/">1459203519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459204724/">1459204724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459207881/">1459207881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459208266/">1459208266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459209275/">1459209275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459209877/">1459209877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459213302/">1459213302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459213887/">1459213887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459214873/">1459214873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459220157/">1459220157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459229476/">1459229476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459231609/">1459231609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459232010/">1459232010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459232514/">1459232514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459232810/">1459232810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459233217/">1459233217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459237900/">1459237900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459239818/">1459239818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459240291/">1459240291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459241071/">1459241071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459242463/">1459242463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459245692/">1459245692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459247138/">1459247138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459248915/">1459248915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459251023/">1459251023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459251216/">1459251216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459253551/">1459253551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459254625/">1459254625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459256738/">1459256738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459257698/">1459257698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459259070/">1459259070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459259640/">1459259640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459259729/">1459259729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459261295/">1459261295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459264503/">1459264503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459264652/">1459264652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459265128/">1459265128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459265614/">1459265614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459268160/">1459268160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459272710/">1459272710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459274314/">1459274314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459275931/">1459275931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459276530/">1459276530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459278633/">1459278633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459279801/">1459279801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459281293/">1459281293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459283083/">1459283083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459283376/">1459283376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459283478/">1459283478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459284692/">1459284692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459286375/">1459286375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459287356/">1459287356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459289380/">1459289380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459289438/">1459289438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459289494/">1459289494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459289560/">1459289560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459289801/">1459289801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459290157/">1459290157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459290582/">1459290582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459293556/">1459293556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459294222/">1459294222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459294657/">1459294657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459294716/">1459294716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459295074/">1459295074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459295262/">1459295262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459298017/">1459298017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459305169/">1459305169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459305945/">1459305945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459306123/">1459306123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459306420/">1459306420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459306472/">1459306472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459310980/">1459310980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459313914/">1459313914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459315061/">1459315061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459315930/">1459315930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459317034/">1459317034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459318773/">1459318773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459320456/">1459320456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459320698/">1459320698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459320813/">1459320813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459321061/">1459321061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459321175/">1459321175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459321371/">1459321371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459321594/">1459321594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459333657/">1459333657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459333760/">1459333760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459333881/">1459333881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334000/">1459334000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334048/">1459334048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334114/">1459334114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334184/">1459334184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334228/">1459334228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334336/">1459334336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459334443/">1459334443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459335690/">1459335690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459335876/">1459335876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459336357/">1459336357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459336835/">1459336835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459337798/">1459337798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459338038/">1459338038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459338701/">1459338701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459338764/">1459338764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459338873/">1459338873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459338982/">1459338982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459339089/">1459339089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459339472/">1459339472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459339656/">1459339656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459339710/">1459339710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459340986/">1459340986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459341403/">1459341403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459344117/">1459344117/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459344752/">1459344752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459346710/">1459346710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459347047/">1459347047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459347544/">1459347544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459349317/">1459349317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459350142/">1459350142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459350474/">1459350474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459351057/">1459351057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459352141/">1459352141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459353520/">1459353520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459354931/">1459354931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459355621/">1459355621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459355995/">1459355995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459356266/">1459356266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459356541/">1459356541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459357216/">1459357216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459357425/">1459357425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459358629/">1459358629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459359059/">1459359059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438828/">1459438828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438831/">1459438831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438834/">1459438834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438835/">1459438835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438836/">1459438836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438840/">1459438840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438841/">1459438841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438842/">1459438842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438847/">1459438847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438848/">1459438848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438849/">1459438849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438851/">1459438851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438853/">1459438853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438854/">1459438854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438855/">1459438855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438856/">1459438856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438861/">1459438861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438862/">1459438862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459438864/">1459438864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459454977/">1459454977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459456539/">1459456539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459458519/">1459458519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459459719/">1459459719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459460676/">1459460676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459467011/">1459467011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469022/">1459469022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469196/">1459469196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469263/">1459469263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469264/">1459469264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469319/">1459469319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469441/">1459469441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469554/">1459469554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469675/">1459469675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469737/">1459469737/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469738/">1459469738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469793/">1459469793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469881/">1459469881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469937/">1459469937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459469982/">1459469982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470096/">1459470096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470153/">1459470153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470275/">1459470275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470336/">1459470336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470393/">1459470393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470455/">1459470455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459470523/">1459470523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459471479/">1459471479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459471667/">1459471667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459471719/">1459471719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459472082/">1459472082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459473279/">1459473279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459473757/">1459473757/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459473818/">1459473818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459474537/">1459474537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459474719/">1459474719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459475006/">1459475006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459477041/">1459477041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459478075/">1459478075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459478209/">1459478209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459478933/">1459478933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459479289/">1459479289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459480167/">1459480167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459480240/">1459480240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459482763/">1459482763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459483000/">1459483000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459484378/">1459484378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459487324/">1459487324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459491110/">1459491110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459491227/">1459491227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459491760/">1459491760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459492119/">1459492119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459492602/">1459492602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459493567/">1459493567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459496575/">1459496575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459496926/">1459496926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459497107/">1459497107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459497157/">1459497157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459497224/">1459497224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459498362/">1459498362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459500462/">1459500462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459503588/">1459503588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459504720/">1459504720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459506047/">1459506047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459507123/">1459507123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459507850/">1459507850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459510668/">1459510668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459512236/">1459512236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459512281/">1459512281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459512602/">1459512602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459512715/">1459512715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459512884/">1459512884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459513661/">1459513661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459513799/">1459513799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459516907/">1459516907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459517199/">1459517199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459517322/">1459517322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459517443/">1459517443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459517995/">1459517995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459522544/">1459522544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459522667/">1459522667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459522724/">1459522724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459523330/">1459523330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459523390/">1459523390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459523868/">1459523868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459527175/">1459527175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459527942/">1459527942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459529872/">1459529872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459529983/">1459529983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459531250/">1459531250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459531359/">1459531359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459531482/">1459531482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459531665/">1459531665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459532687/">1459532687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459532741/">1459532741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459533165/">1459533165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459533220/">1459533220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459533347/">1459533347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459534360/">1459534360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459534841/">1459534841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459535034/">1459535034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459535383/">1459535383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459537187/">1459537187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459538261/">1459538261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459538461/">1459538461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459541143/">1459541143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459542169/">1459542169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459543365/">1459543365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459544929/">1459544929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459546298/">1459546298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459547440/">1459547440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459549180/">1459549180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459552005/">1459552005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459552423/">1459552423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459552909/">1459552909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459553430/">1459553430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459558800/">1459558800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459560228/">1459560228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459563347/">1459563347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459564365/">1459564365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459568274/">1459568274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459575221/">1459575221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459580089/">1459580089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459601449/">1459601449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459612600/">1459612600/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459618012/">1459618012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459618306/">1459618306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459620224/">1459620224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459623228/">1459623228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459625630/">1459625630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459631200/">1459631200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459631861/">1459631861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459658174/">1459658174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459658223/">1459658223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459658224/">1459658224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459661233/">1459661233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459665254/">1459665254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459668682/">1459668682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459671134/">1459671134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459681036/">1459681036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459683435/">1459683435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459686861/">1459686861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459691632/">1459691632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459693960/">1459693960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459694835/">1459694835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459703720/">1459703720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459719915/">1459719915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459729340/">1459729340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459730478/">1459730478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459732517/">1459732517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459734132/">1459734132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459742417/">1459742417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459756818/">1459756818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459757059/">1459757059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459759099/">1459759099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459764212/">1459764212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459765282/">1459765282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459767532/">1459767532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459767920/">1459767920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459770676/">1459770676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459774115/">1459774115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459776747/">1459776747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459785448/">1459785448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459786478/">1459786478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459788456/">1459788456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459789711/">1459789711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459789829/">1459789829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459789889/">1459789889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459790133/">1459790133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459790190/">1459790190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459790247/">1459790247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459792044/">1459792044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459792289/">1459792289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459795823/">1459795823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459796965/">1459796965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459798167/">1459798167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459798414/">1459798414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459799364/">1459799364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459800253/">1459800253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459800254/">1459800254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459800256/">1459800256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459800323/">1459800323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801015/">1459801015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801132/">1459801132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801278/">1459801278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801390/">1459801390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801465/">1459801465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801498/">1459801498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801541/">1459801541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801615/">1459801615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801725/">1459801725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801779/">1459801779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459801910/">1459801910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459802734/">1459802734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459804845/">1459804845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459807067/">1459807067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459807562/">1459807562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459807778/">1459807778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459808595/">1459808595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459809765/">1459809765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459810183/">1459810183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459811795/">1459811795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459820011/">1459820011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459820012/">1459820012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459821311/">1459821311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459824198/">1459824198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459825512/">1459825512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459827499/">1459827499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459827620/">1459827620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459828217/">1459828217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459828337/">1459828337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459830250/">1459830250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459830432/">1459830432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459830556/">1459830556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459830612/">1459830612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459830665/">1459830665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459830856/">1459830856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459831266/">1459831266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459836959/">1459836959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459838057/">1459838057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459843214/">1459843214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459844108/">1459844108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459844225/">1459844225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459845309/">1459845309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459849218/">1459849218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459849337/">1459849337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459849820/">1459849820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459856533/">1459856533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459861089/">1459861089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459866563/">1459866563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459868116/">1459868116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459868229/">1459868229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459869621/">1459869621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459870394/">1459870394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459871771/">1459871771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459879033/">1459879033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459879223/">1459879223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459880113/">1459880113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459880168/">1459880168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459880286/">1459880286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459881315/">1459881315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459881919/">1459881919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459882326/">1459882326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459883711/">1459883711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459886353/">1459886353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459886416/">1459886416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459887191/">1459887191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459887312/">1459887312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459887376/">1459887376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459888929/">1459888929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459889834/">1459889834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459891869/">1459891869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459892118/">1459892118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459892179/">1459892179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459892957/">1459892957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459894275/">1459894275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459894337/">1459894337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459895226/">1459895226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459896078/">1459896078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459897880/">1459897880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459899487/">1459899487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459899613/">1459899613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459900866/">1459900866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459903751/">1459903751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459905076/">1459905076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459912929/">1459912929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459913614/">1459913614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459914500/">1459914500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459915871/">1459915871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459917628/">1459917628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459924040/">1459924040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459925466/">1459925466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459926435/">1459926435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459926676/">1459926676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459927697/">1459927697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459929306/">1459929306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459931597/">1459931597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459932554/">1459932554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459934890/">1459934890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459935625/">1459935625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459937239/">1459937239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459937288/">1459937288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459938611/">1459938611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459940952/">1459940952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459942276/">1459942276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459943836/">1459943836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459944140/">1459944140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459949047/">1459949047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459949647/">1459949647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459950493/">1459950493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459951394/">1459951394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459951767/">1459951767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459952300/">1459952300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459953442/">1459953442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459956493/">1459956493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459958653/">1459958653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459959070/">1459959070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459959616/">1459959616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459960027/">1459960027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459960211/">1459960211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459963340/">1459963340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459963928/">1459963928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459969986/">1459969986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459970359/">1459970359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459971071/">1459971071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459971650/">1459971650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459971851/">1459971851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459973420/">1459973420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459974475/">1459974475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459975784/">1459975784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459977290/">1459977290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459977708/">1459977708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459978966/">1459978966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459979925/">1459979925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459982383/">1459982383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459984734/">1459984734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459986773/">1459986773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459987915/">1459987915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459988028/">1459988028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459991657/">1459991657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459993190/">1459993190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459993493/">1459993493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459997631/">1459997631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1459997992/">1459997992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460009865/">1460009865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460012142/">1460012142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460013302/">1460013302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460014195/">1460014195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460014914/">1460014914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460015212/">1460015212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460017565/">1460017565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460017729/">1460017729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460017970/">1460017970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460018630/">1460018630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460018870/">1460018870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460019835/">1460019835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460022109/">1460022109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460023043/">1460023043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460023491/">1460023491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460026185/">1460026185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460028352/">1460028352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460028582/">1460028582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029195/">1460029195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029196/">1460029196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029198/">1460029198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029199/">1460029199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029200/">1460029200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029202/">1460029202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029206/">1460029206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029207/">1460029207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029210/">1460029210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029211/">1460029211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029213/">1460029213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029218/">1460029218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029221/">1460029221/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029222/">1460029222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029223/">1460029223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029224/">1460029224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029225/">1460029225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029228/">1460029228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029229/">1460029229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029232/">1460029232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029234/">1460029234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029236/">1460029236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029279/">1460029279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029281/">1460029281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029287/">1460029287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029288/">1460029288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029291/">1460029291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029297/">1460029297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029298/">1460029298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029299/">1460029299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029300/">1460029300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029311/">1460029311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029312/">1460029312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029318/">1460029318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029320/">1460029320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029321/">1460029321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460029615/">1460029615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460030685/">1460030685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460031537/">1460031537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460032604/">1460032604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460034225/">1460034225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460034830/">1460034830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460035850/">1460035850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460035975/">1460035975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460036747/">1460036747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460036880/">1460036880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460036970/">1460036970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460037050/">1460037050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460043217/">1460043217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044513/">1460044513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044980/">1460044980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044982/">1460044982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044986/">1460044986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044988/">1460044988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044989/">1460044989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460044996/">1460044996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460045001/">1460045001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460045005/">1460045005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460047913/">1460047913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054333/">1460054333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054334/">1460054334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054335/">1460054335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054340/">1460054340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054343/">1460054343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054346/">1460054346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054347/">1460054347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054348/">1460054348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054349/">1460054349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054372/">1460054372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054374/">1460054374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460054375/">1460054375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460055533/">1460055533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460056552/">1460056552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460061541/">1460061541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460062192/">1460062192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460062999/">1460062999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460064010/">1460064010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460065621/">1460065621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460065751/">1460065751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460066641/">1460066641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460066966/">1460066966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460071321/">1460071321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460071396/">1460071396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460071478/">1460071478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460071847/">1460071847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460074598/">1460074598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460078137/">1460078137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460079253/">1460079253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460080787/">1460080787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460080844/">1460080844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460083424/">1460083424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460084148/">1460084148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460088646/">1460088646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460096079/">1460096079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460101362/">1460101362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460102925/">1460102925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460105500/">1460105500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460106947/">1460106947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460108627/">1460108627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460109240/">1460109240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460110918/">1460110918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460111219/">1460111219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460112239/">1460112239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460115540/">1460115540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460118418/">1460118418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460118966/">1460118966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460119205/">1460119205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460119679/">1460119679/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460121238/">1460121238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460123519/">1460123519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460125741/">1460125741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460126177/">1460126177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460126639/">1460126639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460127248/">1460127248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460128318/">1460128318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460130233/">1460130233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460133418/">1460133418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460134079/">1460134079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460134799/">1460134799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460136956/">1460136956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460141766/">1460141766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460142775/">1460142775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460142989/">1460142989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460142992/">1460142992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460143988/">1460143988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460145153/">1460145153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460145193/">1460145193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460145204/">1460145204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460146446/">1460146446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148025/">1460148025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148366/">1460148366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148367/">1460148367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148369/">1460148369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148371/">1460148371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148372/">1460148372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148373/">1460148373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148375/">1460148375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148390/">1460148390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148391/">1460148391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148394/">1460148394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148426/">1460148426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148427/">1460148427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148428/">1460148428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148429/">1460148429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148437/">1460148437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148438/">1460148438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148440/">1460148440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148448/">1460148448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148449/">1460148449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148451/">1460148451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148453/">1460148453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148454/">1460148454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148455/">1460148455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460148457/">1460148457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460149808/">1460149808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460150580/">1460150580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460152136/">1460152136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460153630/">1460153630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460154349/">1460154349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460154770/">1460154770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460155426/">1460155426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460156685/">1460156685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460156805/">1460156805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460157015/">1460157015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460158250/">1460158250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460159295/">1460159295/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460159712/">1460159712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460161780/">1460161780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460165218/">1460165218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460171750/">1460171750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460206305/">1460206305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460209916/">1460209916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460211035/">1460211035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460211881/">1460211881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460225448/">1460225448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460225915/">1460225915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460228685/">1460228685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460229824/">1460229824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460230717/">1460230717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460231075/">1460231075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460231959/">1460231959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460233906/">1460233906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460256610/">1460256610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460258057/">1460258057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460258175/">1460258175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460263626/">1460263626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460319731/">1460319731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460320204/">1460320204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460320566/">1460320566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460322975/">1460322975/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460327597/">1460327597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460328915/">1460328915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460334192/">1460334192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460335152/">1460335152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460336114/">1460336114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460339832/">1460339832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460339892/">1460339892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460340554/">1460340554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460343124/">1460343124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460349966/">1460349966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460353872/">1460353872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460354286/">1460354286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460355919/">1460355919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460360719/">1460360719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460360895/">1460360895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460363056/">1460363056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460366053/">1460366053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460367130/">1460367130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460367854/">1460367854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460368086/">1460368086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460370981/">1460370981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460371578/">1460371578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460373196/">1460373196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460380384/">1460380384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460380567/">1460380567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460381896/">1460381896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460382557/">1460382557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460382856/">1460382856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460383336/">1460383336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460385681/">1460385681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460385852/">1460385852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460386873/">1460386873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460387350/">1460387350/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460387839/">1460387839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460388081/">1460388081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460390364/">1460390364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460390590/">1460390590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460390954/">1460390954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460391435/">1460391435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460391555/">1460391555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460391918/">1460391918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460393121/">1460393121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460393651/">1460393651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460393778/">1460393778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460394019/">1460394019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460395272/">1460395272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460395571/">1460395571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460396704/">1460396704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460398956/">1460398956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460398958/">1460398958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460399156/">1460399156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460399662/">1460399662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460399920/">1460399920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460400150/">1460400150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460401189/">1460401189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460401832/">1460401832/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460401963/">1460401963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460403921/">1460403921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460404365/">1460404365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460408754/">1460408754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460409654/">1460409654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460410658/">1460410658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460412175/">1460412175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460412920/">1460412920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460413075/">1460413075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460413488/">1460413488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460414631/">1460414631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460415955/">1460415955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460418105/">1460418105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460418232/">1460418232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460418770/">1460418770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460425007/">1460425007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460425244/">1460425244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460426036/">1460426036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460427472/">1460427472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460430112/">1460430112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460430524/">1460430524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460430767/">1460430767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460431248/">1460431248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460431551/">1460431551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460432096/">1460432096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460434069/">1460434069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460434557/">1460434557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460438814/">1460438814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460438984/">1460438984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460440431/">1460440431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460441267/">1460441267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460441385/">1460441385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460442892/">1460442892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460446250/">1460446250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460447030/">1460447030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460450094/">1460450094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460450400/">1460450400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460450689/">1460450689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460450813/">1460450813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460452544/">1460452544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460455973/">1460455973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460457533/">1460457533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460459206/">1460459206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460462035/">1460462035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460464909/">1460464909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460465574/">1460465574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460466712/">1460466712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460466948/">1460466948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460468457/">1460468457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460469591/">1460469591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460470123/">1460470123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460470548/">1460470548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460470735/">1460470735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460470736/">1460470736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460470850/">1460470850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460471215/">1460471215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460472945/">1460472945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460474529/">1460474529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460476618/">1460476618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460477214/">1460477214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460477692/">1460477692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460479604/">1460479604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460481476/">1460481476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460481648/">1460481648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460482733/">1460482733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460485193/">1460485193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460486036/">1460486036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460488556/">1460488556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460488967/">1460488967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460489564/">1460489564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460490587/">1460490587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460491791/">1460491791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460492210/">1460492210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460492267/">1460492267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460492867/">1460492867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460493528/">1460493528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460494123/">1460494123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460494369/">1460494369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460495564/">1460495564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460495983/">1460495983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460497208/">1460497208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460498152/">1460498152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460498153/">1460498153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460498333/">1460498333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460498766/">1460498766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460499468/">1460499468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460500614/">1460500614/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460500845/">1460500845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460505054/">1460505054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460506969/">1460506969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460507206/">1460507206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460509785/">1460509785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460509907/">1460509907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460510749/">1460510749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460511890/">1460511890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460516267/">1460516267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460521072/">1460521072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460521906/">1460521906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460526170/">1460526170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460530247/">1460530247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460530664/">1460530664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460532893/">1460532893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460534212/">1460534212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460535165/">1460535165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460536727/">1460536727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460537687/">1460537687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460538888/">1460538888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460539014/">1460539014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460541357/">1460541357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460542074/">1460542074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460544047/">1460544047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460544406/">1460544406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460546207/">1460546207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460547950/">1460547950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548386/">1460548386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548390/">1460548390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548394/">1460548394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548396/">1460548396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548618/">1460548618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548619/">1460548619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548620/">1460548620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548621/">1460548621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548622/">1460548622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548774/">1460548774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548775/">1460548775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548776/">1460548776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548778/">1460548778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548779/">1460548779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460548782/">1460548782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460550114/">1460550114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460550895/">1460550895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460551904/">1460551904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460552096/">1460552096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460552756/">1460552756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460552987/">1460552987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460553165/">1460553165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554401/">1460554401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554402/">1460554402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554403/">1460554403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554404/">1460554404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554407/">1460554407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554408/">1460554408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554409/">1460554409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554410/">1460554410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554420/">1460554420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554421/">1460554421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554422/">1460554422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554424/">1460554424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554427/">1460554427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554428/">1460554428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554446/">1460554446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554447/">1460554447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554449/">1460554449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554499/">1460554499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554500/">1460554500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554501/">1460554501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554505/">1460554505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554506/">1460554506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554507/">1460554507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554508/">1460554508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554513/">1460554513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554514/">1460554514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554515/">1460554515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554516/">1460554516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554520/">1460554520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554521/">1460554521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554522/">1460554522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554524/">1460554524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554529/">1460554529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554530/">1460554530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554531/">1460554531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554533/">1460554533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554534/">1460554534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554536/">1460554536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554537/">1460554537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554538/">1460554538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554539/">1460554539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554542/">1460554542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554543/">1460554543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554544/">1460554544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554545/">1460554545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554885/">1460554885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460554904/">1460554904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460555157/">1460555157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460555452/">1460555452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460556396/">1460556396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460556592/">1460556592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460557378/">1460557378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460557956/">1460557956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460559409/">1460559409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460561023/">1460561023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460562460/">1460562460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460567861/">1460567861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460568821/">1460568821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460570144/">1460570144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460571470/">1460571470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460572484/">1460572484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460573146/">1460573146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460579683/">1460579683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460579988/">1460579988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460581841/">1460581841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460582022/">1460582022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460582319/">1460582319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460583287/">1460583287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460583476/">1460583476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460584604/">1460584604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460585451/">1460585451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460585748/">1460585748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460586520/">1460586520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460586702/">1460586702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460587002/">1460587002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460587250/">1460587250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460587600/">1460587600/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460590127/">1460590127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460590314/">1460590314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460590362/">1460590362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460591803/">1460591803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460591869/">1460591869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460592643/">1460592643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460600858/">1460600858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460600986/">1460600986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460606269/">1460606269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460607828/">1460607828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460609449/">1460609449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460609571/">1460609571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460613880/">1460613880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460614839/">1460614839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460615089/">1460615089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460617120/">1460617120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460617515/">1460617515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460619402/">1460619402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460619762/">1460619762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460621629/">1460621629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460623846/">1460623846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460625703/">1460625703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460627084/">1460627084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460627507/">1460627507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460628374/">1460628374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460630395/">1460630395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460634051/">1460634051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460634471/">1460634471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460636203/">1460636203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460643222/">1460643222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460645138/">1460645138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460646945/">1460646945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460648025/">1460648025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460648151/">1460648151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460648742/">1460648742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460649228/">1460649228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460649287/">1460649287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460650246/">1460650246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460651042/">1460651042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460653368/">1460653368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460655283/">1460655283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460655476/">1460655476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460655698/">1460655698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460656000/">1460656000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460657183/">1460657183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460657184/">1460657184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460659120/">1460659120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460660084/">1460660084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460660505/">1460660505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460662069/">1460662069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460662487/">1460662487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460663441/">1460663441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460664710/">1460664710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460665242/">1460665242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460665300/">1460665300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460666269/">1460666269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460666869/">1460666869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460667041/">1460667041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460670948/">1460670948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460672321/">1460672321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460675741/">1460675741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460675927/">1460675927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460677609/">1460677609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460681259/">1460681259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460681678/">1460681678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460689059/">1460689059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460689306/">1460689306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460689544/">1460689544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460689798/">1460689798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460691114/">1460691114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460691459/">1460691459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460692373/">1460692373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460693143/">1460693143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460693988/">1460693988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460695149/">1460695149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460695543/">1460695543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460695730/">1460695730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460700642/">1460700642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460701671/">1460701671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460701966/">1460701966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460704073/">1460704073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460705454/">1460705454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460711499/">1460711499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460711989/">1460711989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460712411/">1460712411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460714629/">1460714629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460715170/">1460715170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460716839/">1460716839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460718764/">1460718764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460719738/">1460719738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460721461/">1460721461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460721588/">1460721588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460723536/">1460723536/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460723554/">1460723554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460723555/">1460723555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460726146/">1460726146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460726328/">1460726328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460727711/">1460727711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460729424/">1460729424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460729764/">1460729764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460729918/">1460729918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460730000/">1460730000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460730017/">1460730017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460731733/">1460731733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460734122/">1460734122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460737121/">1460737121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460738750/">1460738750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460739224/">1460739224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460743850/">1460743850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460746839/">1460746839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460747208/">1460747208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460748882/">1460748882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460752647/">1460752647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460757255/">1460757255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460758461/">1460758461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460758944/">1460758944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460760017/">1460760017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460760641/">1460760641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460761095/">1460761095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460763314/">1460763314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460763615/">1460763615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460764165/">1460764165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460765901/">1460765901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460766076/">1460766076/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460771361/">1460771361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460772365/">1460772365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460775991/">1460775991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460776346/">1460776346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460790320/">1460790320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460798657/">1460798657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460802202/">1460802202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460811759/">1460811759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460823438/">1460823438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460824345/">1460824345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460828959/">1460828959/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460864844/">1460864844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460865142/">1460865142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460897846/">1460897846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460903062/">1460903062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460907501/">1460907501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460911047/">1460911047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460929635/">1460929635/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460930478/">1460930478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460938038/">1460938038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460938698/">1460938698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460959215/">1460959215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460962340/">1460962340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460964140/">1460964140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460964861/">1460964861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460966558/">1460966558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460966775/">1460966775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460966971/">1460966971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460968104/">1460968104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460968878/">1460968878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460969126/">1460969126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460970559/">1460970559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460970903/">1460970903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460971225/">1460971225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460971760/">1460971760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460975189/">1460975189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460978115/">1460978115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460978183/">1460978183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460982198/">1460982198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460982498/">1460982498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460982801/">1460982801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460983398/">1460983398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460984803/">1460984803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460985874/">1460985874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460988978/">1460988978/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460990302/">1460990302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460991382/">1460991382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460992650/">1460992650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460993136/">1460993136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460993313/">1460993313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460997031/">1460997031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460997222/">1460997222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460998230/">1460998230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1460999565/">1460999565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461000225/">1461000225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461001255/">1461001255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461002926/">1461002926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461003405/">1461003405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461003470/">1461003470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461005383/">1461005383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461006170/">1461006170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461008695/">1461008695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461009992/">1461009992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461010669/">1461010669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461012982/">1461012982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461013107/">1461013107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461013953/">1461013953/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461013954/">1461013954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461014487/">1461014487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461014540/">1461014540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461014603/">1461014603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461014782/">1461014782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461015260/">1461015260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461016894/">1461016894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461017723/">1461017723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461020425/">1461020425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461021687/">1461021687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461021872/">1461021872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461024381/">1461024381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461032840/">1461032840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461033202/">1461033202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461034346/">1461034346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461034835/">1461034835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461035606/">1461035606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461035660/">1461035660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461035962/">1461035962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461044998/">1461044998/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461044999/">1461044999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461045001/">1461045001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461046311/">1461046311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461051480/">1461051480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461051582/">1461051582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461052969/">1461052969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461057043/">1461057043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461058123/">1461058123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461058185/">1461058185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461058314/">1461058314/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461058603/">1461058603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461059203/">1461059203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461059265/">1461059265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461059336/">1461059336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461060109/">1461060109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461060523/">1461060523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461060524/">1461060524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461062212/">1461062212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461063408/">1461063408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461065148/">1461065148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461065273/">1461065273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461067548/">1461067548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461067852/">1461067852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461068508/">1461068508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461068868/">1461068868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461070370/">1461070370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461071742/">1461071742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461074151/">1461074151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461074392/">1461074392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461075395/">1461075395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461075822/">1461075822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461076001/">1461076001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461076060/">1461076060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461076115/">1461076115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461077198/">1461077198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461077434/">1461077434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461079510/">1461079510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461079511/">1461079511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461080313/">1461080313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461081171/">1461081171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461082358/">1461082358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461082474/">1461082474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461085123/">1461085123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461086860/">1461086860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461090161/">1461090161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461091177/">1461091177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461091897/">1461091897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461093572/">1461093572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461096823/">1461096823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461098023/">1461098023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461100120/">1461100120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461100661/">1461100661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461101617/">1461101617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461104795/">1461104795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461106176/">1461106176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461106416/">1461106416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461107432/">1461107432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461107974/">1461107974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461108211/">1461108211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461108277/">1461108277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461108877/">1461108877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461110017/">1461110017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461111693/">1461111693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461111937/">1461111937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461114267/">1461114267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461114585/">1461114585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461118904/">1461118904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461124178/">1461124178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461124419/">1461124419/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461124594/">1461124594/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461128432/">1461128432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461129049/">1461129049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461133722/">1461133722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461135456/">1461135456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461136291/">1461136291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461136358/">1461136358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461138349/">1461138349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461139054/">1461139054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461142355/">1461142355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461142903/">1461142903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461143265/">1461143265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461143320/">1461143320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461144639/">1461144639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461145233/">1461145233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461145776/">1461145776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461145968/">1461145968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461146260/">1461146260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461147224/">1461147224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461148899/">1461148899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461151122/">1461151122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461153460/">1461153460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461154500/">1461154500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461156511/">1461156511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461157292/">1461157292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461159394/">1461159394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461159698/">1461159698/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461161313/">1461161313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461161317/">1461161317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461161918/">1461161918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461162522/">1461162522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461162858/">1461162858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461165703/">1461165703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461166965/">1461166965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461167621/">1461167621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461170504/">1461170504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461170623/">1461170623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461172249/">1461172249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461172672/">1461172672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461173562/">1461173562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461173924/">1461173924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461174341/">1461174341/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461174828/">1461174828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461176385/">1461176385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461176566/">1461176566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461177888/">1461177888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461178901/">1461178901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461179260/">1461179260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461180464/">1461180464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461181008/">1461181008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461184849/">1461184849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461185622/">1461185622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461186423/">1461186423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461187205/">1461187205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461188099/">1461188099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461189066/">1461189066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461189429/">1461189429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461189545/">1461189545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461190024/">1461190024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461192119/">1461192119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461192370/">1461192370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461196020/">1461196020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461196201/">1461196201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461196262/">1461196262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461200766/">1461200766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461203645/">1461203645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461206463/">1461206463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461206590/">1461206590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461211086/">1461211086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461212828/">1461212828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461213190/">1461213190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461214505/">1461214505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461217124/">1461217124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461217441/">1461217441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461217996/">1461217996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461219423/">1461219423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461224104/">1461224104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461226382/">1461226382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461226981/">1461226981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461230707/">1461230707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461231428/">1461231428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461233218/">1461233218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461233298/">1461233298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461234240/">1461234240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461238802/">1461238802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461238989/">1461238989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461239109/">1461239109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461240549/">1461240549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461245081/">1461245081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461246083/">1461246083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461247028/">1461247028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461247934/">1461247934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461248345/">1461248345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461249445/">1461249445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461250829/">1461250829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461251776/">1461251776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461252490/">1461252490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461252799/">1461252799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461252833/">1461252833/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461252973/">1461252973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461253380/">1461253380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461253572/">1461253572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461253828/">1461253828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461253889/">1461253889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461255193/">1461255193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461257291/">1461257291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461257926/">1461257926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461258000/">1461258000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461258402/">1461258402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461259986/">1461259986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461260100/">1461260100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461260590/">1461260590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461261243/">1461261243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461261617/">1461261617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461262394/">1461262394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461263948/">1461263948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461264241/">1461264241/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461265984/">1461265984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461266882/">1461266882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461267243/">1461267243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461268213/">1461268213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461269185/">1461269185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461272165/">1461272165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461272227/">1461272227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461272656/">1461272656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461273313/">1461273313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461276377/">1461276377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461276378/">1461276378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461277449/">1461277449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461277511/">1461277511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461278527/">1461278527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461280210/">1461280210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461280932/">1461280932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461281524/">1461281524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461283564/">1461283564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461285366/">1461285366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461285548/">1461285548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461285902/">1461285902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461289115/">1461289115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461289632/">1461289632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461290781/">1461290781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461292082/">1461292082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461296586/">1461296586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461297435/">1461297435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461297436/">1461297436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461297904/">1461297904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461300429/">1461300429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461301485/">1461301485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461302284/">1461302284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461304506/">1461304506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461304684/">1461304684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461308219/">1461308219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461309243/">1461309243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461309802/">1461309802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461310510/">1461310510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461310511/">1461310511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461315883/">1461315883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461316035/">1461316035/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461316931/">1461316931/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317804/">1461317804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317805/">1461317805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317806/">1461317806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317808/">1461317808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317809/">1461317809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317814/">1461317814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317815/">1461317815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317816/">1461317816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317817/">1461317817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317818/">1461317818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317821/">1461317821/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317825/">1461317825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317826/">1461317826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317840/">1461317840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317841/">1461317841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317842/">1461317842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317843/">1461317843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461317844/">1461317844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318088/">1461318088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318089/">1461318089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318090/">1461318090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318091/">1461318091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318094/">1461318094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318100/">1461318100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318101/">1461318101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318102/">1461318102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318103/">1461318103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318106/">1461318106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318107/">1461318107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318108/">1461318108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318109/">1461318109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318111/">1461318111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318125/">1461318125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318126/">1461318126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318127/">1461318127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318128/">1461318128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318129/">1461318129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318371/">1461318371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318372/">1461318372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318374/">1461318374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318382/">1461318382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318383/">1461318383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318386/">1461318386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318388/">1461318388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318389/">1461318389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318391/">1461318391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318407/">1461318407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318408/">1461318408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318409/">1461318409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318410/">1461318410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318415/">1461318415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318694/">1461318694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318695/">1461318695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318696/">1461318696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318699/">1461318699/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318706/">1461318706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318708/">1461318708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461318709/">1461318709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461322440/">1461322440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461325259/">1461325259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461327848/">1461327848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461328324/">1461328324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461329286/">1461329286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461330570/">1461330570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461332339/">1461332339/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461333192/">1461333192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461333244/">1461333244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461333543/">1461333543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461334163/">1461334163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461335226/">1461335226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461335887/">1461335887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461337631/">1461337631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461338464/">1461338464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461338644/">1461338644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461338707/">1461338707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461339305/">1461339305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343505/">1461343505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343680/">1461343680/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343748/">1461343748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343749/">1461343749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343862/">1461343862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343922/">1461343922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343923/">1461343923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461343981/">1461343981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461344112/">1461344112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461344284/">1461344284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461344343/">1461344343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461344807/">1461344807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461345547/">1461345547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461347022/">1461347022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461348454/">1461348454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461348692/">1461348692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461349849/">1461349849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461350069/">1461350069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461354830/">1461354830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461355308/">1461355308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461355452/">1461355452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461355579/">1461355579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461356082/">1461356082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461359807/">1461359807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461362452/">1461362452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461365453/">1461365453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461366895/">1461366895/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461369532/">1461369532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461370730/">1461370730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461385912/">1461385912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461390065/">1461390065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461401574/">1461401574/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461401802/">1461401802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461403788/">1461403788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461404082/">1461404082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461404153/">1461404153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461404154/">1461404154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461404155/">1461404155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461405414/">1461405414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461406906/">1461406906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407027/">1461407027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407028/">1461407028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407116/">1461407116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407151/">1461407151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407189/">1461407189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407190/">1461407190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407204/">1461407204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407205/">1461407205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407272/">1461407272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407273/">1461407273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407274/">1461407274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407324/">1461407324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407325/">1461407325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407395/">1461407395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407396/">1461407396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461407453/">1461407453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461408122/">1461408122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461409565/">1461409565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461409965/">1461409965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461414942/">1461414942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461431102/">1461431102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461435115/">1461435115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461442734/">1461442734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461442791/">1461442791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461443692/">1461443692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461445963/">1461445963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461446388/">1461446388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461459230/">1461459230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461459289/">1461459289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461460131/">1461460131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461467206/">1461467206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461467207/">1461467207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461472552/">1461472552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461485155/">1461485155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461507275/">1461507275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461510707/">1461510707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461522883/">1461522883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461524402/">1461524402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461538302/">1461538302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461547074/">1461547074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461548445/">1461548445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461552582/">1461552582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461555069/">1461555069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461556487/">1461556487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461563693/">1461563693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461569027/">1461569027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461569385/">1461569385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461570232/">1461570232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461571363/">1461571363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461576651/">1461576651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461577605/">1461577605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461578266/">1461578266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461580908/">1461580908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461584155/">1461584155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461586308/">1461586308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461588534/">1461588534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461589551/">1461589551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461589743/">1461589743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461589968/">1461589968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461592012/">1461592012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461592401/">1461592401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461594595/">1461594595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461595548/">1461595548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461596106/">1461596106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461596813/">1461596813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461598313/">1461598313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461598915/">1461598915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461599323/">1461599323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461600588/">1461600588/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461602656/">1461602656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461603671/">1461603671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461604277/">1461604277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461605542/">1461605542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461605785/">1461605785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461609070/">1461609070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461609550/">1461609550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461612425/">1461612425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461613395/">1461613395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461614660/">1461614660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461615497/">1461615497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461616516/">1461616516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461618245/">1461618245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461619867/">1461619867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461624550/">1461624550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461625214/">1461625214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461625806/">1461625806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461630185/">1461630185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461630300/">1461630300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461631856/">1461631856/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461632343/">1461632343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461634443/">1461634443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461635819/">1461635819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461635937/">1461635937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461636971/">1461636971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461638084/">1461638084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461638586/">1461638586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461640203/">1461640203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461644701/">1461644701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461645007/">1461645007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461647575/">1461647575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461652694/">1461652694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461653404/">1461653404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461653576/">1461653576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461657479/">1461657479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461658088/">1461658088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461659045/">1461659045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461660364/">1461660364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461660720/">1461660720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461662105/">1461662105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461662407/">1461662407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461663046/">1461663046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461663246/">1461663246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461666120/">1461666120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461669181/">1461669181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461672258/">1461672258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461672377/">1461672377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461672535/">1461672535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461673211/">1461673211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461674276/">1461674276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461674705/">1461674705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461679612/">1461679612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461681267/">1461681267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461681778/">1461681778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461681913/">1461681913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461682196/">1461682196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461682809/">1461682809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461683103/">1461683103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461683224/">1461683224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461685503/">1461685503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461685615/">1461685615/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461685805/">1461685805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461686516/">1461686516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461688334/">1461688334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461689876/">1461689876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461690559/">1461690559/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461690903/">1461690903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461691628/">1461691628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461692286/">1461692286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461692637/">1461692637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461694573/">1461694573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461694618/">1461694618/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461695465/">1461695465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461695701/">1461695701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461696417/">1461696417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461696724/">1461696724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461699244/">1461699244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461700203/">1461700203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461701582/">1461701582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461702966/">1461702966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461703083/">1461703083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461703204/">1461703204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461703922/">1461703922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461704738/">1461704738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461704888/">1461704888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461705058/">1461705058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461705598/">1461705598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461706138/">1461706138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461706199/">1461706199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461708904/">1461708904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461713413/">1461713413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461723779/">1461723779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461724446/">1461724446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461727558/">1461727558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461728645/">1461728645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461729481/">1461729481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461730985/">1461730985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461736266/">1461736266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461736746/">1461736746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461739204/">1461739204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461740465/">1461740465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461743413/">1461743413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461745284/">1461745284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461746939/">1461746939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461750777/">1461750777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461751627/">1461751627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461753415/">1461753415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461753540/">1461753540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461757616/">1461757616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461758161/">1461758161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461761578/">1461761578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461763375/">1461763375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461763864/">1461763864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461766322/">1461766322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461767773/">1461767773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461767890/">1461767890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461768363/">1461768363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461769088/">1461769088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461769316/">1461769316/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461773131/">1461773131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461773266/">1461773266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461773399/">1461773399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461775020/">1461775020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461775330/">1461775330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461775688/">1461775688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461775885/">1461775885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461776239/">1461776239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461776398/">1461776398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461776827/">1461776827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461777601/">1461777601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461778087/">1461778087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461780591/">1461780591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461781786/">1461781786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461783120/">1461783120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461785510/">1461785510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461785569/">1461785569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461788096/">1461788096/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461790619/">1461790619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461791808/">1461791808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461791930/">1461791930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461792231/">1461792231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461792647/">1461792647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461797512/">1461797512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461799376/">1461799376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461800688/">1461800688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461802018/">1461802018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461811253/">1461811253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461811427/">1461811427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461811793/">1461811793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461813958/">1461813958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461820007/">1461820007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461820132/">1461820132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461822534/">1461822534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461823134/">1461823134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461825534/">1461825534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461826015/">1461826015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461827465/">1461827465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461828048/">1461828048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461831420/">1461831420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461831655/">1461831655/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461832131/">1461832131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461832248/">1461832248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461832442/">1461832442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461832498/">1461832498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461833932/">1461833932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461834266/">1461834266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461834955/">1461834955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461835370/">1461835370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461836290/">1461836290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461838136/">1461838136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461838192/">1461838192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461840892/">1461840892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461841257/">1461841257/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461843595/">1461843595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461844653/">1461844653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461844795/">1461844795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461845183/">1461845183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461849835/">1461849835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461850246/">1461850246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461852168/">1461852168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461854434/">1461854434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461854435/">1461854435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461854568/">1461854568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461854994/">1461854994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461857627/">1461857627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461859319/">1461859319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461859933/">1461859933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461861721/">1461861721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461862259/">1461862259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461862918/">1461862918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461863647/">1461863647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461864181/">1461864181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461865802/">1461865802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461871310/">1461871310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461872507/">1461872507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461872697/">1461872697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461874062/">1461874062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461874246/">1461874246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461874789/">1461874789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461875146/">1461875146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461875205/">1461875205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461876287/">1461876287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461877566/">1461877566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461877791/">1461877791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461879881/">1461879881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461880246/">1461880246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461881747/">1461881747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461882293/">1461882293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461883613/">1461883613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886130/">1461886130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886133/">1461886133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886138/">1461886138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886191/">1461886191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886192/">1461886192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886194/">1461886194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461886543/">1461886543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461887564/">1461887564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461891839/">1461891839/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461894601/">1461894601/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461898603/">1461898603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461899301/">1461899301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461899639/">1461899639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461899930/">1461899930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461903705/">1461903705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461906112/">1461906112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461909281/">1461909281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461910303/">1461910303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461910544/">1461910544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461911151/">1461911151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461916730/">1461916730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461917690/">1461917690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461921949/">1461921949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461923321/">1461923321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461927530/">1461927530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461929990/">1461929990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461931246/">1461931246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461931553/">1461931553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461931670/">1461931670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461932813/">1461932813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461934304/">1461934304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461935204/">1461935204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461935501/">1461935501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461935863/">1461935863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461939897/">1461939897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461942282/">1461942282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461945412/">1461945412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461945521/">1461945521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461945970/">1461945970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461945971/">1461945971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461949354/">1461949354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461949779/">1461949779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461950015/">1461950015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461950077/">1461950077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461950628/">1461950628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461951575/">1461951575/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461952661/">1461952661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461953620/">1461953620/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461955360/">1461955360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461958961/">1461958961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461959431/">1461959431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461962016/">1461962016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461962373/">1461962373/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461964230/">1461964230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461964542/">1461964542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461966510/">1461966510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461967293/">1461967293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461967471/">1461967471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461967780/">1461967780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461967891/">1461967891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461968796/">1461968796/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461969400/">1461969400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461970411/">1461970411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461970907/">1461970907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461972518/">1461972518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461974433/">1461974433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461974665/">1461974665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461975814/">1461975814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461978357/">1461978357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461978522/">1461978522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461982951/">1461982951/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461985666/">1461985666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461986136/">1461986136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1461991838/">1461991838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462002939/">1462002939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462005815/">1462005815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462009649/">1462009649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462019141/">1462019141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462029100/">1462029100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462055982/">1462055982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462085013/">1462085013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462104932/">1462104932/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108238/">1462108238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108239/">1462108239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108290/">1462108290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108291/">1462108291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108292/">1462108292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108293/">1462108293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108294/">1462108294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108356/">1462108356/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108357/">1462108357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108358/">1462108358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108359/">1462108359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108360/">1462108360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108409/">1462108409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108410/">1462108410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108411/">1462108411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108412/">1462108412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108413/">1462108413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108476/">1462108476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108477/">1462108477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108478/">1462108478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108479/">1462108479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108480/">1462108480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108530/">1462108530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108531/">1462108531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108532/">1462108532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108533/">1462108533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108534/">1462108534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108596/">1462108596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108597/">1462108597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108598/">1462108598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108599/">1462108599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108600/">1462108600/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108661/">1462108661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108662/">1462108662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108663/">1462108663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108664/">1462108664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108665/">1462108665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108715/">1462108715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108716/">1462108716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108717/">1462108717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462108718/">1462108718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462112937/">1462112937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462113150/">1462113150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114721/">1462114721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114722/">1462114722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114723/">1462114723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114724/">1462114724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114790/">1462114790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114825/">1462114825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462114826/">1462114826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462124016/">1462124016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462127381/">1462127381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462143449/">1462143449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462143940/">1462143940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462149031/">1462149031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462151913/">1462151913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462153293/">1462153293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462154189/">1462154189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462158509/">1462158509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462160554/">1462160554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462160677/">1462160677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462161454/">1462161454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462164518/">1462164518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462169199/">1462169199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462171126/">1462171126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462172865/">1462172865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462173157/">1462173157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462173391/">1462173391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462177537/">1462177537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462182038/">1462182038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462191030/">1462191030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462198036/">1462198036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462198596/">1462198596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462199678/">1462199678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462203632/">1462203632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462203751/">1462203751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462205439/">1462205439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462205448/">1462205448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462205612/">1462205612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462205960/">1462205960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462208193/">1462208193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462210110/">1462210110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462210495/">1462210495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462211024/">1462211024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462211800/">1462211800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462211979/">1462211979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462213454/">1462213454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462213722/">1462213722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462214442/">1462214442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462215291/">1462215291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462216254/">1462216254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462216255/">1462216255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462216309/">1462216309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462217206/">1462217206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462218462/">1462218462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462219032/">1462219032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462220613/">1462220613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462221032/">1462221032/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462222782/">1462222782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462224292/">1462224292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462225311/">1462225311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462227342/">1462227342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462228663/">1462228663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462230799/">1462230799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462232904/">1462232904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462234960/">1462234960/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462235261/">1462235261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462235379/">1462235379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462239040/">1462239040/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462242967/">1462242967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462244026/">1462244026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462246727/">1462246727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462247320/">1462247320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462249621/">1462249621/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462249726/">1462249726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462249776/">1462249776/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462250085/">1462250085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462253018/">1462253018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462253516/">1462253516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462257100/">1462257100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462258360/">1462258360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462259269/">1462259269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462259503/">1462259503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462262434/">1462262434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462263641/">1462263641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462263642/">1462263642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462263762/">1462263762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462264721/">1462264721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462264907/">1462264907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462265322/">1462265322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462265564/">1462265564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462266263/">1462266263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462266822/">1462266822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462267957/">1462267957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462268135/">1462268135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462269231/">1462269231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462269957/">1462269957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462270065/">1462270065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462270475/">1462270475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462270703/">1462270703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462271105/">1462271105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462271375/">1462271375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462273002/">1462273002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462274138/">1462274138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462275038/">1462275038/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462275056/">1462275056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462278101/">1462278101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462279069/">1462279069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462280667/">1462280667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462281278/">1462281278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462281418/">1462281418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462281462/">1462281462/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462283330/">1462283330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462284411/">1462284411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462285185/">1462285185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462286075/">1462286075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462286533/">1462286533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462287528/">1462287528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462288656/">1462288656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462291067/">1462291067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462291717/">1462291717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462293053/">1462293053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462293997/">1462293997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462294128/">1462294128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462295747/">1462295747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462296281/">1462296281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462296409/">1462296409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462296538/">1462296538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462296723/">1462296723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462301506/">1462301506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462301868/">1462301868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462303490/">1462303490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462305403/">1462305403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462305950/">1462305950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462306908/">1462306908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462306909/">1462306909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462307318/">1462307318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462307454/">1462307454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462307919/">1462307919/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462307988/">1462307988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462309460/">1462309460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462310395/">1462310395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462310481/">1462310481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462311095/">1462311095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462311340/">1462311340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462313858/">1462313858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462315122/">1462315122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462316261/">1462316261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462316646/">1462316646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462318225/">1462318225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462321716/">1462321716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462321898/">1462321898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462322319/">1462322319/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462322497/">1462322497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462323525/">1462323525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462323701/">1462323701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462328775/">1462328775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462329386/">1462329386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462337388/">1462337388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462338706/">1462338706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462338824/">1462338824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462338877/">1462338877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462339357/">1462339357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462339916/">1462339916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462340323/">1462340323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462340506/">1462340506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462341964/">1462341964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462342907/">1462342907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462346021/">1462346021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462347224/">1462347224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462347522/">1462347522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462347996/">1462347996/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462348658/">1462348658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462349801/">1462349801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462350587/">1462350587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462350624/">1462350624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462351485/">1462351485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462353048/">1462353048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462354957/">1462354957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462355985/">1462355985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462358921/">1462358921/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462361972/">1462361972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462361973/">1462361973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462362952/">1462362952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462366969/">1462366969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462368048/">1462368048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462368247/">1462368247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462371056/">1462371056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462373983/">1462373983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462376086/">1462376086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462376738/">1462376738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462377095/">1462377095/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462377521/">1462377521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462377842/">1462377842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462378128/">1462378128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462378479/">1462378479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462379617/">1462379617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462380396/">1462380396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462381185/">1462381185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462381300/">1462381300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462383079/">1462383079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462383168/">1462383168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462384009/">1462384009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462384303/">1462384303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462384662/">1462384662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462384865/">1462384865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462385077/">1462385077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462386220/">1462386220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462387296/">1462387296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462393885/">1462393885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462394031/">1462394031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462396418/">1462396418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462396485/">1462396485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462397082/">1462397082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462398157/">1462398157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462398583/">1462398583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462398584/">1462398584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462399847/">1462399847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462401336/">1462401336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462402545/">1462402545/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462404630/">1462404630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462406198/">1462406198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462407458/">1462407458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462408965/">1462408965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462410401/">1462410401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462415700/">1462415700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462415813/">1462415813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462416284/">1462416284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462417692/">1462417692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462417913/">1462417913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462418920/">1462418920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462419822/">1462419822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462420055/">1462420055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462424561/">1462424561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462425695/">1462425695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462425822/">1462425822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462426066/">1462426066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462429914/">1462429914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434852/">1462434852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434853/">1462434853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434854/">1462434854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434859/">1462434859/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434860/">1462434860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434861/">1462434861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434862/">1462434862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434866/">1462434866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434869/">1462434869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434871/">1462434871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434872/">1462434872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434875/">1462434875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434877/">1462434877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434878/">1462434878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434881/">1462434881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434883/">1462434883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434884/">1462434884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434885/">1462434885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434889/">1462434889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434890/">1462434890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434891/">1462434891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434892/">1462434892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434893/">1462434893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434896/">1462434896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434897/">1462434897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434899/">1462434899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462434900/">1462434900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462439866/">1462439866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462440891/">1462440891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462442324/">1462442324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462442573/">1462442573/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462443586/">1462443586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462445208/">1462445208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462447814/">1462447814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462448393/">1462448393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462449758/">1462449758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462449982/">1462449982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462457395/">1462457395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462457396/">1462457396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462459183/">1462459183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462459299/">1462459299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462459366/">1462459366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462459659/">1462459659/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462460440/">1462460440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462460912/">1462460912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462463312/">1462463312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462465906/">1462465906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462465969/">1462465969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462466394/">1462466394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462466799/">1462466799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462466868/">1462466868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462466987/">1462466987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462466988/">1462466988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462468915/">1462468915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462469390/">1462469390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462469690/">1462469690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462470706/">1462470706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462471002/">1462471002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462472989/">1462472989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462473342/">1462473342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462473822/">1462473822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462477610/">1462477610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462478500/">1462478500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462478862/">1462478862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462479039/">1462479039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462479279/">1462479279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462480546/">1462480546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462480667/">1462480667/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462481686/">1462481686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462483260/">1462483260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462486362/">1462486362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462487862/">1462487862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462488887/">1462488887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462490800/">1462490800/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462493453/">1462493453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462499902/">1462499902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462501042/">1462501042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462501583/">1462501583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462501758/">1462501758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462502712/">1462502712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462503995/">1462503995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462504167/">1462504167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462505125/">1462505125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462506442/">1462506442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462509329/">1462509329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462510646/">1462510646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462518623/">1462518623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462519395/">1462519395/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462519641/">1462519641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462519939/">1462519939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462520443/">1462520443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462526263/">1462526263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462529078/">1462529078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462530504/">1462530504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462536343/">1462536343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462537992/">1462537992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462537993/">1462537993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462539872/">1462539872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462540460/">1462540460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462542741/">1462542741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462545674/">1462545674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462546277/">1462546277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462547484/">1462547484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462549216/">1462549216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462551155/">1462551155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462553722/">1462553722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462555231/">1462555231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462555913/">1462555913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462556262/">1462556262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462557284/">1462557284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462557883/">1462557883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462558611/">1462558611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462560162/">1462560162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462561061/">1462561061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462561980/">1462561980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462562332/">1462562332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462563240/">1462563240/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462563298/">1462563298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462563538/">1462563538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462564190/">1462564190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462564260/">1462564260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462564581/">1462564581/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462564613/">1462564613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462565079/">1462565079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462565192/">1462565192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462565390/">1462565390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462565460/">1462565460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462567490/">1462567490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462567623/">1462567623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462568280/">1462568280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462569718/">1462569718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462570674/">1462570674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462572775/">1462572775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462575120/">1462575120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462575543/">1462575543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462576310/">1462576310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462576666/">1462576666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462578890/">1462578890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462580390/">1462580390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462583057/">1462583057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462584772/">1462584772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462586793/">1462586793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462587102/">1462587102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462596947/">1462596947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462600247/">1462600247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462602285/">1462602285/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462608408/">1462608408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462612660/">1462612660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462623710/">1462623710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462638114/">1462638114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462654554/">1462654554/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462673624/">1462673624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462693497/">1462693497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462694267/">1462694267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462696488/">1462696488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462702732/">1462702732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462708008/">1462708008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462714017/">1462714017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462718515/">1462718515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462732315/">1462732315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462757440/">1462757440/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462759129/">1462759129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462761284/">1462761284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462762846/">1462762846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462765007/">1462765007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462771059/">1462771059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462771604/">1462771604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462772389/">1462772389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462775086/">1462775086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462784082/">1462784082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462784449/">1462784449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462785051/">1462785051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462785101/">1462785101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462785229/">1462785229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462785767/">1462785767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462786781/">1462786781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462787086/">1462787086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462787332/">1462787332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462787509/">1462787509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462787685/">1462787685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462788769/">1462788769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462789853/">1462789853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462791296/">1462791296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462791946/">1462791946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462792907/">1462792907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462794048/">1462794048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462794590/">1462794590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462797583/">1462797583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462798726/">1462798726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462801846/">1462801846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462801972/">1462801972/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462802958/">1462802958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462804360/">1462804360/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462804501/">1462804501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462805183/">1462805183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462806289/">1462806289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462807000/">1462807000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462807428/">1462807428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462808031/">1462808031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462811748/">1462811748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462811981/">1462811981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462812942/">1462812942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462813188/">1462813188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462814021/">1462814021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462814143/">1462814143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462814391/">1462814391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462815460/">1462815460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462816027/">1462816027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462816862/">1462816862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462818365/">1462818365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462818477/">1462818477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462819616/">1462819616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462820282/">1462820282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462820394/">1462820394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462823943/">1462823943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462824753/">1462824753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462824793/">1462824793/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462825148/">1462825148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462825451/">1462825451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462826701/">1462826701/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462827247/">1462827247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462828198/">1462828198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462833365/">1462833365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462837257/">1462837257/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462837738/">1462837738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462840017/">1462840017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462840927/">1462840927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462841405/">1462841405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462842367/">1462842367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462843505/">1462843505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462843622/">1462843622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462844708/">1462844708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462845954/">1462845954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462847226/">1462847226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462850279/">1462850279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462851537/">1462851537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462852820/">1462852820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462853277/">1462853277/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462853418/">1462853418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462854136/">1462854136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462863136/">1462863136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462868944/">1462868944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462869845/">1462869845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462869909/">1462869909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462869970/">1462869970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462870084/">1462870084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462870337/">1462870337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462870444/">1462870444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462871066/">1462871066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462872258/">1462872258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462872552/">1462872552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462873267/">1462873267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462874045/">1462874045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462874884/">1462874884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462874954/">1462874954/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462875365/">1462875365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462875553/">1462875553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462875983/">1462875983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462876930/">1462876930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462877108/">1462877108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462877169/">1462877169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462877587/">1462877587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462877777/">1462877777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462877957/">1462877957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462882089/">1462882089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462883587/">1462883587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462884068/">1462884068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462885595/">1462885595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462889256/">1462889256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462891393/">1462891393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462891572/">1462891572/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462918985/">1462918985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462920064/">1462920064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462920376/">1462920376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462920784/">1462920784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462920908/">1462920908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462921031/">1462921031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462922233/">1462922233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462922357/">1462922357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462923854/">1462923854/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462924819/">1462924819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462927151/">1462927151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462928171/">1462928171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462928299/">1462928299/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462934230/">1462934230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462938194/">1462938194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462939145/">1462939145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462939274/">1462939274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462939884/">1462939884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462939990/">1462939990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462940113/">1462940113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462942956/">1462942956/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462944367/">1462944367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462945987/">1462945987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462949898/">1462949898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462950077/">1462950077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462952113/">1462952113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462953896/">1462953896/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462953899/">1462953899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462954266/">1462954266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462956190/">1462956190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462956379/">1462956379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462956552/">1462956552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462957792/">1462957792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462958768/">1462958768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462958834/">1462958834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462960027/">1462960027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462960334/">1462960334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462961778/">1462961778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462961885/">1462961885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963420/">1462963420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963421/">1462963421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963423/">1462963423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963424/">1462963424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963425/">1462963425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963426/">1462963426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963427/">1462963427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963429/">1462963429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963430/">1462963430/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963431/">1462963431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963432/">1462963432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963435/">1462963435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963436/">1462963436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963437/">1462963437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963439/">1462963439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963441/">1462963441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963443/">1462963443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963445/">1462963445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963446/">1462963446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963449/">1462963449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963450/">1462963450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963451/">1462963451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963452/">1462963452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963453/">1462963453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963455/">1462963455/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963456/">1462963456/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963457/">1462963457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963458/">1462963458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963459/">1462963459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462963461/">1462963461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462964297/">1462964297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462964353/">1462964353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462965005/">1462965005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462965006/">1462965006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462965376/">1462965376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462967174/">1462967174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462968148/">1462968148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462968730/">1462968730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462971491/">1462971491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462972988/">1462972988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462973297/">1462973297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462974551/">1462974551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462974731/">1462974731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462975583/">1462975583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462975929/">1462975929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462976178/">1462976178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462976534/">1462976534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462977784/">1462977784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462977966/">1462977966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462978029/">1462978029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462980907/">1462980907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462981147/">1462981147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462981337/">1462981337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462981421/">1462981421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462982111/">1462982111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462983308/">1462983308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462984474/">1462984474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462984477/">1462984477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462987843/">1462987843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462987985/">1462987985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462988110/">1462988110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462988111/">1462988111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462988647/">1462988647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462989250/">1462989250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462990735/">1462990735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462991351/">1462991351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462992016/">1462992016/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462992425/">1462992425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462993331/">1462993331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462993692/">1462993692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462993929/">1462993929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462994052/">1462994052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1462999270/">1462999270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463000766/">1463000766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463002144/">1463002144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463002874/">1463002874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463003407/">1463003407/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463005925/">1463005925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463006595/">1463006595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463010378/">1463010378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463010974/">1463010974/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463011873/">1463011873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463012175/">1463012175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463013006/">1463013006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463018046/">1463018046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463019134/">1463019134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463020994/">1463020994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463021706/">1463021706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463025244/">1463025244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463025496/">1463025496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463025676/">1463025676/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463026153/">1463026153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463027047/">1463027047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463027657/">1463027657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463028844/">1463028844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463030831/">1463030831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463030850/">1463030850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463031507/">1463031507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463034078/">1463034078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463035336/">1463035336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463036516/">1463036516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463036790/">1463036790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463039767/">1463039767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463041338/">1463041338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463041934/">1463041934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463041992/">1463041992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463042467/">1463042467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463043258/">1463043258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463043754/">1463043754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463044036/">1463044036/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463044988/">1463044988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463045529/">1463045529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463045778/">1463045778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463045886/">1463045886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463047093/">1463047093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463048390/">1463048390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463052052/">1463052052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463052479/">1463052479/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463052586/">1463052586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463052705/">1463052705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463052767/">1463052767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463053308/">1463053308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463055645/">1463055645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463056433/">1463056433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463056603/">1463056603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463056660/">1463056660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463057920/">1463057920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463058770/">1463058770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463059421/">1463059421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463059547/">1463059547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463059846/">1463059846/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463059907/">1463059907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463060091/">1463060091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463060809/">1463060809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463063073/">1463063073/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463063074/">1463063074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463063694/">1463063694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463063695/">1463063695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463063872/">1463063872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463064048/">1463064048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463064461/">1463064461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463064463/">1463064463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463064946/">1463064946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463065541/">1463065541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463068124/">1463068124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463068731/">1463068731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463073465/">1463073465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463074359/">1463074359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463074957/">1463074957/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463075019/">1463075019/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463075080/">1463075080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463075260/">1463075260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463075321/">1463075321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463075748/">1463075748/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463079098/">1463079098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463079405/">1463079405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463080245/">1463080245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463081735/">1463081735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463086122/">1463086122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463086247/">1463086247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463088396/">1463088396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463088580/">1463088580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463089719/">1463089719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463091345/">1463091345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463091400/">1463091400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463092894/">1463092894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463093505/">1463093505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463093684/">1463093684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463094874/">1463094874/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463095064/">1463095064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463096498/">1463096498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463097514/">1463097514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463098069/">1463098069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463099873/">1463099873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463100101/">1463100101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463103099/">1463103099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463103516/">1463103516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463107421/">1463107421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463108437/">1463108437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463109159/">1463109159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463110656/">1463110656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463111745/">1463111745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463115704/">1463115704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463118466/">1463118466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463118715/">1463118715/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463122598/">1463122598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463125182/">1463125182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463125305/">1463125305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463129558/">1463129558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463132203/">1463132203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463132327/">1463132327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463132495/">1463132495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463134001/">1463134001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463134002/">1463134002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463134070/">1463134070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463137061/">1463137061/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463138024/">1463138024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463139636/">1463139636/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463140431/">1463140431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463142399/">1463142399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463143180/">1463143180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463143968/">1463143968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463146535/">1463146535/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463148643/">1463148643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463149002/">1463149002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463150566/">1463150566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463152006/">1463152006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463152901/">1463152901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463154466/">1463154466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463155302/">1463155302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463157274/">1463157274/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463157997/">1463157997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463158421/">1463158421/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463161359/">1463161359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463162860/">1463162860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463164599/">1463164599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463164728/">1463164728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463165607/">1463165607/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463166412/">1463166412/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463167538/">1463167538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463168139/">1463168139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463169661/">1463169661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463171304/">1463171304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463171431/">1463171431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463172463/">1463172463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463172797/">1463172797/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463174449/">1463174449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463175414/">1463175414/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463175580/">1463175580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463176423/">1463176423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463176660/">1463176660/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463177436/">1463177436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463178049/">1463178049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463179897/">1463179897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463180756/">1463180756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463180979/">1463180979/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463183197/">1463183197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463183810/">1463183810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463184376/">1463184376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463184647/">1463184647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463185596/">1463185596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463188323/">1463188323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463190577/">1463190577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463193161/">1463193161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463209308/">1463209308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463214948/">1463214948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463219566/">1463219566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463227549/">1463227549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463256465/">1463256465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463256875/">1463256875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463258495/">1463258495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463275474/">1463275474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463321066/">1463321066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463333549/">1463333549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463337033/">1463337033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463361331/">1463361331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463361933/">1463361933/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463362353/">1463362353/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463363126/">1463363126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463365047/">1463365047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463365475/">1463365475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463367447/">1463367447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463370152/">1463370152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463371239/">1463371239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463372798/">1463372798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463379517/">1463379517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463380950/">1463380950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463381485/">1463381485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463382330/">1463382330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463384315/">1463384315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463391691/">1463391691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463393313/">1463393313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463399795/">1463399795/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463402198/">1463402198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463405738/">1463405738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463406147/">1463406147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463406330/">1463406330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463408251/">1463408251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463408437/">1463408437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463409072/">1463409072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463409571/">1463409571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463409625/">1463409625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463412986/">1463412986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463414930/">1463414930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463415464/">1463415464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463416784/">1463416784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463418227/">1463418227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463419191/">1463419191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463419547/">1463419547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463419612/">1463419612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463419786/">1463419786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463421345/">1463421345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463421527/">1463421527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463422188/">1463422188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463423802/">1463423802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463423923/">1463423923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463424055/">1463424055/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463424763/">1463424763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463425065/">1463425065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463426861/">1463426861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463430165/">1463430165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463430587/">1463430587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463430604/">1463430604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463433836/">1463433836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463434115/">1463434115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463434175/">1463434175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463434539/">1463434539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463436642/">1463436642/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463436817/">1463436817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463436886/">1463436886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463437124/">1463437124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463437844/">1463437844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463439879/">1463439879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463441403/">1463441403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463441687/">1463441687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463442104/">1463442104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463442819/">1463442819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463445294/">1463445294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463450017/">1463450017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463450318/">1463450318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463451633/">1463451633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463451634/">1463451634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463453207/">1463453207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463453920/">1463453920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463454397/">1463454397/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463463039/">1463463039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463463050/">1463463050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463463460/">1463463460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463467115/">1463467115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463469214/">1463469214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463470838/">1463470838/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463470891/">1463470891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463471014/">1463471014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463471432/">1463471432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463471488/">1463471488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463472276/">1463472276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463472873/">1463472873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463473808/">1463473808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463474081/">1463474081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463475637/">1463475637/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463476529/">1463476529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463477197/">1463477197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463479774/">1463479774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463480069/">1463480069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463485355/">1463485355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463485836/">1463485836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463487697/">1463487697/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463488169/">1463488169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463488651/">1463488651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463488777/">1463488777/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463489672/">1463489672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463490273/">1463490273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463490696/">1463490696/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463490999/">1463490999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463491291/">1463491291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463491773/">1463491773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463495467/">1463495467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463496818/">1463496818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463496992/">1463496992/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463497653/">1463497653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463498023/">1463498023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463499041/">1463499041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463499152/">1463499152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463501191/">1463501191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463501488/">1463501488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463501611/">1463501611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463502162/">1463502162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463502939/">1463502939/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463503720/">1463503720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463505403/">1463505403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463506249/">1463506249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463508475/">1463508475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463508648/">1463508648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463508762/">1463508762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463509716/">1463509716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463510147/">1463510147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463510276/">1463510276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463511111/">1463511111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463511406/">1463511406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463511520/">1463511520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463514105/">1463514105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463514817/">1463514817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515007/">1463515007/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515381/">1463515381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515382/">1463515382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515383/">1463515383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515384/">1463515384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515386/">1463515386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515388/">1463515388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515389/">1463515389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515390/">1463515390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515393/">1463515393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515413/">1463515413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515415/">1463515415/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515416/">1463515416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515418/">1463515418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515438/">1463515438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515439/">1463515439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515441/">1463515441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515442/">1463515442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515602/">1463515602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515717/">1463515717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515861/">1463515861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515862/">1463515862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515863/">1463515863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515866/">1463515866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515867/">1463515867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515868/">1463515868/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515869/">1463515869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515872/">1463515872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515875/">1463515875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515876/">1463515876/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515877/">1463515877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515879/">1463515879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515886/">1463515886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515888/">1463515888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515889/">1463515889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463515892/">1463515892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516205/">1463516205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516206/">1463516206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516208/">1463516208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516210/">1463516210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516211/">1463516211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516212/">1463516212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516216/">1463516216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516217/">1463516217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516218/">1463516218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516220/">1463516220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516223/">1463516223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516228/">1463516228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516230/">1463516230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516231/">1463516231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516233/">1463516233/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463516234/">1463516234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463518059/">1463518059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463519923/">1463519923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463520401/">1463520401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463521247/">1463521247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463522079/">1463522079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463522200/">1463522200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463523337/">1463523337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463525085/">1463525085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463525324/">1463525324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463525685/">1463525685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463525862/">1463525862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463526045/">1463526045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463526654/">1463526654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463526952/">1463526952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463527364/">1463527364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463527830/">1463527830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463527902/">1463527902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463528691/">1463528691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463534745/">1463534745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463534943/">1463534943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463534947/">1463534947/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463535110/">1463535110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463535324/">1463535324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463535460/">1463535460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463535648/">1463535648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463536661/">1463536661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463538767/">1463538767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463539844/">1463539844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463540713/">1463540713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463540714/">1463540714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463540912/">1463540912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463541054/">1463541054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463541349/">1463541349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463542127/">1463542127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463542541/">1463542541/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463543750/">1463543750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463544369/">1463544369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463545408/">1463545408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463547710/">1463547710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463547934/">1463547934/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463549329/">1463549329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463549521/">1463549521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463549681/">1463549681/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463553099/">1463553099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463553700/">1463553700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463554368/">1463554368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463554486/">1463554486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463555037/">1463555037/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463555328/">1463555328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463557480/">1463557480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463559643/">1463559643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463559948/">1463559948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463560219/">1463560219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463560787/">1463560787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463561029/">1463561029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463562877/">1463562877/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463563064/">1463563064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463563302/">1463563302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463563427/">1463563427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463563778/">1463563778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463564567/">1463564567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463566852/">1463566852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463567323/">1463567323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463568590/">1463568590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463569011/">1463569011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463570383/">1463570383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463571020/">1463571020/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463571999/">1463571999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463572186/">1463572186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463572248/">1463572248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463573629/">1463573629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463581604/">1463581604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588085/">1463588085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588389/">1463588389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588447/">1463588447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588448/">1463588448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588505/">1463588505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588561/">1463588561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588627/">1463588627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588682/">1463588682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463588862/">1463588862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594155/">1463594155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594327/">1463594327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594328/">1463594328/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594386/">1463594386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594754/">1463594754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594755/">1463594755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594756/">1463594756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594864/">1463594864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594924/">1463594924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463594925/">1463594925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463595047/">1463595047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463595937/">1463595937/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463601401/">1463601401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463601522/">1463601522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463601945/">1463601945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463602137/">1463602137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463603689/">1463603689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463603815/">1463603815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463606270/">1463606270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463607464/">1463607464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463607525/">1463607525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463607771/">1463607771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463608062/">1463608062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463609801/">1463609801/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463610340/">1463610340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463614058/">1463614058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463615563/">1463615563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463616578/">1463616578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463617725/">1463617725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463618746/">1463618746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463619578/">1463619578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463620484/">1463620484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463622106/">1463622106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463622765/">1463622765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463624438/">1463624438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463624439/">1463624439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463629182/">1463629182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463630557/">1463630557/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463631220/">1463631220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463632189/">1463632189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463632371/">1463632371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463632728/">1463632728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463632908/">1463632908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463633204/">1463633204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463634226/">1463634226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463634343/">1463634343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463638311/">1463638311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463641003/">1463641003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463642200/">1463642200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463642318/">1463642318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463642562/">1463642562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463642981/">1463642981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463643945/">1463643945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463644308/">1463644308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463645145/">1463645145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463647120/">1463647120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463649290/">1463649290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463649700/">1463649700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463650362/">1463650362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463650486/">1463650486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463650604/">1463650604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463650718/">1463650718/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463650910/">1463650910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463651378/">1463651378/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463654560/">1463654560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463655047/">1463655047/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463655287/">1463655287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463657264/">1463657264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463658522/">1463658522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463660806/">1463660806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463661888/">1463661888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463662429/">1463662429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463663146/">1463663146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463663439/">1463663439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463663507/">1463663507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463664284/">1463664284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463664650/">1463664650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463665129/">1463665129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463665303/">1463665303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463666017/">1463666017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463666508/">1463666508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463667049/">1463667049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463667101/">1463667101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463668369/">1463668369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463668778/">1463668778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463670469/">1463670469/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463676645/">1463676645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463678331/">1463678331/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463679467/">1463679467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463679886/">1463679886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463679949/">1463679949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463679999/">1463679999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463680118/">1463680118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463680119/">1463680119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463680238/">1463680238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463680658/">1463680658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463680791/">1463680791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463681139/">1463681139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463682762/">1463682762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463682995/">1463682995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463683072/">1463683072/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463683493/">1463683493/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463683955/">1463683955/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463684199/">1463684199/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463684562/">1463684562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463685464/">1463685464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463689716/">1463689716/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463708651/">1463708651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463723525/">1463723525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737733/">1463737733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737734/">1463737734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737735/">1463737735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737736/">1463737736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737739/">1463737739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737740/">1463737740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737741/">1463737741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737742/">1463737742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737743/">1463737743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737747/">1463737747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737750/">1463737750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737753/">1463737753/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737754/">1463737754/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737755/">1463737755/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737758/">1463737758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737761/">1463737761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737762/">1463737762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737763/">1463737763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737765/">1463737765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737769/">1463737769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737770/">1463737770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737771/">1463737771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737772/">1463737772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737778/">1463737778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737779/">1463737779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737780/">1463737780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463737781/">1463737781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463739706/">1463739706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463739911/">1463739911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463740271/">1463740271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463740464/">1463740464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463740641/">1463740641/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463740853/">1463740853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463780312/">1463780312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463781626/">1463781626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463781751/">1463781751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463782056/">1463782056/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463785591/">1463785591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463788599/">1463788599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463788961/">1463788961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463788962/">1463788962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789008/">1463789008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789077/">1463789077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789134/">1463789134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789192/">1463789192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789193/">1463789193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789318/">1463789318/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789374/">1463789374/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789375/">1463789375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789499/">1463789499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789500/">1463789500/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789555/">1463789555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789611/">1463789611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463789670/">1463789670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463792435/">1463792435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463793215/">1463793215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463797774/">1463797774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463797872/">1463797872/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463797949/">1463797949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463798013/">1463798013/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463798193/">1463798193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463798372/">1463798372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463798428/">1463798428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463799509/">1463799509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463799692/">1463799692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463800471/">1463800471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463800890/">1463800890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463802458/">1463802458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463802571/">1463802571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463803658/">1463803658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463804553/">1463804553/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463804738/">1463804738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463805583/">1463805583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463805875/">1463805875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463806480/">1463806480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463812721/">1463812721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463819616/">1463819616/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463819914/">1463819914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463822317/">1463822317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463830355/">1463830355/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463838819/">1463838819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463843135/">1463843135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463843195/">1463843195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463843853/">1463843853/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463844029/">1463844029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463845475/">1463845475/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463847037/">1463847037/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463847160/">1463847160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463850273/">1463850273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463850334/">1463850334/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463850450/">1463850450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463851001/">1463851001/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463852256/">1463852256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463852488/">1463852488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463853991/">1463853991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463854293/">1463854293/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463856942/">1463856942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463862511/">1463862511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463868090/">1463868090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463868508/">1463868508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463873677/">1463873677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463906560/">1463906560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463910039/">1463910039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463916212/">1463916212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463920239/">1463920239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463921252/">1463921252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463927313/">1463927313/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463928157/">1463928157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463947891/">1463947891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463949093/">1463949093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463954196/">1463954196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463957738/">1463957738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463958156/">1463958156/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463958209/">1463958209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463958330/">1463958330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463959528/">1463959528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463961880/">1463961880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463963918/">1463963918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463965119/">1463965119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463969675/">1463969675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463970576/">1463970576/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463970700/">1463970700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463974289/">1463974289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463975376/">1463975376/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463977413/">1463977413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463979628/">1463979628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463982160/">1463982160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463982270/">1463982270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463982760/">1463982760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463984492/">1463984492/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463984556/">1463984556/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463985170/">1463985170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463985220/">1463985220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463985993/">1463985993/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463987071/">1463987071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463987668/">1463987668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463988402/">1463988402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463988640/">1463988640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463988700/">1463988700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463989473/">1463989473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463989711/">1463989711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463990380/">1463990380/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463990677/">1463990677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463991091/">1463991091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463991333/">1463991333/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463992159/">1463992159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463992828/">1463992828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463994159/">1463994159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463994275/">1463994275/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463994579/">1463994579/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463995473/">1463995473/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1463996320/">1463996320/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464006271/">1464006271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464012216/">1464012216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464012270/">1464012270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464012336/">1464012336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464012392/">1464012392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464012633/">1464012633/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464012929/">1464012929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464013051/">1464013051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464013234/">1464013234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464013361/">1464013361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464013417/">1464013417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464013654/">1464013654/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464013779/">1464013779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464014317/">1464014317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464015816/">1464015816/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464017069/">1464017069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464017675/">1464017675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464018155/">1464018155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464018394/">1464018394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464021281/">1464021281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464032875/">1464032875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464033713/">1464033713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464044411/">1464044411/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464053744/">1464053744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464054226/">1464054226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464055194/">1464055194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056227/">1464056227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056294/">1464056294/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056410/">1464056410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056464/">1464056464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056526/">1464056526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056597/">1464056597/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056598/">1464056598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056720/">1464056720/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056779/">1464056779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464056836/">1464056836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464057037/">1464057037/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464057106/">1464057106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464057245/">1464057245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464057486/">1464057486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464057675/">1464057675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464057725/">1464057725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464058033/">1464058033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464060139/">1464060139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464061864/">1464061864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464062291/">1464062291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464062465/">1464062465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464063006/">1464063006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464064450/">1464064450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464064503/">1464064503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464064997/">1464064997/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464066006/">1464066006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464068643/">1464068643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464070690/">1464070690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464070807/">1464070807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464073149/">1464073149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464074943/">1464074943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464075555/">1464075555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464075725/">1464075725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464076385/">1464076385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464077709/">1464077709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464078365/">1464078365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464078485/">1464078485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464079205/">1464079205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464079689/">1464079689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464080343/">1464080343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464080531/">1464080531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464080704/">1464080704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464081014/">1464081014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464081303/">1464081303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464081794/">1464081794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464082219/">1464082219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464082220/">1464082220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464082265/">1464082265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464082991/">1464082991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464083178/">1464083178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464083595/">1464083595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464086223/">1464086223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464087424/">1464087424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464087852/">1464087852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464090307/">1464090307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464091566/">1464091566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464092767/">1464092767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464093605/">1464093605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464094986/">1464094986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464095765/">1464095765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464096906/">1464096906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464097148/">1464097148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464097870/">1464097870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464098236/">1464098236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464099497/">1464099497/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464099555/">1464099555/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464099663/">1464099663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464100236/">1464100236/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464101899/">1464101899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464102198/">1464102198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464102434/">1464102434/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464103505/">1464103505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464104175/">1464104175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464107837/">1464107837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464108008/">1464108008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464110714/">1464110714/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464111315/">1464111315/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464111736/">1464111736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464112276/">1464112276/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464112330/">1464112330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464113142/">1464113142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464113511/">1464113511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464114163/">1464114163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464114289/">1464114289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464114401/">1464114401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464115060/">1464115060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464117099/">1464117099/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464118779/">1464118779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464119264/">1464119264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464123288/">1464123288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464124125/">1464124125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464125137/">1464125137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464125210/">1464125210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464126166/">1464126166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464126401/">1464126401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464126941/">1464126941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464127004/">1464127004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464127185/">1464127185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464127539/">1464127539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464127903/">1464127903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464128207/">1464128207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464129168/">1464129168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464130000/">1464130000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464130129/">1464130129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464130357/">1464130357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464130785/">1464130785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464131024/">1464131024/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464131810/">1464131810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464132351/">1464132351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464132942/">1464132942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464133123/">1464133123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464133358/">1464133358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464134136/">1464134136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464134209/">1464134209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464140803/">1464140803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464141889/">1464141889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464142902/">1464142902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464143025/">1464143025/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464143270/">1464143270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464146090/">1464146090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464150405/">1464150405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464157900/">1464157900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464158448/">1464158448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464159349/">1464159349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464159461/">1464159461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464159524/">1464159524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464160247/">1464160247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464161812/">1464161812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464162226/">1464162226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464163721/">1464163721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464163899/">1464163899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464164685/">1464164685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464165647/">1464165647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464166190/">1464166190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464167798/">1464167798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464169188/">1464169188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464169847/">1464169847/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464170091/">1464170091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464170630/">1464170630/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464172246/">1464172246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464172487/">1464172487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464172717/">1464172717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464172909/">1464172909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464173023/">1464173023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464174098/">1464174098/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464175361/">1464175361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464176741/">1464176741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464176981/">1464176981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464177227/">1464177227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464177336/">1464177336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464177399/">1464177399/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464178599/">1464178599/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464181067/">1464181067/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464182197/">1464182197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464182198/">1464182198/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464182330/">1464182330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464182443/">1464182443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464182508/">1464182508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464183790/">1464183790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464185812/">1464185812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464185981/">1464185981/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464186279/">1464186279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464186528/">1464186528/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464186829/">1464186829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464188205/">1464188205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464188924/">1464188924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464189530/">1464189530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464189578/">1464189578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464191803/">1464191803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464191861/">1464191861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464192165/">1464192165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464193249/">1464193249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464202064/">1464202064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464204521/">1464204521/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464204769/">1464204769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464204770/">1464204770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464204771/">1464204771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464204827/">1464204827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464205006/">1464205006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464205422/">1464205422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464205786/">1464205786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464205844/">1464205844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464206021/">1464206021/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464207520/">1464207520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464207819/">1464207819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464208730/">1464208730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464208904/">1464208904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464209204/">1464209204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464209392/">1464209392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464210711/">1464210711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464211781/">1464211781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464212149/">1464212149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464212330/">1464212330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464212691/">1464212691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464213952/">1464213952/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464215930/">1464215930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464216041/">1464216041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464218379/">1464218379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464219108/">1464219108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464224089/">1464224089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464224739/">1464224739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464227629/">1464227629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464228109/">1464228109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464232077/">1464232077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464232190/">1464232190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464233451/">1464233451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464234349/">1464234349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464235191/">1464235191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464235242/">1464235242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464239279/">1464239279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464241789/">1464241789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464244851/">1464244851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464244968/">1464244968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464245738/">1464245738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464246287/">1464246287/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464250961/">1464250961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464251092/">1464251092/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464251634/">1464251634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464251739/">1464251739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464252406/">1464252406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464253129/">1464253129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464254690/">1464254690/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464255409/">1464255409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464257140/">1464257140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464258404/">1464258404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464259550/">1464259550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464259786/">1464259786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464264290/">1464264290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464264435/">1464264435/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464264532/">1464264532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464265060/">1464265060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464265910/">1464265910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464266746/">1464266746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464269989/">1464269989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464270883/">1464270883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464271312/">1464271312/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464271540/">1464271540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464271779/">1464271779/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464271906/">1464271906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464272029/">1464272029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464272080/">1464272080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464273767/">1464273767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464273884/">1464273884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464274781/">1464274781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464274899/">1464274899/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464275148/">1464275148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464276229/">1464276229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464276460/">1464276460/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464277431/">1464277431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464280540/">1464280540/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464280858/">1464280858/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464280923/">1464280923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464280983/">1464280983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464281268/">1464281268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464282710/">1464282710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464285220/">1464285220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464286606/">1464286606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464291770/">1464291770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464295006/">1464295006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305571/">1464305571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305631/">1464305631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305687/">1464305687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305744/">1464305744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305745/">1464305745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305803/">1464305803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305875/">1464305875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305928/">1464305928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464305984/">1464305984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464306281/">1464306281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464306472/">1464306472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464307789/">1464307789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464307843/">1464307843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464308209/">1464308209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464309403/">1464309403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464313183/">1464313183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464313908/">1464313908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464316485/">1464316485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464317273/">1464317273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464320090/">1464320090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464321346/">1464321346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464322781/">1464322781/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464323093/">1464323093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464326450/">1464326450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464328727/">1464328727/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464329332/">1464329332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464329567/">1464329567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464332750/">1464332750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464333280/">1464333280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464335567/">1464335567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464336831/">1464336831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464340857/">1464340857/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464341154/">1464341154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464343850/">1464343850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464344323/">1464344323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464348881/">1464348881/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464350093/">1464350093/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464353149/">1464353149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464355306/">1464355306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464357345/">1464357345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464358124/">1464358124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464358903/">1464358903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464360651/">1464360651/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464360764/">1464360764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464362688/">1464362688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464364179/">1464364179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464365143/">1464365143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464367004/">1464367004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464368336/">1464368336/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464369107/">1464369107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464370354/">1464370354/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464370969/">1464370969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464371677/">1464371677/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464373368/">1464373368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464375278/">1464375278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464375640/">1464375640/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464380688/">1464380688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464380809/">1464380809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464380866/">1464380866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464381162/">1464381162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464381282/">1464381282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464381409/">1464381409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464383028/">1464383028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464383212/">1464383212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464383513/">1464383513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464383985/">1464383985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464384945/">1464384945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464385426/">1464385426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464386144/">1464386144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464391181/">1464391181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464392389/">1464392389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464394426/">1464394426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464394969/">1464394969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464395267/">1464395267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464405693/">1464405693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464409538/">1464409538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464414760/">1464414760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464422069/">1464422069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464423005/">1464423005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464425010/">1464425010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464425012/">1464425012/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464425558/">1464425558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464426999/">1464426999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464427863/">1464427863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464431763/">1464431763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464431902/">1464431902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464432030/">1464432030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464432153/">1464432153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464432298/">1464432298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464434252/">1464434252/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464440372/">1464440372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464444695/">1464444695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464444820/">1464444820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464448169/">1464448169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464451713/">1464451713/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464476010/">1464476010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464490416/">1464490416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464501217/">1464501217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464501758/">1464501758/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464505900/">1464505900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464549392/">1464549392/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464557562/">1464557562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464559182/">1464559182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464562665/">1464562665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464564043/">1464564043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464564340/">1464564340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464570464/">1464570464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464575385/">1464575385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464576467/">1464576467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464581684/">1464581684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464584022/">1464584022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464591157/">1464591157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464591789/">1464591789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464591791/">1464591791/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464591990/">1464591990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464592897/">1464592897/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464593873/">1464593873/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464593916/">1464593916/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464597045/">1464597045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464598182/">1464598182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464599084/">1464599084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464599136/">1464599136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464601840/">1464601840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464601969/">1464601969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464602451/">1464602451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464602567/">1464602567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464605925/">1464605925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464607788/">1464607788/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464607900/">1464607900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464608628/">1464608628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464608693/">1464608693/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464609880/">1464609880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464611678/">1464611678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464612106/">1464612106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464612820/">1464612820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464614685/">1464614685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464615050/">1464615050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464615108/">1464615108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464616306/">1464616306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464616487/">1464616487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464616961/">1464616961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464617200/">1464617200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464618049/">1464618049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464621284/">1464621284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464621520/">1464621520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464624708/">1464624708/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464626203/">1464626203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464627707/">1464627707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464630644/">1464630644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464631369/">1464631369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464635317/">1464635317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464637121/">1464637121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464638146/">1464638146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464643182/">1464643182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464645465/">1464645465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464645518/">1464645518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464651279/">1464651279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464651468/">1464651468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464654161/">1464654161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464654766/">1464654766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464661004/">1464661004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464661302/">1464661302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464663335/">1464663335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464664842/">1464664842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464666278/">1464666278/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464666398/">1464666398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464666586/">1464666586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464666700/">1464666700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464667902/">1464667902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464675224/">1464675224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464676661/">1464676661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464679608/">1464679608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464682180/">1464682180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464684644/">1464684644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464687348/">1464687348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464688726/">1464688726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464690936/">1464690936/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464692148/">1464692148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464695027/">1464695027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464695806/">1464695806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464696165/">1464696165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464696524/">1464696524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464696945/">1464696945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464698623/">1464698623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464701739/">1464701739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464701925/">1464701925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464702515/">1464702515/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464702892/">1464702892/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464703066/">1464703066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464703301/">1464703301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464706547/">1464706547/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464706719/">1464706719/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464708229/">1464708229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464708279/">1464708279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464708584/">1464708584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464709546/">1464709546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464709661/">1464709661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464710506/">1464710506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464710736/">1464710736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464710985/">1464710985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464711886/">1464711886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464712127/">1464712127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464713752/">1464713752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464714052/">1464714052/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464715245/">1464715245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464717819/">1464717819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464718598/">1464718598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464719561/">1464719561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464725375/">1464725375/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464725438/">1464725438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464725562/">1464725562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464725626/">1464725626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464725740/">1464725740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464726396/">1464726396/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464727424/">1464727424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464729049/">1464729049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464729526/">1464729526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464729766/">1464729766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464731139/">1464731139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464731986/">1464731986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464732101/">1464732101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464733357/">1464733357/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464734323/">1464734323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464734438/">1464734438/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464736116/">1464736116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464736604/">1464736604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464738519/">1464738519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464738646/">1464738646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464738815/">1464738815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464739552/">1464739552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464740400/">1464740400/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464742672/">1464742672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464743454/">1464743454/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464745063/">1464745063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464745064/">1464745064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464745706/">1464745706/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464746015/">1464746015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464746731/">1464746731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464747394/">1464747394/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464747817/">1464747817/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464748663/">1464748663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464749571/">1464749571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464752446/">1464752446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464752745/">1464752745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464753051/">1464753051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464755875/">1464755875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464756051/">1464756051/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464756644/">1464756644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464759102/">1464759102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464763008/">1464763008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464763074/">1464763074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464763674/">1464763674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464763845/">1464763845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464763911/">1464763911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464764145/">1464764145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464765649/">1464765649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464766009/">1464766009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464766010/">1464766010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464767928/">1464767928/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464770148/">1464770148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464770209/">1464770209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464770388/">1464770388/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464770752/">1464770752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464773870/">1464773870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464773925/">1464773925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464775134/">1464775134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464775843/">1464775843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464776213/">1464776213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464776332/">1464776332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464776385/">1464776385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464776987/">1464776987/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464777290/">1464777290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464783527/">1464783527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464783586/">1464783586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464784124/">1464784124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464786234/">1464786234/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464787011/">1464787011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464787546/">1464787546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464787664/">1464787664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464787730/">1464787730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464787852/">1464787852/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464788088/">1464788088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464788687/">1464788687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464789044/">1464789044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464789291/">1464789291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464792301/">1464792301/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464796849/">1464796849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464797813/">1464797813/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464797814/">1464797814/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464798044/">1464798044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464798173/">1464798173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464798232/">1464798232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464800145/">1464800145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464801228/">1464801228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464803262/">1464803262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464803569/">1464803569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464803863/">1464803863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464805968/">1464805968/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464806094/">1464806094/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464806151/">1464806151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464807222/">1464807222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464807650/">1464807650/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464808303/">1464808303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464809147/">1464809147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464809565/">1464809565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464810530/">1464810530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464812268/">1464812268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464812335/">1464812335/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464812625/">1464812625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464814674/">1464814674/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464815033/">1464815033/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464815330/">1464815330/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464815875/">1464815875/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464816822/">1464816822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464818812/">1464818812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464819107/">1464819107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464819292/">1464819292/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464821393/">1464821393/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464822774/">1464822774/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464823008/">1464823008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464824812/">1464824812/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464824988/">1464824988/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464826672/">1464826672/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464829364/">1464829364/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464847124/">1464847124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464848085/">1464848085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464849043/">1464849043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464849348/">1464849348/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464850070/">1464850070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464850188/">1464850188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464851027/">1464851027/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464852824/">1464852824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464853123/">1464853123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464853848/">1464853848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464854329/">1464854329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464854930/">1464854930/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464855284/">1464855284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464856612/">1464856612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464856678/">1464856678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464858107/">1464858107/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464859246/">1464859246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464859494/">1464859494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464859670/">1464859670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464860034/">1464860034/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464860204/">1464860204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464861291/">1464861291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464861764/">1464861764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464862552/">1464862552/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464862967/">1464862967/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464864709/">1464864709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464864942/">1464864942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867137/">1464867137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867139/">1464867139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867145/">1464867145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867147/">1464867147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867150/">1464867150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867151/">1464867151/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867152/">1464867152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867153/">1464867153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867157/">1464867157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867160/">1464867160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867162/">1464867162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867163/">1464867163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867164/">1464867164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867166/">1464867166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867167/">1464867167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867169/">1464867169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867170/">1464867170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867172/">1464867172/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867175/">1464867175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867176/">1464867176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867178/">1464867178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867183/">1464867183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867186/">1464867186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867188/">1464867188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867190/">1464867190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867191/">1464867191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867193/">1464867193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867194/">1464867194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867196/">1464867196/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867482/">1464867482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867483/">1464867483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867488/">1464867488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867489/">1464867489/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867490/">1464867490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867494/">1464867494/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867495/">1464867495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867496/">1464867496/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867499/">1464867499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867501/">1464867501/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867502/">1464867502/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867504/">1464867504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867506/">1464867506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867507/">1464867507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867508/">1464867508/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867510/">1464867510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867511/">1464867511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867512/">1464867512/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867513/">1464867513/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867514/">1464867514/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867516/">1464867516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867517/">1464867517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867518/">1464867518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867519/">1464867519/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867520/">1464867520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867522/">1464867522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867523/">1464867523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867524/">1464867524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867525/">1464867525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867526/">1464867526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867527/">1464867527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867531/">1464867531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867533/">1464867533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867534/">1464867534/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867537/">1464867537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867539/">1464867539/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464867542/">1464867542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464868010/">1464868010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464873112/">1464873112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464874903/">1464874903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875326/">1464875326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875722/">1464875722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875723/">1464875723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875728/">1464875728/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875729/">1464875729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875730/">1464875730/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875731/">1464875731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875734/">1464875734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875735/">1464875735/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875736/">1464875736/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875739/">1464875739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875741/">1464875741/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875742/">1464875742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875750/">1464875750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875751/">1464875751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875752/">1464875752/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464875810/">1464875810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464877546/">1464877546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464878146/">1464878146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464878694/">1464878694/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464879231/">1464879231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464879648/">1464879648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464880011/">1464880011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464880366/">1464880366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464880612/">1464880612/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464880785/">1464880785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464881385/">1464881385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464882470/">1464882470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464883836/">1464883836/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464884135/">1464884135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464884436/">1464884436/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464887495/">1464887495/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464888215/">1464888215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464888458/">1464888458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464890794/">1464890794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464890915/">1464890915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464892417/">1464892417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464893317/">1464893317/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464895181/">1464895181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464895596/">1464895596/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464896445/">1464896445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464896915/">1464896915/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464898537/">1464898537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464899617/">1464899617/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464900217/">1464900217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464900457/">1464900457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464900938/">1464900938/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464901057/">1464901057/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464901417/">1464901417/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464904237/">1464904237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464905197/">1464905197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464905377/">1464905377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464905437/">1464905437/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464906397/">1464906397/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464907297/">1464907297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464908017/">1464908017/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464909577/">1464909577/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464910209/">1464910209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464912216/">1464912216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464915370/">1464915370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464915431/">1464915431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464915490/">1464915490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464918912/">1464918912/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464920950/">1464920950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464921371/">1464921371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464921670/">1464921670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464923230/">1464923230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464925090/">1464925090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464930311/">1464930311/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464930368/">1464930368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464930431/">1464930431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464930551/">1464930551/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464931150/">1464931150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464931391/">1464931391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464931450/">1464931450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464933045/">1464933045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464933358/">1464933358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464936310/">1464936310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464936550/">1464936550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464937329/">1464937329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464938710/">1464938710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464940871/">1464940871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464941950/">1464941950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464942687/">1464942687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464943571/">1464943571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464945250/">1464945250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464946090/">1464946090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464946571/">1464946571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464948731/">1464948731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464950591/">1464950591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464952452/">1464952452/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464953531/">1464953531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464953891/">1464953891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464954550/">1464954550/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464957251/">1464957251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464959769/">1464959769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464960008/">1464960008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464960670/">1464960670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464960851/">1464960851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464963610/">1464963610/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464964326/">1464964326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464964391/">1464964391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464964451/">1464964451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464965538/">1464965538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464967870/">1464967870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464971170/">1464971170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464973692/">1464973692/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464973871/">1464973871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464975048/">1464975048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464975078/">1464975078/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464975194/">1464975194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464976571/">1464976571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464976691/">1464976691/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464978206/">1464978206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464979827/">1464979827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464980427/">1464980427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464981267/">1464981267/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464982046/">1464982046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464982466/">1464982466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464982946/">1464982946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464983426/">1464983426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464985818/">1464985818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464985886/">1464985886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464987926/">1464987926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464988166/">1464988166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464988406/">1464988406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464990986/">1464990986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464995546/">1464995546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464996026/">1464996026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464996206/">1464996206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464996604/">1464996604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464996746/">1464996746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1464998365/">1464998365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465001906/">1465001906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465002146/">1465002146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465007457/">1465007457/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465009766/">1465009766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465011506/">1465011506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465025126/">1465025126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465025606/">1465025606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465025786/">1465025786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465025966/">1465025966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465026026/">1465026026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465029014/">1465029014/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465036586/">1465036586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465039803/">1465039803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465043486/">1465043486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465047306/">1465047306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465052046/">1465052046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465055707/">1465055707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465061453/">1465061453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465071066/">1465071066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465078447/">1465078447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465083011/">1465083011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465088286/">1465088286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465090807/">1465090807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465093855/">1465093855/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465112442/">1465112442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465113522/">1465113522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465113762/">1465113762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465113882/">1465113882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465114182/">1465114182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465114302/">1465114302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465118862/">1465118862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465126205/">1465126205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465147602/">1465147602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465155882/">1465155882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465158611/">1465158611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465160682/">1465160682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465168662/">1465168662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465169802/">1465169802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465174062/">1465174062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465175848/">1465175848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465177168/">1465177168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465177948/">1465177948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465179389/">1465179389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465180288/">1465180288/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465183529/">1465183529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465184249/">1465184249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465187468/">1465187468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465188185/">1465188185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465188634/">1465188634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465188809/">1465188809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465190368/">1465190368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465190943/">1465190943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465191520/">1465191520/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465193729/">1465193729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465195108/">1465195108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465196128/">1465196128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465197273/">1465197273/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465200210/">1465200210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465202249/">1465202249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465204468/">1465204468/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465204668/">1465204668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465204771/">1465204771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465204829/">1465204829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465205188/">1465205188/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465206090/">1465206090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465206688/">1465206688/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465207408/">1465207408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465210888/">1465210888/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465212570/">1465212570/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465212638/">1465212638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465212808/">1465212808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465214909/">1465214909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465216349/">1465216349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465217309/">1465217309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465217428/">1465217428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465217488/">1465217488/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465218028/">1465218028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465218088/">1465218088/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465218213/">1465218213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465219048/">1465219048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465221150/">1465221150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465221268/">1465221268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465222890/">1465222890/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465223191/">1465223191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465223790/">1465223790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465224929/">1465224929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465227446/">1465227446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465229186/">1465229186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465229666/">1465229666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465229845/">1465229845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465229906/">1465229906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465230446/">1465230446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465230506/">1465230506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465232491/">1465232491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465233747/">1465233747/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465234271/">1465234271/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465234709/">1465234709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465235427/">1465235427/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465235608/">1465235608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465235668/">1465235668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465236253/">1465236253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465237346/">1465237346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465238187/">1465238187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465240226/">1465240226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465240646/">1465240646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465245030/">1465245030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465248571/">1465248571/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252173/">1465252173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252174/">1465252174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252231/">1465252231/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252290/">1465252290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252410/">1465252410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252472/">1465252472/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252771/">1465252771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465252950/">1465252950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465253130/">1465253130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465253253/">1465253253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465253372/">1465253372/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465253790/">1465253790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465253910/">1465253910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465254871/">1465254871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465255770/">1465255770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465255806/">1465255806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465256370/">1465256370/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465258352/">1465258352/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465258532/">1465258532/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465258592/">1465258592/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465259011/">1465259011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465261711/">1465261711/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465262132/">1465262132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465265138/">1465265138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465265310/">1465265310/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465266155/">1465266155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465266391/">1465266391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465266675/">1465266675/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465268611/">1465268611/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465269091/">1465269091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465269511/">1465269511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465272511/">1465272511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465277250/">1465277250/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465279474/">1465279474/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465279770/">1465279770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465279891/">1465279891/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465280790/">1465280790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465280910/">1465280910/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465281091/">1465281091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465281211/">1465281211/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465284272/">1465284272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465284632/">1465284632/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465285712/">1465285712/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465285831/">1465285831/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465286491/">1465286491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465287991/">1465287991/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465288223/">1465288223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465290091/">1465290091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465291409/">1465291409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465291533/">1465291533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465292851/">1465292851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465292973/">1465292973/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465294290/">1465294290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465295733/">1465295733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465295911/">1465295911/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465298191/">1465298191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465299060/">1465299060/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465299391/">1465299391/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465301070/">1465301070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465301851/">1465301851/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465304671/">1465304671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465305090/">1465305090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465305210/">1465305210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465305511/">1465305511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465305751/">1465305751/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465306296/">1465306296/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465306410/">1465306410/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465307490/">1465307490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465307671/">1465307671/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465309591/">1465309591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465309769/">1465309769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465309878/">1465309878/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465310131/">1465310131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465310190/">1465310190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465310790/">1465310790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465311152/">1465311152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465313068/">1465313068/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465313371/">1465313371/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465313971/">1465313971/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465315470/">1465315470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465315533/">1465315533/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465315830/">1465315830/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465316516/">1465316516/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465317869/">1465317869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465320634/">1465320634/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465320700/">1465320700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465322131/">1465322131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465322431/">1465322431/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465323091/">1465323091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465324110/">1465324110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465324176/">1465324176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465324471/">1465324471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465324591/">1465324591/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465324652/">1465324652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465325010/">1465325010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465326086/">1465326086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465326386/">1465326386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465331467/">1465331467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465334484/">1465334484/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465334485/">1465334485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465334544/">1465334544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465334664/">1465334664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465334665/">1465334665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465334785/">1465334785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465335030/">1465335030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465335147/">1465335147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465335504/">1465335504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465336164/">1465336164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465337064/">1465337064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465337124/">1465337124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465337185/">1465337185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465337304/">1465337304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465338746/">1465338746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465340603/">1465340603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465342045/">1465342045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465347084/">1465347084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465347205/">1465347205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465347564/">1465347564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465351105/">1465351105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465352309/">1465352309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465352429/">1465352429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465352609/">1465352609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465353026/">1465353026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465353629/">1465353629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465356870/">1465356870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465356989/">1465356989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465360049/">1465360049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465361189/">1465361189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465362749/">1465362749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465363108/">1465363108/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465363589/">1465363589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465363805/">1465363805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465368332/">1465368332/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465368451/">1465368451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465371809/">1465371809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465372169/">1465372169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465372589/">1465372589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465373369/">1465373369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465373609/">1465373609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465374869/">1465374869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465375950/">1465375950/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465376969/">1465376969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465378409/">1465378409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465380569/">1465380569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465380870/">1465380870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465380871/">1465380871/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465381109/">1465381109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465381409/">1465381409/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465381531/">1465381531/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465381709/">1465381709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465383031/">1465383031/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465385402/">1465385402/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465385613/">1465385613/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465386511/">1465386511/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465390049/">1465390049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465390770/">1465390770/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465390949/">1465390949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465391729/">1465391729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465392153/">1465392153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465392810/">1465392810/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465393169/">1465393169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465393949/">1465393949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465394429/">1465394429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465395149/">1465395149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465395449/">1465395449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465397969/">1465397969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465400849/">1465400849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465401029/">1465401029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465401449/">1465401449/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465401990/">1465401990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465402109/">1465402109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465402291/">1465402291/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465403849/">1465403849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465404569/">1465404569/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465405289/">1465405289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465406969/">1465406969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465407005/">1465407005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465407989/">1465407989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465407990/">1465407990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465408349/">1465408349/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465408829/">1465408829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465409069/">1465409069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465411529/">1465411529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465412670/">1465412670/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465412969/">1465412969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465413509/">1465413509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465413749/">1465413749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465415849/">1465415849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465416030/">1465416030/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465416209/">1465416209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465418429/">1465418429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465419269/">1465419269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465420229/">1465420229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465421490/">1465421490/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465421909/">1465421909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465422329/">1465422329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465422869/">1465422869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465422929/">1465422929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465423829/">1465423829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465424729/">1465424729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465428606/">1465428606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465428809/">1465428809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465429050/">1465429050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465429351/">1465429351/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465429829/">1465429829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465430970/">1465430970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465431631/">1465431631/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465432169/">1465432169/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465432710/">1465432710/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465434750/">1465434750/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465434989/">1465434989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465436248/">1465436248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465436850/">1465436850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465443450/">1465443450/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465444829/">1465444829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465446268/">1465446268/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465446509/">1465446509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465446629/">1465446629/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465447050/">1465447050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465448729/">1465448729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465449089/">1465449089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465450304/">1465450304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465453048/">1465453048/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465454549/">1465454549/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465454848/">1465454848/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465455329/">1465455329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465455429/">1465455429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465461053/">1465461053/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465464389/">1465464389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465465889/">1465465889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465467270/">1465467270/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465467809/">1465467809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465468049/">1465468049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465468529/">1465468529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465469130/">1465469130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465470929/">1465470929/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465471886/">1465471886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465472069/">1465472069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465473390/">1465473390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465473689/">1465473689/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465474949/">1465474949/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465477649/">1465477649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465478009/">1465478009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465478369/">1465478369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465478969/">1465478969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465480049/">1465480049/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465480050/">1465480050/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465480548/">1465480548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465482682/">1465482682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465482989/">1465482989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465483168/">1465483168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465483590/">1465483590/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465484609/">1465484609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465484850/">1465484850/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465485090/">1465485090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465486530/">1465486530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465486709/">1465486709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465489771/">1465489771/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465490195/">1465490195/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465490729/">1465490729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465491990/">1465491990/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465492769/">1465492769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465493309/">1465493309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465493470/">1465493470/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465497091/">1465497091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465497329/">1465497329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465497509/">1465497509/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465497568/">1465497568/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465498229/">1465498229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465500448/">1465500448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465502011/">1465502011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465502069/">1465502069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465504223/">1465504223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465505790/">1465505790/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465506451/">1465506451/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465507530/">1465507530/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465508369/">1465508369/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465508429/">1465508429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465509149/">1465509149/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465509390/">1465509390/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465512208/">1465512208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465512869/">1465512869/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465513290/">1465513290/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465514008/">1465514008/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465515010/">1465515010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465519649/">1465519649/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465520249/">1465520249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465520309/">1465520309/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465526969/">1465526969/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465532070/">1465532070/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465532491/">1465532491/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465532608/">1465532608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465536652/">1465536652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465539269/">1465539269/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465539989/">1465539989/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465543289/">1465543289/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465543529/">1465543529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465543829/">1465543829/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465548329/">1465548329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465550009/">1465550009/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465552109/">1465552109/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465552589/">1465552589/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465558251/">1465558251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465560090/">1465560090/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465560389/">1465560389/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465565909/">1465565909/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465566089/">1465566089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465568249/">1465568249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465568609/">1465568609/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465569043/">1465569043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465569870/">1465569870/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465575510/">1465575510/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465575749/">1465575749/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465576529/">1465576529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465576709/">1465576709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465577849/">1465577849/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465578384/">1465578384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465579818/">1465579818/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465580246/">1465580246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465581863/">1465581863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465582584/">1465582584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465583604/">1465583604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465583723/">1465583723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465585763/">1465585763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465589304/">1465589304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465590323/">1465590323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465590608/">1465590608/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465591343/">1465591343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465592604/">1465592604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465592783/">1465592783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465592964/">1465592964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465593204/">1465593204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465594284/">1465594284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465594343/">1465594343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465596143/">1465596143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465597283/">1465597283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465599743/">1465599743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465601663/">1465601663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465603283/">1465603283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465606164/">1465606164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465609824/">1465609824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465610664/">1465610664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465610784/">1465610784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465611504/">1465611504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465612366/">1465612366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465612644/">1465612644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465612944/">1465612944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465614684/">1465614684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465623074/">1465623074/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465623075/">1465623075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465631244/">1465631244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465633104/">1465633104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465634543/">1465634543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465639585/">1465639585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465644605/">1465644605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465650323/">1465650323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465650683/">1465650683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465659143/">1465659143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465662503/">1465662503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465662683/">1465662683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465662923/">1465662923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465666255/">1465666255/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465670543/">1465670543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465677043/">1465677043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465692743/">1465692743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465698658/">1465698658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465704444/">1465704444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465713143/">1465713143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465719023/">1465719023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465719563/">1465719563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465723944/">1465723944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465725444/">1465725444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465741464/">1465741464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465741704/">1465741704/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465743744/">1465743744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465743923/">1465743923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465752648/">1465752648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465754723/">1465754723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465758203/">1465758203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465763416/">1465763416/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765731/">1465765731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765732/">1465765732/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765733/">1465765733/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765734/">1465765734/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765739/">1465765739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765740/">1465765740/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765742/">1465765742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765759/">1465765759/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765760/">1465765760/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765761/">1465765761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765772/">1465765772/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765773/">1465765773/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465765775/">1465765775/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766185/">1465766185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766186/">1465766186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766187/">1465766187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766189/">1465766189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766192/">1465766192/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766193/">1465766193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766194/">1465766194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766213/">1465766213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766215/">1465766215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766218/">1465766218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766226/">1465766226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766228/">1465766228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465766229/">1465766229/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465802124/">1465802124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465803924/">1465803924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465804524/">1465804524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465805124/">1465805124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465806263/">1465806263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465814724/">1465814724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465817461/">1465817461/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465821264/">1465821264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465827864/">1465827864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465828260/">1465828260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465829124/">1465829124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465829303/">1465829303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465830503/">1465830503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465831524/">1465831524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465831944/">1465831944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465832184/">1465832184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465832603/">1465832603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465834764/">1465834764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465839044/">1465839044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465845983/">1465845983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465849808/">1465849808/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465854083/">1465854083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465859966/">1465859966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465885884/">1465885884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465895304/">1465895304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465900763/">1465900763/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465901363/">1465901363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465901543/">1465901543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465901723/">1465901723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465902203/">1465902203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465902323/">1465902323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465902446/">1465902446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465903283/">1465903283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465903703/">1465903703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465903806/">1465903806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465904724/">1465904724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465904784/">1465904784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465905381/">1465905381/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465906284/">1465906284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465907663/">1465907663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465907785/">1465907785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465908623/">1465908623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465909707/">1465909707/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465911387/">1465911387/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465911564/">1465911564/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465912045/">1465912045/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465912283/">1465912283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465914756/">1465914756/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465915344/">1465915344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465915583/">1465915583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465915884/">1465915884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465917743/">1465917743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465918248/">1465918248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465919543/">1465919543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465922423/">1465922423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465925429/">1465925429/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465928843/">1465928843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465936254/">1465936254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465938203/">1465938203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465940543/">1465940543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465941265/">1465941265/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465941983/">1465941983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465942283/">1465942283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465944623/">1465944623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465948703/">1465948703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465949063/">1465949063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465949123/">1465949123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465957815/">1465957815/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465960044/">1465960044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465961064/">1465961064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465967483/">1465967483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465968504/">1465968504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465970483/">1465970483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465976281/">1465976281/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465979405/">1465979405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465979483/">1465979483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465980264/">1465980264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465980383/">1465980383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465981644/">1465981644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465981943/">1465981943/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465983084/">1465983084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465984403/">1465984403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465984824/">1465984824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465986383/">1465986383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465987163/">1465987163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465987823/">1465987823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465988003/">1465988003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465988303/">1465988303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465990163/">1465990163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465990204/">1465990204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465995023/">1465995023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465995623/">1465995623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465997844/">1465997844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1465999703/">1465999703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466000244/">1466000244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466001122/">1466001122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466001503/">1466001503/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466005343/">1466005343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466005463/">1466005463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466006003/">1466006003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466006123/">1466006123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466006243/">1466006243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466006543/">1466006543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466008163/">1466008163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466008765/">1466008765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466009964/">1466009964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466011284/">1466011284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466011866/">1466011866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466025083/">1466025083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466025923/">1466025923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466027065/">1466027065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466027363/">1466027363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466029464/">1466029464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466030244/">1466030244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466045063/">1466045063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466046803/">1466046803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466061323/">1466061323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466061443/">1466061443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466062043/">1466062043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466062764/">1466062764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466062834/">1466062834/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466063063/">1466063063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466063303/">1466063303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466068886/">1466068886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466070683/">1466070683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466075424/">1466075424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466075903/">1466075903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466079084/">1466079084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466080043/">1466080043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466081124/">1466081124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466085504/">1466085504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466087064/">1466087064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466087243/">1466087243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466087428/">1466087428/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466087963/">1466087963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466088804/">1466088804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466088863/">1466088863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466095466/">1466095466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466095883/">1466095883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466096003/">1466096003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466098223/">1466098223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466098702/">1466098702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466109005/">1466109005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466118923/">1466118923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466121683/">1466121683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466121804/">1466121804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466124863/">1466124863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466150363/">1466150363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466157563/">1466157563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466159183/">1466159183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466160143/">1466160143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466165483/">1466165483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466166083/">1466166083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466166863/">1466166863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466168063/">1466168063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466169994/">1466169994/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466169995/">1466169995/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466173944/">1466173944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466178329/">1466178329/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466181203/">1466181203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466181863/">1466181863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466187444/">1466187444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466201183/">1466201183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466217383/">1466217383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466221044/">1466221044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466235323/">1466235323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466243663/">1466243663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466247263/">1466247263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466252303/">1466252303/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466254283/">1466254283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466265863/">1466265863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466280923/">1466280923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466287583/">1466287583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466292683/">1466292683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466324903/">1466324903/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466325623/">1466325623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466332645/">1466332645/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466371043/">1466371043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466390543/">1466390543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466410762/">1466410762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466411363/">1466411363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466414003/">1466414003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466414723/">1466414723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466418203/">1466418203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466418323/">1466418323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466419823/">1466419823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466422101/">1466422101/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466423423/">1466423423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466424023/">1466424023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466424203/">1466424203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466424324/">1466424324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466424563/">1466424563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466429347/">1466429347/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466429439/">1466429439/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466429441/">1466429441/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466429444/">1466429444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466429448/">1466429448/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466429537/">1466429537/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466430562/">1466430562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466430924/">1466430924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466432136/">1466432136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466432603/">1466432603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466433145/">1466433145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466433623/">1466433623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466436985/">1466436985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466442023/">1466442023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466442383/">1466442383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466443103/">1466443103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466444422/">1466444422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466445385/">1466445385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466446344/">1466446344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466448684/">1466448684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466448744/">1466448744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466449823/">1466449823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466450483/">1466450483/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466452343/">1466452343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466453183/">1466453183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466453664/">1466453664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466455163/">1466455163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466458923/">1466458923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466461203/">1466461203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466461863/">1466461863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466463783/">1466463783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466471582/">1466471582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466473803/">1466473803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466475603/">1466475603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466482204/">1466482204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466488143/">1466488143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466491739/">1466491739/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466491863/">1466491863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466492342/">1466492342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466494383/">1466494383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466495583/">1466495583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466495883/">1466495883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466497323/">1466497323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466500561/">1466500561/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466500920/">1466500920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466501722/">1466501722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466501723/">1466501723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466504100/">1466504100/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466506743/">1466506743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466506980/">1466506980/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466508244/">1466508244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466509322/">1466509322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466510219/">1466510219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466511125/">1466511125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466512260/">1466512260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466513580/">1466513580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466513820/">1466513820/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466515860/">1466515860/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466516764/">1466516764/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466519595/">1466519595/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466520059/">1466520059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466520844/">1466520844/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466520901/">1466520901/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466521083/">1466521083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466521263/">1466521263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466522403/">1466522403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466523119/">1466523119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466524079/">1466524079/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466529182/">1466529182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466541723/">1466541723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466541840/">1466541840/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466541963/">1466541963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466541964/">1466541964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466542023/">1466542023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466542082/">1466542082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466542144/">1466542144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466542263/">1466542263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466542923/">1466542923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466543041/">1466543041/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466543160/">1466543160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466543463/">1466543463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466543944/">1466543944/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466546042/">1466546042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466547543/">1466547543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466549043/">1466549043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466549220/">1466549220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466552280/">1466552280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466553480/">1466553480/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466554080/">1466554080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466557203/">1466557203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466557619/">1466557619/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466559603/">1466559603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466561280/">1466561280/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466561403/">1466561403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466563623/">1466563623/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466563743/">1466563743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466565603/">1466565603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466567703/">1466567703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466568359/">1466568359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466572563/">1466572563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466574063/">1466574063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466576103/">1466576103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466578143/">1466578143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466579882/">1466579882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466580003/">1466580003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466580902/">1466580902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466581803/">1466581803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466582163/">1466582163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466583783/">1466583783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466583784/">1466583784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466589244/">1466589244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466591523/">1466591523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466594583/">1466594583/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466596081/">1466596081/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466596261/">1466596261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466598363/">1466598363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466600163/">1466600163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466600940/">1466600940/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466601000/">1466601000/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466601359/">1466601359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466603580/">1466603580/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466605889/">1466605889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466616605/">1466616605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466617224/">1466617224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466636425/">1466636425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466636544/">1466636544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466636604/">1466636604/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466636664/">1466636664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466636724/">1466636724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466637444/">1466637444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466637744/">1466637744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466638203/">1466638203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466638404/">1466638404/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466643146/">1466643146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466643325/">1466643325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466646385/">1466646385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466652984/">1466652984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466653224/">1466653224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466653284/">1466653284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466653344/">1466653344/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466653584/">1466653584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466655504/">1466655504/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466656464/">1466656464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466657664/">1466657664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466661624/">1466661624/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466663185/">1466663185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466668164/">1466668164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466668284/">1466668284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466669124/">1466669124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466669683/">1466669683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466669724/">1466669724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466670085/">1466670085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466671403/">1466671403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466672064/">1466672064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466673325/">1466673325/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466674709/">1466674709/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466675964/">1466675964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466678546/">1466678546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466679805/">1466679805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466682263/">1466682263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466683584/">1466683584/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466684964/">1466684964/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466685745/">1466685745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466685925/">1466685925/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466687726/">1466687726/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466687784/">1466687784/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466687906/">1466687906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466688084/">1466688084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466688206/">1466688206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466688446/">1466688446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466688865/">1466688865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466688924/">1466688924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466689765/">1466689765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466693064/">1466693064/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466693307/">1466693307/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466694084/">1466694084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466696063/">1466696063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466697155/">1466697155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466697924/">1466697924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466703046/">1466703046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466724418/">1466724418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466724478/">1466724478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466724538/">1466724538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466724598/">1466724598/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466724646/">1466724646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466724656/">1466724656/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466725259/">1466725259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466726638/">1466726638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466733661/">1466733661/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466734018/">1466734018/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466735458/">1466735458/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466739418/">1466739418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466743558/">1466743558/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466751118/">1466751118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466751238/">1466751238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466751538/">1466751538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466752498/">1466752498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466754358/">1466754358/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466754478/">1466754478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466760304/">1466760304/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466767071/">1466767071/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466767768/">1466767768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466770080/">1466770080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466770678/">1466770678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466771471/">1466771471/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466773499/">1466773499/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466774398/">1466774398/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466774459/">1466774459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466778700/">1466778700/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466783140/">1466783140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466783158/">1466783158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466787058/">1466787058/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466790478/">1466790478/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466791498/">1466791498/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466791738/">1466791738/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466792459/">1466792459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466796538/">1466796538/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466796898/">1466796898/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466797678/">1466797678/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466798159/">1466798159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466799476/">1466799476/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466799778/">1466799778/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466800798/">1466800798/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466800920/">1466800920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466804638/">1466804638/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466811010/">1466811010/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466820264/">1466820264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466821880/">1466821880/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466859384/">1466859384/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466860284/">1466860284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466869884/">1466869884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466874924/">1466874924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466878224/">1466878224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466888664/">1466888664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466890464/">1466890464/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466890644/">1466890644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466894544/">1466894544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466897724/">1466897724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466957184/">1466957184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466958744/">1466958744/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466959884/">1466959884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466961984/">1466961984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466970144/">1466970144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466973084/">1466973084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466978124/">1466978124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466979444/">1466979444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466981544/">1466981544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466982144/">1466982144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466988204/">1466988204/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466988444/">1466988444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1466992824/">1466992824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467000084/">1467000084/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467002363/">1467002363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467003324/">1467003324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467009264/">1467009264/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467010403/">1467010403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467012924/">1467012924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467016825/">1467016825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467020124/">1467020124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467028359/">1467028359/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467029904/">1467029904/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467039113/">1467039113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467039695/">1467039695/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467040279/">1467040279/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467041179/">1467041179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467043517/">1467043517/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467045799/">1467045799/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467046397/">1467046397/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467047177/">1467047177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467047717/">1467047717/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467047837/">1467047837/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467048377/">1467048377/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467049340/">1467049340/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467051077/">1467051077/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467051918/">1467051918/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467051977/">1467051977/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467052238/">1467052238/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467052338/">1467052338/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467052578/">1467052578/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467052999/">1467052999/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467053298/">1467053298/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467053657/">1467053657/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467054259/">1467054259/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467061819/">1467061819/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467065418/">1467065418/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467066140/">1467066140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467066729/">1467066729/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467067602/">1467067602/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467072197/">1467072197/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467072379/">1467072379/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467073639/">1467073639/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467074239/">1467074239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467074780/">1467074780/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467076401/">1467076401/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467077119/">1467077119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467079039/">1467079039/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467080062/">1467080062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467080422/">1467080422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467080962/">1467080962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467081059/">1467081059/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467081443/">1467081443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467081862/">1467081862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467083002/">1467083002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467092011/">1467092011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467092137/">1467092137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467092140/">1467092140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467092963/">1467092963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467093562/">1467093562/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467094222/">1467094222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467097882/">1467097882/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467098242/">1467098242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467099683/">1467099683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467099863/">1467099863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467100823/">1467100823/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467101363/">1467101363/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467101423/">1467101423/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467101542/">1467101542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467101914/">1467101914/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467102323/">1467102323/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467106228/">1467106228/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467108086/">1467108086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467108443/">1467108443/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467114861/">1467114861/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467115644/">1467115644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467118163/">1467118163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467119543/">1467119543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467120863/">1467120863/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467121103/">1467121103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467123324/">1467123324/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467123385/">1467123385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467123984/">1467123984/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467124282/">1467124282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467124403/">1467124403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467125123/">1467125123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467125842/">1467125842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467126080/">1467126080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467126321/">1467126321/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467127224/">1467127224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467127225/">1467127225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467128963/">1467128963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467129803/">1467129803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467129879/">1467129879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467131182/">1467131182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467133284/">1467133284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467134075/">1467134075/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467134970/">1467134970/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467135089/">1467135089/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467136524/">1467136524/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467136643/">1467136643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467137244/">1467137244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467137783/">1467137783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467138444/">1467138444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467138563/">1467138563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467139343/">1467139343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467139883/">1467139883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467140721/">1467140721/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467141983/">1467141983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467142465/">1467142465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467143063/">1467143063/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467143433/">1467143433/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467145104/">1467145104/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467145893/">1467145893/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467148102/">1467148102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467159206/">1467159206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467160286/">1467160286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467164906/">1467164906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467168146/">1467168146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467170668/">1467170668/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467171266/">1467171266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467175226/">1467175226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467177266/">1467177266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467179487/">1467179487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467179906/">1467179906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467180086/">1467180086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467180266/">1467180266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467181406/">1467181406/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467181766/">1467181766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467181886/">1467181886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467182366/">1467182366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467182486/">1467182486/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467186567/">1467186567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467187586/">1467187586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467190889/">1467190889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467191666/">1467191666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467191966/">1467191966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467192927/">1467192927/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467194006/">1467194006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467195447/">1467195447/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467197786/">1467197786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467197906/">1467197906/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467198029/">1467198029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467199769/">1467199769/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467201146/">1467201146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467203054/">1467203054/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467204626/">1467204626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467204687/">1467204687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467205946/">1467205946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467206186/">1467206186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467206426/">1467206426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467210091/">1467210091/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467212308/">1467212308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467213087/">1467213087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467215186/">1467215186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467216566/">1467216566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467216867/">1467216867/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467217226/">1467217226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467225567/">1467225567/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467226886/">1467226886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467227606/">1467227606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467229767/">1467229767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467230366/">1467230366/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467230606/">1467230606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467230908/">1467230908/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467231146/">1467231146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467233246/">1467233246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467233444/">1467233444/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467237926/">1467237926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467240266/">1467240266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467240627/">1467240627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467242787/">1467242787/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467244587/">1467244587/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467244946/">1467244946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467245426/">1467245426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467250467/">1467250467/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467250647/">1467250647/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467252865/">1467252865/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467252986/">1467252986/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467254187/">1467254187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467260666/">1467260666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467261026/">1467261026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467261266/">1467261266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467261746/">1467261746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467262767/">1467262767/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467264386/">1467264386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467268586/">1467268586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467269306/">1467269306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467271346/">1467271346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467272306/">1467272306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467273026/">1467273026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467275966/">1467275966/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467282087/">1467282087/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467283886/">1467283886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467284186/">1467284186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467285746/">1467285746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467288326/">1467288326/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467288686/">1467288686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467288926/">1467288926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467289408/">1467289408/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467297253/">1467297253/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467315809/">1467315809/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467316529/">1467316529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467316648/">1467316648/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467317669/">1467317669/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467317789/">1467317789/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467318029/">1467318029/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467319961/">1467319961/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467320069/">1467320069/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467320249/">1467320249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467320485/">1467320485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467322283/">1467322283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467322822/">1467322822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467323432/">1467323432/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467323731/">1467323731/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467325046/">1467325046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467326183/">1467326183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467326841/">1467326841/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467326900/">1467326900/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467327140/">1467327140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467330920/">1467330920/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467333563/">1467333563/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467338422/">1467338422/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467340702/">1467340702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467341842/">1467341842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467343102/">1467343102/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467345202/">1467345202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467346413/">1467346413/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467354382/">1467354382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467360983/">1467360983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467362300/">1467362300/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467362482/">1467362482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467362722/">1467362722/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467362962/">1467362962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467363080/">1467363080/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467363143/">1467363143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467363560/">1467363560/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467364761/">1467364761/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467370161/">1467370161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467370884/">1467370884/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467371842/">1467371842/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467372085/">1467372085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467375023/">1467375023/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467376222/">1467376222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467376342/">1467376342/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467380662/">1467380662/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467381028/">1467381028/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467383663/">1467383663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467387260/">1467387260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467389242/">1467389242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467391523/">1467391523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467392182/">1467392182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467395420/">1467395420/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467396382/">1467396382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467399382/">1467399382/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467399802/">1467399802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467404183/">1467404183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467417862/">1467417862/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467427582/">1467427582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467427822/">1467427822/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467437481/">1467437481/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467451222/">1467451222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467454762/">1467454762/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467459982/">1467459982/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467506902/">1467506902/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467521362/">1467521362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467564802/">1467564802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467567263/">1467567263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467586942/">1467586942/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467604768/">1467604768/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467604886/">1467604886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467605006/">1467605006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467613226/">1467613226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467614664/">1467614664/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467618146/">1467618146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467618686/">1467618686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467620426/">1467620426/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467621506/">1467621506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467630506/">1467630506/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467630686/">1467630686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467630687/">1467630687/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467637886/">1467637886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467640225/">1467640225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467640297/">1467640297/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467640646/">1467640646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467641546/">1467641546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467642158/">1467642158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467642446/">1467642446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467644308/">1467644308/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467645146/">1467645146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467645626/">1467645626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467648206/">1467648206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467652345/">1467652345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467652948/">1467652948/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467654446/">1467654446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467654507/">1467654507/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467656004/">1467656004/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467656362/">1467656362/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467657922/">1467657922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467661582/">1467661582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467670164/">1467670164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467690622/">1467690622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467690802/">1467690802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467690922/">1467690922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467696142/">1467696142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467697518/">1467697518/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467702022/">1467702022/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467705922/">1467705922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467709644/">1467709644/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467710783/">1467710783/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467714383/">1467714383/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467714742/">1467714742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467722305/">1467722305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467724043/">1467724043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467725962/">1467725962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467726263/">1467726263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467726802/">1467726802/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467727703/">1467727703/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467729682/">1467729682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467730282/">1467730282/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467730523/">1467730523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467731725/">1467731725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467737066/">1467737066/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467739522/">1467739522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467740963/">1467740963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467742548/">1467742548/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467742643/">1467742643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467744683/">1467744683/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467745463/">1467745463/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467746062/">1467746062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467748403/">1467748403/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467748946/">1467748946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467750803/">1467750803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467752424/">1467752424/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467757883/">1467757883/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467759262/">1467759262/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467759742/">1467759742/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467763886/">1467763886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467764002/">1467764002/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467769361/">1467769361/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467773963/">1467773963/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467776962/">1467776962/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467777202/">1467777202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467777622/">1467777622/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467780983/">1467780983/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467787043/">1467787043/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467787523/">1467787523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467796283/">1467796283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467796702/">1467796702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467798322/">1467798322/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467798682/">1467798682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467804746/">1467804746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467812123/">1467812123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467812244/">1467812244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467812782/">1467812782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467813922/">1467813922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467814702/">1467814702/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467816082/">1467816082/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467816201/">1467816201/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467816923/">1467816923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467821123/">1467821123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467824543/">1467824543/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467826284/">1467826284/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467828625/">1467828625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467829582/">1467829582/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467831743/">1467831743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467832824/">1467832824/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467836782/">1467836782/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467842183/">1467842183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467846682/">1467846682/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467848542/">1467848542/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467854843/">1467854843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467855143/">1467855143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467856343/">1467856343/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467858923/">1467858923/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467858924/">1467858924/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467862232/">1467862232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467862958/">1467862958/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467863302/">1467863302/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467863482/">1467863482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467863843/">1467863843/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467864804/">1467864804/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467864922/">1467864922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467865042/">1467865042/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467869477/">1467869477/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467870015/">1467870015/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467872062/">1467872062/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467872182/">1467872182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467872482/">1467872482/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467872522/">1467872522/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467878183/">1467878183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467880044/">1467880044/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467880260/">1467880260/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467882083/">1467882083/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467882922/">1467882922/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467884243/">1467884243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467884724/">1467884724/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467886643/">1467886643/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467891065/">1467891065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467895103/">1467895103/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467895585/">1467895585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467900743/">1467900743/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467901887/">1467901887/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467902663/">1467902663/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467903263/">1467903263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467903684/">1467903684/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467904523/">1467904523/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467906144/">1467906144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908105/">1467908105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908111/">1467908111/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908112/">1467908112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908113/">1467908113/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908114/">1467908114/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908115/">1467908115/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908116/">1467908116/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908117/">1467908117/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908118/">1467908118/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908119/">1467908119/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908120/">1467908120/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908121/">1467908121/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908122/">1467908122/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908123/">1467908123/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908124/">1467908124/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908125/">1467908125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908126/">1467908126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908127/">1467908127/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908128/">1467908128/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908129/">1467908129/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908130/">1467908130/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908131/">1467908131/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908132/">1467908132/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908133/">1467908133/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908134/">1467908134/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908135/">1467908135/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908136/">1467908136/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908137/">1467908137/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908138/">1467908138/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908139/">1467908139/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908140/">1467908140/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908141/">1467908141/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908142/">1467908142/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908143/">1467908143/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908144/">1467908144/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908145/">1467908145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908146/">1467908146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908147/">1467908147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908148/">1467908148/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908150/">1467908150/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908152/">1467908152/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908153/">1467908153/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908154/">1467908154/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908155/">1467908155/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908157/">1467908157/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908158/">1467908158/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908159/">1467908159/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908160/">1467908160/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908161/">1467908161/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908162/">1467908162/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908163/">1467908163/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908164/">1467908164/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908165/">1467908165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908166/">1467908166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908167/">1467908167/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908168/">1467908168/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908170/">1467908170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908171/">1467908171/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908173/">1467908173/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908174/">1467908174/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908175/">1467908175/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908176/">1467908176/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908177/">1467908177/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908178/">1467908178/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908179/">1467908179/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908180/">1467908180/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908181/">1467908181/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908182/">1467908182/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908183/">1467908183/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908184/">1467908184/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908186/">1467908186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908187/">1467908187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908189/">1467908189/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908190/">1467908190/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908191/">1467908191/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908193/">1467908193/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908194/">1467908194/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908200/">1467908200/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908202/">1467908202/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908203/">1467908203/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908205/">1467908205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908206/">1467908206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908207/">1467908207/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908208/">1467908208/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908209/">1467908209/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908210/">1467908210/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908212/">1467908212/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908213/">1467908213/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908214/">1467908214/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908215/">1467908215/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908216/">1467908216/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908217/">1467908217/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908219/">1467908219/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908220/">1467908220/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908222/">1467908222/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908223/">1467908223/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908224/">1467908224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908227/">1467908227/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908230/">1467908230/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908232/">1467908232/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908235/">1467908235/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908237/">1467908237/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908239/">1467908239/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908242/">1467908242/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908244/">1467908244/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908247/">1467908247/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908249/">1467908249/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908251/">1467908251/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908254/">1467908254/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908256/">1467908256/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908258/">1467908258/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908261/">1467908261/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908263/">1467908263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908544/">1467908544/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467908603/">1467908603/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467909803/">1467909803/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467910766/">1467910766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467910941/">1467910941/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467912442/">1467912442/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467912652/">1467912652/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467913224/">1467913224/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467916283/">1467916283/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467917723/">1467917723/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467918685/">1467918685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467919405/">1467919405/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467919466/">1467919466/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467923459/">1467923459/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467923786/">1467923786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467925826/">1467925826/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467926786/">1467926786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467927985/">1467927985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467931886/">1467931886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467934218/">1467934218/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467936806/">1467936806/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467938186/">1467938186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467941666/">1467941666/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467943106/">1467943106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467944725/">1467944725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467945086/">1467945086/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467952106/">1467952106/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467952705/">1467952705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467955585/">1467955585/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467955886/">1467955886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467957505/">1467957505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467958646/">1467958646/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467958885/">1467958885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467961165/">1467961165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467961827/">1467961827/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467962185/">1467962185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467963147/">1467963147/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467965306/">1467965306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467966653/">1467966653/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467968305/">1467968305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467969805/">1467969805/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467973046/">1467973046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467974794/">1467974794/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467978446/">1467978446/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467982886/">1467982886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467986245/">1467986245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467988337/">1467988337/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467991226/">1467991226/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467991945/">1467991945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467993445/">1467993445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467995368/">1467995368/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467998065/">1467998065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1467999625/">1467999625/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468005505/">1468005505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468009894/">1468009894/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468015526/">1468015526/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468016546/">1468016546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468016665/">1468016665/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468016785/">1468016785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468017266/">1468017266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468017807/">1468017807/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468018165/">1468018165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468020085/">1468020085/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468020628/">1468020628/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468022005/">1468022005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468023866/">1468023866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468029686/">1468029686/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468030889/">1468030889/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468031465/">1468031465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468032866/">1468032866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468036165/">1468036165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468038026/">1468038026/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468042263/">1468042263/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468043605/">1468043605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468046605/">1468046605/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468048345/">1468048345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468051346/">1468051346/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468056146/">1468056146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468063225/">1468063225/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468063835/">1468063835/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468076305/">1468076305/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468079185/">1468079185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468080146/">1468080146/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468083505/">1468083505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468099586/">1468099586/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468099765/">1468099765/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468107003/">1468107003/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468107685/">1468107685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468113685/">1468113685/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468117913/">1468117913/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468130965/">1468130965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468139453/">1468139453/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468149145/">1468149145/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468150243/">1468150243/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468189885/">1468189885/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468199546/">1468199546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468203985/">1468203985/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468223965/">1468223965/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468224745/">1468224745/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468225286/">1468225286/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468226126/">1468226126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468226306/">1468226306/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468226425/">1468226425/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468226606/">1468226606/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468227206/">1468227206/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468228766/">1468228766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468229186/">1468229186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468230627/">1468230627/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468233658/">1468233658/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468235546/">1468235546/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468236792/">1468236792/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468237825/">1468237825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468239386/">1468239386/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468239505/">1468239505/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468242746/">1468242746/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468243046/">1468243046/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468243527/">1468243527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468245385/">1468245385/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468245866/">1468245866/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468247125/">1468247125/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468249946/">1468249946/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468251926/">1468251926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468255766/">1468255766/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468258272/">1468258272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468258886/">1468258886/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468259245/">1468259245/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468264465/">1468264465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468265185/">1468265185/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468266266/">1468266266/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468267705/">1468267705/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468267825/">1468267825/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468267945/">1468267945/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468268365/">1468268365/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468268785/">1468268785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468269112/">1468269112/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468270465/">1468270465/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468271367/">1468271367/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468271907/">1468271907/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468273529/">1468273529/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468279879/">1468279879/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468280785/">1468280785/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468285105/">1468285105/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468301845/">1468301845/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468302565/">1468302565/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468304006/">1468304006/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468304725/">1468304725/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468307487/">1468307487/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468308626/">1468308626/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468308926/">1468308926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468312272/">1468312272/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468314445/">1468314445/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468314566/">1468314566/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468314926/">1468314926/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468315170/">1468315170/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468315345/">1468315345/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468315525/">1468315525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468315828/">1468315828/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468316005/">1468316005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468316248/">1468316248/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468316485/">1468316485/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468317126/">1468317126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468320327/">1468320327/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468322011/">1468322011/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468323110/">1468323110/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468325246/">1468325246/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468325905/">1468325905/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468331126/">1468331126/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468331187/">1468331187/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468332864/">1468332864/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468333527/">1468333527/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468334186/">1468334186/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468337065/">1468337065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468339165/">1468339165/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468339166/">1468339166/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468339525/">1468339525/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468344205/">1468344205/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468346005/">1468346005/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468346065/">1468346065/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/1468346786/">1468346786/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td>Dir</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/latest/">latest/</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound-win32-pgo-bm71-build1-build5.txt.gz">mozilla-inbound-win32-pgo-bm71-build1-build5.txt.gz</a></td>
+ <td>701K</td>
+ <td>15-Jan-2016 22:36</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound-win32-pgo-bm72-build1-build202.txt.gz">mozilla-inbound-win32-pgo-bm72-build1-build202.txt.gz</a></td>
+ <td>574K</td>
+ <td>18-May-2016 14:14</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound-win32-pgo-bm73-build1-build218.txt.gz">mozilla-inbound-win32-pgo-bm73-build1-build218.txt.gz</a></td>
+ <td>614K</td>
+ <td>26-Mar-2016 13:09</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound-win32-pgo-bm74-build1-build140.txt.gz">mozilla-inbound-win32-pgo-bm74-build1-build140.txt.gz</a></td>
+ <td>579K</td>
+ <td>17-May-2016 18:25</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound-win32-pgo-bm91-build1-build191.txt.gz">mozilla-inbound-win32-pgo-bm91-build1-build191.txt.gz</a></td>
+ <td>572K</td>
+ <td>26-May-2016 22:49</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-chromez-bm110-tests1-windows-build2478.txt.gz">mozilla-inbound_win7-ix_test-chromez-bm110-tests1-windows-build2478.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-dromaeojs-e10s-bm109-tests1-windows-build1907.txt.gz">mozilla-inbound_win7-ix_test-dromaeojs-e10s-bm109-tests1-windows-build1907.txt.gz</a></td>
+ <td>46K</td>
+ <td>08-Apr-2016 21:07</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-g1-bm111-tests1-windows-build1066.txt.gz">mozilla-inbound_win7-ix_test-g1-bm111-tests1-windows-build1066.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-mochitest-1-bm126-tests1-windows-build96.txt.gz">mozilla-inbound_win7-ix_test-mochitest-1-bm126-tests1-windows-build96.txt.gz</a></td>
+ <td>84K</td>
+ <td>05-Mar-2016 16:10</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-other_nol64-bm110-tests1-windows-build1297.txt.gz">mozilla-inbound_win7-ix_test-other_nol64-bm110-tests1-windows-build1297.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-other_nol64-bm110-tests1-windows-build1298.txt.gz">mozilla-inbound_win7-ix_test-other_nol64-bm110-tests1-windows-build1298.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-svgr-bm110-tests1-windows-build2614.txt.gz">mozilla-inbound_win7-ix_test-svgr-bm110-tests1-windows-build2614.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-svgr-bm110-tests1-windows-build2616.txt.gz">mozilla-inbound_win7-ix_test-svgr-bm110-tests1-windows-build2616.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-tp5o-bm110-tests1-windows-build2394.txt.gz">mozilla-inbound_win7-ix_test-tp5o-bm110-tests1-windows-build2394.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win7-ix_test-xperf-bm112-tests1-windows-build1264.txt.gz">mozilla-inbound_win7-ix_test-xperf-bm112-tests1-windows-build1264.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-chromez-bm110-tests1-windows-build2478.txt.gz">mozilla-inbound_win8_test-chromez-bm110-tests1-windows-build2478.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-chromez-bm111-tests1-windows-build2156.txt.gz">mozilla-inbound_win8_test-chromez-bm111-tests1-windows-build2156.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-dromaeojs-bm111-tests1-windows-build2156.txt.gz">mozilla-inbound_win8_test-dromaeojs-bm111-tests1-windows-build2156.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-g1-bm112-tests1-windows-build622.txt.gz">mozilla-inbound_win8_test-g1-bm112-tests1-windows-build622.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-other_nol64-bm109-tests1-windows-build1352.txt.gz">mozilla-inbound_win8_test-other_nol64-bm109-tests1-windows-build1352.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-svgr-bm110-tests1-windows-build2417.txt.gz">mozilla-inbound_win8_test-svgr-bm110-tests1-windows-build2417.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-tp5o-bm110-tests1-windows-build2444.txt.gz">mozilla-inbound_win8_test-tp5o-bm110-tests1-windows-build2444.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_win8_test-tp5o-bm110-tests1-windows-build2446.txt.gz">mozilla-inbound_win8_test-tp5o-bm110-tests1-windows-build2446.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-chromez-bm109-tests1-windows-build2126.txt.gz">mozilla-inbound_xp-ix_test-chromez-bm109-tests1-windows-build2126.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-chromez-bm110-tests1-windows-build2446.txt.gz">mozilla-inbound_xp-ix_test-chromez-bm110-tests1-windows-build2446.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g1-bm110-tests1-windows-build1410.txt.gz">mozilla-inbound_xp-ix_test-g1-bm110-tests1-windows-build1410.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm111-tests1-windows-build1705.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm111-tests1-windows-build1705.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:18</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1549.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1549.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:18</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1550.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1550.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:24</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1551.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1551.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:24</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1552.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm112-tests1-windows-build1552.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:25</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm119-tests1-windows-build1752.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm119-tests1-windows-build1752.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:25</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm126-tests1-windows-build852.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm126-tests1-windows-build852.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:23</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-g2-e10s-bm126-tests1-windows-build853.txt.gz">mozilla-inbound_xp-ix_test-g2-e10s-bm126-tests1-windows-build853.txt.gz</a></td>
+ <td>26K</td>
+ <td>24-Mar-2016 15:25</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-other_nol64-bm110-tests1-windows-build1386.txt.gz">mozilla-inbound_xp-ix_test-other_nol64-bm110-tests1-windows-build1386.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-svgr-bm109-tests1-windows-build1998.txt.gz">mozilla-inbound_xp-ix_test-svgr-bm109-tests1-windows-build1998.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-svgr-bm112-tests1-windows-build1122.txt.gz">mozilla-inbound_xp-ix_test-svgr-bm112-tests1-windows-build1122.txt.gz</a></td>
+ <td>3K</td>
+ <td>23-Oct-2015 12:40</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-tp5o-e10s-bm109-tests1-windows-build1892.txt.gz">mozilla-inbound_xp-ix_test-tp5o-e10s-bm109-tests1-windows-build1892.txt.gz</a></td>
+ <td>82K</td>
+ <td>15-Apr-2016 16:04</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-tp5o-e10s-bm110-tests1-windows-build1585.txt.gz">mozilla-inbound_xp-ix_test-tp5o-e10s-bm110-tests1-windows-build1585.txt.gz</a></td>
+ <td>82K</td>
+ <td>04-Apr-2016 23:11</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-tp5o-e10s-bm111-tests1-windows-build1739.txt.gz">mozilla-inbound_xp-ix_test-tp5o-e10s-bm111-tests1-windows-build1739.txt.gz</a></td>
+ <td>82K</td>
+ <td>15-Apr-2016 15:33</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-tp5o-e10s-bm119-tests1-windows-build1674.txt.gz">mozilla-inbound_xp-ix_test-tp5o-e10s-bm119-tests1-windows-build1674.txt.gz</a></td>
+ <td>82K</td>
+ <td>15-Apr-2016 16:04</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-tp5o-e10s-bm127-tests1-windows-build874.txt.gz">mozilla-inbound_xp-ix_test-tp5o-e10s-bm127-tests1-windows-build874.txt.gz</a></td>
+ <td>82K</td>
+ <td>15-Apr-2016 16:04</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp-ix_test-tp5o-e10s-bm127-tests1-windows-build875.txt.gz">mozilla-inbound_xp-ix_test-tp5o-e10s-bm127-tests1-windows-build875.txt.gz</a></td>
+ <td>82K</td>
+ <td>15-Apr-2016 16:06</td>
+ </tr>
+
+
+
+ <tr>
+ <td>File</td>
+ <td><a href="/pub/firefox/tinderbox-builds/mozilla-inbound-win32/mozilla-inbound_xp_ix_test-mochitest-devtools-chrome-1-bm110-tests1-windows-build55.txt.gz">mozilla-inbound_xp_ix_test-mochitest-devtools-chrome-1-bm110-tests1-windows-build55.txt.gz</a></td>
+ <td>58K</td>
+ <td>26-May-2016 14:03</td>
+ </tr>
+
+
+ </table>
+</body></html>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-1.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-1.svg
new file mode 100644
index 000000000..1a1288858
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-1.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
+ x="0" y="0" width="1" height="1">
+ <feFlood flood-color="#00ff00" result="flood"/>
+ <feDisplacementMap x="10%" y="10%" width="80%" height="80%" style="color-interpolation-filters:sRGB"
+ in2="SourceGraphic" in="flood" scale="1" xChannelSelector="R" yChannelSelector="G"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="0" y="0" width="100" height="100" fill="#ff0000"/>
+</g>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-2.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-2.svg
new file mode 100644
index 000000000..385d08391
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-2.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
+ x="0" y="0" width="1" height="1">
+ <feImage xlink:href="http://example.com/tests/layout/generic/test/file_taintedfilters_red-flood-for-feImage.svg" result="flood"/>
+ <feDisplacementMap x="10%" y="10%" width="80%" height="80%" style="color-interpolation-filters:sRGB"
+ in="SourceGraphic" in2="flood" scale="1" xChannelSelector="R" yChannelSelector="G"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="0" y="0" width="100" height="100" fill="#00ff00"/>
+</g>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-3.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-3.svg
new file mode 100644
index 000000000..49990ede0
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-3.svg
@@ -0,0 +1,17 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
+ x="0" y="0" width="1" height="1">
+ <feImage xlink:href="file_taintedfilters_red-flood-for-feImage.svg" result="flood"/>
+ <feMerge result="map">
+ <feMergeNode in="SourceGraphic"/>
+ <feMergeNode in="flood"/>
+ </feMerge>
+ <feDisplacementMap x="10%" y="10%" width="80%" height="80%" style="color-interpolation-filters:sRGB"
+ in="SourceGraphic" in2="map" scale="1" xChannelSelector="R" yChannelSelector="G"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="0" y="0" width="100" height="100" fill="#00ff00"/>
+</g>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-ref.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-ref.svg
new file mode 100644
index 000000000..39a175bba
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-ref.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<rect x="10" y="10" width="80" height="80" fill="#00ff00"/>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-1.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-1.svg
new file mode 100644
index 000000000..dd52f2644
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-1.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
+ x="0" y="0" width="1" height="1">
+ <feImage xlink:href="file_taintedfilters_red-flood-for-feImage.svg" result="flood"/>
+ <feDisplacementMap x="10%" y="10%" width="80%" height="80%" style="color-interpolation-filters:sRGB"
+ in="SourceGraphic" in2="flood" scale="1" xChannelSelector="R" yChannelSelector="G"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="0" y="0" width="100" height="100" fill="#00ff00"/>
+</g>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-2.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-2.svg
new file mode 100644
index 000000000..17f7f7c1f
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-2.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
+ x="0" y="0" width="1" height="1">
+ <feImage xlink:href="http://example.com/tests/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg" result="flood"/>
+ <feDisplacementMap x="10%" y="10%" width="80%" height="80%" style="color-interpolation-filters:sRGB"
+ in="SourceGraphic" in2="flood" scale="1" xChannelSelector="R" yChannelSelector="G"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="0" y="0" width="100" height="100" fill="#00ff00"/>
+</g>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-ref.svg b/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-ref.svg
new file mode 100644
index 000000000..8ac9dff0b
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-ref.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<rect x="10" y="50" width="40" height="40" fill="#00ff00"/>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg b/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg
new file mode 100644
index 000000000..207594336
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
+
+<rect x="0" y="0" width="100" height="100" fill="#ff0000"/>
+
+</svg>
diff --git a/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg^headers^ b/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg^headers^
new file mode 100644
index 000000000..144198c6e
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg^headers^
@@ -0,0 +1,4 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
+Access-Control-Allow-Credentials: true
+Content-Type: image/svg+xml
+Cache-Control: no-cache, must-revalidate
diff --git a/layout/generic/test/file_taintedfilters_red-flood-for-feImage.svg b/layout/generic/test/file_taintedfilters_red-flood-for-feImage.svg
new file mode 100644
index 000000000..207594336
--- /dev/null
+++ b/layout/generic/test/file_taintedfilters_red-flood-for-feImage.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
+
+<rect x="0" y="0" width="100" height="100" fill="#ff0000"/>
+
+</svg>
diff --git a/layout/generic/test/frame_selection_underline-ref.xhtml b/layout/generic/test/frame_selection_underline-ref.xhtml
new file mode 100644
index 000000000..91b932dce
--- /dev/null
+++ b/layout/generic/test/frame_selection_underline-ref.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" class="willBeRemoved">
+<head>
+<link rel="stylesheet" type="text/css" href="frame_selection_underline.css"/>
+<script type="text/javascript">
+<![CDATA[
+
+function init(aTest)
+{
+ var target = document.getElementById("target");
+ var decoration = document.getElementById("decoration");
+ var leftSpacer = document.getElementById("leftspacer");
+ var rightSpacer = document.getElementById("rightspacer");
+
+ var docShell =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShell);
+ var controller =
+ docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsISelectionDisplay)
+ .QueryInterface(Components.interfaces.nsISelectionController);
+
+ const nsISelectionController = Components.interfaces.nsISelectionController;
+ if (aTest.selection.isIME) {
+ leftSpacer.style.display = rightSpacer.style.display = "inline-block";
+ } else {
+ leftSpacer.style.display = rightSpacer.style.display = "none";
+ }
+
+ target.style.fontFamily = aTest.font.family;
+ target.style.fontSize = aTest.font.defaultSize;
+
+ decoration.style.textDecorationStyle = aTest.decoration.styleName;
+ decoration.style.textDecorationColor = aTest.selection.decorationColor;
+
+ document.documentElement.removeAttribute("class");
+ setTimeout(function () {
+ document.documentElement.setAttribute("class", "willBeRemoved"); }, 0);
+}
+
+]]>
+</script>
+</head>
+<body class="reference">
+ <div id="target"><span id="decoration"><span id="leftspacer">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span id="rightspacer"></span>&nbsp;</span></div>
+</body>
+</html>
diff --git a/layout/generic/test/frame_selection_underline.css b/layout/generic/test/frame_selection_underline.css
new file mode 100644
index 000000000..b64ead881
--- /dev/null
+++ b/layout/generic/test/frame_selection_underline.css
@@ -0,0 +1,51 @@
+html {
+ font-size: 16px;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+}
+
+@font-face {
+ font-family: "AhemTest";
+ src: url(../../../../tests/fonts/Ahem.ttf);
+}
+
+@font-face {
+ font-family: "mplusTest";
+ src: url(../../../../tests/fonts/mplus/mplus-1p-regular.ttf);
+}
+
+/* For aligning the two spacers (see below) to the left most and the right most,
+ the div must create a new blocking format context. */
+div#target {
+ position: absolute;
+}
+
+span#decoration {
+ margin-left: 0.333em;
+}
+
+body.reference div span#decoration {
+ text-decoration: underline;
+}
+
+/* both ends of selection underlines for IME are clipped for making the
+ boundaries of clauses in composition string clear. These spacers will
+ cover the ends in the reference. */
+span#leftspacer, span#rightspacer {
+ background-color: white;
+ position: absolute;
+ width: 1px;
+ height: 100%;
+ overflow: hidden;
+}
+
+span#leftspacer {
+ left: 0.333em;
+}
+
+span#rightspacer {
+ right: 0;
+}
diff --git a/layout/generic/test/frame_selection_underline.xhtml b/layout/generic/test/frame_selection_underline.xhtml
new file mode 100644
index 000000000..4769eca6c
--- /dev/null
+++ b/layout/generic/test/frame_selection_underline.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" class="willBeRemoved">
+<head>
+<link rel="stylesheet" type="text/css" href="frame_selection_underline.css"/>
+<script type="text/javascript">
+<![CDATA[
+
+function init(aTest)
+{
+ var docShell =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShell);
+ var controller =
+ docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsISelectionDisplay)
+ .QueryInterface(Components.interfaces.nsISelectionController);
+
+ var selections = [
+ controller.SELECTION_SPELLCHECK,
+ controller.SELECTION_IME_RAWINPUT,
+ controller.SELECTION_IME_SELECTEDRAWTEXT,
+ controller.SELECTION_IME_CONVERTEDTEXT,
+ controller.SELECTION_IME_SELECTEDCONVERTEDTEXT,
+ ];
+ for (var i = 0; i < selections.length; i++) {
+ var sel = controller.getSelection(selections[i]);
+ sel.removeAllRanges();
+ }
+
+ var target = document.getElementById("target");
+ target.style.fontFamily = aTest.font.family;
+ target.style.fontSize = aTest.font.defaultSize;
+
+ var range = document.createRange();
+ range.selectNodeContents(target);
+ controller.getSelection(aTest.selection.type).addRange(range);
+
+ document.documentElement.removeAttribute("class");
+ setTimeout(function () {
+ document.documentElement.setAttribute("class", "willBeRemoved"); }, 0);
+}
+
+]]>
+</script>
+</head>
+<body class="test">
+ <div id="target"><span id="decoration">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/test/mochitest.ini b/layout/generic/test/mochitest.ini
new file mode 100644
index 000000000..934ffc8a4
--- /dev/null
+++ b/layout/generic/test/mochitest.ini
@@ -0,0 +1,140 @@
+[DEFAULT]
+skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM, ONLY IN CHUNK 10
+support-files =
+ ../../reftests/backgrounds/blue-32x32.png
+ ../../reftests/backgrounds/fuchsia-32x32.png
+ ../../../dom/plugins/test/mochitest/plugin-utils.js
+ plugin_clipping_helper.xhtml
+ plugin_clipping_helper2.xhtml
+ plugin_clipping_helper_transformed.xhtml
+ plugin_clipping_helper_table.xhtml
+ plugin_clipping_lib.js
+ plugin_focus_helper.html
+ file_BrokenImageReference.png
+ file_Dolske.png
+ file_IconTestServer.sjs
+ file_LoadingImageReference.png
+ file_SlowImage.sjs
+ bug1174521.html
+
+[test_bug240933.html]
+[test_bug263683.html]
+[test_bug288789.html]
+[test_bug290397.html]
+[test_bug323656.html]
+[test_bug344830.html]
+support-files = bug344830_testembed.svg
+[test_bug348681.html]
+[test_bug382429.html]
+[test_bug384527.html]
+[test_bug385751.html]
+[test_bug389630.html]
+[test_bug391747.html]
+[test_bug392746.html]
+[test_bug392923.html]
+[test_bug394173.html]
+[test_bug394239.html]
+[test_bug402380.html]
+[test_bug404872.html]
+[test_bug405178.html]
+[test_bug416168.html]
+[test_bug421436.html]
+[test_bug421839-1.html]
+skip-if = true # Disabled for calling finish twice
+[test_bug421839-2.html]
+support-files = bug421839-2-page.html
+[test_bug424627.html]
+[test_bug438840.html]
+[test_bug448860.html]
+[test_bug448987.html]
+skip-if = true # Bug 932296
+support-files = file_bug448987.html file_bug448987_ref.html file_bug448987_notref.html
+[test_bug449653.html]
+support-files = file_bug449653_1.html file_bug449653_1_ref.html
+[test_bug460532.html]
+[test_bug468167.html]
+[test_bug470212.html]
+[test_bug488417.html]
+skip-if = true # Bug 489560
+[test_bug496275.html]
+skip-if = toolkit == 'android' #CRASH_DUMP
+[test_bug503813.html]
+skip-if = toolkit == 'android' #CRASH_DUMP
+[test_bug507902.html]
+skip-if = true # Bug 510001
+[test_bug514732.html]
+skip-if = toolkit == 'android' #CRASH_DUMP # timeouts
+support-files = file_bug514732_1.html file_bug514732_helper.html
+[test_bug527306.html]
+[test_bug579767.html]
+support-files = file_bug579767_1.html file_bug579767_2.html
+[test_bug522632.html]
+[test_bug524925.html]
+[test_bug589621.html]
+[test_bug589623.html]
+[test_bug597333.html]
+[test_bug633762.html]
+support-files = bug633762_iframe.html
+[test_bug666225.html]
+[test_bug719503.html]
+[test_bug719515.html]
+[test_bug719518.html]
+[test_bug719523.html]
+[test_bug735641.html]
+[test_bug748961.html]
+[test_bug756984.html]
+[test_bug784410.html]
+[test_bug785324.html]
+[test_bug791616.html]
+[test_bug831780.html]
+[test_bug841361.html]
+[test_bug904810.html]
+[test_bug938772.html]
+[test_bug970363.html]
+[test_bug1062406.html]
+[test_bug1174521.html]
+[test_bug1198135.html]
+[test_bug1307853.html]
+support-files = file_bug1307853.html
+[test_contained_plugin_transplant.html]
+skip-if = os=='win'
+[test_image_selection.html]
+[test_image_selection_2.html]
+[test_invalidate_during_plugin_paint.html]
+skip-if = toolkit == 'android'
+[test_intrinsic_size_on_loading.html]
+[test_movement_by_characters.html]
+[test_movement_by_words.html]
+# Disable the caret movement by word test on Linux because the shortcut keys
+# are defined in system level. So, it depends on the environment.
+# Disable on Windows for too many intermittent failures (bug 916143).
+skip-if = (toolkit == "gtk2") || (toolkit == "gtk3") || (os == "win")
+[test_overflow_event.html]
+[test_page_scroll_with_fixed_pos.html]
+support-files = page_scroll_with_fixed_pos_window.html
+[test_plugin_clipping.xhtml]
+skip-if = e10s
+[test_plugin_clipping2.xhtml]
+skip-if = e10s
+[test_plugin_clipping_transformed.xhtml]
+skip-if = e10s
+[test_plugin_clipping_table.xhtml]
+skip-if = e10s
+[test_plugin_focus.html]
+skip-if = os == 'win' # bug 1207918
+[test_plugin_mouse_coords.html]
+skip-if = toolkit == 'android'
+[test_plugin_position.xhtml]
+skip-if = e10s
+[test_scroll_behavior.html]
+[test_selection_expanding.html]
+support-files = selection_expanding_xbl.xml
+[test_selection_preventDefault.html]
+[test_selection_splitText-normalize.html]
+[test_selection_touchevents.html]
+[test_taintedfilters.html]
+support-files = file_taintedfilters_feDisplacementMap-tainted-1.svg file_taintedfilters_feDisplacementMap-tainted-2.svg file_taintedfilters_feDisplacementMap-tainted-3.svg file_taintedfilters_feDisplacementMap-tainted-ref.svg file_taintedfilters_feDisplacementMap-untainted-ref.svg file_taintedfilters_feDisplacementMap-untainted-1.svg file_taintedfilters_feDisplacementMap-untainted-2.svg file_taintedfilters_red-flood-for-feImage-cors.svg file_taintedfilters_red-flood-for-feImage-cors.svg^headers^ file_taintedfilters_red-flood-for-feImage.svg
+[test_scroll_position_restore.html]
+support-files = file_scroll_position_restore.html
+[test_scroll_animation_restore.html]
+[test_scroll_position_iframe.html]
diff --git a/layout/generic/test/page_scroll_with_fixed_pos_window.html b/layout/generic/test/page_scroll_with_fixed_pos_window.html
new file mode 100644
index 000000000..8647e7252
--- /dev/null
+++ b/layout/generic/test/page_scroll_with_fixed_pos_window.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no" />
+ <title>Scrolling by pages with fixed-pos headers and footers</title>
+ <style>
+ .fp { position:fixed; left:0; width:100%; }
+ .fp2 { position:fixed; left:0; width:100%; }
+ </style>
+</head>
+<body onscroll="didScroll()" onload="test()">
+<div class="fp" id="top" style="top:0; height:10px; background:yellow;"></div>
+<div class="fp2" id="top2" style="top:10px; height:11px; background:blue;"></div>
+<div class="fp" style="top:50%; height:7px; background:cyan;"></div>
+<div class="fp2" id="bottom2" style="bottom:9px; height:12px; background:red;"></div>
+<div class="fp" id="bottom" style="bottom:0; height:13px; background:yellow;"></div>
+<p id="target">Something to click on to get focus
+<div style="height:3000px;"></div>
+<pre id="test">
+<script class="testbody">
+var SimpleTest = window.opener.SimpleTest;
+var SpecialPowers = window.opener.SpecialPowers;
+var is = window.opener.is;
+
+function showElements(show, classname) {
+ var elements = document.getElementsByClassName(classname);
+ for (var i = 0; i < elements.length; ++i) {
+ elements[i].style.display = show ? '' : 'none';
+ }
+}
+function showFixedPosElements(show) {
+ showElements(show, "fp");
+}
+function showFixedPosElements2(show) {
+ showElements(show, "fp2");
+}
+
+var nextCont;
+function didScroll() {
+ var c = nextCont;
+ nextCont = null;
+ if (c) {
+ c();
+ }
+}
+
+function scrollDownOnePageWithContinuation(cont) {
+ document.documentElement.scrollTop = 0;
+ nextCont = cont;
+ window.scrollByPages(1);
+}
+
+function test() {
+ var smoothScrollPref = "general.smoothScroll";
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, runTest);
+}
+
+function runTest() {
+ showFixedPosElements(false);
+ showFixedPosElements2(false);
+ scrollDownOnePageWithContinuation(function() {
+ var fullPageScrollDown = document.documentElement.scrollTop;
+
+ showFixedPosElements(true);
+ scrollDownOnePageWithContinuation(function() {
+ var fullPageScrollDownWithHeaderAndFooter = document.documentElement.scrollTop;
+ is(fullPageScrollDownWithHeaderAndFooter, fullPageScrollDown - (10 + 13),
+ "Reduce scroll distance by size of small header and footer");
+
+ document.getElementById("bottom").style.height = (window.innerHeight - 20) + "px";
+ scrollDownOnePageWithContinuation(function() {
+ is(document.documentElement.scrollTop, fullPageScrollDown - 10,
+ "Ignore really big elements when reducing scroll size");
+ document.getElementById("bottom").style.height = "13px";
+
+ document.getElementById("top").style.width = "100px";
+ scrollDownOnePageWithContinuation(function() {
+ is(document.documentElement.scrollTop, fullPageScrollDown - 13,
+ "Ignore elements that don't span the entire viewport side");
+ document.getElementById("top").style.width = "100%";
+
+ showFixedPosElements2(true);
+ scrollDownOnePageWithContinuation(function() {
+ is(document.documentElement.scrollTop, fullPageScrollDown - (10 + 11 + 9 + 12),
+ "Combine multiple overlapping elements");
+ showFixedPosElements2(false);
+
+ document.getElementById("top").style.width = "400px";
+ scrollDownOnePageWithContinuation(function() {
+ is(document.documentElement.scrollTop, fullPageScrollDown - (10 + 13),
+ "Don't ignore elements that span more than half the viewport side");
+ document.getElementById("top").style.width = "100%";
+
+ document.getElementById("top").style.top = "-40px";
+ document.getElementById("top").style.transform = "translateY(38px)";
+ scrollDownOnePageWithContinuation(function() {
+ is(document.documentElement.scrollTop,
+ fullPageScrollDown - (10 + 13 - 40 + 38),
+ "Account for offset and transform");
+ document.getElementById("top").style.width = "100%";
+
+ // Scroll back up so test results are visible
+ document.documentElement.scrollTop = 0;
+ SimpleTest.finish();
+ window.close();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/plugin_clipping_helper.xhtml b/layout/generic/test/plugin_clipping_helper.xhtml
new file mode 100644
index 000000000..b9306d623
--- /dev/null
+++ b/layout/generic/test/plugin_clipping_helper.xhtml
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping">
+<head>
+ <style>
+ embed { width:200px; height:200px; display:block; }
+ iframe { border:none; }
+ </style>
+</head>
+<body>
+
+<!-- Use a XUL element here so we can get its boxObject.screenX/Y -->
+<hbox style="height:10px; position:absolute; left:0; top:0; z-index:-100;" id="h1"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox style="width:100px;"></hbox><hbox id="h2"/>
+</hbox>
+
+<!-- Non-clipped plugin -->
+<embed id="p1" type="application/x-test" wmode="window"
+ style="position:absolute; left:300px; top:0"></embed>
+<!-- Clipped to the top and left by the viewport -->
+<embed id="p2" type="application/x-test" wmode="window"
+ style="position:absolute; left:-100px; top:-100px;"></embed>
+<!-- Clipped by a scrollable DIV -->
+<div style="overflow:auto; width:200px; height:200px;
+ position:absolute; left:100px; top:0;">
+ <div style="position:relative; left:-100px; top:-100px;">
+ <embed id="p3" type="application/x-test" wmode="window"></embed>
+ </div>
+</div>
+<!-- Clipped by a scrollable DIV *and* to the viewport -->
+<div style="overflow:auto; width:200px; height:200px; position:absolute; top:100px; left:-100px;">
+ <div style="position:relative; top:-100px;">
+ <embed id="p4" type="application/x-test" wmode="window"></embed>
+ </div>
+</div>
+<!-- Clipped by an iframe -->
+<iframe id="f1" style="position:absolute; left:200px; top:200px; width:200px; height:200px;"
+ src="data:text/html,&lt;embed style='position:absolute; left:-100px; top:-100px; width:200px; height:200px;' id='p5' type='application/x-test' wmode='window'&gt;"></iframe>
+
+<script src="plugin_clipping_lib.js"></script>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function runTests() {
+ checkClipRegion("p1", [[0, 0, 200, 200]]);
+ checkClipRegion("p2", [[100, 100, 200, 200]]);
+ checkClipRegion("p3", [[100, 100, 200, 200]]);
+ checkClipRegion("p4", [[100, 100, 200, 200]]);
+ checkClipRegionForFrame("f1", "p5", [[100, 100, 200, 200]]);
+
+ window.opener.SimpleTest.finish();
+ window.close();
+}
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/plugin_clipping_helper2.xhtml b/layout/generic/test/plugin_clipping_helper2.xhtml
new file mode 100644
index 000000000..e22163d9d
--- /dev/null
+++ b/layout/generic/test/plugin_clipping_helper2.xhtml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping: Dynamic Tests">
+<head>
+ <style>
+ embed { width:300px; height:200px; display:block; }
+ </style>
+</head>
+<body>
+
+<!-- Use a XUL element here so we can get its boxObject.screenX/Y -->
+<hbox style="height:10px; position:absolute; left:0; top:0; z-index:-100;" id="h1"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox style="width:100px;"></hbox><hbox id="h2"/>
+</hbox>
+
+<div id="d1" style="width:200px; overflow:hidden; position:absolute; top:0; left:0;">
+ <embed id="p1" type="application/x-test" wmode="window" style="position:relative"></embed>
+</div>
+<div id="d2" style="width:200px; height:200px; overflow:hidden; position:absolute; top:100px; left:0;">
+ <embed id="p2" type="application/x-test" wmode="window"></embed>
+ <div id="zbox" style="position:absolute; left:50px; top:50px; width:100px; height:100px; background:yellow;">
+ </div>
+</div>
+
+<div id="scroll"
+ style="position:absolute; top:0; left:0; width:300px; height:400px; overflow:scroll;">
+ <div id="sbox"
+ style="margin-top:350px; margin-left:50px; margin-bottom:1000px; width:100px; height:100px; background:blue;"></div>
+</div>
+
+<script src="plugin_clipping_lib.js"></script>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+var scroll = document.getElementById("scroll");
+var zbox = document.getElementById("zbox");
+var sbox = document.getElementById("sbox");
+var p1 = document.getElementById("p1");
+var d2 = document.getElementById("d2");
+
+function runTests() {
+
+ checkClipRegion("p1", [[0, 0, 200, 100]]);
+ checkClipRegion("p2", [[0, 0, 200, 50], [0, 50, 50, 150], [150, 50, 200, 150], [0, 150, 200, 200]]);
+
+ scroll.scrollTop = 150;
+
+ // A non-zero timeout is needed on X11 (unless an XSync could be performed)
+ // to delay an OOP plugin's X requests enough so that the X server processes
+ // them after the parent processes requests (for the changes above).
+ setTimeout(part2, 1000);
+}
+
+function part2() {
+ checkClipRegion("p2", [[0, 0, 200, 50], [0, 50, 50, 200], [150, 50, 200, 200]]);
+
+ zbox.style.zIndex = -1;
+
+ setTimeout(part3, 1000);
+}
+
+function part3() {
+ checkClipRegion("p2", [[0, 0, 200, 100], [0, 100, 50, 200], [150, 100, 200, 200]]);
+
+ sbox.style.background = "";
+
+ setTimeout(part4, 1000);
+}
+
+function part4() {
+ checkClipRegion("p2", [[0, 0, 200, 200]]);
+
+ p1.style.zIndex = 1;
+
+ setTimeout(part5, 1000);
+}
+
+function part5() {
+ checkClipRegion("p1", [[0, 0, 200, 200]]);
+ checkClipRegion("p2", [[0, 100, 200, 200]]);
+
+ // Test subpixel stuff
+ p1.style.zIndex = -1;
+ zbox.style.zIndex = 1;
+ zbox.style.top = "50.3px;"
+ d2.style.top = "100.3px";
+
+ setTimeout(done, 1000);
+}
+
+function done() {
+ checkClipRegionNoBounds("p2", [[0, 0, 200, 50], [0, 50, 50, 150], [150, 50, 200, 150], [0, 150, 200, 200]]);
+
+ window.opener.SimpleTest.finish();
+ window.close();
+}
+
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/plugin_clipping_helper_table.xhtml b/layout/generic/test/plugin_clipping_helper_table.xhtml
new file mode 100644
index 000000000..bd4073e10
--- /dev/null
+++ b/layout/generic/test/plugin_clipping_helper_table.xhtml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping: Plugins and Tables">
+<head>
+ <style>
+ embed { width:300px; height:200px; display:block; }
+ </style>
+</head>
+<body>
+
+<!-- Use a XUL element here so we can get its boxObject.screenX/Y -->
+<hbox style="height:10px; position:absolute; left:0; top:0; z-index:-100;" id="h1"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox style="width:100px;"></hbox><hbox id="h2"/>
+</hbox>
+
+<embed id="p1" type="application/x-test" wmode="window"
+ style="position:absolute; top:0; left:0;"></embed>
+<table style="width:300px; height:100px; position:absolute; top:100px; left:0; background:white;">
+</table>
+<embed id="p2" type="application/x-test" wmode="window"
+ style="position:absolute; top:200px; left:0;"></embed>
+<table style="width:300px; height:300px; position:absolute; top:300px; left:0; background:white; border-collapse:collapse;">
+</table>
+
+<script src="plugin_clipping_lib.js"></script>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function runTests() {
+ // p1 is partially covered by a table with an opaque background
+ checkClipRegion("p1", [[0, 0, 300, 100]]);
+ // p2 is partially covered by a table with an opaque background
+ checkClipRegion("p2", [[0, 0, 300, 100]]);
+
+ window.opener.SimpleTest.finish();
+ window.close();
+}
+
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/plugin_clipping_helper_transformed.xhtml b/layout/generic/test/plugin_clipping_helper_transformed.xhtml
new file mode 100644
index 000000000..2e5ef71d0
--- /dev/null
+++ b/layout/generic/test/plugin_clipping_helper_transformed.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping: Plugins in Transforms">
+<head>
+ <style>
+ embed { width:300px; height:200px; display:block; }
+ </style>
+</head>
+<body>
+
+<!-- Use a XUL element here so we can get its boxObject.screenX/Y -->
+<hbox style="height:10px; position:absolute; left:0; top:0; z-index:-100;" id="h1"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox style="width:100px;"></hbox><hbox id="h2"/>
+</hbox>
+
+<div style="width:200px; position:absolute; top:0; left:0; -moz-transform:rotate(90deg)">
+ <embed id="p1" type="application/x-test" wmode="window"></embed>
+</div>
+<svg xmlns="http://www.w3.org/2000/svg" style="width:200px; position:absolute; top:200px; left:0;">
+ <foreignObject width="200" height="200">
+ <embed xmlns="http://www.w3.org/1999/xhtml" id="p2" type="application/x-test" wmode="window"></embed>
+ </foreignObject>
+</svg>
+
+<script src="plugin_clipping_lib.js"></script>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function runTests() {
+ // p1 and p2 are both in a transformed context so they should be hidden.
+ checkClipRegionNoBounds("p1", []);
+ checkClipRegionNoBounds("p2", []);
+
+ window.opener.childDone();
+}
+
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/plugin_clipping_lib.js b/layout/generic/test/plugin_clipping_lib.js
new file mode 100644
index 000000000..39b8777f9
--- /dev/null
+++ b/layout/generic/test/plugin_clipping_lib.js
@@ -0,0 +1,163 @@
+var checkClipRegion, checkClipRegionForFrame, checkClipRegionNoBounds;
+
+(function() {
+var windowFrameX, windowFrameY;
+
+// Import test API
+var is = window.opener.is;
+var ok = window.opener.ok;
+var todo = window.opener.todo;
+var finish = window.opener.SimpleTest.finish;
+window.onerror = function (event) { window.opener.onerror(event); window.close(); };
+
+function dumpRect(r) {
+ return "{" + r.join(",") + "}";
+}
+
+function dumpRegion(rects) {
+ var s = [];
+ for (var i = 0; i < rects.length; ++i) {
+ var r = rects[i];
+ s.push(dumpRect(r));
+ }
+ return s.join(", ");
+}
+
+function generateSpan(coords) {
+ coords.sort(function(a,b) { return a - b; });
+ var result = [coords[0]];
+ for (var i = 1; i < coords.length; ++i) {
+ if (coords[i] != coords[i - 1]) {
+ result.push(coords[i]);
+ }
+ }
+ return result;
+}
+
+function containsRect(r1, r2) {
+ return r1[0] <= r2[0] && r1[2] >= r2[2] &&
+ r1[1] <= r2[1] && r1[3] >= r2[3];
+}
+
+function subtractRect(r1, r2, rlist) {
+ var spanX = generateSpan([r1[0], r1[2], r2[0], r2[2]]);
+ var spanY = generateSpan([r1[1], r1[3], r2[1], r2[3]]);
+ for (var i = 1; i < spanX.length; ++i) {
+ for (var j = 1; j < spanY.length; ++j) {
+ var subrect = [spanX[i - 1], spanY[j - 1], spanX[i], spanY[j]];
+ if (containsRect(r1, subrect) && !containsRect(r2, subrect)) {
+ rlist.push(subrect);
+ }
+ }
+ }
+}
+
+function regionContainsRect(rs, r) {
+ var rectList = [r];
+ for (var i = 0; i < rs.length; ++i) {
+ var newList = [];
+ for (var j = 0; j < rectList.length; ++j) {
+ subtractRect(rectList[j], rs[i], newList);
+ }
+ if (newList.length == 0)
+ return true;
+ rectList = newList;
+ }
+ return false;
+}
+
+function regionContains(r1s, r2s) {
+ for (var i = 0; i < r2s.length; ++i) {
+ if (!regionContainsRect(r1s, r2s[i]))
+ return false;
+ }
+ return true;
+}
+
+function equalRegions(r1s, r2s) {
+ return regionContains(r1s, r2s) && regionContains(r2s, r1s);
+}
+
+// Checks that a plugin's clip region equals the specified rectangle list.
+// The rectangles are given relative to the plugin's top-left. They are in
+// [left, top, right, bottom] format.
+function checkClipRegionWithDoc(doc, offsetX, offsetY, id, rects, checkBounds) {
+ var p = doc.getElementById(id);
+ var bounds = p.getBoundingClientRect();
+ var pX = p.getEdge(0);
+ var pY = p.getEdge(1);
+
+ if (checkBounds) {
+ var pWidth = p.getEdge(2) - pX;
+ var pHeight = p.getEdge(3) - pY;
+
+ is(pX, windowFrameX + bounds.left + offsetX, id + " plugin X");
+ is(pY, windowFrameY + bounds.top + offsetY, id + " plugin Y");
+ is(pWidth, bounds.width, id + " plugin width");
+ is(pHeight, bounds.height, id + " plugin height");
+ }
+
+ // Now check clip region. 'rects' is relative to the plugin's top-left.
+ var clipRects = [];
+ var n = p.getClipRegionRectCount();
+ for (var i = 0; i < n; ++i) {
+ // Convert the clip rect to be relative to the plugin's top-left.
+ clipRects[i] = [
+ p.getClipRegionRectEdge(i, 0) - pX,
+ p.getClipRegionRectEdge(i, 1) - pY,
+ p.getClipRegionRectEdge(i, 2) - pX,
+ p.getClipRegionRectEdge(i, 3) - pY
+ ];
+ }
+
+ ok(equalRegions(clipRects, rects), "Matching regions for '" + id +
+ "': expected " + dumpRegion(rects) + ", got " + dumpRegion(clipRects));
+}
+
+checkClipRegion = function checkClipRegion(id, rects) {
+ checkClipRegionWithDoc(document, 0, 0, id, rects, true);
+}
+
+checkClipRegionForFrame = function checkClipRegionForFrame(fid, id, rects) {
+ var f = document.getElementById(fid);
+ var bounds = f.getBoundingClientRect();
+ checkClipRegionWithDoc(f.contentDocument, bounds.left, bounds.top, id, rects, true);
+}
+
+checkClipRegionNoBounds = function checkClipRegionNoBounds(id, rects) {
+ checkClipRegionWithDoc(document, 0, 0, id, rects, false);
+}
+
+function loaded() {
+ var h1 = document.getElementById("h1");
+ var h2 = document.getElementById("h2");
+ var hwidth = h2.boxObject.screenX - h1.boxObject.screenX;
+ if (hwidth != 100) {
+ // Maybe it's a DPI issue
+ todo(false, "Unexpected DPI?");
+ finish();
+ window.close();
+ return;
+ }
+
+ if (!document.getElementById("p1").identifierToStringTest) {
+ todo(false, "Test plugin not available");
+ finish();
+ window.close();
+ return;
+ }
+
+ var bounds = h1.getBoundingClientRect();
+ windowFrameX = h1.boxObject.screenX - bounds.left - window.screenX;
+ windowFrameY = h1.boxObject.screenY - bounds.top - window.screenY;
+
+ // Run actual test code
+ runTests();
+}
+
+// Need to run 'loaded' after painting is unsuppressed, or we'll set clip
+// regions to empty. The timeout must be non-zero on X11 so that
+// gtk_container_idle_sizer runs after the GtkSocket gets the plug_window.
+window.addEventListener("load",
+ function () { setTimeout(loaded, 1000); }, false);
+})();
diff --git a/layout/generic/test/plugin_focus_helper.html b/layout/generic/test/plugin_focus_helper.html
new file mode 100644
index 000000000..49f712b5a
--- /dev/null
+++ b/layout/generic/test/plugin_focus_helper.html
@@ -0,0 +1,134 @@
+<html>
+<head>
+ <title>Test that clicking on plugins transfers focus correctly</title>
+ <style>
+ embed { width:200px; height:200px; }
+ </style>
+</head>
+<body>
+
+<p><input type="text" id="input"></input>
+<p><embed id="p1" type="application/x-test" wmode="window" paintscript="didPaint('p1')">
+ <embed id="p2" type="application/x-test" paintscript="didPaint('p2')"></p>
+
+<script type="text/javascript">
+var SimpleTest = window.opener.SimpleTest;
+var is = window.opener.is;
+var ok = window.opener.ok;
+var todo = window.opener.todo;
+var info = window.opener.info;
+
+SimpleTest.waitForExplicitFinish();
+
+// We don't want to trigger native mouse events until the document is fully
+// loaded and each plugin has painted once.
+var expectPaints = 2;
+var loaded = false;
+function didPaint(id) {
+ ok(--expectPaints >= 0, "Got plugin painted event from "+id);
+ document.getElementById(id).setAttribute("paintscript", null);
+ if (expectPaints == 0) {
+ if (document.readyState == "complete") {
+ theTest();
+ } else {
+ info("Waiting for document load to continue");
+ window.addEventListener("load", function() { theTest(); });
+ }
+ }
+}
+
+//
+// Begin the test
+//
+function theTest() {
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+var gWindowUtils = SpecialPowers.wrap(window)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+
+var nativeMouseDown;
+var nativeMouseUp;
+
+function nativeClickElement(id) {
+ return function() {
+
+ var element = document.getElementById(id);
+ var bounds = element.getBoundingClientRect();
+ var x = (bounds.left + window.mozInnerScreenX + 10);
+ var y = (bounds.top + window.mozInnerScreenY + 10);
+
+ SimpleTest.executeSoon(function () {
+ gWindowUtils.sendNativeMouseEvent(x, y, nativeMouseDown, 0, element);
+ gWindowUtils.sendNativeMouseEvent(x, y, nativeMouseUp, 0, element);
+ });
+ };
+}
+
+function done() {
+ // Something about this test is losing focus to the OS intermittently, remove
+ // the windowed plugins and wait for this window to be focused before
+ // continuing. (Bug 874843)
+ for (var p of [ "p1", "p2" ]) {
+ p = document.getElementById(p);
+ p.parentNode.removeChild(p);
+ p = null;
+ }
+ SimpleTest.waitForFocus(window.opener.childDone, window);
+}
+
+var step = 0;
+var steps = [
+ { event:"focus", id:"input", action:nativeClickElement("p1") },
+ { event:"blur", id:"input" },
+ { event:"focus", id:"p1", action:nativeClickElement("p2") },
+ { event:"blur", id:"p1" },
+ { event:"focus", id:"p2", action:nativeClickElement("input") },
+ { event:"blur", id:"p2" },
+ { event:"focus", id:"input", action:done }
+];
+
+function handleEvent(event) {
+ if (step >= steps.length)
+ return;
+
+ var s = steps[step++];
+ is(event.type, s.event, "Check event type");
+ is(event.target.id, s.id, "Check event target");
+
+ if (s.action) {
+ SimpleTest.executeSoon(s.action);
+ }
+}
+
+var elems = ["input", "p1", "p2"];
+for (var i = 0; i < elems.length; ++i) {
+ var e = document.getElementById(elems[i]);
+ e.addEventListener("focus", handleEvent, false);
+ e.addEventListener("blur", handleEvent, false);
+}
+
+function doTest() {
+ input.focus();
+}
+
+if (navigator.platform.indexOf("Mac") >= 0) {
+ nativeMouseDown = 1; // NSLeftMouseDown
+ nativeMouseUp = 2; // NSLeftMouseUp
+ SimpleTest.waitForFocus(doTest, window);
+} else if (navigator.platform.indexOf("Win") >= 0) {
+ nativeMouseDown = 2; // MOUSEEVENTF_LEFTDOWN
+ nativeMouseUp = 4; // MOUSEEVENTF_LEFTUP
+ SimpleTest.waitForFocus(doTest, window);
+} else {
+ // XXX(johns): our gtk2 sendNativeMouseEvent doesn't support clicks
+ todo(false, "Platform not supported");
+ done();
+}
+
+} // function theTest
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/selection_expanding_xbl.xml b/layout/generic/test/selection_expanding_xbl.xml
new file mode 100644
index 000000000..aade8f8bb
--- /dev/null
+++ b/layout/generic/test/selection_expanding_xbl.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="binding">
+ <content>
+ <html:div xmlns:html="http://www.w3.org/1999/xhtml">
+ <p>xxxxxxx xxxxxxx xxxxxxx</p>
+ <children/>
+ </html:div>
+ </content>
+ </binding>
+</bindings>
diff --git a/layout/generic/test/test_backspace_delete.xul b/layout/generic/test/test_backspace_delete.xul
new file mode 100644
index 000000000..c31b3c9a7
--- /dev/null
+++ b/layout/generic/test/test_backspace_delete.xul
@@ -0,0 +1,325 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Test BackSpace/Delete Keys">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function execTests() {
+ var e = document.getElementById("edit");
+ var doc = e.contentDocument;
+ var win = e.contentWindow;
+ var root = doc.documentElement;
+ var editor = doc.body;
+ var sel = win.getSelection();
+ win.focus();
+
+ function setupTest(html, firstChildOffsetForCaret, node) {
+ // Work around bug 474255 --- we need to have nonempty content before we turn on
+ // editing, or the tests below break because the editor doesn't notice when we
+ // insert non-empty content using innerHTML.
+ doc.designMode = 'off';
+ editor.innerHTML = html;
+ doc.designMode = 'on';
+ var n = editor.firstChild;
+ if (node) {
+ n = node();
+ }
+ sel.collapse(n, firstChildOffsetForCaret);
+ }
+
+ var eatSpace;
+ var deleteImmediately;
+
+ function getPrefs(branch) {
+ const prefSvcContractID = "@mozilla.org/preferences-service;1";
+ const prefSvcIID = Components.interfaces.nsIPrefService;
+ return Components.classes[prefSvcContractID].getService(prefSvcIID)
+ .getBranch(branch);
+ }
+
+ function setPref(branch, pref, newValue) {
+ getPrefs(branch).setBoolPref(pref, newValue);
+ return newValue;
+ }
+
+ function restorePref(branch, pref, newValue) {
+ try {
+ getPrefs(branch).clearUserPref(pref);
+ } catch(ex) {}
+ }
+
+ function setEatSpace(newValue) {
+ eatSpace = setPref("layout.word_select.", "eat_space_to_next_word", newValue);
+ }
+
+ function restoreEatSpace() {
+ restorePref("layout.word_select.", "eat_space_to_next_word");
+ }
+
+ function setDeleteImmediately(newValue) {
+ deleteImmediately = setPref("bidi.edit.", "delete_immediately", newValue);
+ }
+
+ function restoreDeleteImmediately() {
+ restorePref("bidi.edit.", "delete_immediately");
+ }
+
+ function doCommand(cmd) {
+ var controller = document.commandDispatcher.getControllerForCommand(cmd);
+ if (controller) {
+ try {
+ controller.doCommand(cmd);
+ ok(true, 'doCommand(' + cmd + ') succeeded');
+ } catch(ex) {
+ ok(false, 'exception in doCommand(' + cmd + '): ', ex.message);
+ }
+ }
+ }
+
+ function testRight(node, offset) {
+ doCommand("cmd_charNext");
+ var msg = "Right movement broken in \"" + editor.innerHTML + "\", offset " + offset;
+ is(sel.anchorNode, node, msg);
+ is(sel.anchorOffset, offset, msg);
+ }
+
+ function selErrString(dir) {
+ return dir + " selection broken with eatSpace=" + eatSpace + " in \"" + editor.innerHTML + "\"";
+ }
+
+ function testWordSelRight(startNode, startOffset, endNode, endOffset) {
+ doCommand("cmd_selectWordNext");
+ var selRange = sel.getRangeAt(0);
+ is(selRange.startContainer, startNode, selErrString("Word right"));
+ is(selRange.startOffset, startOffset, selErrString("Word right"));
+ is(selRange.endContainer, endNode, selErrString("Word right"));
+ is(selRange.endOffset, endOffset, selErrString("Word right"));
+ }
+
+ function testDelete(node, offset, text, richtext) {
+ doCommand("cmd_deleteCharForward");
+ var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
+ if(typeof node == 'function'){
+ node = node();
+ }
+ is(sel.anchorNode, node, msg);
+
+ is(sel.anchorOffset, offset, msg);
+ let text_result = richtext ? editor.innerHTML : editor.textContent;
+ is(text_result, text, msg);
+ }
+
+ function testBackspace(node, offset, text) {
+ doCommand("cmd_deleteCharBackward");
+ var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
+ is(sel.anchorNode, node, msg);
+
+ is(sel.anchorOffset, offset, msg);
+ is(editor.textContent, text, msg);
+ }
+
+ function testDeletePrevWord(node, offset, text) {
+ doCommand("cmd_deleteWordBackward");
+ var msg = "Delete previous word broken in \"" + editor.innerHTML + "\", offset " + offset;
+ is(sel.anchorNode, node, msg);
+ is(sel.anchorOffset, offset, msg);
+ is(editor.textContent, text, msg);
+ }
+
+ function testDeleteNextWord(node, offset, text) {
+ doCommand("cmd_deleteWordForward");
+ var msg = "Delete next word broken in \"" + editor.innerHTML + "\", offset " + offset;
+ is(sel.anchorNode, node, msg);
+ is(sel.anchorOffset, offset, msg);
+ todo_is(editor.textContent, text, msg);
+ }
+
+ // Test cell-wise deletion of Delete
+ setupTest("สวัสดีพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡", 0);
+ testRight(editor.firstChild, 1);
+ testDelete(editor.firstChild, 1, "สสดีพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 2);
+ testDelete(editor.firstChild, 2, "สสพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 4);
+ testDelete(editor.firstChild, 4, "สสพ่à¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 5);
+ testDelete(editor.firstChild, 5, "สสพ่à¹à¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡", false);
+ testRight(editor.firstChild, 8);
+ testDelete(editor.firstChild, 8, "สสพ่à¹à¸žà¸µà¹ˆà¸­à¸‡", false);
+ testRight(editor.firstChild, 9);
+ testDelete(editor.firstChild, 9, "สสพ่à¹à¸žà¸µà¹ˆà¸­", false);
+
+ // Test character-wise deletion of Backspace
+ setupTest("สวัสดีพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡", 0);
+ testRight(editor.firstChild, 1);
+ testBackspace(editor.firstChild, 0, "วัสดีพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 2);
+ testBackspace(editor.firstChild, 1, "วสดีพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 2);
+ testBackspace(editor.firstChild, 1, "วดีพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 3);
+ testBackspace(editor.firstChild, 2, "วดพ่อà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 4);
+ testBackspace(editor.firstChild, 3, "วดพอà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 4);
+ testBackspace(editor.firstChild, 3, "วดพà¹à¸¡à¹ˆà¸žà¸µà¹ˆà¸™à¹‰à¸­à¸‡");
+ testRight(editor.firstChild, 4);
+ testBackspace(editor.firstChild, 3, "วดพม่พี่น้อง");
+ testRight(editor.firstChild, 5);
+ testBackspace(editor.firstChild, 4, "วดพมพี่น้อง");
+ testRight(editor.firstChild, 7);
+ testBackspace(editor.firstChild, 6, "วดพมพีน้อง");
+ testRight(editor.firstChild, 8);
+ testBackspace(editor.firstChild, 7, "วดพมพีนอง");
+ testRight(editor.firstChild, 8);
+ testBackspace(editor.firstChild, 7, "วดพมพีนง");
+ testRight(editor.firstChild, 8);
+ testBackspace(editor.firstChild, 7, "วดพมพีน");
+
+ // Tests for Bug 417745
+
+ setEatSpace(true);
+
+ setupTest("Quick yellow fox", 0);
+ testWordSelRight(editor.firstChild, 0, editor.firstChild, 6);
+ testDelete(editor.firstChild, 0, "yellow fox");
+ testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
+ testDelete(editor.firstChild, 0, "fox");
+
+ setEatSpace(false);
+
+ setupTest("Quick yellow fox", 0);
+ testWordSelRight(editor.firstChild, 0, editor.firstChild, 5);
+ // editor converts the leading space to an &nbsp;, otherwise it
+ // wouldn't show up which would confuse users
+ testDelete(editor.firstChild, 0, "\u00A0yellow fox");
+ testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
+ testDelete(editor.firstChild, 0, "\u00A0fox");
+ testWordSelRight(editor.firstChild, 0, editor.firstChild, 4);
+ testDelete(editor, 0, "");
+
+ restoreEatSpace();
+
+ // Tests for Bug 419217
+
+ setupTest("foo<div>bar</div>", 3);
+ testDelete(function(){return editor.firstChild;}, 3, "foobar", true);
+
+ // Tests for Bug 419406
+ var s = "helloשלו×";
+
+ setDeleteImmediately(true);
+
+ setupTest(s, 4);
+ testRight(editor.firstChild, 5);
+ testDelete(editor.firstChild, 5, "helloלו×");
+
+ setDeleteImmediately(false);
+
+ setupTest(s, 4);
+ testRight(editor.firstChild, 5);
+ testDelete(editor.firstChild, 5, "helloשלו×");
+
+ // Tests for bug 1034337
+ s = "اهلاhello";
+
+ setDeleteImmediately(true);
+
+ setupTest(s, 4);
+ // first delete an ltr character to make sure that the caret is ltr
+ testDelete(editor.firstChild, 4, "اهلاello");
+ testBackspace(editor.firstChild, 3, "اهلello");
+
+ setDeleteImmediately(false);
+
+ setupTest(s, 4);
+ // first delete an ltr character to make sure that the caret is ltr
+ testDelete(editor.firstChild, 4, "اهلاello");
+ testBackspace(editor.firstChild, 4, "اهلاello");
+
+ restoreDeleteImmediately();
+
+ // Tests for Bug 462188
+ setupTest("You should not see this text.", 29);
+ testDeletePrevWord(editor.firstChild, 24, "You should not see this ");
+ testDeletePrevWord(editor.firstChild, 19, "You should not see ");
+ testDeletePrevWord(editor.firstChild, 15, "You should not ");
+ testDeletePrevWord(editor.firstChild, 11, "You should ");
+ testDeletePrevWord(editor.firstChild, 4, "You ");
+ testDeletePrevWord(editor, 0, "");
+
+ setupTest("You should not see this text.", 0);
+ testDeleteNextWord(editor.firstChild, 0, "\u00A0should not see this text.");
+ testDeleteNextWord(editor.firstChild, 0, "\u00A0not see this text.");
+ testDeleteNextWord(editor.firstChild, 0, "\u00A0see this text.");
+ testDeleteNextWord(editor.firstChild, 0, "\u00A0this text.");
+ testDeleteNextWord(editor.firstChild, 0, "\u00A0text.");
+ // testDeleteNextWord(editor, 0, "");
+
+ // Tests for Bug 502259
+ setupTest("<p>Bug</p>\n<p>502259</p>", 1);
+ testDelete(function(){return editor.firstChild.firstChild;}, 3, "<p>Bug502259</p>", true);
+
+ // Tests for Bug 507936
+ var nodecallback = function(){return editor.firstChild.firstChild.lastChild.firstChild.lastChild;};
+ setupTest("<ol><li>one<ol><li>two</li></ol></li></ol>\n<p>three</p>", 3, nodecallback);
+ testDelete(nodecallback, 0, "<ol><li>one<ol><li>twothree</li></ol></li></ol>", true);
+
+ setupTest("<ol><li>one<ol><li>two</li></ol></li></ol>\n<hr>\n<p>three</p>", 3, nodecallback);
+ testDelete(nodecallback, 3,
+ "<ol><li>one<ol><li>two</li></ol></li></ol><p>three</p>", true);
+
+ // Tests for Bug 519751
+ var nodecallback = function(){return editor.firstChild.lastChild;};
+ setupTest("<p>one</p><ol><li>two</li><li>three</li></ol>", 3, nodecallback);
+ testDelete(nodecallback, 0, "<p>onetwo</p><ol><li>three</li></ol>", true);
+
+ nodecallback = function(){return editor.firstChild.childNodes[1].firstChild;};
+ setupTest("<ol><li>one</li><li>two</li></ol><ol><li>three</li><li>four</li></ol>", 3, nodecallback);
+ testDelete(function(){return editor.firstChild.childNodes[2].firstChild;},
+ 0, "<ol><li>one</li><li>two</li><li>three</li><li>four</li></ol>", true);
+ /*todo_is(false, true, 'The above testDelete should use the same nodecallback' +
+ 'as in the proceeding setupTest: the cursor should stay at the end of "two", while currently it is at the beginning of "three" after delete');*/
+
+ // More Tests for Bug 507936
+ nodecallback = function(){return editor.firstChild.firstChild.firstChild;}
+ setupTest("<div><div>abcdef</div><div>bar</div><div>ghi</div></div>", 5, nodecallback);
+ sel.extend(editor.lastChild.lastChild.lastChild, 1);
+ testDelete(editor.lastChild.lastChild.lastChild, 5, "<div><div>abcdehi</div></div>", true);
+
+ setupTest("<div><div>abcdef</div><div>ghi</div></div>", 5, nodecallback);
+ sel.extend(editor.lastChild.lastChild.lastChild, 1);
+ testDelete(editor.lastChild.lastChild.lastChild, 5, "<div><div>abcdehi</div></div>", true);
+
+ nodecallback = function(){return editor.firstChild.firstChild;}
+ setupTest("<div>abcdef<div><div>bar</div>ghi</div></div>", 5, nodecallback);
+ sel.extend(editor.lastChild.lastChild.lastChild, 1);
+ expectednodecallback = function(){return editor.lastChild.lastChild;}
+ testDelete(expectednodecallback, 0, "<div>abcdehi</div>", true);
+
+ setupTest("<div>abcdef<div>ghi</div></div>", 5, nodecallback);
+ sel.extend(editor.lastChild.lastChild.lastChild, 1);
+ testDelete(expectednodecallback, 0, "<div>abcdehi</div>", true);
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(execTests);
+]]>
+</script>
+
+<body id="html_body" xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462188">Mozilla Bug 462188</a>
+<p id="display"></p>
+
+<pre id="test">
+</pre>
+<iframe id="edit" width="200" height="100" src="about:blank"/>
+</body>
+</window>
diff --git a/layout/generic/test/test_bug1062406.html b/layout/generic/test/test_bug1062406.html
new file mode 100644
index 000000000..0172b9708
--- /dev/null
+++ b/layout/generic/test/test_bug1062406.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1062406
+-->
+<head>
+ <title>Test for Bug 1062406</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1062406">Mozilla Bug 1062406</a>
+<div id="content" style="display: none">
+
+</div>
+<div style="width:10000px; height:10000px"></div>
+<pre id="test">
+<script type="application/javascript">
+
+ /** Test for Bug 1062406 **/
+ window.scroll(100, 100);
+ window.scroll(NaN, NaN);
+ is(window.scrollX, 0, "window.scroll x parameter must treat NaN as 0.");
+ is(window.scrollY, 0, "window.scroll y parameter must treat NaN as 0.");
+
+ window.scroll(100, 100);
+ window.scroll(Infinity, Infinity);
+ is(window.scrollX, 0, "window.scroll x parameter must treat Infinity as 0.");
+ is(window.scrollY, 0, "window.scroll y parameter must treat Infinity as 0.");
+
+ window.scroll(100, 100);
+ window.scroll(-Infinity, -Infinity);
+ is(window.scrollX, 0, "window.scroll x parameter must treat -Infinity as 0.");
+ is(window.scrollY, 0, "window.scroll y parameter must treat -Infinity as 0.");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug1174521.html b/layout/generic/test/test_bug1174521.html
new file mode 100644
index 000000000..f8f4499da
--- /dev/null
+++ b/layout/generic/test/test_bug1174521.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1174521
+-->
+<head>
+ <title>Test for Bug 1174521</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1174521">Mozilla Bug 1174521</a>
+<div id="content">
+ <iframe src="bug1174521.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(function() {
+ var iframe = document.querySelector("iframe");
+ var win = iframe.contentWindow;
+ var doc = iframe.contentDocument;
+ var link = doc.querySelector("a");
+ SimpleTest.waitForFocus(function() {
+ synthesizeMouseAtCenter(link, {}, win);
+ }, win);
+ });
+
+ onmessage = function(e) {
+ ok(e.data.msg, "DONE", "We should be able to click the link");
+ SimpleTest.finish();
+ };
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug1198135.html b/layout/generic/test/test_bug1198135.html
new file mode 100644
index 000000000..3b5496d79
--- /dev/null
+++ b/layout/generic/test/test_bug1198135.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1198135
+-->
+<html><head>
+<title>Test for Bug 1198135</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1198135">Mozilla Bug 1198135</a>
+<style>
+.example {
+ perspective: 100px;
+ height: 300px;
+ width: 300px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ border: 1px solid blue;
+}
+
+.example__group {
+ position: relative;
+ transform-style: preserve-3d;
+ height: 300px;
+ top: 0;
+}
+.example__layer {
+ position: absolute;
+ top:0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+.layer--a {
+ box-shadow: inset 0 0 0 1px red;
+}
+.layer--b {
+ box-shadow: inset 0 0 0 1px blue;
+ transform: translateZ(50px) scale(.45);
+}
+.layer--c {
+ box-shadow: inset 0 0 0 1px green;
+}
+.layer--d {
+ box-shadow: inset 00px 0 0px 1px orange;
+ transform: translateZ(50px) scale(.45);
+}
+.layer--e {
+ box-shadow: inset 00px 0 0px 1px orange;
+ transform: translateZ(50px) scale(.45) translateY(300px);
+}
+</style>
+</head>
+
+<body>
+
+<div class="example" id="first">
+ <div class="example__group">
+ <div class="example__layer layer--a"></div>
+ <div class="example__layer layer--b"></div>
+ </div>
+ <div class="example__group">
+ <div class="example__layer layer--c"></div>
+ <div class="example__layer layer--d"></div>
+ </div>
+</div>
+
+<div class="example" id="second">
+ <div class="example__group">
+ <div class="example__layer layer--a"></div>
+ <div class="example__layer layer--b"></div>
+ </div>
+ <div class="example__group">
+ <div class="example__layer layer--c"></div>
+ <div class="example__layer layer--e"></div>
+ </div>
+</div>
+
+<script>
+ is(document.getElementById("first").scrollHeight, 600, "Scroll height should be computed correctly");
+ document.getElementById("first").scrollTop = 150;
+ is(document.getElementById("first").scrollHeight, 600, "Scroll height should be a constant when scrolling");
+
+ // The true height is 727.5 and we don't always snap the same direction.
+ isfuzzy(document.getElementById("second").scrollHeight, 728, 1, "Scroll height should be computed correctly");
+ document.getElementById("second").scrollTop = 150;
+ isfuzzy(document.getElementById("second").scrollHeight, 728, 1, "Scroll height should be a constant when scrolling");
+</script>
+
+</body></html>
diff --git a/layout/generic/test/test_bug1307853.html b/layout/generic/test/test_bug1307853.html
new file mode 100644
index 000000000..5dbcf4f10
--- /dev/null
+++ b/layout/generic/test/test_bug1307853.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<title>Test for Mozilla Bug 1307853</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+iframe {
+ margin: 0; padding: 0; border: none;
+}
+</style>
+<body onload="run_tests()">
+<iframe id="i" src="file_bug1307853.html" style="width: 200px; height: 100px"></iframe>
+<script>
+function run_tests() {
+ test(function() {
+ var iframe = document.getElementById("i");
+ var idoc = iframe.contentDocument;
+ var iwin = iframe.contentWindow;
+ var inner = idoc.getElementById("inner");
+
+ assert_equals(inner.clientWidth, 60,
+ "width of element before iframe resize");
+ iframe.style.width = "300px";
+ assert_equals(inner.clientWidth, 40,
+ "width of element after iframe resize");
+ },
+ "resize_iframe_percent_padding_fixed_width_boxsizing_borderbox");
+}
+</script>
diff --git a/layout/generic/test/test_bug240933.html b/layout/generic/test/test_bug240933.html
new file mode 100644
index 000000000..e4e20c37e
--- /dev/null
+++ b/layout/generic/test/test_bug240933.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=240933
+-->
+
+<head>
+ <title>Test for Bug 240933</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=240933">
+ Mozilla Bug 240933
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+
+ <pre id="test">
+ <script type="application/javascript">
+
+ /** Test for Bug 240933 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.waitForFocus(function() {
+ var t = document.getElementById("t");
+ synthesizeMouse(t, t.clientWidth / 2, 5, {}, window);
+ is(t.selectionStart, 3, "The selection should be set before the newline");
+ is(t.selectionEnd, 3, "The selection should be set before the newline");
+
+ t = document.getElementById("ta");
+ t.focus();
+ t.selectionStart = t.selectionEnd = t.value.length;
+ var val = t.value;
+ synthesizeKey("VK_RETURN", {});
+ is(t.value, val + "\n", "Pressing enter right after focusing the textarea should work");
+
+ t = document.getElementById("tb");
+ t.focus();
+ synthesizeKey("VK_RETURN", {});
+ is(t.value, "\n", "Pressing enter for the first time should work");
+ synthesizeKey("VK_RETURN", {});
+ is(t.value, "\n\n", "Pressing enter for the second time should work");
+ synthesizeKey("VK_BACK_SPACE", {});
+ is(t.value, "\n", "Pressing backspace for the first time should work");
+ synthesizeKey("VK_BACK_SPACE", {});
+ is(t.value, "", "Pressing backspace for the second time should work");
+ SimpleTest.finish();
+ });
+
+ </script>
+ </pre>
+
+ <textarea id="t" rows="10" cols="10">abc
+</textarea>
+ <textarea id="ta" rows="10" cols="10">
+test
+
+</textarea>
+ <textarea id="tb" rows="10" cols="10"></textarea>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_bug263683.html b/layout/generic/test/test_bug263683.html
new file mode 100644
index 000000000..1fb72c5bc
--- /dev/null
+++ b/layout/generic/test/test_bug263683.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=263683
+-->
+
+<head>
+ <title>Test for Bug 263683</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onload="onLoad();" onunload="onUnload();">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=263683">
+ Mozilla Bug 263683
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+
+ <pre id="test">
+ <script type="application/javascript">
+
+ /** Test for Bug 263683 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var userSetBG = false;
+ var userValueBG = null;
+ var prefNameBG = "ui.textHighlightBackground";
+ var userSetFG = false;
+ var userValueFG = null;
+ var prefNameFG = "ui.textHighlightForeground";
+
+ function onLoad() {
+ SpecialPowers.pushPrefEnv({'set': [[prefNameBG, "#EF0FFF"], [prefNameFG, "#FFFFFF"]]}, startTest);
+ }
+
+ function startTest() {
+ var textToSelect = document.getElementById("selecttext");
+
+ // Take a snapshot now. This will be used to check that removing the
+ // ranges removes the highlighting correctly
+ var noHighlight = snapshotWindow(window);
+
+ var controller = SpecialPowers.wrap(window).
+ QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
+ getInterface(SpecialPowers.Ci.nsIWebNavigation).
+ QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
+ getInterface(SpecialPowers.Ci.nsISelectionDisplay).
+ QueryInterface(SpecialPowers.Ci.nsISelectionController);
+
+ // Get selection
+ var findSelection = controller.getSelection(controller.SELECTION_FIND);
+
+ // Lastly add range
+ var range = document.createRange();
+ range.selectNodeContents(textToSelect);
+ findSelection.addRange(range);
+
+ // Take a snapshot of the highlighting
+ var highlighted = snapshotWindow(window);
+
+ // Clear the highlighting, and take another snapshot
+ findSelection.removeAllRanges();
+ var removedHighlight = snapshotWindow(window);
+
+ // Manually "highlight" the text so we can check the rendering
+ textToSelect.style.backgroundColor="#EF0FFF";
+ textToSelect.style.color="#FFFFFF";
+ var manualHighlight = snapshotWindow(window);
+
+ // Test 1: Did the highlighting render correctly?
+ var res = compareSnapshots(highlighted, manualHighlight, true);
+ ok(res[0], "SELECTION_FIND highlighting renders correctly");
+
+ // Test 2: Does removing the ranges from the SELECTION_FIND selection
+ // work as expected?
+ res = compareSnapshots(removedHighlight, noHighlight, true);
+ ok(res[0], "Removing ranges from FIND_SELECTION works correctly");
+
+ SimpleTest.finish();
+ }
+
+ </script>
+ </pre>
+
+ <p><span id="selecttext">Text to be selected</span></p>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug288789.html b/layout/generic/test/test_bug288789.html
new file mode 100644
index 000000000..5c5e72569
--- /dev/null
+++ b/layout/generic/test/test_bug288789.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=288789
+-->
+<head>
+ <title>Test for Bug 288789</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=288789">Mozilla Bug 288789</a>
+<p id="display"></p>
+<div id="content">
+<textarea id="ta" dir="rtl">
+
+&#x05d0;a&#x05d1;
+
+</textarea>
+<textarea id="tb">
+
+abc
+
+</textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 288789 **/
+
+SimpleTest.waitForExplicitFinish();
+
+// This seems to be necessary because the selection is not set up properly otherwise
+setTimeout(test, 0);
+
+function test() {
+ var textarea = $("ta");
+
+ function collapse(offset) {
+ textarea.selectionStart = offset;
+ textarea.selectionEnd = offset;
+ }
+
+ function testRight(offset) {
+ synthesizeKey("VK_RIGHT", {});
+ is(textarea.selectionStart, offset, "Right movement broken");
+ }
+
+ function testLeft(offset) {
+ synthesizeKey("VK_LEFT", {});
+ is(textarea.selectionStart, offset, "Left movement broken");
+ }
+
+ textarea.focus();
+ collapse(0);
+ ok(true, "Testing forward movement in RTL mode");
+ for (var i = 0; i < textarea.textContent.length; ++i) {
+ if (i == 0) {
+ testRight(i);
+ }
+ if (textarea.textContent[i] == 'a') {
+ testLeft(i);
+ } else {
+ testLeft(i + 1);
+ }
+ if (i == textarea.textContent.length - 1) {
+ testLeft(i + 1);
+ }
+ }
+ ok(true, "Testing backward movement in RTL mode");
+ for (var i = textarea.textContent.length; i > 0; --i) {
+ if (i == textarea.textContent.length) {
+ testLeft(i);
+ }
+ if (i > 0 && textarea.textContent[i - 1] == 'a') {
+ testRight(i);
+ } else {
+ testRight(i - 1);
+ }
+ if (i == 1) {
+ testRight(i - 1);
+ }
+ }
+
+ textarea = $("tb");
+ textarea.focus();
+ collapse(0);
+ ok(true, "Testing forward movement in LTR mode");
+ for (var i = 0; i < textarea.textContent.length; ++i) {
+ if (i == 0) {
+ testLeft(i);
+ }
+ testRight(i + 1);
+ if (i == textarea.textContent.length - 1) {
+ testRight(i + 1);
+ }
+ }
+ ok(true, "Testing backward movement in LTR mode");
+ for (var i = textarea.textContent.length; i > 0; --i) {
+ if (i == textarea.textContent.length) {
+ testRight(i);
+ }
+ testLeft(i - 1);
+ if (i == 1) {
+ testLeft(i - 1);
+ }
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug290397.html b/layout/generic/test/test_bug290397.html
new file mode 100644
index 000000000..b907b53fa
--- /dev/null
+++ b/layout/generic/test/test_bug290397.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=290397
+-->
+<head>
+ <title>Test for Bug 290397</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=290397">Mozilla Bug 290397</a>
+<p id="display">
+<map name=a>
+ <area coords=0,0,20,20 href=#pass>
+ <area shape=default href=#fail>
+</map>
+<img src=file_Dolske.png usemap=#a id=image>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 290397 **/
+SimpleTest.waitForExplicitFinish();
+onhashchange = function() {
+ is(location.hash, "#pass", "Got the wrong area");
+ SimpleTest.finish();
+};
+function runTest() {
+ var image = document.getElementById("image");
+ SimpleTest.waitForFocus(() => synthesizeMouse(image, 10, 10, {}));
+}
+addLoadEvent(runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug323656.html b/layout/generic/test/test_bug323656.html
new file mode 100644
index 000000000..a7b47602b
--- /dev/null
+++ b/layout/generic/test/test_bug323656.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=323656
+-->
+<head>
+ <title>Test for Bug 323656</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ /**
+ * The idea is that "color" inherits by default while "border-color" does
+ * not. So if the former is red and the latter is green on a parent, and
+ * the child's border-color is set to "inherit", it'll be green only if
+ * the child is inheriting from the parent. If not, it'll either be
+ * whatever the border-color is on what it's inheriting from, which will
+ * be red if what it's inheriting from has the default (currentColor)
+ *border-color).
+ */
+
+ /* 't' for "test" */
+ #display, #display *
+ { color: red; border: 0px hidden red; background: transparent }
+ #display .t { border-color: green }
+ #display .t > :first-child
+ { border-color: inherit; border-style: solid; border-width: 10px }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=323656">Mozilla Bug 323656</a>
+<p id="display">
+ <select size="1" class="t">
+ <option id="testOption"></option>
+ </select>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 323656 **/
+var s = document.defaultView.getComputedStyle($("testOption"), "");
+is(s.borderRightColor, "rgb(0, 128, 0)", "Inheritance broken");
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug344830.html b/layout/generic/test/test_bug344830.html
new file mode 100644
index 000000000..a70f229ed
--- /dev/null
+++ b/layout/generic/test/test_bug344830.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=344830
+-->
+<head>
+ <title>Test for Bug 344830</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=344830">Mozilla Bug 344830</a>
+<p id="display"></p>
+<div id="content" style="display: block">
+ <embed name="svg1"
+ width="200" height="200"
+ type="image/svg+xml"
+ src="bug344830_testembed.svg">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 344830 **/
+function getSVG() {
+ var embed = document.embeds["svg1"];
+ var svgDocument = embed.getSVGDocument();
+ var element = svgDocument.getElementById("g1");
+ ok(embed, "document.embeds[] works with SVG");
+ ok(svgDocument, "document.embeds[] works with SVG and embed.getSVGDocument()");
+ ok(element, "document.embeds[] works with SVG and svgDocument.getElementById()");
+ SimpleTest.finish();
+}
+addLoadEvent(getSVG);
+SimpleTest.waitForExplicitFinish()
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug348681.html b/layout/generic/test/test_bug348681.html
new file mode 100644
index 000000000..590330797
--- /dev/null
+++ b/layout/generic/test/test_bug348681.html
@@ -0,0 +1,492 @@
+<!DOCTYPE HTML>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=348681
+-->
+
+<head>
+ <title>Test for Bug 348681</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<body onload="loaded()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=348681">Mozilla Bug 348681</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+
+ <pre id="test">
+ <script type="application/javascript">
+
+ /** Test for Bug 348681 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function loaded() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.testing.selection.GetRangesForInterval", true]]}, doTest);
+ }
+
+ var rangeChecker = {
+ ranges: [],
+
+ reset: function() {
+ this.ranges = [];
+ },
+
+ check: function(testID, selection) {
+ is(selection.rangeCount, this.ranges.length,
+ "Test "+testID+": correct number of ranges in selection");
+ var rangesMatch = true;
+ var foundMsg = "Found ";
+ var expectedMsg = "Expected ";
+ var maxIndex = Math.max(selection.rangeCount, this.ranges.length);
+ for (var x = 0; x < maxIndex; x++) {
+ var expect = null;
+ if (x < this.ranges.length)
+ expect = this.ranges[x];
+
+ var found = null;
+ if (x < selection.rangeCount)
+ found = selection.getRangeAt(x);
+
+ if (found) {
+ foundMsg +="["+found.startOffset+","+found.endOffset+"] ";
+ }
+ if (expect) {
+ expectedMsg +="["+expect.start+","+expect.end+"] ";
+ }
+
+ if (found && expect) {
+ if (found.startContainer != expect.node ||
+ found.endContainer != expect.node ||
+ found.startOffset != expect.start ||
+ found.endOffset != expect.end)
+ rangesMatch = false;
+ } else {
+ rangesMatch = false;
+ }
+ }
+ var okMsg = "Test "+testID+": correct ranges in selection.";
+ okMsg = okMsg + foundMsg + expectedMsg;
+ ok(rangesMatch, okMsg);
+ },
+
+ addExpected: function(node, start, end) {
+ var expected = {};
+ expected.node = node;
+ expected.start = start;
+ expected.end = end;
+ this.ranges[this.ranges.length] = expected;
+ this.ranges.sort(function(a,b) {return a.start - b.start;});
+ }
+ }
+
+ var intervalChecker = {
+ ranges: [],
+
+ reset: function() {
+ this.ranges = [];
+ },
+
+ check: function(testID, testArr) {
+ is(testArr.length, this.ranges.length,
+ "Test "+testID+": correct number of ranges for interval");
+ var rangesMatch = true;
+ var foundMsg = "Found ";
+ var expectedMsg = "Expected ";
+ var maxIndex = Math.max(testArr.length, this.ranges.length);
+ for (var x = 0; x < maxIndex; x++) {
+ var expect = null;
+ if (x < this.ranges.length)
+ expect = this.ranges[x];
+
+ var found = null;
+ if (x < testArr.length) {
+ // testArr may contain the results coming from
+ // nsISelectionPrivate.GetRangesForInterval(), which are
+ // wrappers for the underlying objects, therefore we may
+ // need to unwrap them before comparing them with the
+ // expected objects.
+ found = SpecialPowers.unwrap(testArr[x]);
+ }
+
+ if (found) {
+ foundMsg +="["+found.startOffset+","+found.endOffset+"] ";
+ }
+ if (expect) {
+ expectedMsg +="["+expect.start+","+expect.end+"] ";
+ }
+
+ if (found && expect) {
+ if (found.startContainer != expect.node ||
+ found.endContainer != expect.node ||
+ found.startOffset != expect.start ||
+ found.endOffset != expect.end)
+ rangesMatch = false;
+ } else {
+ rangesMatch = false;
+ }
+ }
+ var okMsg = "Test "+testID+": correct ranges for interval.";
+ okMsg = okMsg + foundMsg + expectedMsg;
+ ok(rangesMatch, okMsg);
+ },
+
+ addExpected: function(node, start, end) {
+ var expected = {};
+ expected.node = node;
+ expected.start = start;
+ expected.end = end;
+ this.ranges[this.ranges.length] = expected;
+ this.ranges.sort(function(a,b) {return a.start - b.start;});
+ }
+ }
+
+ function doTest() {
+ var testNode = document.getElementById("testparagraph").firstChild;
+
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ ok(selection.rangeCount == 0, "Test 0 - No selections so far");
+
+ // Test 1. Add a single range, to ensure we've not broken anything.
+ var range1 = document.createRange();
+ range1.setStart(testNode, 0);
+ range1.setEnd(testNode, 5);
+ selection.addRange(range1);
+ rangeChecker.addExpected(testNode, 0, 5);
+ rangeChecker.check(1, selection);
+
+ // Test 2. Add a non-overlapping range, to ensure it gets added too.
+ var range2 = document.createRange();
+ range2.setStart(testNode, 8);
+ range2.setEnd(testNode, 10);
+ selection.addRange(range2);
+ rangeChecker.addExpected(testNode, 8, 10);
+ rangeChecker.check(2, selection);
+
+ // Test 3. Add the same range again. This should silently succeed.
+ selection.addRange(range2);
+ rangeChecker.check(3, selection);
+
+ // Test 4. Add a range that is left-adjacent to an existing range.
+ var range3 = document.createRange();
+ range3.setStart(testNode, 6);
+ range3.setEnd(testNode, 8);
+ selection.addRange(range3);
+ rangeChecker.addExpected(testNode, 6, 8);
+ rangeChecker.check(4, selection);
+
+ // Test 5. Add a range that is right-adjacent to an existing range.
+ selection.removeRange(range2);
+ selection.addRange(range2);
+ rangeChecker.check(5, selection);
+
+ // Test 6. Add a range, add a second range that overlaps it.
+ rangeChecker.reset();
+ selection.removeAllRanges();
+ selection.addRange(range2);
+ var range4 = document.createRange();
+ range4.setStart(testNode, 9);
+ range4.setEnd(testNode, 11);
+ selection.addRange(range4);
+ rangeChecker.addExpected(testNode, 8, 9);
+ rangeChecker.addExpected(testNode, 9, 11);
+ rangeChecker.check(6, selection);
+
+ // Test 7. Add a range, and this time a left-hand overlap.
+ rangeChecker.reset();
+ selection.removeAllRanges();
+ var range5 = document.createRange();
+ range5.setStart(testNode, 5);
+ range5.setEnd(testNode, 7);
+ selection.addRange(range3);
+ selection.addRange(range5);
+ rangeChecker.addExpected(testNode, 5, 7);
+ rangeChecker.addExpected(testNode, 7, 8);
+ rangeChecker.check(7, selection);
+
+ // Test 8. Add a range, and add a second range wholly contained
+ // within it.
+ rangeChecker.reset();
+ selection.removeAllRanges();
+ var range6 = document.createRange();
+ range6.setStart(testNode, 0);
+ range6.setEnd(testNode, 10);
+ selection.addRange(range6);
+ selection.addRange(range5);
+ rangeChecker.addExpected(testNode, 0, 5);
+ rangeChecker.addExpected(testNode, 5, 7);
+ rangeChecker.addExpected(testNode, 7, 10);
+ rangeChecker.check(8, selection);
+
+ // Test 9. Add a range that will wholly contain some existing ranges.
+ rangeChecker.reset();
+ selection.addRange(range6);
+ rangeChecker.addExpected(testNode, 0, 10);
+ rangeChecker.check(9, selection);
+
+ // Test 10. This time with the last range being a partial overlap.
+ selection.removeAllRanges();
+ selection.addRange(range1);
+ selection.addRange(range3);
+ selection.addRange(range4);
+ selection.addRange(range6);
+ rangeChecker.addExpected(testNode, 10, 11);
+ rangeChecker.check(10, selection);
+
+ // Test 11. Check we can add a collapsed range without problem.
+ selection.removeAllRanges();
+ rangeChecker.reset();
+ range1.collapse(true);
+ selection.addRange(range1);
+ rangeChecker.addExpected(testNode, 0, 0);
+ rangeChecker.check(11, selection);
+
+ // Test 12. Check we can add a collapsed range twice without a problem.
+ // Part 1 - No other ranges present.
+ selection.addRange(range1);
+ rangeChecker.check(12, selection);
+
+ // Test 13. Check we can add a collapsed range twice without problem.
+ // Part 2 - Collapsed range is before all existing ranges.
+ selection.removeAllRanges();
+ rangeChecker.reset();
+ selection.addRange(range2);
+ selection.addRange(range1);
+ selection.addRange(range1);
+ rangeChecker.addExpected(testNode, 0, 0);
+ rangeChecker.addExpected(testNode, 8, 10);
+ rangeChecker.check(13, selection);
+
+ // Test 14. Check we can add a collapsed range twice without problem.
+ // Part 3 - Collapsed range is after all existing ranges.
+ selection.removeAllRanges();
+ rangeChecker.reset();
+ selection.addRange(range3);
+ range4.collapse(false);
+ selection.addRange(range3);
+ selection.addRange(range4);
+ rangeChecker.addExpected(testNode, 6, 8);
+ rangeChecker.addExpected(testNode, 11, 11);
+ rangeChecker.check(14, selection);
+
+ // Test 15. Check that when adding a range where after overlap
+ // adjustment an exisiting range would collapse that the collapsed range
+ // is removed - part 1 (LHS).
+ selection.removeAllRanges();
+ rangeChecker.reset();
+ selection.addRange(range2);
+ var range7 = document.createRange();
+ range7.setStart(testNode, 6);
+ range7.setEnd(testNode, 10);
+ selection.addRange(range7);
+ rangeChecker.addExpected(testNode, 6, 10);
+ rangeChecker.check(15, selection);
+
+ // Test 16. Check that when adding a range where after overlap
+ // adjustment an exisiting range would collapse that the collapsed range
+ // is removed - part 2 (RHS).
+ selection.removeAllRanges();
+ selection.addRange(range3);
+ selection.addRange(range7);
+ rangeChecker.check(16, selection);
+
+ // Test 17. Check GetRangesForInterval returns correct results.
+ // Part 1 - Test interval matches a range, adjacency not allowed.
+ selection.removeAllRanges();
+ selection.addRange(range2);
+ selection.addRange(range3);
+ var range8 = document.createRange();
+ range8.setStart(testNode, 10);
+ range8.setEnd(testNode, 12);
+ selection.addRange(range8);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 8, 10);
+ var privSel = SpecialPowers.wrap(selection)
+ .QueryInterface(SpecialPowers.Ci.nsISelectionPrivate);
+ ok(privSel, "Test 17 - QIed to instance of nsISelection2 interface");
+ var results = privSel.GetRangesForInterval(testNode, 8, testNode, 10,
+ false);
+ intervalChecker.check(17, results);
+
+ // Test 18. Check GetRangesForInterval returns correct results.
+ // Part 2 - Test interval matches a range, adjacent ranges allowed.
+ intervalChecker.addExpected(testNode, 6, 8);
+ intervalChecker.addExpected(testNode, 10, 12);
+ results = privSel.GetRangesForInterval(testNode, 8, testNode, 10,
+ true);
+ intervalChecker.check(18, results);
+
+ // Test 19. Check GetRangesForInterval returns correct results.
+ // Part 3 - Test interval not selected.
+ intervalChecker.reset();
+ results = privSel.GetRangesForInterval(testNode, 14, testNode, 16,
+ true);
+ intervalChecker.check(19, results);
+
+ // Test 20. Check GetRangesForInterval returns correct results.
+ // Part 4 - Test interval is not equal to, and entirely contained in
+ // an existing range.
+ selection.removeAllRanges();
+ selection.addRange(range6);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 0, 10);
+ results = privSel.GetRangesForInterval(testNode, 5, testNode, 7,
+ true);
+ intervalChecker.check(20, results);
+
+ // Test 21. Check GetRangesForInterval returns correct results.
+ // Part 5 - Test interval is not equal to, and entirely contains an
+ // existing range.
+ selection.removeAllRanges();
+ selection.addRange(range3);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 6, 8);
+ results = privSel.GetRangesForInterval(testNode, 5, testNode, 9,
+ true);
+ intervalChecker.check(21, results);
+
+ // Test 22. Check GetRangesForInterval returns correct results.
+ // Part 6 - Test interval is end-adjacent to range at position 0 in
+ // the range array, and adjacencies permitted.
+ selection.removeAllRanges();
+ selection.addRange(range2);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 8, 10);
+ results = privSel.GetRangesForInterval(testNode, 6, testNode, 8,
+ true);
+ intervalChecker.check(22, results);
+
+ // Test 23. Check GetRangesForInterval returns correct results.
+ // Part 7 - Test interval is end-adjacent to range at position 0 in
+ // the range array, and adjacencies not permitted.
+ intervalChecker.reset();
+ results = privSel.GetRangesForInterval(testNode, 6, testNode, 8,
+ false);
+ intervalChecker.check(23, results);
+
+ // Test 24. Check GetRangesForInterval returns correct results.
+ // Part 8 - Test interval is start-adjacent to last range in array,
+ // and adjacencies permitted.
+ intervalChecker.addExpected(testNode, 8, 10);
+ results = privSel.GetRangesForInterval(testNode, 10, testNode, 12,
+ true);
+ intervalChecker.check(24, results);
+
+ // Test 25. Check GetRangesForInterval returns correct results.
+ // Part 9 - Test interval is start-adjacent to last range in array,
+ // and adjacencies not permitted.
+ intervalChecker.reset();
+ results = privSel.GetRangesForInterval(testNode, 10, testNode, 12,
+ false);
+ intervalChecker.check(25, results);
+
+ // Test 26. Check GetRangesForInterval returns correct results.
+ // Part 10 - Test interval is equal to a collapsed range at position 0
+ // in the range array, and adjacencies permitted.
+ selection.removeAllRanges();
+ selection.addRange(range4);
+ intervalChecker.addExpected(testNode, 11, 11);
+ results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+ true);
+ intervalChecker.check(26, results);
+
+ // Test 27. Check GetRangesForInterval returns correct results.
+ // Part 11 - Test interval is equal to a collapsed range at position 0
+ // in the range array, and adjacencies not permitted.
+ results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+ false);
+ intervalChecker.check(27, results);
+
+ // Test 28. Check GetRangesForInterval returns correct results.
+ // Part 12 - Test interval is equal to a collapsed range at end of the
+ // range array, and adjacencies permitted.
+ selection.removeAllRanges();
+ selection.addRange(range2);
+ selection.addRange(range4);
+ results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+ true);
+ intervalChecker.check(28, results);
+
+ // Test 29. Check GetRangesForInterval returns correct results.
+ // Part 13 - Test interval is equal to a collapsed range at end of the
+ // range array, and adjacencies not permitted.
+ results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+ false);
+ intervalChecker.check(29, results);
+
+ // Test 30. Check GetRangesForInterval returns correct results.
+ // Part 14 - Test interval is a collapsed range contained in an
+ // existing range.
+ selection.removeAllRanges();
+ selection.addRange(range3);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 6, 8);
+ results = privSel.GetRangesForInterval(testNode, 7, testNode, 7,
+ true);
+ intervalChecker.check(30, results);
+
+ // Test 31. Check GetRangesForInterval returns correct results.
+ // Part 15 - Test interval equals a collapsed range which is not stored
+ // at either endpoint of the selection's range array,
+ // adjacencies not allowed.
+ selection.removeAllRanges();
+ range3.collapse(false);
+ selection.addRange(range5);
+ selection.addRange(range3);
+ selection.addRange(range8);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 8, 8);
+ results = privSel.GetRangesForInterval(testNode, 8, testNode, 8,
+ false);
+ intervalChecker.check(31, results);
+
+ // Test 32. Check GetRangesForInterval returns correct results.
+ // Part 16 - Test interval equals a collapsed range which is not stored
+ // at either endpoint of the selection's range array,
+ // adjacencies allowed.
+ results = privSel.GetRangesForInterval(testNode, 8, testNode, 8,
+ true);
+ intervalChecker.check(32, results);
+
+ // Test 33. Check GetRangesForInterval returns correct results.
+ // Part 17 - Test interval contains a collapsed range which is not
+ // stored at either endpoint of the selection's range array.
+ results = privSel.GetRangesForInterval(testNode, 7, testNode, 9,
+ false);
+ intervalChecker.check(33, results);
+
+ // Test 34. Check GetRangesForInterval returns correct results.
+ // Part 18 - Test interval left-ovelaps an existing range.
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 5, 7);
+ results = privSel.GetRangesForInterval(testNode, 2, testNode, 6,
+ true);
+ intervalChecker.check(34, results);
+
+ // Test 35. Check GetRangesForInterval returns correct results.
+ // Part 19 - Test interval right-ovelaps an existing range.
+ selection.removeAllRanges();
+ selection.addRange(range8);
+ intervalChecker.reset();
+ intervalChecker.addExpected(testNode, 10, 12);
+ results = privSel.GetRangesForInterval(testNode, 11, testNode, 13,
+ true);
+ intervalChecker.check(35, results);
+
+ SimpleTest.finish();
+ }
+ </script>
+ </pre>
+
+ <p id="testparagraph">
+This will be the first child of the p node above. The intention is that it is a suitably long text node to which we can add multiple ranges in order to test the various aspects of the bug.
+</p>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug382429.html b/layout/generic/test/test_bug382429.html
new file mode 100644
index 000000000..e820c765c
--- /dev/null
+++ b/layout/generic/test/test_bug382429.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=382429
+-->
+<head>
+ <title>Test for Bug 382429</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=382429">Mozilla Bug 382429</a>
+<p id="display">&#x05e2; 3/4</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function do_Replace()
+{
+/** Test for Bug 382429 **/
+ var t = $("display").firstChild;
+
+ t.data = t.data.replace(/3\/4/, "3 \u05d3 4");
+ ok(true, "We should get here without crash or hang");
+ is(t.data, "\u05e2 3 \u05d3 4", "replace succeeded");
+ SimpleTest.finish();
+}
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(do_Replace, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug384527.html b/layout/generic/test/test_bug384527.html
new file mode 100644
index 000000000..6bb3c5bd1
--- /dev/null
+++ b/layout/generic/test/test_bug384527.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=384527
+-->
+<head style="display: inline-table;">
+ <title>Test for Bug 384527</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style style="display: block; direction: rtl;">
+ style::first-letter {float: right;}
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=384527">Mozilla Bug 384527</a>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function do_Test()
+{
+/** Test for Bug 384527 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(do_Test, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug385751.html b/layout/generic/test/test_bug385751.html
new file mode 100644
index 000000000..43a56d379
--- /dev/null
+++ b/layout/generic/test/test_bug385751.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=385751
+-->
+<head>
+ <title>Test for Bug 385751</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>#content:first-letter {float: right; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=385751">Mozilla Bug 385751</a>
+<div id="content"><span style="direction: rtl; unicode-bidi: embed;">*::first
+m</span></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function do_Test()
+{
+/** Test for Bug 385751 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(do_Test, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug389630.html b/layout/generic/test/test_bug389630.html
new file mode 100644
index 000000000..a5a9c04a6
--- /dev/null
+++ b/layout/generic/test/test_bug389630.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=389630
+-->
+<head>
+ <title>Test for Bug 389630</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>.fl:first-letter { }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=389630">Mozilla Bug 389630</a>
+<div id="content" style="direction: rtl;"><table><tr><td width="1"><div id="div" class="fl"><font><span>x y</span></font></div></td></tr></table></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function boom()
+{
+/** Test for Bug 389630 **/
+ document.getElementById("div").className = "";
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(boom, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug391747.html b/layout/generic/test/test_bug391747.html
new file mode 100644
index 000000000..39336013e
--- /dev/null
+++ b/layout/generic/test/test_bug391747.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=391747
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Test for bug 391747</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=391747">Mozilla Bug 391747</a>
+<p id="display"></p>
+<div id="content" style="display: block">
+<iframe id="iframe_391747" src="data:text/html,<table><tr><td style='width:500px;height:500px;border:1px solid blue'>x</td>"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function ctrlclick_391747(doc,x,y){
+ var wu = SpecialPowers.getDOMWindowUtils(doc.defaultView);
+ wu.sendMouseEvent('mousedown', x, y, 0, 1, 2);
+ wu.sendMouseEvent('mouseup', x, y, 0, 1, 2);
+}
+
+function select_391747(doc){
+ var range = doc.createRange();
+ range.setStart(doc, 0);
+ range.setEnd(doc, 0);
+ doc.defaultView.getSelection().addRange(range);
+}
+
+function boom_391747() {
+ var target = document.getElementById('iframe_391747')
+ select_391747(target.contentDocument)
+ ctrlclick_391747(target.contentDocument, 100, 100);
+ ok(true, "pass");
+ SimpleTest.finish();
+}
+
+addLoadEvent(boom_391747);
+SimpleTest.waitForExplicitFinish()
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug392746.html b/layout/generic/test/test_bug392746.html
new file mode 100644
index 000000000..df0b441f2
--- /dev/null
+++ b/layout/generic/test/test_bug392746.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=392746
+-->
+<head>
+ <title>Test for Bug 392746</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=392746">Mozilla Bug 392746</a>
+<div id="content">
+text text text text text <span id="d">ddd text text </span>text text text <br>
+text <span id="c">ccc text</span> text text <span id="e">eee text</span> text text text <span id="b">bbb text</span><br>
+ text text text text text text <span id="a">aaa text</span> text text text <br>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+
+function ctrlselect(aX,aY, aX2, aY2) {
+ var modifyers = (navigator.platform.indexOf("Mac") >= 0) ? 8 : 2;
+ wu.sendMouseEvent('mousedown', aX, aY, 0, 1, modifyers);
+ wu.sendMouseEvent('mousemove', aX2, aY2, 0, 0, modifyers);
+ wu.sendMouseEvent('mouseup', aX2, aY2, 0, 1, modifyers);
+}
+
+ function test() {
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+
+ var a=document.getElementById('a').getBoundingClientRect();
+ ctrlselect(a.left+1, a.top+1, a.right-1, a.top+1);
+ var b=document.getElementById('b').getBoundingClientRect();
+ ctrlselect(b.left+1, b.top+1, b.right-1, b.top+1);
+ var c=document.getElementById('c').getBoundingClientRect();
+ ctrlselect(c.left+1, c.top+1, c.right-1, c.top+1);
+ var d=document.getElementById('d').getBoundingClientRect();
+ ctrlselect(d.left+1, d.top+1, d.right-1, d.top+1);
+ var e=document.getElementById('e').getBoundingClientRect();
+ ctrlselect(e.right-1, e.top+1, e.left+1, e.top+1);
+
+ ok(sel.getRangeAt(0).toString() == 'ddd text text ', 'First selection range should be "ddd text text "');
+ ok(sel.getRangeAt(1).toString() == 'ccc text', 'First selection range should be "ccc text"');
+ ok(sel.getRangeAt(2).toString() == 'eee text', 'First selection range should be "eee text"');
+ ok(sel.getRangeAt(3).toString() == 'bbb text', 'First selection range should be "bbb text"');
+ ok(sel.getRangeAt(4).toString() == 'aaa text', 'First selection range should be "aaa text"');
+
+ ok(sel.focusNode == sel.anchorNode, 'focusNode and anchorNode should be the same');
+ ok(sel.focusNode.parentNode == document.getElementById('e'), 'focusNode.parentNode should be the same as the node with id=e');
+ ok(sel.focusOffset == 0, 'focusOffset should be 0');
+ ok(sel.anchorOffset == 8, 'anchorOffset should be 8');
+
+ wu.sendMouseEvent('mousedown', 0, 0, 0, 1, 0);
+ wu.sendMouseEvent('mousemove', 0, 0, 0, 0, 0);
+ wu.sendMouseEvent('mouseup', 0, 0, 0, 1, 0);
+
+ SimpleTest.finish();
+}
+
+ window.onload=function() {
+ SimpleTest.waitForExplicitFinish();
+ setTimeout(test, 0);
+ };
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug392923.html b/layout/generic/test/test_bug392923.html
new file mode 100644
index 000000000..5e157976d
--- /dev/null
+++ b/layout/generic/test/test_bug392923.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=392923
+-->
+<head>
+ <title>Test for Bug 392923</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>p { -moz-column-count: 2; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=392923">Mozilla Bug 392923</a>
+<div id="content">
+<p>&#x5E2;&#x5D1;&#x5E8;&#x5D9;&#x5EA; Lorem ipsum dolor sit
+amet, consectetuer adipiscing elit. Maecenas volutpat. Duis purus lectus,
+hendrerit vitae, blandit ac, tristique quis, neque. Cras a nisl. Cum sociis
+natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In
+consectetuer lorem sit amet quam facilisis mollis. Etiam dolor. Donec
+elementum leo in ligula. Duis lacus diam, sodales vitae, rutrum a,
+feugiat non, tortor. Maecenas cursus lobortis diam. Mauris in pede eu
+purus ultricies vehicula. &#x5E2;&#x5D1;&#x5E8;&#x5D9;&#x5EA;</p>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function test()
+{
+/** Test for Bug 392923 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(test, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug394173.html b/layout/generic/test/test_bug394173.html
new file mode 100644
index 000000000..7a2700a93
--- /dev/null
+++ b/layout/generic/test/test_bug394173.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=394173
+-->
+<head>
+ <title>Test for Bug 394173</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>.fl:first-letter { }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=394173">Mozilla Bug 394173</a>
+<div id="content">
+<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%0A%3Chead%3E%0A%3Cstyle%3E%0Adiv%3A%3Afirst-letter%20%7B%20%7D%0Aspan%3A%3Aafter%20%7B%20content%3A%22before%20text%22%3B%20%7D%0A%3C/style%3E%0A%3C/head%3E%0A%3Cbody%3E%0A%3Cdiv%20style%3D%22position%3A%20fixed%3B%20direction%3A%20rtl%3B%22%3E%0A%20%20%3Cspan%20style%3D%22%20direction%3A%20ltr%3B%20unicode-bidi%3A%20bidi-override%3B%20font-size%3A%2070px%3B%20%22%3E%0A%20%20%20%20%3Cspan%20style%3D%22display%3A%20table%3B%20position%3A%20fixed%3B%22%3E%3C/span%3E%0A%20%20%3C/span%3E%0A%3C/div%3E%0A%3Cscript%3E%0Afunction%20doe%28i%29%7B%0Adocument.documentElement.setAttribute%28%27style%27%2C%20%27%27%29%3B%0A%7D%0AsetTimeout%28doe%2C100%29%3B%0A%3C/script%3E%0A%3C/body%3E%0A%3C/html%3E" style="width:200px;height: 200px;"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function boom()
+{
+/** Test for Bug 394173 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(boom, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug394239.html b/layout/generic/test/test_bug394239.html
new file mode 100644
index 000000000..6b44049d4
--- /dev/null
+++ b/layout/generic/test/test_bug394239.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=394239
+-->
+<head>
+ <title>Test for Bug 394239</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ #a::after { content:"anonymous text"; }
+ </style>
+</head>
+<body>
+<div id="content">
+ <object id="a" style="position: relative;">
+ <div style="position: absolute;">
+ <input>
+ </div>&#1593;
+ <div style="position: absolute;">
+ <span style="position: fixed;">t</span>
+ </div>
+ </object>
+</div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=394239">Mozilla Bug 394239</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.expectAssertions(1);
+
+/** Test for Bug 394239 **/
+function test()
+{
+ $("a").style.direction="rtl";
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(test, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug402380.html b/layout/generic/test/test_bug402380.html
new file mode 100644
index 000000000..5799d238c
--- /dev/null
+++ b/layout/generic/test/test_bug402380.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=402380
+-->
+<head>
+ <title>Test for Bug 402380</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ div::first-letter { color: green; }
+ span:before { content: open-quote "This "; }
+ span:after { content: close-quote; }
+ </style>
+</head>
+<body style="font-family: monospace; width: 7ch; border: 1px solid orange;"
+ onload="document.getElementById('div').style.direction = 'rtl';">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=402380">Mozilla Bug 402380</a>
+<div id="div"><span>is text</span></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function test()
+{
+/** Test for Bug 402380 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(test, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug404872.html b/layout/generic/test/test_bug404872.html
new file mode 100644
index 000000000..42acabcb6
--- /dev/null
+++ b/layout/generic/test/test_bug404872.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=404872
+-->
+<head>
+ <title>Test for Bug 404872</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=404872">Mozilla Bug 404872</a>
+<p id="display">
+ <select multiple id="a">
+ <option name="value1">value1</option>
+ <option name="value2">value2</option>
+ <option name="value3">value3</option>
+ </select>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/* Focus, press key down twice, and make sure we successfully selected the second item. */
+$('a').focus();
+synthesizeKey("VK_DOWN", {});
+synthesizeKey("VK_DOWN", {});
+is($("a").value, "value2", "value2 should be selected!");
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug405178.html b/layout/generic/test/test_bug405178.html
new file mode 100644
index 000000000..b39a5f19f
--- /dev/null
+++ b/layout/generic/test/test_bug405178.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=405178
+-->
+<head>
+ <title>Test for Bug 405178</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=405178">Mozilla Bug 405178</a>
+<p id="display"><div id="div" style="-moz-column-width: 10px;">&#23377;&#1741; </div></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 405178 **/
+
+var div, textNode;
+
+function boom()
+{
+ div = document.getElementById("div");
+ textNode = div.firstChild;
+
+ div.removeChild(textNode);
+ setTimeout(boom2, 200);
+}
+
+function boom2()
+{
+ textNode.data = "";
+ div.appendChild(document.createTextNode("X"))
+ var foo = document.body.offsetHeight;
+
+ ok(true, "Test is successful if we get here without crashing");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+addLoadEvent(boom);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/generic/test/test_bug416168.html b/layout/generic/test/test_bug416168.html
new file mode 100644
index 000000000..02461b9dd
--- /dev/null
+++ b/layout/generic/test/test_bug416168.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=416168
+-->
+<head>
+ <title>Test for Bug 416168</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=416168">Mozilla Bug 416168</a>
+<p id="display">
+
+<div id="a" style="overflow: hidden; outline: 1px solid black; height: 0px;">
+ <div style="margin-top: 50px; width:20px; height:20px;"></div>
+</div>
+
+<div id="b" style="overflow: hidden; outline: 1px solid black; width:0px; height: 100px;">
+</div>
+
+<div id="c" style="overflow: hidden; outline: 1px solid black; width:100px; height: 0px;">
+</div>
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var a = document.getElementById("a");
+is(a.scrollHeight, 70, "margin-top included");
+
+var b = document.getElementById("b");
+is(b.scrollHeight, 100, "zero width");
+
+var c = document.getElementById("c");
+is(c.scrollWidth, 100, "zero height");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug421436.html b/layout/generic/test/test_bug421436.html
new file mode 100644
index 000000000..5a8e2e0df
--- /dev/null
+++ b/layout/generic/test/test_bug421436.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=421436
+-->
+<head>
+ <title>Test for Bug 421436</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=421436">Mozilla Bug 421436</a>
+<p id="display"></p>
+<div id="content">
+<span id="outer"><span id="inner">Hello</span><br></span>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 421436 **/
+var inner = document.getElementById("inner");
+var outer = document.getElementById("outer");
+
+is(inner.getBoundingClientRect().left, outer.getBoundingClientRect().left, "left edges equal");
+todo_is(inner.getBoundingClientRect().right, outer.getBoundingClientRect().right, "right edges equal");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug421839-1.html b/layout/generic/test/test_bug421839-1.html
new file mode 100644
index 000000000..962b26f93
--- /dev/null
+++ b/layout/generic/test/test_bug421839-1.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=421839
+-->
+<head>
+ <title>Test for Bug 421839</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=421839">Mozilla Bug 421839</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+Moving with you mouse from above to below and back a few times across the iframe, shouldn't crash Mozilla<br/>
+
+<iframe id="iframe" src="data:text/html;charset=utf-8,text%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%0A%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3Etext%3Cbr%3E"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 421839 **/
+
+var counter = 0;
+
+SimpleTest.waitForExplicitFinish();
+
+var doc = document;
+if (document.getElementById('iframe'))
+ doc = document.getElementById('iframe').contentDocument;
+
+function toggleIframe(){
+ var x=document.getElementById('iframe');
+ x.style.display = x.style.display == 'none' ? x.style.display = '' : x.style.display = 'none';
+ setTimeout(toggleIframe,100);
+
+ if (++counter == 4)
+ setTimeout(finish, 200);
+
+}
+setTimeout(toggleIframe,100);
+
+function ctrlclick(i){
+ var wu = SpecialPowers.getDOMWindowUtils(doc.defaultView);
+ var wu2 = SpecialPowers.getDOMWindowUtils(top);
+
+ try
+ {
+ wu.sendMouseEvent('mousedown', 2*i, 2*i, 0, 1, 0);
+ wu2.sendMouseEvent('mousemove', 500*i, 500*i, 0, 0, 0);
+ //wu.sendMouseEvent('mouseup', 2*i, 2*i, 0, 1, 2);
+ }
+ catch(e)
+ {
+ }
+
+ i+=1;
+ if (i>50)
+ i =0;
+
+ setTimeout(ctrlclick,20,i);
+
+ if (++counter == 4)
+ setTimeout(finish, 200);
+
+}
+
+setTimeout(ctrlclick,20,0);
+
+function finish()
+{
+ ok(true, "This is a mochikit version of a crash test. To complete is to pass.");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug421839-2.html b/layout/generic/test/test_bug421839-2.html
new file mode 100644
index 000000000..5d187dc1c
--- /dev/null
+++ b/layout/generic/test/test_bug421839-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=421839
+-->
+<head>
+ <title>Test for Bug 421839</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=421839">Mozilla Bug 421839</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 421839 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+setTimeout(finish, 5000);
+
+function finish()
+{
+ ok(true, "This is a mochikit version of a crash test. To complete is to pass.");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug424627.html b/layout/generic/test/test_bug424627.html
new file mode 100644
index 000000000..dd5f00c89
--- /dev/null
+++ b/layout/generic/test/test_bug424627.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=424627
+-->
+<head>
+ <title>Test for Bug 424627</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=424627">Mozilla Bug 424627</a>
+<p id="display"></p>
+<div id="content">
+<table><tr><td><textarea cols="60" rows="4" style="border: 20px solid blue;">text
+more text
+even more text
+enough text</textarea></td></tr></table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 424627 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var t = document.querySelector("textarea")
+ t.focus();
+ var originalValue = t.value;
+ synthesizeMouse(t, 10, 30, {accelKey: true});
+ synthesizeKey("x", {});
+ t = document.querySelector("textarea")
+ ok(t, "The text area should not be removed from the document");
+ isnot(t.value, originalValue, "Its value should be modified");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug438840.html b/layout/generic/test/test_bug438840.html
new file mode 100644
index 000000000..1d26f2350
--- /dev/null
+++ b/layout/generic/test/test_bug438840.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test Character Movement (including nsTextFrame::PeekOffsetCharacter)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div>content before the editor</div>
+<div contentEditable="true" id="editor" style="width:200px;height:150px;overflow-y:auto;overflow-x:hidden;"><p>paragraph1</p>
+<p>paragraph2</p>
+<p>paragraph3</p>
+<p>paragraph4</p>
+<p>paragraph5</p>
+<p>paragraph6</p>
+</div>
+<div>content after the editor</div>
+<pre id="test">
+<script class="testbody" type="text/javascript;version=1.7">
+
+function test() {
+ var sel = window.getSelection();
+ var editor = document.getElementById("editor");
+
+ var keymodifier={};
+ //in windows/linux, pageup/pagedown will trigger movement of caret
+ //while in Mac, pageup/pagedown will just scroll. We need to press
+ //alt-pageup/pagedown in Mac to actually move caret
+ if(navigator.platform.indexOf("Mac") >= 0){
+ keymodifier.altKey=true;
+ }
+
+ sel.collapse(editor.firstChild.firstChild, 1);
+ synthesizeKey("VK_PAGE_UP", keymodifier);
+ is(sel.anchorNode, editor.firstChild.firstChild, 'after pageup caret should still be in the first paragraph');
+
+ synthesizeKey("VK_PAGE_DOWN", keymodifier);
+ is(sel.anchorNode.parentNode.parentNode, editor, 'pagedown should not move caret outside the editor');
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(test);
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug448860.html b/layout/generic/test/test_bug448860.html
new file mode 100644
index 000000000..6d1a8edca
--- /dev/null
+++ b/layout/generic/test/test_bug448860.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=448860
+-->
+<head>
+ <title>Test for Bug 448860</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+:focus {outline:2px solid red}
+</style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448860">Mozilla Bug 448860</a>
+<p id="display">
+
+<img src='data:image/gif,GIF89a%93%01D%01%F7%00%00%F9%F9%F9%FA%FA%F5%EB%E9%CE%FC%FC%FC%E9%E7%CC%EE%EE%EE%FE%FE%FEhhg%A0%A0%A0HHH%ED%EB%D0333%F6%F6%F6%F0%EE%D2%C3%C3%C1%F1%EF%D3%DB%D9%C0%DB%DC%DChhh%9D%9D%9B%D5%D3%BB%C9%C9%C9%F1%F1%F1%E0%E0%E0%CA%C8%B2%ED%EB%D1%E3%E1%C7%DF%DE%C4%8F%8E%82%B9%B9%B8%F5%F4%E8%E8%E8%E7%D0%CE%B7%B3%B2%9F%E6%E6%E6%BF%BD%A9%B9%B8%A4%F3%F0%D4%D7%D7%D7%F4%F4%F4%DF%DF%DD%E4%E4%E4%84%84%83%F0%F0%EF%F7%F5%D7%90%90%8Duup%EE%EE%ED%EA%EA%E9%E2%E3%E2%9C%9D%9B%EB%E9%D2%D3%D3%D2yyt%F0%EE%DD%93%92%85%E5%E4%C9%EB%EC%EB%F5%F2%D6%85%85z%93%93%91%C4%C3%AD%ED%ED%ED%E5%E3%C9%88%88%83%7F%7Ev%AA%AA%A7%F5%F5%F5%C5%C4%C2%CF%CF%CF%8C%8B%80%AD%AD%AC%DD%DD%DC%F9%F7%D9%8C%8C%8A%B8%B8%B7%B1%B1%B1%80%80%7E%85%85%7E%A8%A6%96%AD%AC%9B%87%87%86%F7%F4%D7%C6%C6%C5%97%97%94%5B%5B%5B%EF%EF%E1%AF%AE%9C%9F%9E%8F%AC%AB%99%92%93%92%95%94%87%9C%9B%8C%80%81%81%F7%F7%EE%A7%A6%95%FD%FC%F6%9E%9D%8E%A3%A2%92%FC%FB%DD%98%98%98%EA%EB%EA%98%98%8B%8E%8F%8E%95%95%88%ED%ED%E8%D6%D6%D6ddc%81%81yooi%A1%A0%90%9B%9A%8C%5B%5BZ%AA%AA%A8%A4%A3%93xyv%A7%A7%A7%7E%7Ev%D0%D0%CF%C8%C8%C7%AC%AD%AC%B1%B1%B0%A6%A5%A4%97%97%95%9A%9B%96%F0%EF%EF%C7%C7%C7%F3%F3%F3%C4%C2%AD%BF%BF%BF%D9%D9%D9%A2%A3%A2%AA%A8%96%D3%D3%D3%E9%E9%E9%E3%E3%E1%F8%F8%ED%A7%A7%A6%98%9A%99%A2%A2%99%A7%A6%99%A3%A5%A4%E2%E2%E3%F0%F0%EB%A0%A0%96ooj%C9%C9%C3%CA%CD%CE%AA%AD%A0%A0%9F%96%F4%F4%EF%EA%E9%E4%E3%E2%D2y%80%80%E8%E6%CB%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%21%F9%04%00%00%00%00%00%2C%00%00%00%00%93%01D%01%00%08%FF%007%09%1CH%B0%A0%C1%83%08%13%2A%5C%C8%B0%A1%C3%87%10%23J%9CH%B1%A2%C5%8B%183j%DCx%B1%00%00%8E%20C%8A%1CI%B2%A4%C9%93%28S%A6%F4%A8%B2%A5%CB%970c%CA%9CI%D3%21%CB%9A8s%EA%DC%C9%B3%27%CC%9B%3E%83%0A%1DJ%B4hO%A0F%93%2A%5D%CA%B4iD%A4N%A3J%9DJu%27%D4%AAX%B3j%DD%AA%F1%2A%D7%AF%60%C3%8A%F5%2A%B6%AC%D9%B3K%C9%A2%5D%CB%B6-M%B5n%E3%CA%9D%1B%12.%DD%BBx%F36%B4%AB%B7%AF%DF%BC%7C%FF%0A%1E%BC60%E1%C3%88%B7%1AN%CC%B8q%D3%C5%8E%23K%0E%0Ay%B2%E5%CB3%2Bc%DE%CC%F9%A4%E6%CE%A0Cw%FD%28%BA%B4%E9%92%9FO%AB%5E%7D05%EB%D7%AB%5D%C3%9E-Z6%ED%DB%9Bm%E3%DE-Y7%EF%DF%89%7D%03%1F.X8%F1%E3%80I%23_%9E%5B9%F3%E7%BD%9DC%9F%1E%5C%3A%F5%EB%C5%ADc%DF%9E%9C%BB%F7%BE%C6%BF%8B%FFw%1A%7E%BC%F9%A4%E5%CF%AB%17%9A%7E%BD%7B%AB%DA%DF%CB%A7%DA%7E%BE%FD%9F%F1%EF%EBG%9F%7F%BF%7F%F6%FD%FD%27%20%7C%03%168T%7D%06%26h%11%82%0A6%F8T%80%0EFh%12%83%12V%88%10%85%16f8%10%86%1Af%C8a%87%15%7E%08b%84%22%8E%D8%60%89%26%26%88b%8A%05%AE%C8%A2%80.%BE%E8_%8C2%EAGc%8D%F6%DD%88%A3%7C%3A%EE%E8%5E%8F%3E%AA%07d%90%E6%0DI%A4xF%1E%E9%5D%92Jn%C7d%93%D7%3D%09%E5tRN%F9%5C%95V.%87e%96%C7m%C9%E5p%5E%7E%F9%5B%98b%EEFf%99%B7%9D%89%26U%00%0C%00%E0%9A%AF%01%F0B%0E1%0C%12%C1%9Bp%9EvB%11G%B4%B0%83%1B%244%E1%A6Oj%E6Y%D4%0A%08p%00B%03I4%20%C0%17z%0C%CAS%A1%86%0A%05%C0%11%84%8C%A1%00%01%9Ah%22%00%04F%A4%60%C0Q%10V%DAX%04-%94%20%40%A7%AC%96%40%02%10%92%EA%FFD%A9%A9%3DU%80E%12%AC%B2%CA%A9%19%0E%C4%8A%D3%AC%B4%EE%E4%C0%17%3A%E4%CAj%03%3D%28%01%C0%A8%B2%96%1A%ECa%0E%10R%AC%B1%9A%10%D0%00%16%1D%F8%FA%96%B3%CF%0E%E6%C0%15%25P%DB%A9%ABd%2C%DBl%B7%989%10B%B8%E2%2A%00%01%0F%29h%2B%13%B0%E8%D2%D4%01%09%EC%8A%3B%06%1AEp%EB%99%BF%F5%EA%D5%C1%08%0D%88%DBi%1278%60%EE%AF%00%07%8C%97%1E%3D%14l%B0%0E_0%C1%00%B35%D1%EB0L%40%80%A0%80%C1%9A4%40%82%10%16%C8%8B%DF%C6%8E%01%80%06%04%AB%1A%AC%00%05-%C4%9B%93%C6%28%ABDC%08%D5%82%ACI%12%40D%60%F2K4%D7%8C%12%02%18%B4%0C%B2%14%3B%98%D0%F0HA%0Bm%92%0A%2C%EB%ACI%09%1C%D0p1%C3N%1F%F6%C2%0D%1Bp%AA%B3%02Wtp%02%C6%99-%9D%F5YJ%80%20u%A7%04%40%40%C5%0B%3F%B7%D4%F4%D9%20%1D%C1E%CEk%2B%E0%02%0Cq%AB%FF47%DD%1A%D1%81%85%00%5E%AF-%C0%0DJc%0Dx%5E%7Cp%B0%E9%DA%B9b%40%06%03%8A%2FNW%07%1C%E0P8%E4%9AhP%C3%09%7D%A3%F4%B7%E5%13%BD%B0%83%00Fs%AE%09%0E%5C%F4%9A%B1%D9%A4%B3yD%0F%1F%AB%AE%2B%05M%5C%5Dv%ECqY%D0F%03%9B%ABN%C0%06%40%5C%10%FA%84%B0%F3%1EU%117%E4k%3B%DB%18%B4%90%7CF%A3%2B%CF%10%03tP%90%FA%F3%9F%EE%F0%02%D9%40Oo%BDR%29%D40%ED%F3%B9%0A0%82%B2%BB%8Fo%96%01%1DH%8B%BE%B1%02l%60%84%09%E0%CB-%BE%FBD%7D%10%04%0E%DB%9B%9F%02%5E%B5%3F%8AT%8F%7F%05%11A%13%D65%3Fj%09%00%077%C8VL%0E%E8%BE%08%E8%81%0C%3C%A0%C2%0Dz%80%B7%06%E6%EAe%2A%88A%FEDW%40%04%D6%C4%04T%80%00%04%28%40%01%96%05%D0%83%04%18%A0%12%2Cp2%13%FA%E4%03wr%C8%14%E4%A0%03%D4%A1%CE%83%20C%9D%19%FF%84p%3C%90P%B0%5E%0E%E0%01%1F%F4%20%82%86t%80%81%40%CC%DB%06%94%80%82%11%A2%A6%846%D4%08%03%A8P%B4%F55q%21%83%88X%14%21%27%85%2B%0C%A2%88%1B9b%B7V%00%89%0D4%40%0A%18%00%C2%07%142%00%3EP%A0vc%D4Y%FDZ%10%034R%0F%8BY%C4%C8%0A%16%B1%81U%25%21%0B-%A0%1CB%06Y%C8%3C%AEm_%15%F0%23F%D4%F8%AC%1C%10rU%D6%92%83%10%14i%90%220%82%00%C1s%24%B5%C6%D0%82%0A%E8%8E%84%81%D4%C9%07%18%A1%81%96%09%A0%01f%40%40%04V%60%90%23%90%40b%A2%04Y%12vP%84S%FE%2B%959a%C0%CA%8C%F6J%12p%80%07%17%20%C8%00Z%80%01%3C%E6R%5C%0D%08%01%25%3AP%04%1A%E4%10y%C0%C4%89%018%A0%BD%5CYk%0C%20%E0%E3%09%18%00%80%0A%C8%A1k%CF%04%19%01p%D0%83%10%7C%C1%0DFP%186%B39%13%18%E8%C1%0D%E8%A4V%12%B0%00%FF%04%1E%F0%80%0C.%00%C1%0B%D3%A9%AB%06%94%40%07I%20%00%17%E8%00%B0%14p%8B%92%5Cj%D3A%180%81%16%84%80%02%3A%2B%81%14%A4%A0%03G%11%D4v%02H%02%1A%22%E9%90%01%1C%21%08%AE%DB%0B%20%E9Y%90%17%1C%C0%05J%88%E9%0D8%D0%02%04%A8%E0%A2%1E%FD%28A%05%A0%01%25%AC%C0%8A%04%E9%00%10F%10%84%3B%2Cl%21%10%B5R%130%B0%3A%0Dl%60%03%10%00%01%06%3C%E6L%9D%A63%09M0A%DC%8A%D0%04B%40%C0%0Dt%10%01%E8T%CA%D2%91%9C%A0%06%1A%D0%15%28%05%A0%80%81ZU%94%EAS%82%08%0F%02%83%26%60%01%03%B0%A4%C3%05%C4z%BC%A4B%A9%05%18%7D%AB%60%0DV%82%2B4A%04%A7%8C%00%10%D0%00%02%0D%E8%C0%08G%40A%0C%5EpT%85%F8UI%270%82%5B%07%FB%D6%06%80%20%08T%D0%EA%14%9A%A0%A8B6%60%07L%88%C0d%2Bk%D9%95%B2%14%00%2A%C0%40%F0%AC%A5%03%29%FF%8Ca%0CI%A8%2Ag%1D%29%80%12l%40%0E%1C%B8%02%0E%D2%8A%BA%20t%40%B5%94%05%AAA.%1B%24%03D%60%07%B8d%9B%06%B8%C0%81%1D%B8%00%08%1C%28%DAn%09J%80%1F%B2%0A%0758n%0CV%C0Z%A4%BA6%9B%00%98%40%23u%B5%8160%A1%02Sp%C0%14%3A%10%84%CDn%D7%918po%04R%40%5E%E5%B6%E6%BC%C0%3CA%0Bp%A5%2B%1C%5Ca%02%11%B8%40%0CR%20%02%11%D0%01%8A%F7%25%28%0EH%20%CB%14%94%CC%BF%FF-%2BG%2C%C0%83%12%14%AE%7B%26%98%EC%09%C6%C9%00%14%A05%94%11%1E%E3%F0nP%84%F1JrC%00N%E5%A5%08%C6%2A%01%E0%EE%02%94%1D%80%01v%CC%80%23%5C%21%BA%DE%04%25%8AS%AC%B3%B6%ED%00%05%C95%60%8CS%19%81%E6%B1J%01%3D%88%99%2F%0D%60%02%0E%9C%CFSl%FD%98%0A%21%90V%22sn%C5%26%18%5BE%98%1B%24%18%28%E1%8E%9D%12%19%19%3E%60%9D%0B%28a%FF%0C%AF%EC%E8%06%A4%DAN%23%D4%20%085%E8%A6%97%A5F%00%0D%B8%A1%97%0BZr%20%070%85%20LK%01%20P%01e%0D%F2%81%16%E8%00%02%23%B8%02%23%CC%10S%15%D0%C1%04%11%88%C0%14%DA%E0%A9%3D%EBQ%03%90%B0%1A%86%13Bf%1F%19%00%00%83%80%A2%02F%E0%82d%16%04%00z0B%0Bx%80%00%3AL%21%D3%11%90%AC%08%3E%F0%81%23%DC%80%C0%9E%16%17%A7%18%D1%01_%3EH%C3%20%214%B1t%05%82%1Dt%A0%20%06%88%01%13%8A%90i%05%EF%1A%06%2F%B0%009%01%90%83%16%E0%2B%D8%84%0D%C1%11%C4%ACddsd%00%0E%C8%C2%95%87%87%06%04%90%0D%00%2B%D8u%0E%B2%BD%ED61k%00%5C%85%80n%C1-2%04%8C%B5%DC%E6%CEH%0E%EE%E0m%E7y%8A%00f%60%02%C6%06%00%80q%B6I%C7%0795%138%00%3Cp%1BKd%24%7B1%8C%03%8E%11%06%20%80%0B%1B%E8%60%AB0%C0%83S%1A%00%E2%0B%FF%19%00%87%B3%00l%8B%EF%EC%0BBX%81%C6%05R%EA%1A%0D%80%09%83%1B%A8%B5np%07%8D%0F%E0%02%2AX%94%CB5%F1%B2%1D8%00nc%16%B4%09%EF%C0%B5%7D%B7%AA%07%7C0vCN%DD%81%1BtZ%941%3Ch%12%C6%D0C%D5%E9%20%0B%13%88A%C9%92%CE%F1%8A%7C%A0c%06%A7%96%B5%E2%E9s%0B%28%01%C2%40%7C%A5%14J%40%81%2Cp%01%0DF%F8%C2%D4%86L%BF%0D%EC%60%93%A3%BE%90%D2%DD7%00%25%7C%DBX%DDU%C0%E3B%86%BB%0F%04%BE%20%3Fo%02%05%80%CC%3D%29%40%00%0BAP%01%0F%98%E0%80%0Et%80%0C%40%C0k%C1b%D8V%14%F7%D9%0CGx%FCr%07%3F%BE%29%60%A1%95%C6b%27%168%90%05%D8%87%EC%C0%27%98%C8%A9%EF%09%CA%F9%3D%90%10mP%81%03%2AP%01%13%A0%00%05%0A%96%B6%12%CC%B0%28%01H%15%02%21g%5B%A7%E2%8C0%07%A8%9E%205O%11%03%800%F9%5C%B9%CA%05%13p%40%FF%11%12EtO%29%00%084%F0%F9%0Bj%40%3B%DBq%0A%07r8%80%1E%AA%99%E0%18%7C%20%07%2B%B0%80%FEs%10%01%3E%28%21%0B%23%D0%06F%80%06r%A0o%9D%02%02F%10%7Cw%16%7F%F83%11%D9g%22%15%20%5C%5E%03J%25%80%06%0E%90k%17%10%01%83%60%04%E5%E7Yos%7D%021%00%26%B0%03%1B%E0tj%27%00%18%D0%06%97%96k%29%80%7F%0E7%000%C8p%16%F0%01%26%40%07M%D0%04%99V%03%23%A0%01%3D%10%04m%D0%01%26%60%024P%04%1D%E0%02%7C%F0%02%0E%C8z%CA%C3%04%23%A0%2A%A8%A3%018%40%08%3C%60%02%22%90m3%88%00%A73wcp%3F%1Ag%00%D8s7%F6U-%02%10%02.P%01%09%F6%01%2F%40N%3A%06%3E%27%C7%00%2B%F0%011%90i%22Pu5%A0%04%D6t%860%00%874%60%022%27%11%0F8%22%A8B%02%2CD%01_%E0%02%97fa%0F%07%000%D0%015%E0%06_%FF%C0%05A%A04%20%B8%09%03%90%03%0B%D4%7B%EA%D4%00YP%03E%80%02%22%B0%02%0C%80r%09%D1%86%2B0o%0Cp%02%1F%80%02%2C%08%03%DA%06%00%A7%B8%02%2F%F0o%10%F1%87%20%F2s%40p%03F%D0%04%A9%85%5C%0Bsj%2Fp%07%9D%C7%04%1D%10%03RWR%11%10%04%025d1%D4%03.%40%03%28%00%03%A0%F3x%27go%03p%02y%18%8B%F6%B6%09%3B%C6p%12ule%17%11%03%C0%000%90%81%B9f%7F%FDE%10%A7%16o%17%B0%8E0P%8CS%07%004%10%04z%E6%40%18%E0%02%C6%D7%8E3%27%10%D3%98%8D%06%B1cI%F8%8D%12%11%8E%2F%C0k9%A0m%A28%10mh%01%F9%17%8A%16%11%8E%9B%165%F4C%01.%D0%89%F8%88%27%00%09%110%08%00%1Ay%90%02q%04%AEvr1%88%11%D5%C8%04.%00%91%D2W%03%0E%F0%8C%0Ci%91%17%A9%11M%D0%03%1C%E0%00%22%A1rzP%03%EBEtr%FF%40%06%11%F0%01%2B%C9%92-y%3D%CF%98%03%D2%C1%00%07%00%02m%13%04%3F%15%12%03%B0%02z%C0%01%B0%D76m%B0_cG%14%B4X%8B%0E%B0%03%90HD%03Qx%23%B0%2A%CE%07%2B%21%91%8ELp%03%9AC%00%28%85cJHv%3F%D9%10%DB%A7%03%25%D0%00%5CP%01%A3%02%00%08%F0%05%A9%83%05G%90%8F%A3%08ot%60%06%0A%90%05%3D%C3%93%93%18%3Ek%D9%10%16%B0%03%E1%D2g5p1%23%B8x%D5%02%01%3B%80%00%17%A8%97%FD%08o%2A%20%075%A0ZSY%14U%D9%21%11%C0%05%B8%D4%03G%C0%00%2A%D0%5D%3E%A4%00o%09%01%24%40%09%90%40%05%20qj%11P%05%09%B6h%FCQ%98%29%27%04%CD4%7D%20%40%060p%00%21%D0%03%180U%23%80%05%24%20w%20P%03%E5e%11%5E%18b0%90%9C%94%91%96%B1S%01%40%20%05%BA%12%02d%B0%02%7BB%07z0%08%13%20%04S%00X%0D%A0%01%F1%FFD%5E%C9%E6%86%EE%F8%9C%B6%89%10%16Pu%06%E8%29%14%904%B18%8E%B8%96i-%00%01%0A%85%00%28%20%94J%D9%8DJ%D1%99%0DB%03%E2w%07T%80%06%AF%F4d%24%A0%04%17%40%5E%AF%08%8B%2F%80%02-%20%05%81%22%95%94%89%10%83%D9%3Ek%C9%00S0%08%7C%B0%08_p%05_%D0%03I%40L%1A%80-%22%80%2A%13%00%3A1H%03V%D7%04w%90%A0%13%AA%15%FE9%20E%C0%01%23%B0%28IpP%40%B6%2Ax%09%03%C3%22n%83b%02.%80%01%90%92%9F%CE%29%171%2A%20%15%10%02%9A%C2w%21S%8F%83%D01%20%00%04%CB%22%04A%00%01%C9b%02%3C%F9%A20%0A%9D%28s%2Fi%D7.%1AP%A3mU%88%16%A5%01%1A%10A.%96%1D%859%00DCy%06%83%89%9D%A3B8%00%A1-%10%01%CDY%A1aq%A4%FEq%29%98%40y%5E3%03%9C%02%A8j%D7%5D%B0%C4%04%829%18z%BA%1F%2F%00%03%01%FF%10%00%1E%60%036%00%A8%1E%E0%A8%93%1A%00%5E%D0%A8%1E%20.%25%80%094%20sx%3A%16%5Cj%28%15%B0%09%29%90%02%05%A1%7F%2Fp%04i%D0%A8%8Dz%A9%AB%1A%00%28%B0%AA4%B0%AA%99%9A%2B3%00%06s%D2%26Gp%04%081%AA%A5%1A%17%89j%1E%0B%B0%09%08%80%00%04a%01%090%08%1D%B0%00%13%D0%AC%E1%17%00%0EP%05i%40%03%2A%E0%02%CB%DA%01%28%10%003%90%2B6%00%06%83p%00%E0%9A%00%09%00%AE%07%C0%03%041%AC%C5%0A%AC%A1%9A%27%E8j%AC%3C%40C%9B%60%02%3CP%05-%E0%AC%CF%1A%00%2Ap%00-p%00%E2%0A%AEB%10%006%E0%01%5E%E0%05%1Ep%ABE%20_w%A0%02%2A%40%7C%C5w%AE%C4%EA%AE%F0%8A%16%C1%EA%1D%08%B0%00%16K%AC5p%00U%60%02%C4%3A%01T%90%00%16k%B1%07%60%A9%8DJ%034%C0%AF%E3%DA%02%98%CA%AA%8E%1A%00%1F%40%06%E3%DA%AFUp%00%02Q%B1%17%FF%8B%00%19%BB%B1l1%B1%E2%81%AE%09%60%AC%02a%01U%E0%00%F5%EA%00%C27%B2%D0%DA%9D%0E%90%00%CD%9A%AF-%9B%06%E2%BA%AA%5E%F0%B2%07%80%00%E0jS4%EB%B0%08%F0%B3n%C1%B3%DF%D1%AE%03%11o%260%01%21%1B%B2%23k%B4%07%B0%B4M%3B%B2%93J%03%16K%03%8E%FA%02d%B0%B0%0A%5B%01V%5B%10%60%DB%B5%EB%BA%26G0%AC%13%C0%03%22p%04%0E%00%03%96%D0%02E%D8%01%CD%DA%01H%0B%ADi%CB%B4%13%E0%B4%02%1B%00B%F0%AF%5E%60%03%01%60%B5%20%BB%00%E3j%AE%03%D1%B7%0F%BB%09%40%B8%B3%7B%2B%26%5B%7B%00%C3J%06d%10%00d%C0%07%8D%9A%00%1D%90%00U%00%BB%E2%0A%B7%92%5B%05%27K%AE%2A%AB%AD%2C%3B%03%01%00%06%220%01%09P%035%F0%B3%F0Z%BA%60%9B%AE%851%BA_%D2%01%A3%EA%B7%070%01U%F0%AFB%90%00%28%B0%00%0E%E0%00%D0%DB%02%AB%0A%BC.P%05%F6Z%AF%00%FF%3B%03%90%2A%BE%92%3B%AE1P%AC%29%A0%02%09%A0%02%A0%DB%BC%C4Z%B5U%C0%AB%C9kn4%BB%02%F5z%04%60%00%064%F0%AF%AC%FAR%AF%EA%8C%01%40%03%F6%FA%AC%DB%CA%2A%BC%8B%02%0E%F0%01%26%25%BF%290%AA%03A%B3%16P%AC%F2%3B%BF%1C%87%8A%22%10%03%D9%CA%AA%95%8A%A9%97J%B2%2C%DB%AA%C13%A9%60%A0U%9F%9A%A7%CA%0B%27%DC%96%02%26%10%04%E2%5B%C0%01%FB%A8%9D2%03%DB%2A%BE%90%3A%BE%28%26%BE%1C%C07%84%E1%B5%E3Q%8D9%00%03%40%C0%A4%0Dd%04%17%C0I%7F%C1%C3%E21%8D%0C%C0%03%26%E8AY%E0%00F%EC%17Hl%1E%DB%E7%A6%40%84%03%21%206%25%FC%15S%3C%1E0%00%5D%CFd%60%0E%40nG%7C%C2y%02%00%7C%20P%B9%D4gO%BC%99Rl%C6k2%00%1D%E0%06V%0CC%1A%40%08S%E0%C6%E0%01%C7eb%00%0E%C0%01%84%F3L%7D%F6%05%15%20%8Bo%5Cv%0EP%039%FF%B5%C6%1A%40%C8d%7C%C8%E6f%00E%B0%03%00%B4S%A0%12%01%8F%BC%C7%E6%86nF%D0%5D%1F%05B%29p%9Et%D1%C5%D4Ah6%19%86%1E%A4%00%18%C0%07%88%B5%C3%7Cl%25%26%00%04%ED%F9%C9P%C7f%AE%ACa%03%20%04pGP%223%08%B6%8C%A8%AF%0C%25%0E%60%06%DE%A5S%22s%04%CDy%CB%2C%D5hx%25XI%E0%06G%A0%9F%C0%FCZd%10%02%D4%F9V%D6%82%06%1D%D0%87%D3LO%1D08B%9CG%2F3%85z%AC%C9%D9%94%027%80f%82%A5%03%210%01%7C%A5%CC%16b%01K%A0%10%40%BB%09j%F0%AB%0Aa%01%1Ck%02%11K%AA%C4%17%B4%1CK%11%2F%B0T_JP%3C%C3%04w%0A%CF%FEQ%01e%1B%B2%120%10K%00%07x%20%01%14-%01x%20%10%0D%BC%00%15%40C%12%80%00%0F%AD%10%15%40%B3%07%E0%C05%2B%AE%2A%60S%3F%5B%CF%0F%F1%01%3C%40%02M%9CK%C82%85H%A7%D0%FB%FFQ%01%1F-%01%A3j%D3%03%21%01ZP%D1%15%AD%06%9B0%D1%0B%20%01sP%B6%0B%11%D2%9B0%D2%1CV%B36U%AC%EA%8B%BClI%03BU%9C%E1%ECHI%60%04%1D%90%02%86%5C%C6%03R%01%09%40%D1%09%00%07%12%20%D6%02%C1%D0KP%ACh%8D%074%A4%06%0C%5D%01Z%80%00Z%60%B7%09a%BA%0B%20%D2%3C%10%BC4%84%00w%CD%03%1A%3B%AE%070%C1%09q%02%15%A0%07%3B%F0%058P%C7W%F5%05%08%60%02B%B9%C5%60A%CAIQ%01p%40%7Cp%80%00v%FB%D1p%90%00n%ED%D3%17%BD%09%3D%3D%D4sP%ACQ%F0%D1%9B%E0%D5sP%10H%CD%AF%CFV%B3%EA%5B%01%2AP%05%0A%8B%00%AB%9D%10%26%D0%027%80%01%3A%D0u%9CU%02%3D%00%04E%A0%D5Z%FA%D8%C1%CC%19%5E%5D%AC%09%10%05%080%DA%02%B1%048%DD%D3%15%DD%D9x%80%07%1A%FD%D6K%B0%00%9DM%AC%9A%8B%DA%22M%D2%0F%FB%DA%0B%FF%60%B5%9A%1B%D0%08%81%2A.0yU-J%C8%82%92HV%A4%DD%81%A4%29%7D%DC%C9M%DA8%8D%07hm%D9L%3D%D4%99-%01%DF%ED%C0%11%CC%DD%1C%B6%DD%C4%9A%D7%F9z%00%0A%9B%D2%0A%01%00M%00%02%3D%B0%03%F6%B9%5B%0D0%02%28i%A7%A2%DC%DE%06%82%D3%06%81%D3c%0D%D62%A0%91%5D%00%B2jP%AC%12%9D%00%A6z%10%2AP%D7%00%3E%D2%18%AB%02%26%80%D2P%7D%10%86G%00%BB4%02%BBU%02F%00%04%15%80%02Y%CA%18%90%CD%14%16%5E%10%18%DE%05%7B%B0%07k%B0%07%3Ep%01%09%B0%04%09%B0%062%00%D7%C5%0A%D4%07%81%00%0D%2C%D2%27-%E2%0F%9B%02N%9D%D2%DCM%10L%E0%06%81%7Cy%5D%A6S%9C%A2%00F%40%06%CE%08%8D%C1%0D%AA%0A%A2%05NN%108%BD%06u%A0%082%20%012%90%BFq%00%06%8F%90%07g%B0%E4-%7E%10%F2%1A%AF%C5%EA%C0%E8%5B%DF%7F%7E%104%B0%03%98%D8%00A%FF%00%E6%0F%04U%A8e%02%17%90%03%3DY%1D%1D%02%00%29%90%BF%1F%CC%B2%88P%06%8E%CD%10%16P%92%9B%E3%02%D1%F7L%ABRw%D6uk%ABu%E6e%B1%E3U1%00D%60%05%1E%60%05V%80%08%1E%C0%02%0F%60%05%F5%A3%01y%E0%08%EC%BD%11J%90%9B%D2%E7N%E7%AD%3A%F5C%02%07%40%07%99%E6%89%E7%18%19%AAN%15CP%07%2C%E0%29%0F%80%01%21%40%02%23%00%08%21%10%05l%80%07%17%D0%07%A8%FE%E4Y%C0.%BD%C5%29%10%D0%06%2F%FD%3Cm%C3%01%2APm%22%90%03%D18%19%13%FB%07%1A%F9%15%17%10%06%19%C0SP%C0%06Z%D0%08%08%20%03%89%20%08%82%80%04%220%04%2F%CApn%02%03%98%16%01B%C0%04%08%40%A0%9CR%3Fnp%05%EED%08%A8%AC%3AgZ%A78%0E%8A%FC%19%1D%7E%F1%07K%10%08%08%90%08q%40%04%87%90%02e0%04%9B%1E%13%03%B0%04%23%F0%00%10%B0%05Z%60%07H%B0%8E%EB%FF%E8%08%29%60%08%E5%1C%900%C0%045%B0%03%1C%60%06%21%00%F1t%165%F5%F3%27W%00%80%D7%8C_%24%90%EE1%00%E9%1C%A9%F1%7D%91%07%5B%40%02%2AD%01%21%10%06%1C%10%052%A0%06%C3%FD%10%16%A0%08%02%90%01%80%20%011%FF%01%3E%D0%07%16%60%F6C%C0%8F%15Ah%07%D0%03%3A%F0%7E8%10%F7%8A%D7V%9E%02%01%1C%E0%06n%D9%00%E5%0ER%10%90%99%ABu%F2%EA%AA%17%22%B0%04l%40%EB%19p%F8%0F%F0%00%2C%C0%02%1A%C0%01%89%00%F8-%F1%01Q%C0%02m%E3%04%81%E0%03%02%1F%83M%3F%11%27%D0%01m%80%03%BA5%5B%3DP%03%09%B5S%18P%03D%0A%F9z%2B%17%22%90%07%20%DF%08q%10%06b%B0%F2%06%13%ED%8A%B0%EBBQ%01a%40%F9%04%10%02y%20%F0%21%01%03%83%80%06R%10%86%9F%92%05%B7%C4%5D%8D%DC%01%88%A5%FA%AB%CF%16%F0%C3%06%B3%0F%08%18%00%08%8D%95%F8%3F%600%19%00%02e%FF%B8%F5%0B%21%02Q%C02%2C%CF%06H%60%F2%20%81B%EAV%D5%CE%C7%05p%DA%40%0A%20%05%5B%27%05%1E%F5%29F%10%01%D2%0C%1Aa%D2%01M%90%E5%13%01%10%26Td%D9%A0A%C0%83%0C%09%05%10%D0%D4%D0%E1CM%194%3Cq%11%25%10%80M%195n%E4%D8%D1%E3G%90%21%3F%0A%E2%80%01%21%08%272R%FC%19%20%D2%E5%C6%13%1D%80%60H%02%D1%E6C%05%18%B8%E8%B8%D9%F3%26%81%06%14n%18Ac%86%84%06%05%0A%40%18%89a%A1%E5K%A8Q%A5N%A5Z%F5c%01%8CV%B5N5%C1C%C3%0D%04%06%40%9E%10%91%E2C%8E%15%16N0%00%00%A0H%0D3%10p%2C%F4Y%97%80%00%01%1BHp%E8%22h%EB%DF%A8K%A2Px%A0%89%C5%93%BE%1F%00%88%05%BC%89%C6%84%1B%1AJ%D4%B5%A9%A0%C7%97%9A%94%7D%DE%0D%D1DH%87%0EL%10%98%D1Tb%04%15%11%0C%1A%AFf%DDZ%2AV%D7%B1%85%5C%91%A2a%07%FF%1D%C6%1B%2B%88%A6%C2%83G%0B%E0-%A8%28%09%E2%06%82%26%02%0C5%2F%D7%84%81%AF%A3%D8%AB%FFDy%B3%A1%B0%A6%07%24%B4%1C%EA%F34%E3%9F%3E%7F%DAzw%19%81%09%87%1EI%040oH%20%09%86%1A%3D%1A%B0%87h%BA%C9%9D%08%17b%A4%B8%C0%E3%CA%98%10%26%80%21%AB%E8%0A4%100%D8%0E%FC%8B%06%2A%E6%12%40%03%2CZ%28%E2%85%15L%E8%E0%08%0E%B2%A0%A0%841%3AL%22%09%29th%60%3D%FA%98ch%03%0C%DE8%03%09%05%A9%22%22%8A%A3%AE%D3%E4A%0E.%CAM%10%19%A2%E0%20%8A%3D%F6%90%21%10A%04A%E2%02%B3%60X%21%82%29%A6%08%CD%3F%02x%A2o%A1%10v%E0%02%03%05Jl%A8%81%11%82%B8%03%85%0F%5EP%CB%82%29%80%18c%91%0Er%20%B0E4%D3%BC%EAL%AD%C8k%8D%87%11%E6%9BQ%03%0C%18%81%04%92E%E4%C0%A0%01%29%14P%CEJ%40%1D%BAk%83%11%DE%D8%C3%0F5C%12%FFa%0E1%20%C0%0B%A2%07P%12%84%B1%0B8%80%80%85%07%10%CA%00%82%11B%08%01%0A%28%9Ex%82%91E%18%A1%84%12BH%C0A%BD%12%F1%CA%E2%86%116%90%B3%C4%12z%08%C2%01%14%608a%00%5E%07%C8%81%0F%0CZ0a%057%135%B6%C5%04%B7%3A%81%86%0Fr%5BM%0F7%1Emo%BE%0D6P%40%8A%06%FE%0Ct%5B%87%F0BQ%0C%04%F2%28%C4%822%0A%29D%C8%0B%0Ciq%08-%9C%C0%E0%07%BAlz%00%83%28%FC%B8%40%0D-F%60%E1%A6%1F%FA%D5%E0_%0D6%D0D%87%10G%5CN%5B%01%14%40%C3%08%08%0C%B6R%00%1C88%82K%06%C8%1B%80%89%1A%A8Ha%D7c%3BF3Y%AD%8E%B8%A1%0D%13%ACb%C0H%F2%5E%E0%81%82l%21%BA%2B%5Enc%86%E8%A0%E6%A0%A8%04%81F%E6p%E2%890%B6%E0%60%12%1F%8A%FDk%00%00%0C%D9%83%8Dw%91%AB%EB%01%0A%C4%08%E3%0D%C2%ECJ%EEe%12%97%CB%FF%29CM%AAL%8A%83%1D6%DCV%87%10x0%E1%03%8E72%20%87%0E%EE%F8%80%01g%3Dv%DB5%90%AB2%80%0A%0A%40h%22%05%AAL%A0%83%07%2A%3AX%5B%AC%23z%20%E1%28me6%7C%B3%0CX%C8%8B%0010%60%E1q%02%C2%98c%09%1F%00%10z%AA%21%FA%28%A4%0B%27%28%60%A8%F0%9E%12%7F%3C%83%C3m%CA%0B%8B%10%AE%A8%E1%0B%01%20%A8%C1%D2%2A%03M%F8%86%0E%9A%12z%00%0BP%7E%7B%F7%D8%E2%CE%FB%06%08%A4%B8%82%8F%132%02%40%0F24%1A%A0%88%29%22%D0%E8%03%26vH%8F%80%2C%85%05%80%0A%12Z%A8%02%84YK%F7%FE%27%A6%9F%00%81D%01X%C8%00%90%3A%A2%A8%E0%F2%97%2C%40b%0F%17%C4%D8%80%F4%EFen%80%84%2B%1C%DD%20%84%1Bn%20%C1Zn5%00%04%3C%88%80%99%DA%96%11%03%00%80-%EC%E3%5D%03%A7%E2%BB%A8%0C%E0%025%00A%95%A4%10%84%F5mb%02%238%8D%10%16A%FF%85E%5C%81%0AL%B8C%07x%C0%05%01L%26aR%80%00%17j%D0%86%03T%40%0FX%98K%FDl%F8%90%07%7Cj%03%DA%22%40%06%0A%13%02%278%21%0F%1F%98%0A%12%14%E1%04%08%20%04t7%A4%95%1Bz%10%3B%1C%40%40.U%0BT%09H0%88%8D%B1%CF%00%038%A0%03%BD%F8%406I%05%00%99%A8A%16%8Es%97%1A%AC%E0%04%96%12%00%080%D0%83%F1%25A%03%24%40%03%166%A0%03%2A%CE%28%28d%C8%CF%05%9A%F0%C4%252Qf%0F%08%C3%08%7E%10%C8%A9%01%22%0Cu%20%02%035b%87%3A%98%24%8F%82%04T%09n%F0DA%25Gf%25%C8B%07%06%D4%C5%2F%86%12Aa%94%CA%00N%E0%80%1A8%AA%8D5%A8%02%06%92%93%B0%A40D%00%0D%00%D1%24%1D%92%04%20%98%00%06%16%88%C1%F6t%E0%27J%96%EE%01%1C%A0%80-m%A2%81%07%9C%A1%0C%A0%D4H%058%F0%03%19%D5%05%2F%07%C9T5%8D%F9%3DK%FFb%F2%7BR%B8%81%03%5E%E0HQ%86%13%8C%5B%C1%DD%07%94P%CC%86l%40.%81%D4%8C%0000%81%18%EC%0A%004%00B%0D0%A0%03%3C%9A%28%98%3EaA%E7%AE%89C%12%28%A2%0C%0C%14%01%1B%E6%A7%99%C4A%A0n%21%08C%18%A0%20%86%10h%20%03%EC%E4%D64%09%A0%003%8C%AF%7E%25%E0%C2%14%88%25N%90%8E%92%9C%0A%24%03%04%94%A3%C9m%B9%93%0E%8A%D9%22%03%22p%07%1E%04%21%0B8%C0V%20%05%40%81%11%1Cg%9F6a%08%1B5C%80%07%D4A%0De%20%A5F%CE%A0Q%CAd%60%04N%08%83%18%A0%10%02%12%40%F5%09bx%02%09%E0%F5%BD%06%28%B4%98%3D%C0%82%A3%EA%A7%03%0ET%C0%29%21%25%ABU%208%95%154a%87%87%13%40%16%98%60%A6%8C%98%12%06%26p%00%02%94%00%2B%05%D8%B2%01%1CPA%1BF%A0%81%9D%3E%84%05a%20%1CB%9F%10%899%D8%A1%A8%9B%B8%C0%1B4%40Q%E4d%60%FF%0E%23%A0%40e%E7WM%02%40a%0BO%40%A7%CC.%AA%14.%18%21%08%18%F0%D4Z%BF%07%D6%22%8C%B5%AC%AB%8D%CAY%A3%22%82%03%60%C0%7B%02%B8%C1%B0%BCC4%0B%E4%20%05%BB%A1%82%116%90G%02%E0%C0%05%11%88%C0%04%5C%80%85%C0%12%80%05%18%88%84I%0FF%01%85%BA%80%7D2%00%01d%B1%C3%01%ABZ%F4%21%40%D5%C0%1B%C2%00%82V%25%25%29%0Dh%C0d00%82%1D%5C%A1J%1C%08%82i%B1%C9%01%1A%A8%96%B5%F3%0D%89k_b%80%26%DC%A5%27%0A%28A%09%AC%BB%5D%0E%A0%40%BE%9B%D8%22%00N%F0%82%0F%C4%80%0E%BE%E5%21%17%98%C0%25%11%5C%A0%03U%C0A0%05%90%81%27%60%F7%07%F4%E9%21%07%F6%40%A0%3F8%C2%0E%7E%90%04%07%1EK%19w%1A%14%C5%2C%08%01%07%DC%BB4%29P%00%03%18%00A%0FRw%85%10p%C1%C5%F3%D9%AB%0B%9C%8B%1C%05%90%97%BC%FD%EDo%F7%98%23%85%1A%98%E0%04%CC%FF%A4%2F%7D%ED%EB%92%01%04%21v6%11%60%16B0e%E6%18%21%02K%EE%C8%16%07%C0%80%DC1%E1%06%27m%00%06T%10%01%B21%60%05%1F%D8%A0%20%CB%07%81-%BCa%04%0F%F8%EFC%04P%87%10l6%0E%7BxB%25%A6%DA%B8%E5%FC%40%02%14%60%0E%0B%E6%00X%CD%3C%40%0CF%90%83%A7B%20%87%2B%C8%C1%0C%84%80%40%09%C87%82%2A%E8T%00%3D%B8%C1%0Evp%034p%E1%06n%E0%02%09%26%C3%9E%9B%B6%00%05lkr%AB%3B%F2d%91%00%80%07Fn%88%06%DC%00%04%26%CC%F0%9F6%81%80%0B%88%08%92%02%A7%D5%95%04%F0%82%17.p%02%DBy9%07%96%08%40%B3%9B%ED%01%5BZ%C1%03%D3%A6%F6%B4%AD%20%00%0F8%5B%DB%01%F0%40%188%00%85%83%02%AA%0E%9A%80%80%1B1%B0%81%1F%3C%CE%7C%17%9E%26%5E%10%C6%01%40%D4%99%00.%D80e%08%B0%97%11%80%A0%B2%95-%AF%88%F2%A8%00%F4%8A%D6%08%84%80n%FFe%DD%D8%03%C1%7Da%CC%A8%FE%02%13R%C3dW%97%15%D6%21a%40%0BhM%00%0D%B8%A0%08%110A%1B%B0%BC%1C%0Ad%8C%06%2B%006%00%2C%E4%85lw%C0%01%0A%E4b%5CS%00%060l%1B%11%7F%C2%0B%22%B6-%F3%9B7%7B%08%228%04%20%A4e%A5%1F8Aq%09%C9%C0%A3.%FC%00%0D%00%C2%8D%FA%A6%80%C0%1C%02%02%0E%D4%B99%5B%F8o%E7%F2%DAn%14%E7U%138%A80%D5%60%99%D5%1A%10%9A9%02%D8%81%09%E0%1Aq%B4O%1C%24%0C%00%16%15%19B%02%15p%09%06%13%F0%2A%7D%A2H%08%0E%A0%81%07t%98B%97%01%90%02g%F3%80%0C%15k%E6%02%0C%7Fx%C3%13%21%00%A4%E3%EE%0F%C0P%08%09D%5E%F2%91%B7C%00%88%80x%C4%07%22%00V%90%BAM%7E%D0TLU3S%1B%00%C4%A78%C0%06%27p%60%0Bux%03%06%10M%01z%B3%07%89%D6%FD%01%07%E8W%3Fwb%A1%CE%1Bh%C3%05%06%FF%8C%F6%26%AB%FD%23%03%E8%C0%08%A6%8C%97%11%B4%81%06%1B%03%C0%1D%D4%0B%28%1C%A8%93%02%B6j%F9F%7Cu%00%EC%27%20%01%D8%3F%40K%2Ap%80%3BL%80%0FT%D8%03%02%D6%90%87%00%B0%00%11%888%84%24%E2%E0%83%40%C0%81%08p%E8B%FD%E7%AFx%22H%80%082%E0%BF%0C%E2%B0%06%3F%00%83h%02%14%0A%E0%00%0Eh%2A%AA%0A%95P%D9%02%28%D0%847%00%81%D0%A3%80-p%810%A0%80%A0%83%82%5Dk%08%94%18%C0%9E%D8%80q%D3%81%12%F88%C3%B1%8D%0C%DC%3A98%82%14%605%E0s5%E1%FB%88%22p%83S%BB%A9%1D%90%10%14x%01%8C0%01385%2By%25%0D%40%83%0Bp%13%03%B0%80%05p%00%07P%01%15%20%C2%05%10%81M%F8%BE%F0%C3%3E%FF%93%80%40H%01%3Bx%04C%10%81%3FH%01%1FX%825%B0%00%09%E8%BF%00%D0%3F%CB%D3%3F%19%88%3C%FF%5B%83%40%40%84L%89%19%19%C3%00%40%18%81%FF%C1%81%82%9C%C2%949%1A%01%F2%29%3A%0C%F0%99%278%24%14%DB%80%CE%A9%B3%11X%03%23%C0%02%0C%28A%F6%C0%B8%20%A0%B5%9B%C8%B8%08%18%90%15%8C%B8%16%F4%88%08%D8%01M%18%01%23p%01%25%98%02%1A%C8%15V%BB%004p%92%40%C189%28%820%02%80%05%80%814%A0%02%2A%F8%00%18X%80%1C%18%80%EFK%01%04pB%3F%90%00%3F%F8%03%0BH%01%ED%AB%1C%00%20%8250%84%04X%02E%90%813%00%C3%FC%BB%801%94%80%3D%D0%C2%25%28%03%04p%9C%8A%BA0%A2%CB%94%A2C%8E%1F%18%01%28%B8%BD%F6%D0%84%7E%A1%A8%0B%A3%80%27%D8%82%7Da%8Ej%B41.%10An%21%00%08%90D%C8%C2%01%12%A0%82%0B%F8%A8Fl%B5G%EC%88%E6S%02%04%A8%00%130%01%14%88%81%1C%20%BCM%88%013%F0D%40y%10%2C%28%02%15%D4%08%03X%00%3E%E0%03%EC%9B%80%09X%00%CB%B9%83%03H%81%09%C8%BE%FC%5B%1F%FF%00%B0%03%C3%2B%04%5E%99%C5.H%00%1F%D0%825X%003%7C%04-%9C%04%91%5C%83%04%08%04%28%DCEw%09G%1Bj%1D%0E%E0%40%84z%00%08%80%82%A7%81%99%9FB%BA%1B%18D%EF%21%80%0D%10%C4%7FB%C7%E1z%B8x%94%C7%C5%82%0A%03%60%80%0F%B8%00%FD%10%81%1C8%01%CB%D1%08%11%B8%81%CC%10%94%19%F8%13%02%98%01%97%99%25Z%DA%80%1DX%B5%03Z%00%87l%C8%87%8C%C8%03P%92%26h%825%C0%03%09%A8%00%2F%C3%03%3C%60%8C%0A%80%038%28%84%8C%94%01-%0C%04%0B%60I%3B%20%82%FA%93%80%25%D0%3F%00%F0%01%3F%88%02%08%BC%A1%9B%DA%02%9A%94%26%0Ax%031%10%03%12x%00Bl%0F%0D%08%01%23%D0A%EF%D1%89Y%29%1CPt%C7o%3AJ%A4%04%0CSZ%015Z%20%8Ep%8B1x%88%19%F0%82%00%F0%02%1B%98%B6f%F3%82n%D1%0B7X%98%1D%40%00%A3%EC%B2%01%40%00%04%E8%15%030%80%FF%298%00%25%E0%3E%09%88%825%A8%00%02%0B%CE%DC%A8%00%09%20%B0%5E%01%00%C2%CC%BFa%94%3C-%A0E%DC%B2%83%CE%81%C9%9F%C4%800h%CC%D0%C1%00%3C%A3C%F0%F4%89%0C%A0%80%1D%F8-%DCCO%9E%C0%0B%1C%D0Jw%0A%82%08%D8M%D1%9C%AFy%E4M%5E%E9%22%7D%84%811%F8%93ls%00mK%03%1A%08%00%1B%98%91%0D%40%032%98%82%22%B8%83%3B%18%9B%A4%F4M%8E%E8%80%ED3N%09H%00%22%F8%88%0A%C0%BC%C3%F3%83%01%F0%83%04%98%BC%C8K%00%E5%DC%A2%3F%A8%80%28H%A2%9F%B0%89%DB%CB%00%2B%F0%21%CA%BC%B6n%F1F%40%C0%C6v%02%01%A4a%81%CEk%88%D6%81%82%2B%10%C8%D2i%A3%1B%20%2F%0D%E8%81%2B%A0%92%19%D94%E5%7BGp%AA%CFP%BA%CF%A98%00%01%899%0F%D0%84%19h%B6%05%08%00%078%00%2C%D5%D2%29-%8D%2C%00%02%14p%CA%18%F8%80%DF%E3%88%238%02%E59%25%15%98%02%FF%22t%00%22%20%82%3A%98%84.r%84D%28%83%0F0%842%28%83K%90%01%3B%B0%005P%9F%0A%08%D4%40%8D%025P%9E%21%20%02%27%D8%00%C5%21%11%02%C86h%B3%02ik6D%D0%04%2B%C89nk%08%A5%9A%C9%87%19%01%0E%E0N%F6P%2A6%00%26%DC%83%80%2F%F8%82%11%C8%024%F0%B4%11%D1%80%118%80%0E%40%01%03bR%27K%CA%BF%D8%AD%0A%A8%022%E8%80%29H%83%00%A8R%2B%DDR%60%856G%95%89%B4X%8B%EA%13%095%EB%00%13%C890%28%03%118V%8D%E8%03C%08%00%98%AB%D6%98%23%92%21%80%B2%3FX%02w%01%81%1F%28%3A%9B%F3%81%9B%AB%BCl%0B%00%19%C8%C2%25%08%80%F7%DB%3C%01%F8%81%87%F2%D4%E5x%00%40x%83%F2%EC%89%BB%20%81-%90%02n%99%01%1B%B0%01%AE%BC%09%10%C0%029%F8%02Y%C9%02%3D%E1%80%260%014%F3GYe-%27%7D%89%23%C0%BE%05%A8%82%03%18B%FF%02%0D%00%87%5C%00W%AD%82%2A%C8RnsMg%03%81%20%A0%81%E0%04%89%01%88%01%11%40%B0%22%40%80%1A%08%01M%88M%7F%9D%81%19%60Q%2B%98%D7D%C8%83%0A%10%01%008%84D%80%02%0F%88%26%01%B8%B6%84%90%045%808%DC%C9%83%3Dh%A8X%B1%BC%04%40%02u%1D%C3%04%80%03qE%02%098%03%AB%FD%C8%FA%E36%8C%C3I%83%E0%B0%0C%60%28%F0%B4%29%08%08%01%400Gl%FB%D8iC9n%E3W%D9t%B6i%EBW%9C%202%05%28%40%E2l%1E.%29%9B%86uXZm%8C%05%98%020%B0X%1B%08%80%16x%C8%16P%81%03p%01%C3%E56%2CM%00%17%E06%0A0%024%FD%08%00%E8%80%3A%BA%013%08%81%861G%1D%BD%A9N%E9%B3FHZ%892O%E7%F0%01%A3%B5%00%11%40%82%3C%D0%91C%08%80.%90%80%2F%EC%02%22p%DA%00%B0%B9%8F%ECB%09%88%03%C6%15%D7%84%8031%D0I%14%FB%81%FF%C8%84%D7l%AC%9B%3C%CA%8B%10%88%13%9E%02YK%DD6%21%10%82m%2BP%88%C8%AA%20%E0%A3%08%40%81%14%E8%C7%25%CD%5B%2FzX%A8%98%82%04%B0%84%BF%05%D0f%3B%00%07X%80%0Ep%81%03%10%82%C4%CD%B6%87L%80f%5B%8F%B0%EA%22%06%A0%9B1%00A%87%B1%8B%84%90%C3%E3P%1C%CA%60%01pq%A4%04%22%97%0F8%84%0B%40%82Cx%84%CA%93%80.%E8%3F%C5%B3%83%DAm%5D%17%D0%3E%19%D8%3C%EC%80%009%A3Q%BB%10%80%A9%C2%60L%C5%00%170%A8%3C%FA%DA%27%D0%CC%86%A8R%07%D8%BE%04%A8H%8AM%00%00%15%02%EE%D3%3E%EEk%01%02%85%08%05%80%00%0E%D0%83%FC%E8%12%A9%848%EDm%D2%BD%FD%8B6h%02%2Bu%80Wm%B6%0E%D0R%8F%F5%D8lK%83%09%00%D0%29%1D%033X%B9.%12%02S%FBDv%3B%98%0Cp%02%22%80%B8%02%03%00%5B%DCE-%40%80%D7%95%81%05%90%01%FC%3B%03%FEs%81%FF2%BE%DD%09%B6%82%868%0C1%A8W%88%C8%00%10%10%9FIj%D7-%20%AC%C9%84%14Q%F1Q%2A%5D%DC%03H%E1%03Xa%00E%8165%C26%1DP%E8%BD%92%0D0%02%1CN%81%17%A8%98%EC%ED%E1%ED%FDa%AD%18%84%05%28%82%21V%81%09h6%17%90%E1%24%E6%D2r%7DM%BC%D8%AB%0A%C0%DB%8D%A0%02%F9%D8%A9%F3%8C%82%14%E0%E1%B8%EA%C8.X%83GX%00%A7%BD%D2f%DB%BF%FEc%DC.8%838%A0%60%EC%80%82%3D%A6%0F%0C%0B%01%0E%7C%80%A9%DA%80%AA%1A%C0%EC%80%82%F7%84%08%13Fa%15%0Ed%F1%9D%00%14%DE%3E%8A%7DM%9C%20%003%98%80%08H%81%15X%0CJ%1E%CD%02%21%83%05p%5E%2BU%01%16%0E%00%21H%00%14%00V%25%A6R%7Fe%88%1A%26%20T6%1E%BA%D1%5C%1Bz%00%28%F8%B0%A90%00%5E%5C%02%F7%0D%00%5C%B6R8%D0%3E%17H%E8%09%0E%00%12A%E69%7B%12o%C4%00%1A%3D%CF%FF%D6%0B%95%AB%B2%B3%0D%10%03%E4eM%60U%01%1A%10%82N%26%DFf%AB%C8%8A%84%D2%F5%ED%96%06%90%03%02%8A%81p%86%E5q%F6%E1%02%11%01%95%7B6vvH%23Fg%01%0D%00%1A%90%E1Ev%88%B9%E5%83%0B8H%8D%80%01F%E8%DA%9D%EA%A1-X%82IN%20-H%00%FE%FBUg%5B%80%83%FE%C5%0F%FDK%0AV%AE%27%A0C%FAP%B4%10%40%29%87x%80-%08%01%16%08k%9Ad%01%0E%08%01%1F5%E1%F4%0Dd%8Au%E1j%3EBD%AE%C8Km%08%1D%20%01%20%A0%01%96%16%E7%97%0E%3EK%96%0A%1A%08%82%0F%98M%99%5D%DErU%DE%9En%88%B9%9D%80%14%D0%EB%8DX%81E0%EA%9D%C2%8B-h%84%3Fh%9F%3Eh%EA%08F%E31%7Cj%82n%DD%0F%95%E0%C6%F5%80%89%92%40%06%AC%BBD%933fN%11%0A%F8%14%D3%C6%A1Gkm%B5%06%E9%04%E0d%B7n6%17N%00%C3%DB%3E%9E%BE%12%08%00%82%FF%29%B8%00%1Bt%E9%BD%FE%22%EEU%1E%5E%29%8260%83b%9A%E7%12%EEW%7F%15%80%7E%E5%CA%AD%7CY%B8%AD%0C%10%E0%83%C5%F6%08%1A%28%EA%CA%3C%1C%B3%5E%82%21%00%A5%01%10%01A%20%82D%C8%C2%0B%08%00%3B%E8%3F%FEK%D7%2F%7CSa%F4%813X%80t%F5%00%08%10%03%1E9%CCaN%B40p%1Ci%F4%A1%1F%A8%03%12%60%016x%E8%9F%20%004%D0%A6%12%DER%1A%E8%80%05H%80%0E%A0%01%91%1Ei%C6%8D%E0%09%E0U%02%BD%28%23x0%18%10%EA%E1%D6%DB%BF8%81%0B8%02%25%28%23%0Ax%2C%86%F8W%2AU%F1%F6%60qi%92%020e%93%8B%01%82%1E%F0%EA%9D%EA%A7%3E-%165X%02%3C%D0%026%B8Fg%B3%B9%E5%B5T0%C0%00%27%88%02tI%04%03%D7%0C%B3%3E%5EO%21%01%40%F0N1%60U%CE%BA%26%160%02%E3%E3%E8%2C%B5%E6%E6M%E1%8Ee%E7%EDC%01%87D%01uV%01n%93%82%FF%B0%19%1B%86%FDp%FB%ECk%8F%C8%81%16%08%82%1EP%1A%A59%5B%0F%88%CDgs%88ruT%C5%85%B6%F6%08%B2%24%40%C7%1A%88%006%A1%DCb%FA%2F%05x%00u%1B%9D%1C%D5Q%0A%D0%02A%F8%BD%0A%D8%021%00%81%C4%A1%B3%83X%0F%9A%85T%2BxQ%0FhQH%CD%80lC%04%10p%81%25%18%92%0F%90o%89f%8E%0C%20%81%27%08%01%9E%F1%AEoK7%0E%B0%F4%BAhu%82%F8%93%2A%DDi%5Ci%B64P%01%17%E0UC%B6mt%1Ev%1B%10%80%FB%B0%1D6%27%E7%AA%18%00%17x%A2I%CA%B6wn6%14xg%7F%5D%E2i%DF6%DA%04%B8Nk%03%15%20%03%07%60%A9%8D%D8%01%0Dh%99%AC%DB%0B%D4%D3%11%CE%C2%94%EE%9E%11%10%40%80%0B%08o%8E%A8%83K%99%C6%8A%CA%B8%D8%C5%C2%CCa%83%CE%0A4%CF%11%BDL%01%AA-%E8%F7%9F%A0-%A4j%08%E5%E5%B6QV%DC%D7%D4%B6%C2%96%02B%C0%E1%FFX%5D%F6Y%D5%0A%13%20%04%7B%0D%804H%00%19%C6%D8Nv6%26n%814%D0X%8DM%80%29%08%80V%05%F7%22Xy%13%B8%80%89%CF%88%1Bx%B1%FD%22%81%1A%40%00%3F%F0%83%40%0D%84FP%3D%12%F8%B9%E5P%AA8%10w%8DP%83%3Cl%F4%9B%C0%B08%40%022%5D%8C%3C%60C%9D%3A%18%DFm%88a%AA%AEn%29%3Am%D1%81%B0%CB%23%E7%FE%D7%D8%BC%F3%15%DF%CA%E8%5E%F1%84%A9%81%22%10%81%7B%A6x%89ss%8E%40%00%F0%BA%09_%15%F3%B2%84%D2%09xgk%9E%00%15%20yjN%03%3D%A0%01%7D%7CJ%18%88d7i%82%13%93%9A%1A%C0G%A7%14%81%0F%E09%3B%C8%03-%B0%14T%23%00%09%00%1A7%E9%C3%C3%F9%DA%3C%B0%C2%A7h%84%0DX%03%24%92%99%AF%8D%A8%0C%80%02%03%AC%83%27%00%AALa%811%D0%93%0A3%9C%06%20%84A%88%80%D0D%FB%8A%B7%0A%20%F0%89%C0%05i%82.ik%06%D0%FF%8DO%00%1A%20K%B2%84%F0%18%B8%00%14HY%0B%60%00%B6%98J3%95%83%A8%C7%89%20%E8%80%F9%F4%01%0B%18%02%EA%FF%03%1F%18%60Ex%A6%9D%04%01%C1%BC%80%EE%D8%88Bx%26%A3%FF%89%1F%98%83%1F%D4%888%80%02%04%D8%83%3A%80%02DK%29z%C7%006%80%80%7E%01%A2%20z%03%098%806%B8%82%F7%07%08M%02%07%12%2ChP%60%09%23%15D00%B0%E9%21%C4%88%12%27R%ACh%F1%22%C6%8C%1A7r%EC%E81b%01%00%1F%1F%0E%B8%21%E0%A0%A6%19%01hTi%B1%20%C0%84%09%01%0E8%08%10%C0%C5%84%031%17L%20S%01%40%8C%1C%0C%06%0C0%60%B4%E2%0A%20%844%14%24%A0I%83%06%10m%98D%10q%02%40Q%A3D%01X%28s%86%04A%02%02%C6%FE%28%BB%01%8A%8B3%15%90%88%18%E2%10%E2%1C%0A%28%E7%D2%15%98%21%C4%99%01%10%0B%C1%29t%E1%02%829L%EB%D2%CD%F0%06%8A%84%0DN%09%FFh%80%40%81%C2%16%27%5C%5C%F4%C0A%F8%B2%A6%06FL%BC%D0%3B%F23%E8%D0%A2G%93%86%18%F23%80%1D%27Qz%B1%99f%81%03%15%2A%1CT%A9%29%A4J%82%03%3C%17PI%E0%00%C0%8A%86%1A%07%AC8%40%C2%A9%40%02%1B0p%09%02%A4H%84%18%16%3CK40%00%80%08%17%AB%05hxL%81%C4%16%17s%96%14R%C3vz%C4%25PVc%C6%9C%81C%1E%CF%03%16%1A%F2%F1%A1%11%94%1F%ED%0F%3E%80CB%FF%40be%E0%04%04%04%800%D8%5Cc%21W%97%02F%A0p%C2%5B%A5I8%21%85%15vt%DAH9p%A1%C0%5C%1E%C0%D4%C1%02%07%24%90%9Bo%2B%A9%A0S%88%21%FAv%5D%84%17%0D%10%83%03-%84%C0%DE%06%1C%A8%D0A%04%11%A0%20%C2%0A%00%B4X%1D%00Nh%20%16%08o%B8%10%85%16%8D%10%21%08%12H%5C%20%82%0FC%14%05%D1%00ph%C0%DE%7Eu%11%90A%1DH84%80%0F%86X%00%FF%C0%10%3E%B0%91%E5A%10%609%90%00uhB%80%02%0B%1A%24%96c%10%5C%26%40%10%28%08g%21%9F%7D%FA%29%21%86%1E%0DP%C4%15%1C%1E%A4R%00%09tPSL%2B%A5%01%D3%01%91%A6%28%E2%11G%0D%D7A%1B8%24%B1%9A%06%5CL%A0c%0C%22%BCp%C2%94..1%82%00%20%CC%81%00%93N%5Ep%88%08eX0%04V%12%0D%90%07%07%1B%A0%89%19%04Q%88tk%20%814I%04%1Bk%A2i%EC%40N%10%26%00%0E%18%EC%A0B%0D%20%10F%80%1B%1D0%F0%27%B6%D9j%ABQ%A0%1D%01%D0%01%08%C8jb%03L%09%D8%04%93L%E7%B6%90%D3%04T%A4%00%DB%045t4%C0%05%3Cd1%C2%06%0D%E0%40B%0D%D1%E5%60%C1P%3FVd%80%1AohR%87%B0O%96%E1%C3%AC%00%60%25%F0C%00%E4%B1%C5%03%BB%DA%F5%00%0B%0FdP%D0%0Fg%FCQT%19%81%28%B2F%02%91%8C%A0q%06%E2%D2%E5%D4%06%1B%FC%20%FF%A7%26%3Fp0%96%02%C8%0A%40%81%1B5L%11A%11.%D8Y%17%05J%EC%B9-%D1Ec%DB-G%0C%20%40%C1j8%E0%20%96S%2A%D1%D0A%00%1E%0A%21D%006%90%9B%C6%14-%A4%F1%01%00-Dj%82%A0%270%D1%C2%0D%20Pp%40t%2F%D4%DA%91%0F%1C%84%B0%07%5B%7D%D0J%14%C4%12%01%D0G%17%2F%1F%CB%02%04Pp%00%05%04%19%2F%B6%C1%19%3E%FCJ%E6%10CT%10%18%1Bo%60%C0B%CAVX%B1%A6S%02%5C%3E%D6%06%80%B0%D1E%1D%1CT%3C%90%06%1C%B0%A0%09%04%0AH%D1%40A%20%00%D1%82%8E%29%7C%60%C2%01%86%CE%85C%0D%E8%19%BD%3B%EF%13%22m%11%00%2F%40D%83%11c%BC%B9%01%215%04%E1%86b%9A%B4f%13%A2%D0k%E2a%000%7C%00%C3%C3%23%0D%C0%40%0E1tP%83%1BJ%A0%90%83%8F%1F%F90G%97%29L%97%F7%C0C%E0%81%81%CA%28e%40%C1%1CZ%C4%91G%1EQ%84q%FF%F2%03%0F%F4j%88H%0C%E0%B0%3F%F4%A1%0C%22%40%82%1A%0A%B1%84%0A%5C%02%0C%E7%F2%80%07%AC%90%01%0FQ%2F%00V%10%C4%12%B4%D0%2A%5C%8DN9%91%03%02%158%C0%05%0C%94%E0%24%04%80%40%10%1C%10%81%0B%C0%E0%04%0CX%C1%04%C25%17%C6%18%21%05%22%E9%1D%0Es8%92%DFU%04%06%08p%40%0C%2A%A0%04%0C%D4l%03f%80%5D%04%8Ep%03%1D%08d%0668%89%13m0%03%81p%28%04%1DH%C1%05t%A7%3D%AE%E4%E0%025%A0%03C%D8w%91%3F%2C%F0%02%8A%1B%C9%10%12%C1%B4%F6%88e%04%E3%29%84%AB%2A%10%07%0E%BC%21%04%23%A8%C4%19%00%F8%10%EB8l%08%16%F0%81%E2%00%F0%87%0F%5C%22%0Eq8%D7%B9%7C%60%938%88%C0%10%29%A0%D5%10%1A%29%828%BC%E1t%02%80%C0%1A%CEp%07%138%80%09J%E0%C2X4%E0%86AT%A5Gz%B1%00%1D%E0%17%96%06%94%A0%04%0D%60%16%21%7E%A3%C3Y%D2%12%FF%23%3C%A4%88%08%9A%80%05%1E%18%01%04%3ApJ%16%80%A0%23%11%D0%00%08%C6%A3%0B%87%E4%A0%84%15%BAM%8C%19%E1%E3%0Ab%90%02%B7%7DF%80%0B%2B%9F%F9%CE%C0%02%98%CDP%13%24p%C2Z%1C%B1%B0%3E%D8%C7%11yP%C4%1Ed%80%842%DC%D0V%D7%19%00%1E%24%20%01%92%25%40%9E%128C%00%7C%90%808%10a%01ZH%A7%0C%880%00Z%01%A0%0C%7B%08A%CA%28%20%01%BF%88%00%06%22%88%00%0F%DCp%B3%20Te%7D%C1%13%82%0B%98%C8%26%05%8C%00%0B_%20%C1%06%A4%80%81%09%0C%AD%96%26%AD%E5-%27r%01%2A%80a%0CI%E0%90%00B%D0%2F%14t%20%08M%B8A%12%E8%C2%1De%AE%90%7C%CE%E4%C8%F6%00F%9D%8F%0C%F4m%E6%D3%C26%2FC%80%07%60%80%0Dv%B8%C0%07%C6%B4%15%3F%96%01%8BH8D%1F%86J%91%14T%C0%0F%7E%E8%C2x%88%40%04%3B%D8%C4%0E%8A%80C%14d%A0V%19%F8%C1%28%06%FF%B8%80%13%D8%00%82%94%8D%40%0B%17%10%0A%00%5E%08%032%90%80%02-%B8%C0%0A%06p%82%180%21%08W%D0%A8%40%04%B0%01%9CL%C1%01%08%E0%25%07%3A%A0%C5%93R6%87%29%95H%0Cx%20%85%D54%A0%07A0%C1%05%1C%60%04%05%40%85%9B%04i%C0%08%3E%7B%81fN%88%28%A5%1A%89u%7E%0A%BCD%8C%E0t%2B%FB%01%068P%07%22%5C%A0%0CR%8A%10%1F%FF%C80l%5E%04%01%F5%1C%D1%1A%E0%20%010%80A%06%8A%D0B%02%16%20%DD%05H%60%8FH%88%04%05%CAr3%27%14%82%21Y%D9%5E%04%82%40%01%1E%7C%80%01%26%98%40%10B%F0J%82%5C%D2%08%1D0%01%0A%FE25%26HG%AB%95%BDo%D1.%1B%91%13%F0%81%04%25%D4%84%0E%18A%87%F1%11%AA%04%CB%DA%00%1A%3ApW%E2%E2%F7%23%04c%C3RQB%80%1F%00b%0E%8A%A8%00%1C%0D%21%25%8AX%E7%9D%B2E%80%04%40%2C%01%19ta%0Dex%C4%FF%21%E0%10%883%B0%15%9C%D5%DD%C4%10%10p2%A7d%00%03u0%E3P%0D%00%83%16%7C%E1%080p%00%10z0%06%DB%25%27%A6%3C%F0%D7%0AV%F0%02%87%B6%D0%BE%0D%7E%B2%9F%F4%0B%11%03%98%A0%09%21%D8%80%00J%10%02%04%94%F7%0E%210p%5Dt%40%02%F1%C1%A0%A4P%F6%D6%C4%7E0%BA%00%29v%0B%5DP%83%20%5E%E5%5B%27%7F%06%01Q%A8%40%14%9C%E0%07%19H%C0%07%EBT%83%0C%A6%3B%DD%EA%A6%60%0B%B6%15%0B%14%10%D0%96%1F%0D%A0%03%2A%A0%81%038%C0%CA%83%28%00%04%9F%8D%81%29%B7%E2B%3A%9F%B9%D3%80j%27F%04%28%02%25%DC%00%02%3A%18%01%15%3A3%05%12%B0%8E.%0D%80%80%12%1C%60%15N%7B%DA%22%03%F8%03%028%00%81%94%25%F6f%1C%60%15%5B%7C%F0%07%A3%92%26%9E%D1%5D%40%3D%B50%801%9Da%09%E3A%00%02%96P%5D%00%2C%E1Jv%81%C0%19%D4%C0%CE%89%18%80%01%C4%E4%00%0E%88%FF%CC%26%0D%98%01%01%AB%25n%87e%5B%EBu_%08%D4%18%D9%1E%0C%F4%60%861%40%80%BC%03%A0C%0FZ-%96%9A%B1%B7%01f%98%00%0A%A8%C9%EEy%F5%21%11l%C0%97%00R%06%08%17%C4%C1Is%7E%AD%84%8C%3B%879%24%00%01Y%25J%02%96%90%008l%7CDj%F0Am%13%FB%00%28D%C1%11Y%A5%08q%26%10%82%9C%1E%04%B5M0Ay%D5%3D%F0%99%7BD%CA%11%D9%5E%11%CC%D02%0E%14%81%01%13%C0%00%EBn%86%01%0Ch%A0%D5%0A%28A%16Z%40%03%EF%D2%7C%23%02%EC%83%1D%A2%B0%85%11P%00%0As%F0%03%12R%D0%07%06K%08%0F%F5L%01%B4S%10%85%04D%C1%00%5E%7FW%05%1E%07%07-%18%C2%10%0F%10%C0%960%E0%82%3D%D8%E1%03n%E1p%11ffB%02%F0%9D%EF%95%DE%01%10%03%DB%F4%C1%97%C6%E6%111%80%05%06%C1%01H%08%B3%03X%18%8B%26%C6p%03%25%20%00%08%148%BA%00vY%04%FFL%D3%9A%F0S%E6%8A%08%88p%06%274%C2%3C%22%18%93%CC%3D%C2%D5%87%E0%01%0F%0FY%BD%1A%5C%0F%11%09%CCaV%92%F8%81%06H0%F7%0A%A8%01%C7%16%11%02%096%D5%00%E5%40%00%02%BA%02%81%0B%10%10%01%EC%A5%DE%F3%CE%07%89%BB%87%C3%80%0F%E4%A8%03%A5%E6%90%02%20%D0%82%0AD%40%09W%18%03%08v0%01%1A%2C%B8%F94%B7N%99%B0x%01q%FE%F6%A4%06%28D%14%BA%10%07%3B%F4%3E%05%8A%CB%9B%08%F8%F04%10%60%80%047p%8E%0A4%81%12%BC%D7%AC%3D%9F%01%D6%5C%F4%3D%13p%5C%00%13%DC%C0%06p%08%014%C0%06P%C1%F2E%40%0D%EC%00%10P%C5%5D%0D%C5%01f%C4u%94%C9pu%1E%EF%0C%D4%07%5C%80%93%40%09%AD%B0%CF%05%B4%40G%B5%80%0A%0C%02t%E4%88%8E%EC%C8du%A0%0DV%84%E1U%C7%07L%C0%0EP%40c%F4%80%1C%98%81%0B%F0A%0C%B8P%27q%DF4%11%DB%0DV%84kA%FF%9CI%A1%1F%20%D9%8D%12r%5B%8C%08A%8E%C4W%0A%88%C0%F5%E4%C0%A8p%DD%12%7E%E1%26%E4%60D%00%00%1DT%C1%0E%F0%00%10%A8%80%1Et%00%0D%D0%80%09%60%CF%F6%7C%40%0CD%95%17%82%E1%C0%5DG%F6%3C%13%03%C0%C0%05%CC%E1%BF0%00%038%0CV8%A1%1D.%A1%18B%84%05%2C%DF%0B%C4%00%0A%E8%C8_%84%0Az%04U%C0%14%22%25%DA%CA%09%BC%00%C0%E4a%25n%E2%21R%C9%09%E4%C0%07%5C%CF%0B%AC%80%05%B8P%F9%A4%DB%26%A6%E2%26t%18%21%AA%A2%1Dv%22I%E4U%20%3A%0C%DE%98%9F%2B%DE%22.%1A%20%2C%AE%A2%A5%E4%A2%2F%FE%A2%2B%EE%220%0E%231V%A20%16%232%26%E3%01%1E%A326%A33%0E%1C3%3E%A34N%23%7EE%235%5E%236ZV%02f%237vceY%A37%86%A38%FA%CE6%8E%A39%9E%E3%D1%94%23%3A%AE%23%3B%16%9E%3A%B6%23%3C%C6c%BB%C9%23%3D%D6%23%02%DA%7F%23%3E%E6%23%0E%BE%A3%3E%F6%23%3B%82%A3%3F%06d3%02%A4%40%16%241%12%A4A%26d.%22%A4B6d%2A2%A4CF%E4%2B%F2%A3DV%E40B%A4Ef%E4%F3a%A4Fvd%D3q%A4G%86%E4%BA%81%A4H%96%24%94%91%A4I%A6%E47R%A4J%B6%A4%E7%A1%A4K%C6%24%0E%C1%A4L%D6d%7E%B1%A4M%E6%E4I%E2%A4N%F6%E4J%FA%24P%B2%1BM%06%25Q%8E%C6P%16%25R%EE%10%00%04%04%00%3B' alt="全国" width="403" height="324" border="0" usemap="#Map9" id="innermapBox">
+
+
+<map name="Map9">
+ <area shape="poly" coords="228,236,250,213,243,204,275,155,278,120,311,102,335,74,357,80,377,119,382,143,346,148,333,163,316,153,304,173,300,207,260,252,225,235" href="javascript:void(0);" onfocus="mapChange(1);" alt="北海é“・æ±åŒ—エリア" onclick="mapChange(1);">
+ <area shape="poly" coords="212,264,211,243,225,239,257,254,243,265,242,278,229,289,209,278,211,264" href="javascript:void(0);" onfocus="mapChange(2);" alt="é–¢æ±ã‚¨ãƒªã‚¢" onclick="mapChange(2);">
+ <area shape="poly" coords="207,261,205,240,222,236,244,213,237,207,179,215,154,235,175,239,189,232,189,244,185,259" href="javascript:void(0);" onfocus="mapChange(3);" alt="甲信越・北陸エリア" onclick="mapChange(3);">
+ <area shape="poly" coords="163,241,180,242,186,238,182,252,183,261,202,264,205,267,205,284,164,274,161,241" href="javascript:void(0);" onfocus="mapChange(4);" alt="æ±æµ·ã‚¨ãƒªã‚¢" onclick="mapChange(4);">
+ <area shape="poly" coords="129,228,145,231,152,241,157,241,160,275,151,276,134,282,123,277,119,266,127,260,126,247,119,244,126,227" href="javascript:void(0);" onfocus="mapChange(5);" alt="近畿エリア" onclick="mapChange(5);">
+ <area shape="poly" coords="56,225,101,214,123,227,114,244,118,259,114,270,86,266,76,272" href="javascript:void(0);" onfocus="mapChange(6);" onclick="mapChange(6);">
+ <area shape="poly" coords="54,231,65,268,24,292,9,282,27,255,17,241,23,230,53,231" href="javascript:void(0);" onfocus="mapChange(7);" alt="ä¹å·žãƒ»æ²–縄エリア" onclick="mapChange(7);">
+</map>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 448860 **/
+
+function mapChange(mapNo){
+ $('innermapBox').style.display = 'none';
+}
+
+function focusarea() {
+ synthesizeKey("VK_TAB", {});
+ synthesizeKey("VK_TAB", {});
+ synthesizeKey("VK_TAB", {});
+ synthesizeKey("VK_TAB", {});
+ synthesizeKey("VK_TAB", {});
+ ok(true, "we did not crash");
+ SimpleTest.finish();
+}
+function delayed_focusarea() {
+ setTimeout(focusarea,100);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+addLoadEvent(delayed_focusarea);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug448987.html b/layout/generic/test/test_bug448987.html
new file mode 100644
index 000000000..feb0456c6
--- /dev/null
+++ b/layout/generic/test/test_bug448987.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=448987
+-->
+<head>
+ <title>Test for Bug 448987</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ iframe {
+ width: 500px;
+ height: 300px;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448987">Mozilla Bug 448987</a>
+<p id="display"></p>
+<div id="content">
+<iframe id="f1"></iframe>
+<iframe id="f2"></iframe>
+<iframe id="f3"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 448987 **/
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.setIntPref("accessibility.tabfocus", 7);
+
+var f1 = document.getElementById("f1");
+var f2 = document.getElementById("f2");
+var f3 = document.getElementById("f3");
+
+var snapshotf1;
+
+SimpleTest.waitForFocus(function() {
+ f1.src = "file_bug448987.html";
+});
+
+function firstIframeLoaded() {
+ snapshotf1 = snapshotWindow(f1.contentWindow);
+ f2.src="file_bug448987_ref.html";
+}
+
+function secondIframeLoaded() {
+ ok(compareSnapshots(snapshotf1,
+ snapshotWindow(f2.contentWindow), true)[0],
+ "<area shape=default> should render focus outline");
+ f3.src="file_bug448987_notref.html";
+}
+
+function thirdIframeLoaded() {
+ ok(compareSnapshots(snapshotf1,
+ snapshotWindow(f3.contentWindow), false)[0],
+ "file_bug448987.html should render focus outline, file_bug448987_notref.html should not");
+ finish();
+}
+
+function finish()
+{
+ SpecialPowers.clearUserPref("accessibility.tabfocus");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug449653.html b/layout/generic/test/test_bug449653.html
new file mode 100644
index 000000000..ff92c7ee5
--- /dev/null
+++ b/layout/generic/test/test_bug449653.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=449653
+-->
+<head>
+ <title>Test for Bug 449653</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ iframe {
+ width: 500px;
+ height: 300px;
+ }
+ </style>
+</head>
+<body onload="doTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=449653">Mozilla Bug 449653</a>
+<p id="display"></p>
+<div id="content">
+<iframe src="file_bug449653_1.html" id="f1"></iframe>
+<iframe id="f2" src="file_bug449653_1_ref.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 449653 **/
+SimpleTest.waitForExplicitFinish();
+
+var f1 = document.getElementById("f1");
+var f2 = document.getElementById("f2");
+
+function doTest() {
+ ok(compareSnapshots(snapshotWindow(f1.contentWindow),
+ snapshotWindow(f2.contentWindow), true)[0],
+ "No red should be shown");
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug460532.html b/layout/generic/test/test_bug460532.html
new file mode 100644
index 000000000..dc0ff7a00
--- /dev/null
+++ b/layout/generic/test/test_bug460532.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=460532
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Test for bug 460532</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=460532">Mozilla Bug 460532</a>
+<p id="display"></p>
+<div id="content" style="display: block">
+lorem ipsum.
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function select_460532(){
+ var content = document.getElementById('content');
+ var node = content.firstChild;
+
+ var range = document.createRange();
+ range.setStart(node, 0);
+ range.setEnd(node, 5);
+ window.getSelection().addRange(range);
+
+ range = document.createRange();
+ range.setStart(node, 0);
+ range.setEnd(node, 6);
+ window.getSelection().addRange(range);
+}
+
+function click_460532(){
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+ var content = document.getElementById('content');
+ var rect = content.getBoundingClientRect();
+ wu.sendMouseEvent('mousedown', rect.left+5, rect.top+5, 0, 1, 0);
+ wu.sendMouseEvent('mouseup', rect.left+5, rect.top+5, 0, 1, 0);
+ ok(true, "pass");
+ SimpleTest.finish();
+}
+
+function boom_460532() {
+ select_460532();
+ setTimeout(click_460532, 100);
+}
+
+addLoadEvent(boom_460532);
+SimpleTest.waitForExplicitFinish()
+SimpleTest.requestFlakyTimeout("untriaged");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug468167.html b/layout/generic/test/test_bug468167.html
new file mode 100644
index 000000000..2f8cf7c7e
--- /dev/null
+++ b/layout/generic/test/test_bug468167.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=468167
+-->
+<head>
+ <title>Test for Bug 468167</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=468167">Mozilla Bug 468167</a>
+<p id="display">
+<textarea id="ta" rows="10" cols="80"></textarea>
+</p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 468167 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests()
+{
+ var ta = document.getElementById("ta");
+ ta.focus();
+ is(ta.scrollTop, 0, "initially scrolled to top");
+ var s = "";
+ for (var i = 0; i < 40; ++i) {
+ // three characters per line
+ if (i < 10)
+ s += "0";
+ s += i + "\n";
+ }
+ ta.value = s;
+ is(ta.scrollTop, 0, "scrolled to top after adding content");
+ ta.scrollTop = 9999; // scroll down as far as we can
+ isnot(ta.scrollTop, 0, "scrolled down after scrolling down");
+
+ ta.setSelectionRange(0, 99); // 33 lines out of 40
+
+ // Send a backspace key event to delete the selection
+ synthesizeKey("VK_BACK_SPACE", { });
+
+ is(ta.scrollTop, 0, "scrolled to top after deleting selection");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug469613.xul b/layout/generic/test/test_bug469613.xul
new file mode 100644
index 000000000..0310cfe8c
--- /dev/null
+++ b/layout/generic/test/test_bug469613.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=469613
+-->
+<window title="Mozilla Bug 469613"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469613">Mozilla Bug 469613</a>
+
+ <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<vbox style="height: 100px; overflow: auto;" id="scrollbox">
+ <hbox style="height: 200px;"/>
+</vbox>
+
+<script class="testbody" type="application/javascript;version=1.7"><![CDATA[
+
+/** Test for Bug 469613 **/
+
+function doTest() {
+ let scrollbox = document.getElementById("scrollbox");
+ scrollbox.scrollTop = 0;
+
+ // Make sure that the "scroll focus" is inside the scrollbox by moving the
+ // mouse in the scrollbox.
+ synthesizeMouse(scrollbox, 6, 6, { type: "mousemove" });
+ synthesizeMouse(scrollbox, 8, 8, { type: "mousemove" });
+
+ // Now scroll 10px down.
+ synthesizeWheel(scrollbox, 10, 10, { deltaY: 10.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL });
+
+ // Send a 0-delta scroll.
+ synthesizeWheel(scrollbox, 10, 10, { deltaY: 0.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL });
+
+ setTimeout(function() {
+ // Check if the 10px were scrolled.
+ todo(false, "Starting a 0-delta scroll shouldn't cancel a pending async scroll is disabled, see bug 752786");
+ //is(scrollbox.scrollTop, 10, "Starting a 0-delta scroll shouldn't cancel a pending async scroll.");
+
+ // Second test
+ scrollbox.scrollTop = 20;
+
+ // Start an async scroll to 30.
+ synthesizeWheel(scrollbox, 10, 10, { deltaY: 10.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL });
+
+ // Start a sync scroll to 30.
+ scrollbox.scrollTop = 30;
+
+ is(scrollbox.scrollTop, 30, "Setting scrollTop should have immediate effect, even if there was a pending async scroll to the same position.");
+
+
+ // Third test
+ scrollbox.scrollTop = 40;
+
+ // Start an async scroll to 50.
+ synthesizeWheel(scrollbox, 10, 10, { deltaY: 10.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL });
+
+ // Cancel the async scroll.
+ scrollbox.scrollTop = 40;
+
+ // Send a 0-delta scroll.
+ synthesizeWheel(scrollbox, 10, 10, { deltaY: 0.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL });
+
+ setTimeout(function() {
+ is(scrollbox.scrollTop, 40, "Canceling an async scroll should reset the point of reference for relative scrolls (mDestinationX/Y).");
+
+ SimpleTest.finish();
+ }, 0);
+ }, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(() => setTimeout(doTest, 0));
+
+]]></script>
+
+</window>
diff --git a/layout/generic/test/test_bug469774.xul b/layout/generic/test/test_bug469774.xul
new file mode 100644
index 000000000..d3cce0402
--- /dev/null
+++ b/layout/generic/test/test_bug469774.xul
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=469774
+-->
+<window title="Mozilla Bug 469774"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+<vbox height="50"/>
+
+<menupopup id="popup">
+ <textbox id="textbox"/>
+</menupopup>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469774">Mozilla Bug 469774</a>
+
+ <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<script class="testbody" type="application/javascript;version=1.7"><![CDATA[
+
+/** Test for Bug 469774 **/
+
+// Test whether menu popups are blocked from being painted in their parent window.
+
+// Like snapshotWindow, but with DRAWWINDOW_DRAW_CARET
+function snapShot() {
+ var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ canvas.setAttribute("width", 200);
+ canvas.setAttribute("height", 50);
+ var ctx = canvas.getContext("2d");
+ ctx.drawWindow(window, 0, 0, 200, 50, "transparent", ctx.DRAWWINDOW_DRAW_CARET);
+ return canvas;
+}
+
+function doTest() {
+ window.removeEventListener("focus", doTest, false);
+
+ var before = snapShot();
+
+ var popup = document.getElementById("popup");
+ popup.openPopup(null, "after_start", 0, 0, false, false);
+
+ popup.addEventListener("popupshown", function() {
+ var textbox = document.getElementById("textbox");
+ textbox.focus(); // show caret
+
+ var after = snapShot();
+
+ var equal, str1, str2;
+ [equal, str1, str2] = compareSnapshots(after, before, true);
+ ok(equal, "Showing a popup shouldn't affect drawing in its parent window",
+ "got " + str1 + " but expected " + str2);
+
+ popup.hidePopup();
+ SimpleTest.finish();
+ }, false);
+}
+
+SimpleTest.waitForExplicitFinish();
+window.addEventListener("focus", doTest, false);
+
+]]></script>
+
+</window>
diff --git a/layout/generic/test/test_bug470212.html b/layout/generic/test/test_bug470212.html
new file mode 100644
index 000000000..1025d89ef
--- /dev/null
+++ b/layout/generic/test/test_bug470212.html
@@ -0,0 +1,53 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=470212
+-->
+<head>
+ <title>Test for Bug 470212 - crash [@ nsContentUtils::ComparePoints]</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=470212">Mozilla Bug 470212</a>
+<div style="width: 200px;">
+<ca>
+<canvas style="border: 1px solid black;" id="dragSource"></canvas>
+</ca>
+</div>
+
+<pre id="test">
+<script>
+function doShiftDrag(){
+ setTimeout(function() {
+ var wu = SpecialPowers.DOMWindowUtils;
+ var canvas = document.getElementById("dragSource");
+ var canvasRect = canvas.getBoundingClientRect();
+
+ // Drag canvas element starts with a mouse down event, combine with shift
+ // key, follows by two mouse move events.
+
+ // Press on left-top corner of the canvas element.
+ wu.sendMouseEvent('mousedown', canvasRect.left, canvasRect.top, 0, 1, 4);
+ // Move to the center of this cavas element.
+ wu.sendMouseEvent('mousemove', canvasRect.left + (canvasRect.width / 2),
+ canvasRect.top + (canvasRect.height / 2), 0, 0, 4);
+ // move out of cavas's region.
+ wu.sendMouseEvent('mousemove', canvasRect.left + (canvasRect.width / 2),
+ canvasRect.bottom + 10, 0, 0, 4);
+
+ is(window.getSelection().rangeCount, 0, "rangeCount should be 0");
+
+ wu.sendMouseEvent('mouseup', canvasRect.left + (canvasRect.width / 2),
+ canvasRect.bottom + 10, 0, 0, 4);
+
+ SimpleTest.finish();
+ }, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doShiftDrag);
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug488417.html b/layout/generic/test/test_bug488417.html
new file mode 100644
index 000000000..d6b2d5d7c
--- /dev/null
+++ b/layout/generic/test/test_bug488417.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=488417
+-->
+<head>
+ <title>Test for Bug 488417</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=488417">Mozilla Bug 488417</a>
+<div id="display">
+ <table border="1">
+ <tr>
+ <td id="a">A1</td>
+ <td id="b">B1</td>
+ </tr>
+ <tr>
+ <td>A2</td>
+ <td>B2</td>
+ </tr>
+ </table>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+function clickIt(node) {
+ synthesizeMouse(node, node.getBoundingClientRect().width/2,
+ node.getBoundingClientRect().height/2,
+ { accelKey: 1 });
+}
+
+/** Test for Bug 488417 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ // Do the test async so we can unsuppress painting
+ SimpleTest.executeSoon(function() {
+ clickIt($("a"));
+ clickIt($("b"));
+ clickIt($("a"));
+ ok(1, "Got here");
+ // Clean up
+ window.getSelection().removeAllRanges();
+ SimpleTest.finish();
+ });
+});
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug496275.html b/layout/generic/test/test_bug496275.html
new file mode 100644
index 000000000..28cb146d7
--- /dev/null
+++ b/layout/generic/test/test_bug496275.html
@@ -0,0 +1,289 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=496275
+-->
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Test for Bug 496275</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onload="run()">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=496275">
+ Mozilla Bug 496275
+</a>
+<p id="display"></p>
+<div id="c" contenteditable="true">
+ <p id="p1">The first paragraph. Another sentence. Even more text.</p>
+ <p id="p2">Paragraph no. 2.</p>
+ <p id="p3">This paragraph<br>
+is broken up<br>
+into four<br>
+lines</p>
+</div>
+
+<div id="ltr" contenteditable="true">
+ <p id="l1">×לש Hello</p>
+ <p id="l2">Goodbye</p>
+</div>
+
+<div id="rtl" contenteditable="true" dir="rtl">
+ <p id="r1">×”×ª×¨×’×•× ×”×חרון שפיתחנו ×”×•× ×¢×‘×•×¨ Firefox 3.5.6.</p>
+ <p id="r2">קר×ו ×ת הערות ההפצה (×נגלית) להור×ות ורשימת בעיות ידועות.</p>
+</div>
+
+<!-- Special characters: لا is actually two characters, while تَ should be
+ treated as one character. -->
+<div id="special" contenteditable="true">
+ <p id="s1">a لا b تَ c</p>
+</div>
+
+<div>
+ <p>Anchor offset: <span id="anchor-offset"></span></p>
+ <p>Focus offset: <span id="focus-offset"></span></p>
+</div>
+
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+SimpleTest.requestFlakyTimeout("untriaged");
+
+function isOrIsParent(actual, expected, msg) {
+ // actual should be expected or actual's parent node should be expected.
+ msg += " Expected " + actual + " or " + actual.parentNode +
+ " to be " + expected + ".";
+
+ ok(actual == expected || actual.parentNode == expected, msg);
+}
+
+function isAt(anchorNode, anchorOffset, focusNode, focusOffset, msg) {
+ var sel = window.getSelection();
+
+ isOrIsParent(sel.anchorNode, $(anchorNode), msg + ": Wrong anchor node.");
+ is(sel.anchorOffset, anchorOffset, msg + ": Wrong anchor offset.");
+ isOrIsParent(sel.focusNode, $(focusNode), msg + ": Wrong focus node.");
+ is(sel.focusOffset, $(focusOffset), msg + ": Wrong focus offset.");
+}
+
+function run() {
+ var sel = window.getSelection();
+
+ // If nothing is focused, selection.modify() should silently fail.
+ sel.removeAllRanges();
+ sel.modify("move", "forward", "character");
+
+ // Now focus our first div and put the cursor at the beginning of p1.
+ $("c").focus();
+ sel.collapse($("p1"), 0);
+
+ // We're intentionally inconsistent with the capitalization of "move",
+ // "extend", etc. below to test the case-insensitivity of modify().
+
+ // If we move backward, selection.modify() shouldn't do anything, since we're
+ // already at the beginning of the frame.
+ isAt("p1", 0, "p1", 0, "test 0a");
+ sel.modify("Move", "Backward", "Character");
+ isAt("p1", 0, "p1", 0, "test 0b");
+
+ // After this move, the cursor should be at the second character of p1
+ sel.modify("Move", "Forward", "Character");
+ isAt("p1", 1, "p1", 1, "test 1");
+
+ // Select the first character in p1
+ sel.collapse($("p1"), 0);
+ sel.modify("Extend", "Forward", "Character");
+ isAt("p1", 0, "p1", 1, "test 2");
+ sel.modify("Move", "Forward", "Character");
+ isAt("p1", 2, "p1", 2, "test 2a");
+
+ // Select all of p1, then move the selection forward one character a few
+ // times.
+ sel.collapse($("p1"), 0);
+ sel.extend($("p1"), 1);
+ sel.modify("move", "forward", "character");
+ isAt("p2", 0, "p2", 0, "test 3a");
+ sel.modify("move", "forward", "character");
+ isAt("p2", 1, "p2", 1, "test 3b");
+
+ // Put the cursor at the beginning of p3, then extend forward one line.
+ // Now go back twice and forward once. Focus should now be at the end of p3.
+ sel.collapse($("p3"), 0);
+ sel.modify("Extend", "Forward", "Line");
+ sel.modify("extend", "backward", "character");
+ sel.modify("extend", "backward", "character");
+ sel.modify("extend", "forward", "character");
+ isAt("p3", 0, "p3", 14, "test 4");
+
+ // Put the cursor at the beginning of p3, then go forward a few words
+ sel.collapse($("p3"), 0);
+ sel.modify("Move", "Forward", "Word");
+ isAt("p3", 4, "p3", 4, "test 4a");
+ sel.modify("move", "forward", "word");
+ // Go back and forward so the indexes are correct.
+ sel.modify("move", "backward", "character");
+ sel.modify("move", "forward", "character");
+ isAt("p3", 14, "p3", 14, "test 4b");
+
+ // Test the lineboundary granularity
+ sel.collapse($("p3"), 0);
+ sel.modify("Move", "Forward", "Lineboundary");
+ // Go back and forward so the indexes are correct.
+ sel.modify("move", "Backward", "character");
+ sel.modify("move", "forward", "character");
+ isAt("p3", 14, "p3", 14, "test 5");
+
+ //
+ // Test RTL text within a dir=LTR div.
+ //
+ $("ltr").focus();
+ sel.collapse($("l1"), 0);
+ SpecialPowers.wrap(sel).caretBidiLevel = 1;
+ isAt("l1", 0, "l1", 0, "test 6a");
+ sel.modify("Move", "Left", "Character");
+ isAt("l1", 1, "l1", 1, "test 6b");
+ sel.modify("Extend", "Backward", "Character");
+ isAt("l1", 1, "l1", 0, "test 6c");
+ sel.modify("extend", "forward", "character");
+ isAt("l1", 1, "l1", 1, "test 6d");
+ sel.modify("Extend", "Right", "Character");
+ isAt("l1", 1, "l1", 0, "test 6e");
+
+ sel.collapse($("l1"), 0);
+ SpecialPowers.wrap(sel).caretBidiLevel = 1;
+ sel.modify("move", "left", "character");
+ sel.modify("extend", "right", "Word");
+ isAt("l1", 1, "l1", 3, "test 7a");
+ sel.modify("move", "left", "word");
+ isAt("l1", 3, "l1", 3, "test 7b");
+ sel.modify("move", "forward", "word");
+ isAt("l1", 9, "l1", 9, "test 7c");
+ sel.modify("extend", "backward", "word");
+ isAt("l1", 9, "l1", 4, "test 7d");
+ sel.modify("move", "left", "word");
+ isAt("l1", 3, "l1", 3, "test 7e");
+
+ sel.collapse($("l1"), 0);
+ SpecialPowers.wrap(sel).caretBidiLevel = 1;
+ sel.modify("extend", "left", "lineboundary");
+ isAt("l1", 0, "l1", 3, "test 8a");
+ sel.modify("move", "forward", "lineboundary");
+ isAt("l1", 9, "l1", 9, "test 8b");
+ sel.modify("extend", "backward", "lineboundary");
+ isAt("l1", 9, "l1", 0, "test 8c");
+ sel.modify("move", "left", "lineboundary");
+ isAt("l1", 3, "l1", 3, "test 8d");
+ sel.modify("extend", "forward", "lineboundary");
+ isAt("l1", 3, "l1", 9, "test 8e");
+
+ // Put the cursor at the left edge of the first line so that when we go up
+ // and down, where we end up doesn't depend on how the characters line up.
+ sel.collapse($("l1"), 0);
+ SpecialPowers.wrap(sel).caretBidiLevel = 1;
+ sel.modify("move", "left", "lineboundary");
+ isAt("l1", 3, "l1", 3, "test 9a");
+ sel.modify("move", "forward", "Line");
+ isAt("l2", 0, "l2", 0, "test 9b");
+ sel.modify("extend", "backward", "Line");
+ // Apparently going up from the beginning of the line takes us to offset 3 in
+ // the line above. This is a little weird, but it's consistent with how the
+ // keyboard works.
+ isAt("l2", 0, "l1", 3, "test 9c");
+
+ // Same test as above, now with absolute directions.
+ sel.collapse($("l1"), 0);
+ SpecialPowers.wrap(sel).caretBidiLevel = 1;
+ sel.modify("move", "left", "lineboundary");
+ isAt("l1", 3, "l1", 3, "test 10a");
+ sel.modify("move", "right", "line");
+ isAt("l2", 0, "l2", 0, "test 10b");
+ sel.modify("extend", "left", "line");
+ isAt("l2", 0, "l1", 3, "test 10c");
+
+ //
+ // Test RTL text within a dir=RTL div.
+ //
+ $("rtl").focus();
+ sel.collapse($("r1"), 0);
+ sel.modify("move", "forward", "character");
+ isAt("r1", 1, "r1", 1, "test 11a");
+ sel.modify("extend", "backward", "character");
+ isAt("r1", 1, "r1", 0, "test 11b");
+ sel.modify("move", "forward", "word");
+ isAt("r1", 6, "r1", 6, "test 11c");
+ sel.modify("extend", "backward", "word");
+ isAt("r1", 6, "r1", 0, "test 11d");
+ sel.modify("extend", "forward", "lineboundary");
+ isAt("r1", 6, "r1", 45, "test 11e");
+
+ // Same as above, but with absolute directions.
+ sel.collapse($("r1"), 0);
+ sel.modify("move", "left", "character");
+ isAt("r1", 1, "r1", 1, "test 12a");
+ sel.modify("extend", "right", "character");
+ isAt("r1", 1, "r1", 0, "test 12b");
+ sel.modify("move", "left", "word");
+ isAt("r1", 6, "r1", 6, "test 12c");
+ sel.modify("extend", "right", "word");
+ isAt("r1", 6, "r1", 0, "test 12d");
+
+ // Test that move left/right is correct within the RTL div.
+ sel.collapse($("r1"), 0);
+ sel.modify("extend", "left", "lineboundary");
+ isAt("r1", 0, "r1", 45, "test 13a");
+ sel.modify("move", "right", "lineboundary");
+ isAt("r1", 0, "r1", 0, "test 13b");
+
+ // Test that up/down at the first/last line correctly wraps to the
+ // beginning/end of the line.
+ sel.collapse($("r1"), 0);
+ sel.modify("move", "forward", "word");
+ isAt("r1", 6, "r1", 6, "test 14a");
+ // Even in RTL text, "left" still means up.
+ sel.modify("extend", "left", "Line");
+ isAt("r1", 6, "r1", 0, "test 14b");
+ sel.modify("move", "right", "line");
+ isAt("r2", 0, "r2", 0, "test 14c");
+ sel.modify("extend", "forward", "line");
+ isAt("r2", 0, "r2", 57, "test 14d");
+ sel.modify("move", "backward", "line");
+ isAt("r1", 45, "r1", 45, "test 14e");
+
+ // Test some special characters.
+ $("special").focus();
+ sel.collapse($("s1"), 0);
+ sel.modify("move", "forward", "character");
+ sel.modify("move", "forward", "character");
+ sel.modify("move", "forward", "character");
+ isAt("s1", 3, "s1", 3, "test 15a");
+ sel.modify("move", "forward", "character");
+ isAt("s1", 4, "s1", 4, "test 15b");
+ sel.modify("move", "backward", "word");
+ isAt("s1", 2, "s1", 2, "test 15c");
+ sel.modify("move", "forward", "word");
+ sel.modify("move", "forward", "word");
+ sel.modify("move", "forward", "word");
+ isAt("s1", 9, "s1", 9, "test 15d");
+
+ SimpleTest.finish();
+}
+
+function update_debug_info() {
+ var sel = window.getSelection();
+ document.getElementById("anchor-offset").innerHTML = sel.anchorOffset;
+ document.getElementById("focus-offset").innerHTML = sel.focusOffset;
+ setTimeout(update_debug_info, 100);
+}
+
+setTimeout(update_debug_info, 100);
+
+</script>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug503813.html b/layout/generic/test/test_bug503813.html
new file mode 100644
index 000000000..0f62ec2d8
--- /dev/null
+++ b/layout/generic/test/test_bug503813.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=503813
+-->
+<head>
+ <title>Test for Bug 503813</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ #a:after { content:"anonymous text"; display:block; height:10px; }
+ #a:hover:after { content:"hover content"; display:block; height:20px; }
+ #b:after { content:"anonymous text"; display:block; height:10px; }
+ #b:hover:after { content:url(file_Dolske.png); display:block; height:20px; }
+ </style>
+</head>
+<body>
+<div id="content" style="width:300px;">
+ <div id="a" style="margin:10px;"></div>
+ <div id="b" style="margin:10px;"></div>
+</div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=503813">Mozilla Bug 503813</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 503813 **/
+function test()
+{
+ synthesizeMouse(document.getElementById("a"), 5, 5, { type: "mousemove" });
+ is(document.getElementById("a").getBoundingClientRect().height, 20,
+ "Expected hover style");
+ synthesizeMouse(document.getElementById("b"), 5, 5, { type: "mousemove" });
+ is(document.getElementById("b").getBoundingClientRect().height, 20,
+ "Expected hover style");
+ SimpleTest.finish();
+}
+// Run 'test' when we've exited paint suppression and done layout
+window.onload = function() { setTimeout(test, 0); };
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug507902.html b/layout/generic/test/test_bug507902.html
new file mode 100644
index 000000000..efcca8ad7
--- /dev/null
+++ b/layout/generic/test/test_bug507902.html
@@ -0,0 +1,382 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=507902
+-->
+<head>
+ <title>Test for Bug 507902</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=507902">Mozilla Bug 507902</a>
+
+<iframe id="testFrameElem"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+//
+// Mochitest to test nsImageFrame icons
+//
+// The 'loading' icon should be displayed up until we have enough image
+// data to determine the frame size.
+//
+// The 'broken' icon should be displayed when the URL is invalid (either
+// a bad server or a file that fails to be sniffed to an appropriate
+// mimetype).
+//
+
+// Boilerplate
+gWindowUtils = SpecialPowers.getDOMWindowUtils(window);
+
+// URL + paths
+//
+// We have a separate copy of the icons in the test directory to
+// avoid any firefox caching mechanisms that might affect the
+// behavior of the load.
+var us = window.location.href;
+var baseURL = us.substring(0, us.lastIndexOf('/') + 1);
+var loadIconFilename = "file_LoadingImageReference.png";
+var imageFilename = "file_Dolske.png";
+var brokenIconFilename = "file_BrokenImageReference.png";
+var serverFilename = "file_IconTestServer.sjs";
+var serverContinueFlag = "?continue=true";
+var bogusFilename = "oneuponatimewhendolskewasyoung.png";
+
+// Our test image element, inside a div, inside an iframe
+var testFrameElem = document.getElementById("testFrameElem");
+var innerDoc = testFrameElem.contentWindow.document;
+var divContainer = innerDoc.createElement("div");
+divContainer.style.cssFloat = "left";
+innerDoc.body.appendChild(divContainer);
+var testImageElem = new Image();
+divContainer.appendChild(testImageElem);
+var pingImage = new Image();
+
+// Set up the canvases
+var canvases = {};
+var canvasNames = [ "brokenIconTest", "brokenIconReference",
+ "loadingIconTest", "loadingIconReference",
+ "loadedTest", "loadedReference" ];
+var windowElem = document.documentElement;
+for (var i in canvasNames) {
+ var can = document.createElement("canvas");
+ can.setAttribute("width", windowElem.getAttribute("width"));
+ can.setAttribute("height", windowElem.getAttribute("height"));
+ canvases[canvasNames[i]] = can;
+
+ // When the image frame has no idea how to size itself, it sizes itself
+ // to dimensions capable of displaying the alt feedback icons. However, if
+ // the image has been loaded before, something (I don't know what) seems to
+ // remember the size of the last successful image for that URL. So when we
+ // create a new image frame for that URL, it uses that size until it hears
+ // something different. This happens through a refresh (not sure if this is
+ // desired behavior). This means that if you refresh the test, the "loading"
+ // icon for the test image will appear with a border that stretches further
+ // right and down, because that URL previously displayed an image with larger
+ // dimensions. This causes the verify stage to fail. To allow for
+ // successful test refreshes (only useful for people, not automated tests),
+ // we add a clipping region so that we see the left and top borders, along
+ // with the image, but not the bottom and right borders.
+
+ if ((i > 1) && (i < 4)) {
+ var ctx = can.getContext("2d");
+ ctx.beginPath();
+ ctx.rect(0,0, 30, 30);
+ ctx.clip();
+ }
+
+}
+
+// Stage 1 - Load the reference image for the broken icon
+function loadBrokenIconReference() {
+
+ // Debugging - Let's see if setting onload after src is a problem
+ testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 1 called\n");};
+
+ // Debug - Figure out if we're getting an onerror instead of onload
+ testImageElem.onerror = function(event) {dump("test_bug507902.html DEBUG - Got onerror for testImageElem!\n");};
+
+ testImageElem.src = baseURL + brokenIconFilename;
+ stageTransition();
+}
+
+// Stage 2 - Draw the reference image for the broken icon to a canvas
+function drawBrokenIconReference() {
+
+ enableBorderAndPad();
+ drawWindowToCanvas("brokenIconReference");
+ disableBorderAndPad();
+
+ stageTransition();
+}
+
+// Stage 3 - Load the reference image for the loading icon
+function loadLoadingIconReference() {
+
+ // Debugging - Let's see if setting onload after src is a problem
+ testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 3 called\n");};
+
+ testImageElem.src = baseURL + loadIconFilename;
+ stageTransition();
+}
+
+// Stage 4 - Draw the reference image for the loading icon to a canvas
+function drawLoadingIconReference() {
+
+ enableBorderAndPad();
+ drawWindowToCanvas("loadingIconReference");
+ disableBorderAndPad();
+
+ stageTransition();
+}
+
+// Stage 5 - Try to load a broken image
+function loadBrokenImage() {
+ resetImage();
+ testImageElem.src = baseURL + bogusFilename;
+ stageTransition();
+}
+
+// Stage 6 - Draw the screen to a canvas. This should hopefully
+// be the broken icon.
+function drawBrokenIcon() {
+ drawWindowToCanvas("brokenIconTest");
+ stageTransition();
+}
+
+// Stage 7 - Load the reference image for the test image
+function loadImageReference() {
+ resetImage();
+
+ // Debugging - Let's see if setting onload after src is a problem
+ testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 7 called\n");};
+
+ testImageElem.src = baseURL + imageFilename;
+ stageTransition();
+}
+
+// Stage 8 - Draw the reference image for the test image to a canvas
+function drawImageReference() {
+ drawWindowToCanvas("loadedReference");
+ stageTransition();
+}
+
+// Stage 9 - Start a load of the test image from the delay-generating server
+function startServerLoad() {
+
+ // Reset the image
+ resetImage();
+
+ // Debugging info so we can figure out the hang
+ dump("test_bug507902.html DEBUG - starting server load\n");
+
+ // Load the image
+ testImageElem.src = baseURL + serverFilename;
+ stageTransition();
+}
+
+// Stage 10 - Draw the screen to a canvas. This should hopefully be the loading
+// icon.
+function drawLoadingIcon() {
+
+ // Debugging info so we can figure out the hang
+ dump("test_bug507902.html DEBUG - drawing loading icon\n");
+
+ drawWindowToCanvas("loadingIconTest");
+ stageTransition();
+}
+
+// Stage 11 - Tell the server to continue.
+function signalServerContinue() {
+
+ // Debugging info so we can figure out the hang
+ dump("test_bug507902.html DEBUG - signaling server to continue\n");
+
+ pingImage.src = baseURL + serverFilename + serverContinueFlag;
+ stageTransition();
+}
+
+// Stage 12 - Draw the screen to a canvas. This should hopefully be the loaded
+// test image.
+function drawLoadedImage() {
+ drawWindowToCanvas("loadedTest");
+ stageTransition();
+}
+
+
+// Stage 13 - Verify That the appropriate canvases match
+function verifyCanvases() {
+
+ // Verify the broken icon
+ ok(canvasesAreEqual("brokenIconTest", "brokenIconReference"),
+ "Window drawn on broken load should match broken icon reference");
+
+ // Verify the loading icon
+ ok(canvasesAreEqual("loadingIconTest", "loadingIconReference"),
+ "Window drawn mid-load should match loading icon reference");
+
+ // Verify the loaded image
+ ok(canvasesAreEqual("loadedTest", "loadedReference"),
+ "Window drawn post-load should match reference image");
+
+ stageTransition();
+}
+
+// We have a bunch of different things that need to happen in order
+// with different transitions. We make a "stage table" here where
+// each entry contains the stage function ('fn') and a transition
+// ('trans'), which can be one of the following:
+// "instant" - Just calls the next stage directly
+// "onload" - Sets the next stage as an onload event for the image element
+// "onerror" - Sets the next stage as an onerror event for the image element
+// integer - Sets the next stage to be called after the given timeout duration
+// "finish" - Finish the test
+var testStages = [
+ { "fn" : loadBrokenIconReference, "trans" : "onload"},
+ { "fn" : drawBrokenIconReference, "trans" : "instant"},
+ { "fn" : loadLoadingIconReference, "trans" : "onload" },
+ { "fn" : drawLoadingIconReference, "trans" : "instant" },
+ { "fn" : loadBrokenImage, "trans" : "onerror" },
+ { "fn" : drawBrokenIcon, "trans" : "instant" },
+ { "fn" : loadImageReference, "trans" : "onload" },
+ { "fn" : drawImageReference, "trans" : "instant" },
+ // XXXbholley - We use a timeout here because resetting the
+ // image doesn't seem to be quite synchronous. If we make
+ // this transition "instant", then the drawImage call draws
+ // an empty (0,0,0,0) rect to the canvas and we're left with
+ // whatever was there before. I don't know of any good event
+ // mechanism to figure out when the image frame is bootstrapped
+ // enough to display the loading image, so I did trial-and-error
+ // with timeouts. 50ms seems to be enough time for things to work
+ // reliably, so *= 6 for good measure.
+ { "fn" : startServerLoad, "trans" : 300 },
+ { "fn" : drawLoadingIcon, "trans" : "instant" },
+ { "fn" : signalServerContinue, "trans" : "onload" },
+ { "fn" : drawLoadedImage, "trans" : "instant" },
+ { "fn" : verifyCanvases, "trans" : "finish" } ];
+var currentStage = 0;
+
+// Transition function called at the end of each stage
+function stageTransition() {
+
+ // Debugging info so we can figure out the hang
+ dump("test_bug507902.html DEBUG - Current Stage: " + currentStage + "\n");
+
+ // What's our transition?
+ var trans = testStages[currentStage++].trans;
+
+ // If the transition is finish, stop now before we access out of bounds
+ if (trans == "finish") {
+ makeCanvasesVisible(); // Useful for debugging
+ SimpleTest.finish();
+ return;
+ }
+
+ // Otherwise, get the next function
+ var nextfn = testStages[currentStage].fn;
+
+ // Switch based on transition
+ switch (trans) {
+
+ // Continue right away
+ case "instant":
+ nextfn();
+ break;
+
+ // Continue after we get an onload event on the test image
+ case "onload":
+ testImageElem.onload = function(event) {testImageElem.onload = undefined; nextfn();};
+ break;
+
+ // Continue after we get an onerror event on the test image
+ case "onerror":
+ testImageElem.onerror = function(event) {testImageElem.onerror = undefined; nextfn();};
+ break;
+
+ // Timeout
+ default:
+ setTimeout(nextfn, trans);
+ break
+ }
+}
+
+// Lots if asynchronous behavior here
+SimpleTest.waitForExplicitFinish();
+
+// Catch somebody's eye
+dump("This test is failing intermittently, see bug 510001 - If you see orange here, please paste the following debugging output on the bug!\n");
+
+// Kick off the test by invoking the first stage. The stages call each other
+testStages[0].fn();
+
+
+// We need to get rid of the old image element and make a new one. If we
+// don't, the "current/pending" machinery will display the old image until
+// the new one is loaded, so we won't see the loading icon.
+function resetImage() {
+ divContainer.removeChild(testImageElem);
+ testImageElem = null;
+ testImageElem = new Image();
+ divContainer.appendChild(testImageElem);
+}
+
+//
+// Makes the canvases visible. Called before the tests finish. This is useful for
+// debugging.
+//
+function makeCanvasesVisible() {
+ for (var i = 0; i < canvasNames.length - 1; i += 2) {
+ var title = document.createElement("h3");
+ title.innerHTML = canvasNames[i] + ", " + canvasNames[i+1] + ":";
+ document.body.appendChild(title);
+ var myDiv = document.createElement("div");
+ myDiv.appendChild(canvases[canvasNames[i]]);
+ myDiv.appendChild(canvases[canvasNames[i+1]]);
+ document.body.appendChild(myDiv);
+ }
+}
+
+//
+// Enables and disables bordering/padding to mimic the look of alt feedback icons
+//
+function enableBorderAndPad() {
+ divContainer.style.border = "1px";
+ divContainer.style.borderStyle = "inset";
+ testImageElem.style.padding = "3px";
+}
+
+function disableBorderAndPad() {
+ testImageElem.style.padding = 0;
+ divContainer.style.border = "0px";
+ divContainer.style.borderStyle = "";
+}
+
+//
+// Helper canvas methods. This is mostly copped directly from the reftest framework
+//
+
+function drawWindowToCanvas(canvasName) {
+ var win = testFrameElem.contentWindow;
+ var ctx = canvases[canvasName].getContext("2d");
+ // drawWindow always draws one canvas pixel for each CSS pixel in the source
+ // window, so scale the drawing to show the zoom (making each canvas pixel be one
+ // device pixel instead)
+ ctx.drawWindow(win, win.scrollX, win.scrollY,
+ Math.ceil(canvases[canvasName].width),
+ Math.ceil(canvases[canvasName].height),
+ "rgb(255,255,255)");
+}
+
+function canvasesAreEqual(canvas1Name, canvas2Name) {
+ var c1 = canvases[canvas1Name];
+ var c2 = canvases[canvas2Name];
+ var differences = gWindowUtils.compareCanvases(c1, c2, {});
+ return (differences == 0);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug508115.xul b/layout/generic/test/test_bug508115.xul
new file mode 100644
index 000000000..576187a8e
--- /dev/null
+++ b/layout/generic/test/test_bug508115.xul
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=508115
+-->
+<window title="Mozilla Bug 508115"
+ onload="setTimeout(doTest, 0)"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=508115">Mozilla Bug 508115</a>
+
+ <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+<script class="testbody" type="application/javascript"><![CDATA[
+function doTest() {
+ document.getElementById("panel").style.display = '';
+ document.getElementById("deck").selectedIndex = 1;
+ document.getElementById("anchor").open = true;
+ document.getElementById("container").style.width = "0";
+ document.getElementById("anchor2").open = true;
+}
+var count = 0;
+function shown(id) {
+ ok(true, "Shown popup " + id);
+ ++count;
+ if (count >= 2) {
+ SimpleTest.finish();
+ }
+}
+SimpleTest.waitForExplicitFinish();
+]]></script>
+<deck id="deck" style="margin:50px;">
+ <vbox></vbox>
+ <vbox id="panel" style="display:none">
+ <vbox>
+ <menulist id="anchor">
+ <menupopup id="popup" onpopupshown="shown('anchor')">
+ <menuitem label="One"/>
+ <menuitem label="Two"/>
+ <menuitem label="Three"/>
+ </menupopup>
+ </menulist>
+ </vbox>
+ </vbox>
+</deck>
+<description>
+<html:div id="container">
+ <html:span id="span" style="-moz-transform: translate(0,0)">Hello Kitty
+ <menulist id="anchor2" style="display:-moz-inline-box;">
+ <menupopup id="popup" onpopupshown="shown('anchor2')">
+ <menuitem label="One"/>
+ <menuitem label="Two"/>
+ <menuitem label="Three"/>
+ </menupopup>
+ </menulist>
+ </html:span>
+</html:div>
+</description>
+</window>
diff --git a/layout/generic/test/test_bug514732-2.xul b/layout/generic/test/test_bug514732-2.xul
new file mode 100644
index 000000000..e684c3815
--- /dev/null
+++ b/layout/generic/test/test_bug514732-2.xul
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514732
+-->
+<window title="Mozilla Bug 514732"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src= "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=514732">
+ Mozilla Bug 514732</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 514732 **/
+
+SimpleTest.waitForExplicitFinish();
+window.open("file_bug514732_window.xul", "bug514732",
+ "chrome,width=600,height=600,scrollbars");
+
+]]>
+</script>
+
+</window>
diff --git a/layout/generic/test/test_bug514732.html b/layout/generic/test/test_bug514732.html
new file mode 100644
index 000000000..4c168e55e
--- /dev/null
+++ b/layout/generic/test/test_bug514732.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514732
+-->
+<head>
+ <title>Test for Bug 514732</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug?id=514732">Mozilla Bug 514732</a>
+
+<pre id="test">
+<script class="testbody" style="width: 300; height: 150;" type="text/javascript;version=1.8">
+window.open("./file_bug514732_helper.html", "", "width=600,height=600");
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_bug522632.html b/layout/generic/test/test_bug522632.html
new file mode 100644
index 000000000..0fb58cec4
--- /dev/null
+++ b/layout/generic/test/test_bug522632.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=522632
+-->
+<head>
+ <title>Test for Bug 522632</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=522632">Mozilla Bug 522632</a>
+<p id="display"></p>
+<div id="content">
+ <table border="1">
+ <tr>
+ <td style="height: 200px; padding: 0; margin: 0; border: none">
+ <span style="height: 0; min-height: 50%" id="foo"></span>
+ </td>
+ <tr>
+ </table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 522632 **/
+is(document.defaultView.getComputedStyle($("foo"), "").height, "100px",
+ "Unexpected computed height");
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug524925.html b/layout/generic/test/test_bug524925.html
new file mode 100644
index 000000000..b8762cb11
--- /dev/null
+++ b/layout/generic/test/test_bug524925.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=524925
+-->
+
+<head>
+ <title>Test for Bug 524925</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+
+<div id="container" style="width: 100px; height: 100px; overflow: auto;">
+ <div id="container2" style="width: 100px; height: 100px; overflow: visible;">
+ <div id="block" style="width: 50px; height: 50px; background-color: #000; -moz-transform:translatey(0px);"></div>
+ </div>
+</div>
+
+<pre id="test">
+ <script type="application/javascript">
+ // Move 'block' 100 pixels downwards.
+ var block = document.getElementById("block");
+ block.style.MozTransform = "translatey(100px)";
+
+ // Check the result is correct and finish the test
+ var container = document.getElementById("container");
+ is(container.scrollHeight, 150, "Overflow areas should update after dynamic transform changes");
+ </script>
+</pre>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_bug527306.html b/layout/generic/test/test_bug527306.html
new file mode 100644
index 000000000..5ddea4a7a
--- /dev/null
+++ b/layout/generic/test/test_bug527306.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=527306
+-->
+<head>
+ <title>Test for Bug 527306</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<body onload="runTests()">
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug?id=527306">Mozilla Bug 527306</a>
+
+<p><input type="text" id="t1" value="FAIL"></p>
+<p><input type="text" id="t2" value="FAIL"></p>
+<p><input type="text" id="t3" value="FAIL"></p>
+<p><textarea id="t4">FAIL</textarea></p>
+
+<pre id="test">
+<script>
+
+function testElement(t) {
+ t.style.display = "none";
+ t.value = "PASS";
+ is(t.value, "PASS", "Value should be set correctly");
+}
+
+function runTests() {
+ var t = document.getElementById("t1");
+ testElement(t);
+ t = document.getElementById("t2");
+ t.focus();
+ testElement(t);
+ t = document.getElementById("t3");
+ t.focus();
+ t.blur();
+ testElement(t);
+ t = document.getElementById("t4");
+ testElement(t);
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_bug579767.html b/layout/generic/test/test_bug579767.html
new file mode 100644
index 000000000..177a310af
--- /dev/null
+++ b/layout/generic/test/test_bug579767.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=579767
+-->
+<head>
+ <title>Test for Bug 579767</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ iframe {
+ width: 1006px;
+ height: 306px;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=579767">Mozilla Bug 579767</a>
+<p id="display"></p>
+<div id="content">
+<iframe src="file_bug579767_1.html" id="f1"></iframe>
+<iframe src="file_bug579767_2.html" id="f2"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 579767 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ var f1 = document.getElementById("f1");
+ var f2 = document.getElementById("f2");
+ var t1 = f1.contentDocument.documentElement;
+ var t2 = f2.contentDocument.documentElement;
+
+ setTimeout(function() {
+ // drag the vertical handle 10px to the right
+ synthesizeMouse(t1, 100, 6, {type: "mousedown"}, f1.contentWindow);
+ synthesizeMouse(t1, 101, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 102, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 103, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 104, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 105, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 106, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 107, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 108, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 109, 6, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 200, 6, {type: "mouseup" }, f1.contentWindow);
+
+ setTimeout(function() {
+ // drag the horizontal handle 10px to down and 5px to right
+ synthesizeMouse(t1, 2, 92, {type: "mousedown"}, f1.contentWindow);
+ synthesizeMouse(t1, 3, 93, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 4, 94, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 5, 95, {type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 7, 102,{type: "mousemove"}, f1.contentWindow);
+ synthesizeMouse(t1, 7, 102,{type: "mouseup" }, f1.contentWindow);
+
+ setTimeout(function() {
+ // now compare the two windows
+ ok(compareSnapshots(snapshotWindow(f1.contentWindow),
+ snapshotWindow(f2.contentWindow), true)[0],
+ "The borders should be painted correctly after resizing");
+ is(t1.querySelectorAll("frameset")[0].getAttribute("cols"), "11%,89%",
+ "The cols attribute should be correctly updated");
+ is(t1.querySelectorAll("frameset")[1].getAttribute("rows"), "100,200",
+ "The rows attribute should be correctly updated");
+ SimpleTest.finish();
+ }, 0);
+ }, 0);
+ }, 0);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug589621.html b/layout/generic/test/test_bug589621.html
new file mode 100644
index 000000000..abae12df6
--- /dev/null
+++ b/layout/generic/test/test_bug589621.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589621
+-->
+<head>
+ <title>Test for Bug 589621</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589621">Mozilla Bug 589621</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 589621 **/
+var sel = getSelection();
+var range = document.createRange();
+sel.addRange(range);
+function t(n) {
+ try {
+ sel.getRangeAt(n);
+ ok(false, "Should not be here");
+ } catch(e) {
+ ok(e instanceof DOMException, "Should be a DOMException");
+ is(e.name, "IndexSizeError", "Should be an IndexSizeError");
+ is(e.code, DOMException.INDEX_SIZE_ERR, "Should be an INDEX_SIZE_ERR");
+ }
+}
+t(-1);
+t(1);
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug589623.html b/layout/generic/test/test_bug589623.html
new file mode 100644
index 000000000..4a1c0e092
--- /dev/null
+++ b/layout/generic/test/test_bug589623.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589623
+-->
+<head>
+ <title>Test for Bug 589623</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589623">Mozilla Bug 589623</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 589623 **/
+var sel = getSelection();
+sel.removeAllRanges();
+function t(m) {
+ try {
+ sel[m]();
+ ok(false, "Should not be here");
+ } catch(e) {
+ is(e.name, "InvalidStateError", "Should be an InvalidStateError");
+ ok(e instanceof DOMException, "Should be a DOMException");
+ is(e.code, DOMException.INVALID_STATE_ERR, "Should be an INVALID_STATE_ERR");
+ }
+}
+t("collapseToStart");
+t("collapseToEnd");
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug597333.html b/layout/generic/test/test_bug597333.html
new file mode 100644
index 000000000..3eb6f7844
--- /dev/null
+++ b/layout/generic/test/test_bug597333.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=597333
+-->
+<head>
+ <title>Test for Bug 597333</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<body>
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug?id=597333">Mozilla Bug 597333</a>
+
+<textarea id="a">&#x202E;</textarea>
+
+<pre id="test">
+<script>
+
+function runTests() {
+ var t = document.getElementById("a");
+ t.focus();
+ setTimeout(function() {
+ synthesizeKey('VK_RIGHT', {});
+ ok(true, "The browser did not crash!");
+
+ SimpleTest.finish();
+ }, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_bug632379.xul b/layout/generic/test/test_bug632379.xul
new file mode 100644
index 000000000..9c153eabd
--- /dev/null
+++ b/layout/generic/test/test_bug632379.xul
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=632379
+-->
+<window title="Mozilla Bug 632379"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<toolbox flex="1">
+ <menubar>
+ <menu label="MENU" accesskey="m" id="mainMenu">
+ <menupopup maxheight="100" onpopupshown="openSubmenu()">
+ <menu label="menu1" accesskey="1" id="menu1">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu2" accesskey="2" id="menu2">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu3" accesskey="3" id="menu3">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu4" accesskey="4" id="menu4">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu5" accesskey="5" id="menu5">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu6" accesskey="6" id="menu6">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu7" accesskey="7" id="menu7">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu8" accesskey="8" id="menu8">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ <menu label="menu9" accesskey="9" id="menu9">
+ <menupopup onpopupshown="snapshot(this)">
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menubar>
+</toolbox>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=632379">Mozilla Bug 632379</a>
+
+ <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+
+<script class="testbody" type="application/javascript"><![CDATA[
+
+var gFinished = false;
+
+/** Test for Bug 632379 **/
+// Tests whether scrolling a menu affects the position at which popups appear
+var pos = new Array(2);
+var count=0;
+
+function snapshot(elem)
+{
+ todo(false, "snapshot() called");
+ pos[count] = elem.getBoundingClientRect().top;
+ ++count;
+ if (count <= 1) {
+ // close the submenu and open the bottom submenu
+ synthesizeKey("VK_LEFT", {});
+ synthesizeKey("9", {});
+ } else {
+ if (navigator.platform.indexOf("Mac") == -1) {
+ is(pos[1], pos[0], "Popup should open in the same place when the menu is scrolled");
+ } else {
+ todo(false, "This test fails on Mac since it was ported to chrome: Bug 668716.");
+ }
+ todo(false, "Test finished");
+ gFinished = true;
+ SimpleTest.finish();
+ }
+}
+
+function doTest() {
+ todo(false, "doTest() called");
+ // open the top-level menu
+ $("mainMenu").open = true;
+}
+
+function openSubmenu()
+{
+ if (!gFinished) {
+ todo(false, "openSubmenu() called");
+ }
+ // open a submenu in the middle
+ synthesizeKey("5", {});
+}
+
+SimpleTest.waitForExplicitFinish();
+todo(false, "Wait for focus");
+SimpleTest.waitForFocus(doTest);
+
+]]></script>
+</window>
+
diff --git a/layout/generic/test/test_bug633762.html b/layout/generic/test/test_bug633762.html
new file mode 100644
index 000000000..910fa0cd1
--- /dev/null
+++ b/layout/generic/test/test_bug633762.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633762
+-->
+<head>
+ <title>Test for Bug 633762</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<body>
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug?id=633762">Mozilla Bug 633762</a>
+
+<iframe id="i" src="bug633762_iframe.html#a"></iframe>
+
+<pre id="test">
+<script>
+
+var doc;
+
+function runTests() {
+ var i = document.getElementById("i");
+ doc = i.contentDocument;
+ var win = i.contentWindow;
+ // set display none on b
+ doc.getElementById("b").style.display = "none";
+ // flush layout
+ doc.documentElement.offsetLeft;
+ // click in middle of iframe document to give it focus
+ synthesizeMouseAtCenter(i, {}, win);
+ win.focus();
+ // record scrolltop
+ scrollTopBefore = doc.body.scrollTop;
+ // send up arrow key event
+ sendKey("UP");
+
+ window.requestAnimationFrame(finish);
+}
+
+function finish() {
+ // assert that scroll top is now less than before
+ ok(scrollTopBefore > doc.body.scrollTop, "pressing up arrow should scroll up");
+ SimpleTest.finish();
+}
+
+var smoothScrollPref = "general.smoothScroll";
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, runTests);
+});
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_bug666225.html b/layout/generic/test/test_bug666225.html
new file mode 100644
index 000000000..7695ced11
--- /dev/null
+++ b/layout/generic/test/test_bug666225.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666225
+-->
+<head>
+ <title>Test for Bug 666225</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666225">Mozilla Bug 666225</a>
+<p id="display"><input id="i" onmousedown="this.type='file';"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 666225
+ Causes ASSERTION: Unexpected document: 'capturingContent->GetUncomposedDoc() == GetDocument()' (bug 560764) **/
+
+function click()
+{
+ $("i").addEventListener("mouseup", test, false);
+ synthesizeMouseAtCenter($("i"), { type: "mousedown" });
+ synthesizeMouseAtCenter($("i"), { type: "mouseup" });
+}
+
+function test()
+{
+ ok(true, "should not crash");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(click);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug719503.html b/layout/generic/test/test_bug719503.html
new file mode 100644
index 000000000..94d0c5d37
--- /dev/null
+++ b/layout/generic/test/test_bug719503.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=719503
+-->
+<title>Test for Bug 719503</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=719503">Mozilla Bug 719503</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>
+try {
+ getSelection().collapse(document.head, 0);
+ getSelection().deleteFromDocument();
+} catch(e) {
+ ok(false, "Exception thrown");
+}
+ok(true, "Passed, no exception");
+</script>
diff --git a/layout/generic/test/test_bug719515.html b/layout/generic/test/test_bug719515.html
new file mode 100644
index 000000000..932e22714
--- /dev/null
+++ b/layout/generic/test/test_bug719515.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=719515
+-->
+<title>Test for Bug 719515</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=719515">Mozilla Bug 719515</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>
+getSelection().collapse(document.body, 0);
+var range = getSelection().getRangeAt(0);
+getSelection().extend(document.body, 1);
+isnot(range, getSelection().getRangeAt(0),
+ "extend() must replace existing ranges, not mutate them");
+range = getSelection().getRangeAt(0);
+getSelection().extend(document.body, 1);
+isnot(range, getSelection().getRangeAt(0),
+ "extend() must replace existing ranges, not mutate them (even for no-op extend()s");
+</script>
diff --git a/layout/generic/test/test_bug719518.html b/layout/generic/test/test_bug719518.html
new file mode 100644
index 000000000..e686944d5
--- /dev/null
+++ b/layout/generic/test/test_bug719518.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=719518
+-->
+<title>Test for Bug 719518</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=719518">Mozilla Bug 719518</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>
+var thrown = false;
+try {
+ getSelection().extend(document.body, 0);
+} catch(e) {
+ thrown = true;
+ is(e.name, "InvalidStateError",
+ "Need to throw InvalidStateError for extend() with no ranges");
+ ok(e instanceof DOMException,
+ "Need to throw DOMException for extend() with no ranges");
+ is(e.code, DOMException.INVALID_STATE_ERR,
+ "Need to throw INVALID_STATE_ERR for extend() with no ranges");
+}
+ok(thrown, "Need to throw exception for extend() with no ranges");
+</script>
diff --git a/layout/generic/test/test_bug719523.html b/layout/generic/test/test_bug719523.html
new file mode 100644
index 000000000..ff6da7210
--- /dev/null
+++ b/layout/generic/test/test_bug719523.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=719523
+-->
+<title>Test for Bug 719523</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=719523">Mozilla Bug 719523</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>
+getSelection().selectAllChildren(document);
+is(document.childNodes.length, 2, "Sanity check: number of document children");
+is(getSelection().focusNode, document,
+ "Sanity check: document must be selected");
+is(getSelection().focusOffset, 2,
+ "selectAllChildren() must select all the document's children");
+</script>
diff --git a/layout/generic/test/test_bug735641.html b/layout/generic/test/test_bug735641.html
new file mode 100644
index 000000000..8628e0cf9
--- /dev/null
+++ b/layout/generic/test/test_bug735641.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=735641
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 735641</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=735641">Mozilla Bug 735641</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 735641 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var w;
+
+function runTest() {
+ try {
+ var s = w.document.getSelection();
+ s.selectAllChildren(w.document.body);
+ synthesizeMouse(w.document.body,1,1,{},w);
+ ok(s.isCollapsed,"mouse click on <body> did reset the selection in image document");
+ } finally {
+ w.close();
+ }
+ SimpleTest.finish();
+}
+
+w=window.open("file_Dolske.png","_blank");
+w.addEventListener("load", function() {setTimeout(runTest,0)}, false);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug748961.html b/layout/generic/test/test_bug748961.html
new file mode 100644
index 000000000..511fd26dd
--- /dev/null
+++ b/layout/generic/test/test_bug748961.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=748961
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 748961</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=748961">Mozilla Bug 748961</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 748961 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var f = document.createElement('iframe');
+document.body.appendChild(f);
+
+src = "data:text/html,<meta charset='utf-8'><body><p id='sel'>Selection</p><scr" + "ipt>"
+src += "try {"
+src += "var range = document.createRange();"
+src += "range.selectNode(document.getElementById('sel'));"
+src += "var sel = window.getSelection();"
+src += "sel.addRange(range);"
+src += "parent.is(sel.toString(), 'Selection', 'Selection.toString()');"
+src += "document.body.offsetHeight;"
+src += "parent.is(sel.toString(), 'Selection', 'Selection.toString() after explicit flush');"
+src += "} finally { parent.ok(true,'silence no test run warning'); parent.SimpleTest.finish(); }"
+src += "</scr"+"ipt>";
+
+f.src = src;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug756984.html b/layout/generic/test/test_bug756984.html
new file mode 100644
index 000000000..52f415261
--- /dev/null
+++ b/layout/generic/test/test_bug756984.html
@@ -0,0 +1,143 @@
+<!--
+ Important: needs to be in quirks mode for the test to work.
+ If not in quirks mode, the down and up arrow don't position as expected.
+-->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=756984
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 756984</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756984">Mozilla Bug 756984</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<div id="div1">123<br>45678<br></div>
+<div id="div2"><font face="Arial">123</font><br><i>45678</i><br></div>
+<div id="div3"><font face="Courier"><i><strong>123</strong></i></font><br><i>45678</i><br></div>
+<div id="div4"><br>45678<br></div>
+<div id="div5" contenteditable=true spellcheck=false>1234567890<br>abc<br>defghijklmno<br>end</div>
+<div id="div6" contenteditable=true spellcheck=false><font face="Arial">1234567890</font><br><i>abc</i><br><font color="red">defghijklmno</font><br>end</div>
+<div id="div7" contenteditable=true spellcheck=false><font face="Courier"><i><strong>1234567890</strong></i></font><br><i>abc</i><br><font color="red"><b>defghijklmno</b></font><br>end</div>
+
+<pre id="test">
+
+ <script type="application/javascript">
+
+ /** Test for Bug 756984 **/
+ /*
+ * We test that clicking beyond the end of a line terminated with <br> selects the preceding text, if any.
+ * In a contenteditable div, we also test that getting to the end of a line via the "end" key or by using
+ * the left-arrow key from the beginning of the following line selects the text preceding the <br>.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.waitForFocus(function() {
+
+ var sel = window.getSelection();
+ var i, theDiv, selRange;
+
+ for (i = 1; i <= 3; i++) {
+ // click beyond the first line (100px to the left and 2px down), expect text
+ theDiv = document.getElementById("div" + i.toString());
+ theDiv.focus();
+ sel.collapse(theDiv, 0);
+ synthesizeMouse(theDiv, 100, 2, {});
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "#text", "selection should be in text node");
+ is(selRange.endOffset, 3, "offset should be 3");
+ }
+
+ // click beyond the first line (100px to the left and 2px down), expect DIV.
+ // This is the previous behaviour which hasn't changed since the line is empty.
+ // If the processing were wrong, the selection would end up in some other non-empty line.
+ theDiv = document.getElementById("div4");
+ theDiv.focus();
+ sel.collapse(theDiv, 0);
+ synthesizeMouse(theDiv, 100, 2, {});
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "DIV", "selection should be in DIV");
+ is(selRange.endOffset, 0, "offset should be 0");
+
+ // Now we do a more complex test, this time with an editable div.
+ // We have four lines:
+ // 1234567890<br>
+ // abc<br>
+ // defghijklmno<br> <!-- Note: 12 characters long. -->
+ // end
+
+ for (i = 5; i <= 7; i++) {
+ // We do these steps:
+ // 1) Click behind the first line, add "X".
+ theDiv = document.getElementById("div" + i.toString());
+ theDiv.focus();
+ var originalHTML = theDiv.innerHTML;
+ sel.collapse(theDiv, 0);
+ synthesizeMouse(theDiv, 100, 2, {});
+
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "#text", "selection should be in text node (1)");
+ is(selRange.endOffset, 10, "offset should be 10");
+ synthesizeKey("X", {});
+
+ // 2) Down arrow to the end of the second line, add "Y".
+ synthesizeKey("VK_DOWN", {});
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "#text", "selection should be in text node (2)");
+ is(selRange.endOffset, 3, "offset should be 3");
+ synthesizeKey("Y", {});
+
+ // 3) Right arrow and end key to the end of the third line, add "Z".
+ synthesizeKey("VK_RIGHT", {});
+ if (navigator.platform.indexOf("Win") == 0) {
+ synthesizeKey("VK_END", {});
+ } else {
+ // End key doesn't work as expected on Mac and Linux.
+ sel.modify("move", "right", "lineboundary");
+ }
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "#text", "selection should be in text node (3)");
+ is(selRange.endOffset, 12, "offset should be 12");
+ synthesizeKey("Z", {});
+
+ // 4) Up arrow to the end of the second line, add "T".
+ synthesizeKey("VK_UP", {});
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "#text", "selection should be in text node (4)");
+ is(selRange.endOffset, 4, "offset should be 4");
+ synthesizeKey("T", {});
+
+ // 5) Left arrow six times to the end of the first line, add "A".
+ synthesizeKey("VK_LEFT", {});
+ synthesizeKey("VK_LEFT", {});
+ synthesizeKey("VK_LEFT", {});
+ synthesizeKey("VK_LEFT", {});
+ synthesizeKey("VK_LEFT", {});
+ synthesizeKey("VK_LEFT", {});
+ selRange = sel.getRangeAt(0);
+ is(selRange.endContainer.nodeName, "#text", "selection should be in text node (5)");
+ is(selRange.endOffset, 11, "offset should be 11");
+ synthesizeKey("A", {});
+
+ // Check the resulting HTML. First prepare what we expect.
+ originalHTML = originalHTML.replace(/1234567890/, "1234567890XA");
+ originalHTML = originalHTML.replace(/abc/, "abcYT");
+ originalHTML = originalHTML.replace(/defghijklmno/, "defghijklmnoZ");
+ var newHTML = theDiv.innerHTML;
+ is(newHTML, originalHTML, "unexpected HTML");
+ }
+ SimpleTest.finish();
+ });
+ </script>
+
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug784410.html b/layout/generic/test/test_bug784410.html
new file mode 100644
index 000000000..efaf6f943
--- /dev/null
+++ b/layout/generic/test/test_bug784410.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test bug 784410</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="outer" style="overflow:auto; height:200px; border:2px dotted black; transform: translateY(1px)" onscroll="doneScroll()">
+ <div id="d" style="overflow:auto; height:102px;" onscroll="doneScroll()">
+ <div id="inner" style="height:100.1px; border:1px solid black; background:yellow;">Hello</div>
+ </div>
+ <div style="height:500px;"></div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript;version=1.7">
+var sel = window.getSelection();
+var outer = document.getElementById("outer");
+var d = document.getElementById("d");
+var inner = document.getElementById("inner");
+var smoothScrollPref = "general.smoothScroll";
+
+function innerScrollOffset() {
+ return inner.getBoundingClientRect().top - d.getBoundingClientRect().top;
+}
+var innerStartScrollOffset = innerScrollOffset();
+
+var step = 0;
+function doneScroll() {
+ ++step;
+ switch (step) {
+ case 1:
+ is(innerScrollOffset(), innerStartScrollOffset, "Inner element should not have scrolled down");
+ ok(outer.scrollTop > 0, "Outer element should have scrolled down");
+
+ outer.scrollTop = 0;
+ break;
+ case 2:
+ // Wait for paints to flush, so APZ is notified of the new scroll offset.
+ sendWheelAndPaint(inner, 4, 4,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE, deltaY: 1 },
+ function() {});
+ break;
+ case 3:
+ is(innerScrollOffset(), innerStartScrollOffset, "Inner element should not have scrolled down");
+ ok(outer.scrollTop > 0, "Outer element should have scrolled down");
+
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+ SimpleTest.finish();
+ break;
+ }
+}
+
+function test() {
+ sel.collapse(inner.firstChild, 2);
+ synthesizeKey("VK_PAGE_DOWN", {});
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, test);
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug785324.html b/layout/generic/test/test_bug785324.html
new file mode 100644
index 000000000..9b21ac233
--- /dev/null
+++ b/layout/generic/test/test_bug785324.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785324
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 785324</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785324">Mozilla Bug 785324</a>
+<div id="test785324" style="width:100px;height:100px;border:1px solid black;" onclick="this.innerHTML = 'TEST';"></div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 785324 **/
+
+function test()
+{
+ var div1 = document.getElementById('test785324');
+ synthesizeMouse(div1, 25, 25, {});
+ var sel = window.getSelection();
+ is(sel.rangeCount,1,"have Selection");
+ var r = sel.getRangeAt(0);
+ ok(r.startContainer == div1,"startContainer");
+ ok(r.endContainer == div1,"endContainer");
+ ok(r.startOffset == 0,"startOffset");
+ ok(r.endOffset == 0,"endOffset");
+ SimpleTest.finish();
+}
+
+window.onload = function() { setTimeout(test, 0); };
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug791616.html b/layout/generic/test/test_bug791616.html
new file mode 100644
index 000000000..e2816ea73
--- /dev/null
+++ b/layout/generic/test/test_bug791616.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test bug 791616</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+#t {
+ overflow: auto;
+ position: absolute;
+ left: 200px;
+ top: 100px;
+ font: 14px/1.3em "Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace;
+}
+ </style>
+</head>
+<body>
+<p id="display"></p>
+<div id="t" contenteditable>
+ <div>66666666666666</div>
+ <div id="target">777777777777777777777777777777777777777</div>
+</div>
+</div>
+<pre id="test">
+<script class="testbody">
+var t = document.getElementById("t");
+var target = document.getElementById("target");
+var sel = window.getSelection();
+var smoothScrollPref = "general.smoothScroll";
+
+SimpleTest.waitForExplicitFinish();
+t.scrollTop = 0;
+var targetY = target.getBoundingClientRect().top;
+
+SimpleTest.waitForFocus(function() {
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, runTest);
+});
+function runTest() {
+ is(target.getBoundingClientRect().top, targetY, "Target should not have scrolled due to waitForFocus");
+ t.focus();
+ is(target.getBoundingClientRect().top, targetY, "Target should not have scrolled due to focus change");
+
+ // Move the caret to scroll it into view
+ sel.collapse(target.firstChild, 2);
+ synthesizeKey("VK_LEFT", {});
+
+ // Delay until next repaint in case stuff is asynchronous. Also
+ // take a trip through the event loop.
+ setTimeout(function() {
+ window.requestAnimationFrame(function() {
+ is(sel.anchorNode, target.firstChild, "Should have selected 'target' text node");
+ is(sel.anchorOffset, 1, "Selection should have moved left one character");
+ // We should not have needed to scroll the caret into view
+ is(target.getBoundingClientRect().top, targetY, "Target should not have scrolled");
+ SimpleTest.finish();
+ });
+ // Make sure repainting actually happens.
+ target.style.background = "yellow";
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug831780.html b/layout/generic/test/test_bug831780.html
new file mode 100644
index 000000000..1173d8e03
--- /dev/null
+++ b/layout/generic/test/test_bug831780.html
@@ -0,0 +1,32 @@
+<!-- Important: needs to be in quirks mode for the test to be meaningful -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=831780
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 831780</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=831780">Mozilla Bug 831780</a>
+<p id="display" style="width: 1px; height: 1px; overflow: hidden">
+ <!-- No src so it'll be a broken image, but that only happens in quirks
+ mode. In standards mode the test would end up timing-dependent -->
+ <img style="width: 1px; height: 1px">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ /** Test for Bug 831780 **/
+ is($("display").scrollWidth, 1, "scrollWidth should be 1");
+ is($("display").scrollHeight, 1, "scrollHeight should be 1");
+
+ </script>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug841361.html b/layout/generic/test/test_bug841361.html
new file mode 100644
index 000000000..cc7a23dbf
--- /dev/null
+++ b/layout/generic/test/test_bug841361.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=841361
+-->
+<head>
+ <title>Test for Bug 841361</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841361">Mozilla Bug 841361</a>
+<p id="display">
+
+<div style="width:500px; height:0;" id="a"></div>
+
+<div style="width:0; height:500px;" id="b"></div>
+
+<div style="width:500px;" id="c">
+ <div style="width:50px; height:50px; float:left; background:yellow"></div>
+ <div style="width:200px; height:50px; float:left; background:green"></div>
+</div>
+
+<div style="width:500px; height:0; overflow:hidden" id="d"></div>
+
+<div style="width:0; height:500px; overflow:hidden" id="e"></div>
+
+<div style="width:500px; overflow:hidden" id="f">
+ <div style="width:50px; height:50px; float:left; background:yellow"></div>
+ <div style="width:200px; height:50px; float:left; background:green"></div>
+</div>
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function doTest(id, w, h) {
+ var e = document.getElementById(id);
+ is(e.scrollWidth, w, "scrollWidth for element '" + id + "'");
+ is(e.scrollHeight, h, "scrollHeight for element '" + id + "'");
+}
+
+doTest("a", 500, 0);
+doTest("b", 0, 500);
+doTest("c", 500, 50);
+doTest("d", 500, 0);
+doTest("e", 0, 500);
+doTest("f", 500, 50);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug904810.html b/layout/generic/test/test_bug904810.html
new file mode 100644
index 000000000..b2b4355e7
--- /dev/null
+++ b/layout/generic/test/test_bug904810.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=904810
+-->
+<head>
+ <title>Test for Bug 904810</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=904810">Mozilla Bug 904810</a>
+<p id="display">
+<textarea dir="ltr" id="textarea904810">
+first line
+second line
+third line
+</textarea></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 904810 **/
+
+SimpleTest.waitForExplicitFinish();
+
+// We intermittently trigger two "Wrong parent style context" assertions
+// on B2G emulator builds (bug XXXXXXX). The two frames that get incorrect
+// style context parents are scroll bar parts in the <textarea>.
+SimpleTest.expectAssertions(0, 2);
+
+SimpleTest.waitForFocus(function test() {
+ function shiftLeft(i) {
+ for ( ; i > 0; --i)
+ synthesizeKey("VK_LEFT", {shiftKey:true});
+ }
+
+ function shiftRight(i) {
+ for ( ; i > 0; --i)
+ synthesizeKey("VK_RIGHT", {shiftKey:true});
+ }
+
+ const isWindows = /WINNT/.test(SpecialPowers.OS);
+
+ var textarea = document.getElementById('textarea904810');
+ textarea.focus();
+
+ // Testing VK_DOWN with forward selection
+ textarea.selectionStart = 1;
+ textarea.selectionEnd = 1;
+ shiftRight(7);
+ synthesizeKey("VK_DOWN", {});
+ if (isWindows) {
+ is(textarea.selectionStart, 19, "caret moved to next line below selection end");
+ is(textarea.selectionEnd, 19, "caret moved to next line below selection end");
+ } else {
+ is(textarea.selectionStart, 8, "caret moved to visual end of selection");
+ is(textarea.selectionEnd, 8, "caret moved to visual end of selection");
+ }
+
+ // Testing VK_DOWN with backward selection
+ textarea.selectionStart = 8;
+ textarea.selectionEnd = 8;
+ shiftLeft(7);
+ synthesizeKey("VK_DOWN", {});
+ if (isWindows) {
+ is(textarea.selectionStart, 12, "caret moved to next line below selection start");
+ is(textarea.selectionEnd, 12, "caret moved to next line below selection start");
+ } else {
+ is(textarea.selectionStart, 8, "caret moved to visual end of selection");
+ is(textarea.selectionEnd, 8, "caret moved to visual end of selection");
+ }
+
+ // Testing VK_UP with forward selection
+ textarea.selectionStart = 12;
+ textarea.selectionEnd = 12;
+ shiftRight(7);
+ synthesizeKey("VK_UP", {});
+ var result = textarea.selectionEnd
+ if (isWindows) {
+ is(textarea.selectionStart, 8, "caret moved to previous line above selection end");
+ is(textarea.selectionEnd, 8, "caret moved to previous line above selection end");
+ } else {
+ is(textarea.selectionStart, 12, "caret moved to visual start of selection");
+ is(textarea.selectionEnd, 12, "caret moved to visual start of selection");
+ }
+
+ // Testing VK_UP with backward selection
+ textarea.selectionStart = 19;
+ textarea.selectionEnd = 19;
+ shiftLeft(7);
+ synthesizeKey("VK_UP", {});
+ var result = textarea.selectionEnd
+ if (isWindows) {
+ is(textarea.selectionStart, 1, "caret moved to previous line above selection start");
+ is(textarea.selectionEnd, 1, "caret moved to previous line above selection start");
+ } else {
+ is(textarea.selectionStart, 12, "caret moved to visual start of selection");
+ is(textarea.selectionEnd, 12, "caret moved to visual start of selection");
+ }
+
+
+
+ SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_bug938772.html b/layout/generic/test/test_bug938772.html
new file mode 100644
index 000000000..aa1419c92
--- /dev/null
+++ b/layout/generic/test/test_bug938772.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=938772
+-->
+<title>Test for Bug 938772</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938772">Mozilla Bug 938772</a>
+<p id="display"></p>
+<iframe id="i"></iframe>
+<pre id="test">
+<script>
+SimpleTest.waitForExplicitFinish();
+
+i.onload = function() {
+ ok(!i.contentDocument.getElementById("v").failed,
+ "Check whether an error was reported");
+ SimpleTest.finish();
+};
+i.src = "data:text/html,<base href='http://basetag/basetag'><video id='v' onerror='v.failed=true'></video>";
+</script>
+
diff --git a/layout/generic/test/test_bug970363.html b/layout/generic/test/test_bug970363.html
new file mode 100644
index 000000000..e0ac0370a
--- /dev/null
+++ b/layout/generic/test/test_bug970363.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=970363
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 970363</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=970363">Mozilla Bug 970363</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<div contenteditable="true" id='editablediv'>
+ <div id='t'>S<span contenteditable="false">readonly</span>E</div>
+</div>
+<pre id="test">
+
+ <script type="application/javascript">
+
+ /** Test for Bug 970363 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.waitForFocus(function() {
+ var t = document.getElementById("t");
+ var editablediv = document.getElementById("editablediv");
+ var sel = window.getSelection();
+ editablediv.focus();
+ sel.collapse(t, 0);
+
+ // Move past the 'S' char.
+ synthesizeKey("VK_RIGHT", {});
+ // Move across the span.
+ synthesizeKey("VK_RIGHT", {});
+ // Insert an 'I' to show when IP currently is.
+ synthesizeKey("I", {});
+
+ is(t.textContent, "SreadonlyIE", "the 'I' char should be inserted between 'readonly' and 'E'");
+ SimpleTest.finish();
+ });
+
+ </script>
+
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_contained_plugin_transplant.html b/layout/generic/test/test_contained_plugin_transplant.html
new file mode 100644
index 000000000..6901cff10
--- /dev/null
+++ b/layout/generic/test/test_contained_plugin_transplant.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=775965
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 775965</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=775965">Mozilla Bug 775965</a>
+<p id="display"></p>
+<div id="content">
+
+<iframe id="iframe1" src="data:text/html,<embed type='application/x-test' width='200' height='200'></embed>"></iframe>
+<iframe id="iframe2"></iframe>
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 775965 **/
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var iframe1 = document.getElementById("iframe1");
+ var iframe2 = document.getElementById("iframe2");
+ iframe1.parentNode.removeChild(iframe1);
+ iframe1.style.position = "fixed";
+ iframe2.contentDocument.body.appendChild(iframe1);
+ setTimeout(function() { ok(true, "We didn't crash!"); SimpleTest.finish(); }, 0);
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_image_selection.html b/layout/generic/test/test_image_selection.html
new file mode 100644
index 000000000..00d14b176
--- /dev/null
+++ b/layout/generic/test/test_image_selection.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=599368
+-->
+<head>
+ <title>Test for Bug 599368</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=599368">Mozilla Bug 599368</a>
+<iframe id="display" src="about:blank"></iframe>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 599368 **/
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("load", step1, false);
+
+var gImage;
+var gIframe;
+var gBlueNotSelected;
+var gBlueSelected;
+var gFuchsiaSelected;
+
+function step1()
+{
+ gIframe = document.getElementById("display");
+ doc = gIframe.contentDocument;
+
+ gImage = doc.createElement('img');
+ var src = String(window.location).split("/");
+ src.pop();
+ src.push("blue-32x32.png");
+ src = src.join("/");
+ gImage.src = src;
+ gImage.addEventListener("load", step2, false);
+ doc.body.appendChild(gImage);
+
+ doc.designMode = "on";
+}
+
+function step2() {
+ gImage.removeEventListener("load", step2, false);
+
+ gBlueNotSelected = snapshotWindow(gIframe.contentWindow, false);
+
+ synthesizeMouse(gImage, 5, 5, {}, gIframe.contentWindow);
+ setTimeout(step3, 0);
+}
+
+function step3() {
+ gBlueSelected = snapshotWindow(gIframe.contentWindow, false);
+
+ var src = String(window.location).split("/");
+ src.pop();
+ src.push("fuchsia-32x32.png");
+ src = src.join("/");
+ gImage.addEventListener("load", step4, false);
+ gImage.src = src;
+}
+
+function step4() {
+ gImage.removeEventListener("load", step4, false);
+
+ gFuchsiaSelected = snapshotWindow(gIframe.contentWindow, false);
+
+ assert_different(gBlueNotSelected, gBlueSelected,
+ "selecting image should add drag points");
+ assert_different(gBlueSelected, gFuchsiaSelected,
+ "different images should appear different");
+
+ SimpleTest.finish();
+}
+
+function assert_different(shot1, shot2, desc)
+{
+ var [correct, s1, s2] = compareSnapshots(shot1, shot2, false);
+ ok(correct, desc);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_image_selection_2.html b/layout/generic/test/test_image_selection_2.html
new file mode 100644
index 000000000..67a3b0de8
--- /dev/null
+++ b/layout/generic/test/test_image_selection_2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=758179
+-->
+<head>
+ <title>Test for Bug 758179</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+#t1 { width:80px; height:60px; background:yellow; }
+#t1.chosen #i1 { visibility:hidden; }
+ </style>
+</head>
+<body>
+<div id="t1">
+ <img id="i1" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyAQMAAACQ%2B%2Bz9AAAAA1BMVEUA%2FwA0XsCoAAAAD0lEQVQoFWNgGAWjYGgCAAK8AAEb3eOQAAAAAElFTkSuQmCC">
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+var selection = window.getSelection();
+var t1 = document.getElementById("t1");
+
+function doTest() {
+ t1.addEventListener("mousedown", function() {
+ t1.className = "chosen";
+ }, false);
+ t1.addEventListener("mouseup", function() {
+ ok(selection.isCollapsed, "checking that selection is collapsed");
+ }, false);
+ synthesizeMouse(t1, 10, 10, {});
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(doTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_intrinsic_size_on_loading.html b/layout/generic/test/test_intrinsic_size_on_loading.html
new file mode 100644
index 000000000..fc148a0f9
--- /dev/null
+++ b/layout/generic/test/test_intrinsic_size_on_loading.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1205027
+-->
+<head>
+ <title>Test for images intrinsic size while load is pending</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ body { margin: 0; }
+ img { display: block; }
+ </style>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ var initialOffsetTop;
+ var finalOffsetTop;
+
+ function report() {
+ finalOffsetTop = marker.offsetTop;
+ is(initialOffsetTop, 0, "initial offsetTop: " + initialOffsetTop);
+ is(finalOffsetTop, 8, "final offsetTop: " + finalOffsetTop);
+ ok(initialOffsetTop < finalOffsetTop, "offsetTop should get larger.");
+ SimpleTest.finish();
+ }
+
+ function fail() {
+ ok(false, "image loading should not fail.");
+ }
+ </script>
+</head>
+<body onload="report();">
+ <!-- Bunch of tiny images: -->
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+ <img src="file_SlowImage.sjs" onerror="fail();">
+
+ <div id="marker">Marker div</div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205027">Mozilla Bug 1205027</a>
+ <script>
+ initialOffsetTop = marker.offsetTop;
+ // prompt the sjs "server" to proceed to serve data to our <img> elements
+ var img = new Image();
+ img.onerror = fail;
+ img.src = "file_SlowImage.sjs?continue";
+ </script>
+</body>
+</html>
diff --git a/layout/generic/test/test_invalidate_during_plugin_paint.html b/layout/generic/test/test_invalidate_during_plugin_paint.html
new file mode 100644
index 000000000..5894968b5
--- /dev/null
+++ b/layout/generic/test/test_invalidate_during_plugin_paint.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test handling plugins invalidating during paint</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ embed { width:200px; height:200px; display:block; }
+ </style>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: block">
+ <embed id="p1" type="application/x-test" drawmode="solid" color="80808080"></embed>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var p1 = document.getElementById("p1");
+var initialPaintCount;
+
+function checkEnded() {
+ var paints = p1.getPaintCount() - initialPaintCount;
+ if (paints > 20) {
+ ok(true, "Got " + paints + " paints");
+ SimpleTest.finish();
+ return;
+ }
+
+ setTimeout(checkEnded, 30);
+}
+
+function doTest() {
+ initialPaintCount = p1.getPaintCount();
+
+ // Tell the plugin to invalidate every time it paints
+ p1.setInvalidateDuringPaint(true);
+ // Trigger an invalidation to get painting started
+ p1.setColor("FFFFFFFF");
+ // Now we should have an infinite cycle of painting and invalidations.
+
+ // Poll for more than 20 paints to happen.
+ checkEnded();
+}
+
+// Need to run 'doTest' after painting is unsuppressed, or we'll set clip
+// regions to empty.
+addLoadEvent(doTest);
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_movement_by_characters.html b/layout/generic/test/test_movement_by_characters.html
new file mode 100644
index 000000000..ffd423335
--- /dev/null
+++ b/layout/generic/test/test_movement_by_characters.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test Character Movement (including nsTextFrame::PeekOffsetCharacter)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: block">
+<div contentEditable id="editor"></div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript;version=1.7">
+
+SimpleTest.waitForExplicitFinish();
+
+if (navigator.userAgent.indexOf("Windows NT 6.2") >= 0) {
+ todo(false, "Too many intermittent failures on Windows 8 (bug 886781)");
+ SimpleTest.finish();
+} else {
+ setTimeout(focusing, 0);
+}
+
+function focusing() {
+ document.getElementById("editor").focus();
+ // This seems to be necessary because the selection is not set up properly otherwise
+ setTimeout(test, 0);
+}
+
+function test() {
+ var sel = window.getSelection();
+ var editor = document.getElementById("editor");
+
+ function testRight(node, offset) {
+ synthesizeKey("VK_RIGHT", {});
+ is(sel.anchorNode, node, "Right movement broken in " + editor.innerHTML);
+ is(sel.anchorOffset, offset, "Right movement broken in " + editor.innerHTML);
+ }
+
+ function testLeft(node, offset) {
+ synthesizeKey("VK_LEFT", {});
+ is(sel.anchorNode, node, "Left movement broken in " + editor.innerHTML);
+ is(sel.anchorOffset, offset, "Left movement broken in " + editor.innerHTML);
+ }
+
+ editor.innerHTML = "H K";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 1);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 3);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 1);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "<b>H</b> K";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 1);
+ testRight(editor.firstChild.nextSibling, 1);
+ testRight(editor.firstChild.nextSibling, 2);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.nextSibling, 0);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "H <br>K";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 1);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild.nextSibling.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling.nextSibling, 1);
+ testLeft(editor.firstChild.nextSibling.nextSibling, 0);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 1);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "<pre>aa\nbb</pre>";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 1);
+ // at the end of the first line, before the \n
+ testRight(editor.firstChild.firstChild, 2);
+ testRight(editor.firstChild.firstChild, 3);
+ testRight(editor.firstChild.firstChild, 4);
+ testLeft(editor.firstChild.firstChild, 3);
+ // at the end of the first line, before the \n
+ testLeft(editor.firstChild.firstChild, 2);
+ testLeft(editor.firstChild.firstChild, 1);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_movement_by_words.html b/layout/generic/test/test_movement_by_words.html
new file mode 100644
index 000000000..fae98e567
--- /dev/null
+++ b/layout/generic/test/test_movement_by_words.html
@@ -0,0 +1,510 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test Word Movement (including nsTextFrame::PeekOffsetWord)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: block">
+<div contentEditable id="editor"></div>
+</div>
+<p id="catch">Catch-all
+<pre id="test"><script class="testbody" type="text/javascript;version=1.7">
+
+/** Tests for bugs 384147, 981281, 1066756 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function(){setTimeout(focusing, 0)});
+
+function focusing() {
+ document.getElementById("editor").focus();
+ // This seems to be necessary because the selection is not set up properly otherwise
+ setTimeout(test, 0);
+}
+
+var eatSpace;
+var stopAtPunctuation;
+var wordModifiers =
+ (navigator.platform.indexOf("Mac") >= 0) ? {altKey:true} : {ctrlKey:true};
+var sel = window.getSelection();
+var editor = document.getElementById("editor");
+
+function setPrefs(eat_space, stop_at_punctuation, callback) {
+ eatSpace = eat_space;
+ stopAtPunctuation = stop_at_punctuation;
+ SpecialPowers.pushPrefEnv({"set": [["layout.word_select.eat_space_to_next_word", eat_space], ["layout.word_select.stop_at_punctuation", stop_at_punctuation]]}, callback);
+}
+
+function errString(dir) {
+ return dir + " movement broken with eatSpace=" + eatSpace +
+ ", stopAtPunctuation=" + stopAtPunctuation + " in \"" + editor.innerHTML +
+ "\"; sel.anchorNode.parentNode=" + sel.anchorNode.parentNode;
+}
+
+function testRight(node, offset) {
+ synthesizeKey("VK_RIGHT", wordModifiers);
+ is(sel.anchorNode, node, errString("Right"));
+ is(sel.anchorOffset, offset, errString("Right"));
+}
+
+function testLeft(node, offset) {
+ synthesizeKey("VK_LEFT", wordModifiers);
+ is(sel.anchorNode, node, errString("Left"));
+ is(sel.anchorOffset, offset, errString("Left"));
+}
+
+var afterEditorNode = document.getElementById("catch").firstChild;
+
+var ChineseChars = "&#x6F22;&#x5B57;";
+var HiraganaChars = "&#x3072;&#x3089;&#x304C;&#x306A;";
+var KatakanaChars = "&#x30AB;&#x30BF;&#x30AB;&#x30CA;";
+var JapaneseFullStop = "&#x3002;";
+var JapaneseComma = "&#x3001;";
+var ModifierColon = "&#xA789;";
+
+function test() {
+ setPrefs(false, true, test1);
+}
+
+function test1() {
+ editor.innerHTML = "Hello Kitty";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 5);
+ testRight(editor.firstChild, 11);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello</b> Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling, 6);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello </b>Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 5);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.firstChild, 6);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Log out</b> roc";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 3);
+ testRight(editor.firstChild.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling, 5);
+ // In the next test, we expect to be at the end of the
+ // space that is not collapsed away
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 4);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "http://www.mozilla.org";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 7);
+ testRight(editor.firstChild, 11);
+ testRight(editor.firstChild, 19);
+ testLeft(editor.firstChild, 11);
+ testLeft(editor.firstChild, 7);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "Set .rc to <b>'</b>quiz'";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 3);
+ testRight(editor.firstChild, 7);
+ testRight(editor.firstChild, 10);
+ testRight(editor.firstChild.nextSibling.nextSibling, 5);
+ testLeft(editor.firstChild.nextSibling.firstChild, 1);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 5);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + HiraganaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + KatakanaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + HiraganaChars + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 4);
+ testRight(editor.firstChild, 8);
+ testRight(editor.firstChild, 12);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 4);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = HiraganaChars + JapaneseComma + HiraganaChars + JapaneseFullStop + HiraganaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 5);
+ testRight(editor.firstChild, 10);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 10);
+ testLeft(editor.firstChild, 5);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + JapaneseComma + KatakanaChars + JapaneseFullStop + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 5);
+ testRight(editor.firstChild, 10);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 10);
+ testLeft(editor.firstChild, 5);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + JapaneseComma + ChineseChars + JapaneseFullStop + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 3);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 3);
+ testLeft(editor.firstChild, 0);
+
+ // test for bug 1066756
+ editor.innerHTML = "hello" + ModifierColon + " wo" + ModifierColon + "rld";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 13);
+ testLeft(editor.firstChild, 7);
+ testLeft(editor.firstChild, 0);
+
+ // test basic word movement with eat_space_next_to_word true.
+ setPrefs(true, true, test2);
+}
+
+function test2() {
+ editor.innerHTML = "Hello Kitty";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 11);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello</b> Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.nextSibling, 1);
+ testRight(editor.firstChild.nextSibling, 6);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello </b>Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.firstChild, 6);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Log out</b> roc";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 4);
+ testRight(editor.firstChild.nextSibling, 2);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 4);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "http://www.mozilla.org";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 7);
+ testRight(editor.firstChild, 11);
+ testRight(editor.firstChild, 19);
+ testLeft(editor.firstChild, 11);
+ testLeft(editor.firstChild, 7);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "Set .rc to <b>'</b>quiz'";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 4);
+ testRight(editor.firstChild, 8);
+ testRight(editor.firstChild.nextSibling.firstChild, 0);
+ testRight(editor.firstChild.nextSibling.nextSibling, 5);
+ testLeft(editor.firstChild.nextSibling.firstChild, 1);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 5);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + HiraganaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + KatakanaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + HiraganaChars + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 4);
+ testRight(editor.firstChild, 8);
+ testRight(editor.firstChild, 12);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 4);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = HiraganaChars + JapaneseComma + HiraganaChars + JapaneseFullStop + HiraganaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 5);
+ testRight(editor.firstChild, 10);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 10);
+ testLeft(editor.firstChild, 5);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + JapaneseComma + KatakanaChars + JapaneseFullStop + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 5);
+ testRight(editor.firstChild, 10);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 10);
+ testLeft(editor.firstChild, 5);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + JapaneseComma + ChineseChars + JapaneseFullStop + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 3);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 3);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "hello" + ModifierColon + " wo" + ModifierColon + "rld";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 7);
+ testRight(editor.firstChild, 13);
+ testLeft(editor.firstChild, 7);
+ testLeft(editor.firstChild, 0);
+
+ // Test basic word movement with stop_at_punctuation false (bug 981281).
+ setPrefs(false, false, test3);
+}
+
+function test3() {
+ editor.innerHTML = "Hello Kitty";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 5);
+ testRight(editor.firstChild, 11);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello</b> Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling, 6);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello </b>Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 5);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.firstChild, 6);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Log out</b> roc";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 3);
+ testRight(editor.firstChild.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 4);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "http://www.mozilla.org";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 22);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "Set .rc to <b>'</b>quiz'";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 3);
+ testRight(editor.firstChild, 7);
+ testRight(editor.firstChild, 10);
+ testRight(editor.firstChild.nextSibling.nextSibling, 5);
+ testLeft(editor.firstChild, 11);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 4);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + HiraganaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + KatakanaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + HiraganaChars + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 4);
+ testRight(editor.firstChild, 8);
+ testRight(editor.firstChild, 12);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 4);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = HiraganaChars + JapaneseComma + HiraganaChars + JapaneseFullStop + HiraganaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + JapaneseComma + KatakanaChars + JapaneseFullStop + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + JapaneseComma + ChineseChars + JapaneseFullStop + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "hello" + ModifierColon + " wo" + ModifierColon + "rld";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 13);
+ testLeft(editor.firstChild, 7);
+ testLeft(editor.firstChild, 0);
+
+ // And again with eat_space_next_to_word true.
+ setPrefs(true, false, test4);
+}
+
+function test4() {
+ editor.innerHTML = "Hello Kitty";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 11);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello</b> Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.nextSibling, 1);
+ testRight(editor.firstChild.nextSibling, 6);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Hello </b>Kitty";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.nextSibling, 0);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.firstChild, 6);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "<b>Log out</b> roc";
+ sel.collapse(editor.firstChild.firstChild, 0);
+ testRight(editor.firstChild.firstChild, 4);
+ testRight(editor.firstChild.nextSibling, 2);
+ testRight(editor.firstChild.nextSibling, 5);
+ testLeft(editor.firstChild.nextSibling, 1);
+ testLeft(editor.firstChild.firstChild, 4);
+ testLeft(editor.firstChild.firstChild, 0);
+
+ editor.innerHTML = "http://www.mozilla.org";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 22);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "Set .rc to <b>'</b>quiz'";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 4);
+ testRight(editor.firstChild, 8);
+ testRight(editor.firstChild.nextSibling.firstChild, 0);
+ testRight(editor.firstChild.nextSibling.nextSibling, 5);
+ testLeft(editor.firstChild, 11);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 4);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + HiraganaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + KatakanaChars + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 2);
+ testRight(editor.firstChild, 6);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 6);
+ testLeft(editor.firstChild, 2);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + HiraganaChars + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 4);
+ testRight(editor.firstChild, 8);
+ testRight(editor.firstChild, 12);
+ testLeft(editor.firstChild, 8);
+ testLeft(editor.firstChild, 4);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = HiraganaChars + JapaneseComma + HiraganaChars + JapaneseFullStop + HiraganaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = KatakanaChars + JapaneseComma + KatakanaChars + JapaneseFullStop + KatakanaChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 14);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = ChineseChars + JapaneseComma + ChineseChars + JapaneseFullStop + ChineseChars;
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 8);
+ testLeft(editor.firstChild, 0);
+
+ editor.innerHTML = "hello" + ModifierColon + " wo" + ModifierColon + "rld";
+ sel.collapse(editor.firstChild, 0);
+ testRight(editor.firstChild, 7);
+ testRight(editor.firstChild, 13);
+ testLeft(editor.firstChild, 7);
+ testLeft(editor.firstChild, 0);
+
+ SimpleTest.finish();
+}
+
+
+</script></pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_overflow_event.html b/layout/generic/test/test_overflow_event.html
new file mode 100644
index 000000000..a29dd12f4
--- /dev/null
+++ b/layout/generic/test/test_overflow_event.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for overflow events</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ window.addEventListener("load", function(event) {
+ if (event.target == document) {
+ test_bug981637();
+ }
+ }, false);
+
+ /** Test for Bug 981637: correct overflow event firing in updateOverflow **/
+
+ var overflow_fired = false;
+ function overflow_listener(event) {
+ overflow_fired = true;
+ }
+
+ function test_bug981637() {
+ var outerDiv = document.getElementById("bug981637");
+ var innerDiv = outerDiv.firstChild;
+ innerDiv.offsetWidth; // flush layout
+ is(overflow_fired, false, "correct setup");
+ outerDiv.addEventListener("overflow", overflow_listener, false);
+ innerDiv.style.transform = "scale(1.2)";
+ innerDiv.offsetWidth; // flush layout
+ // run finish step after the overflow event fires (off the event loop)
+ setTimeout(test_bug981637_step2, 0);
+ }
+
+ function test_bug981637_step2() {
+ is(overflow_fired, true, "overflow event should have fired after updating transform");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=981637">Mozilla Bug 981637</a>
+<div id="bug981637" style="overflow: hidden; width: 100px; height: 100px;"><div style="transform: scale(0.8); width: 100px; height: 100px"></div></div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_page_scroll_with_fixed_pos.html b/layout/generic/test/test_page_scroll_with_fixed_pos.html
new file mode 100644
index 000000000..551d6e299
--- /dev/null
+++ b/layout/generic/test/test_page_scroll_with_fixed_pos.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no" />
+ <title>Scrolling by pages with fixed-pos headers and footers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<script class="testbody">
+SimpleTest.waitForExplicitFinish();
+window.open("./page_scroll_with_fixed_pos_window.html", "", "width=600,height=600");
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_clipping.xhtml b/layout/generic/test/test_plugin_clipping.xhtml
new file mode 100644
index 000000000..479317695
--- /dev/null
+++ b/layout/generic/test/test_plugin_clipping.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping">
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+window.open("plugin_clipping_helper.xhtml", "", "width=620,height=320");
+SimpleTest.waitForExplicitFinish();
+
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_clipping2.xhtml b/layout/generic/test/test_plugin_clipping2.xhtml
new file mode 100644
index 000000000..2bf879b5a
--- /dev/null
+++ b/layout/generic/test/test_plugin_clipping2.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping: Dynamic Tests">
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+if (navigator.platform.indexOf("Mac") == 0) {
+ todo(false, "Mac plugins do not support complex clipping");
+} else {
+ window.open("plugin_clipping_helper2.xhtml", "", "width=620,height=320");
+ SimpleTest.waitForExplicitFinish();
+}
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_clipping_table.xhtml b/layout/generic/test/test_plugin_clipping_table.xhtml
new file mode 100644
index 000000000..8a842ab6c
--- /dev/null
+++ b/layout/generic/test/test_plugin_clipping_table.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping: Plugins and Tables">
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+window.open("plugin_clipping_helper_table.xhtml", "", "width=620,height=320");
+SimpleTest.waitForExplicitFinish();
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_clipping_transformed.xhtml b/layout/generic/test/test_plugin_clipping_transformed.xhtml
new file mode 100644
index 000000000..ae64c25c2
--- /dev/null
+++ b/layout/generic/test/test_plugin_clipping_transformed.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Clipping: Plugins in Transforms">
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var child;
+function childDone() {
+ child.close();
+ SimpleTest.executeSoon(SimpleTest.finish);
+}
+
+if (navigator.platform.indexOf("Mac") == 0) {
+ todo(false, "Mac plugins do not run in windowed mode");
+} else {
+ SimpleTest.waitForExplicitFinish();
+ child = window.open("plugin_clipping_helper_transformed.xhtml", "", "width=620,height=320");
+}
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_focus.html b/layout/generic/test/test_plugin_focus.html
new file mode 100644
index 000000000..8c368497c
--- /dev/null
+++ b/layout/generic/test/test_plugin_focus.html
@@ -0,0 +1,28 @@
+<html title="Test plugin focus control">
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script class="testbody" type="application/javascript">
+
+var child;
+function childDone() {
+ child.close();
+ // This test has a history of focus issues, but waiting for focus to return
+ // from the child window instead of attempting to reclaim it after finish
+ // seems to prevent some kind of race on win8 (Bug 874843)
+ SimpleTest.waitForFocus(SimpleTest.finish, window);
+}
+
+SimpleTest.waitForExplicitFinish();
+child = window.open("plugin_focus_helper.html", "", "width=620,height=320");
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_mouse_coords.html b/layout/generic/test/test_plugin_mouse_coords.html
new file mode 100644
index 000000000..cba915ec6
--- /dev/null
+++ b/layout/generic/test/test_plugin_mouse_coords.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test delivering mouse events to plugins</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ embed { width:200px; height:200px; display:block; }
+ iframe { border:none; }
+ </style>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: block">
+ <embed id="p1" type="application/x-test"
+ style="position:absolute; left:300px; top:10px;"></embed>
+ <iframe id="f1" style="position:absolute; left:0; top:250px;"
+ src="data:text/html,&lt;embed id='p2' type='application/x-test' style='position:absolute; left:10px; top:10px'&gt;"></iframe>
+ <embed id="p3" type="application/x-test"
+ style="position:absolute; left:320px; top:250px;
+ outline:5px solid blue;
+ border:solid black; border-width:4px 8px 4px 8px;
+ padding:3px 1px;"></embed>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function doTest() {
+ var p1 = document.getElementById("p1");
+ synthesizeMouse(p1, 15, 18, { type:"mousedown" });
+ is(p1.getLastMouseX(), 15, "p1 mouse down X");
+ is(p1.getLastMouseY(), 18, "p1 mouse down Y");
+ synthesizeMouse(p1, 15, 38, { type:"mousemove" });
+ is(p1.getLastMouseX(), 15, "p1 mouse move X");
+ is(p1.getLastMouseY(), 38, "p1 mouse move Y");
+ synthesizeMouse(p1, 15, 28, { type:"mouseup" });
+ is(p1.getLastMouseX(), 15, "p1 mouse up X");
+ is(p1.getLastMouseY(), 28, "p1 mouse up Y");
+
+ var f1 = document.getElementById("f1");
+ var p2 = f1.contentDocument.getElementById("p2");
+ synthesizeMouse(p2, 15, 18, { type:"mousedown" }, f1.contentWindow);
+ is(p2.getLastMouseX(), 15, "p2 mouse down X");
+ is(p2.getLastMouseY(), 18, "p2 mouse down Y");
+ synthesizeMouse(p2, 15, 38, { type:"mousemove" }, f1.contentWindow);
+ is(p2.getLastMouseX(), 15, "p2 mouse move X");
+ is(p2.getLastMouseY(), 38, "p2 mouse move Y");
+ synthesizeMouse(p2, 15, 28, { type:"mouseup" }, f1.contentWindow);
+ is(p2.getLastMouseX(), 15, "p2 mouse up X");
+ is(p2.getLastMouseY(), 28, "p2 mouse up Y");
+
+ var p3 = document.getElementById("p3");
+ // The synthesized coordinates are relative to the border-box, but the plugin
+ // is at offset (9,7) relative to the border-box
+ synthesizeMouse(p3, 15, 18, { type:"mousedown" });
+ is(p3.getLastMouseX(), 6, "p3 mouse down X");
+ is(p3.getLastMouseY(), 11, "p3 mouse down Y");
+ synthesizeMouse(p3, 15, 38, { type:"mousemove" });
+ is(p3.getLastMouseX(), 6, "p3 mouse move X");
+ is(p3.getLastMouseY(), 31, "p3 mouse move Y");
+ synthesizeMouse(p3, 15, 28, { type:"mouseup" });
+ is(p3.getLastMouseX(), 6, "p3 mouse up X");
+ is(p3.getLastMouseY(), 21, "p3 mouse up Y");
+
+ SimpleTest.finish();
+}
+// Need to run 'doTest' after painting is unsuppressed, or we'll set clip
+// regions to empty.
+addLoadEvent(function() { setTimeout(doTest, 1000); } );
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_plugin_position.xhtml b/layout/generic/test/test_plugin_position.xhtml
new file mode 100644
index 000000000..43190e543
--- /dev/null
+++ b/layout/generic/test/test_plugin_position.xhtml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<html xmlns="http://www.w3.org/1999/xhtml" title="Test Plugin Positioning">
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+<!-- Use a XUL element here so we can get its boxObject.screenX/Y -->
+<hbox style="height:10px; position:absolute; left:0; top:0; z-index:-100;" id="h1"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox style="width:100px;"></hbox><hbox id="h2"/>
+</hbox>
+
+<embed id="p" type="application/x-test" width="200" height="200" wmode="window"></embed>
+<embed id="p2" type="application/x-test" wmode="window"
+ style="outline:5px solid blue; width:200px; height:200px;
+ border:solid black; border-width:4px 8px 4px 8px;
+ padding:3px 1px;">
+</embed>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var windowFrameX, windowFrameY;
+
+function checkGeometry(id, x, y, w, h) {
+ var p = document.getElementById(id);
+ var bounds = p.getBoundingClientRect();
+ var pX = p.getEdge(0);
+ var pY = p.getEdge(1);
+ var pWidth = p.getEdge(2) - pX;
+ var pHeight = p.getEdge(3) - pY;
+
+ is(pX, windowFrameX + bounds.left + x, id + " plugin X");
+ is(pY, windowFrameY + bounds.top + y, id + " plugin Y");
+ is(pWidth, w, id + " plugin width");
+ is(pHeight, h, id + " plugin height");
+}
+
+function runTests() {
+ var h1 = document.getElementById("h1");
+ var h2 = document.getElementById("h2");
+ var hwidth = h2.boxObject.screenX - h1.boxObject.screenX;
+ if (hwidth != 100) {
+ // Maybe it's a DPI issue
+ todo(false, "Unexpected DPI?");
+ SimpleTest.finish();
+ return;
+ }
+
+ if (!document.getElementById("p").identifierToStringTest) {
+ todo(false, "Test plugin not available");
+ SimpleTest.finish();
+ return;
+ }
+
+ var bounds = h1.getBoundingClientRect();
+ windowFrameX = h1.boxObject.screenX - bounds.left - window.screenX;
+ windowFrameY = h1.boxObject.screenY - bounds.top - window.screenY;
+
+ checkGeometry("p", 0, 0, 200, 200);
+ // This one tests widget positioning in the presence of borders and padding
+ checkGeometry("p2", 9, 7, 182, 186);
+
+ SimpleTest.finish();
+}
+
+// When load events run, painting may still be suppressed for the window.
+// While painting is suppressed, plugins are hidden so won't be positioned
+// or sized as expected.
+// So call runTests after the load event has finished, when painting will
+// be unsuppressed.
+addLoadEvent(function() { setTimeout(runTests, 1000); });
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+]]>
+</script>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_scroll_animation_restore.html b/layout/generic/test/test_scroll_animation_restore.html
new file mode 100644
index 000000000..b94feac9d
--- /dev/null
+++ b/layout/generic/test/test_scroll_animation_restore.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1247074
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1247074</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .outer {
+ direction: ltr;
+ height: 400px;
+ width: 415px;
+ overflow: hidden;
+ position: relative;
+ }
+ .inner {
+ height: 100%;
+ outline: none;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ .outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247074">Mozilla Bug 1247074</a>
+<p id="display"></p>
+<div class="outer">
+ <div class="inner">
+ <ol>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ </ol>
+ </div>
+</div>
+<script>
+SimpleTest.waitForExplicitFinish();
+window.onload = function() {
+ var elm = document.getElementsByClassName('inner')[0];
+
+ // Take control of the refresh driver
+ var utils = SpecialPowers.DOMWindowUtils;
+ utils.advanceTimeAndRefresh(0);
+
+ // Start a smooth scroll and advance a couple of frames so we're in the
+ // middle of the scroll animation
+ elm.scrollTop = 500;
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+
+ // Trigger a frame reconstruction
+ elm.parentNode.classList.add('contentBefore');
+
+ // Reach a stable state and verify the scroll position is 500
+ utils.restoreNormalRefresh();
+ waitForAllPaintsFlushed(function() {
+ SimpleTest.is(elm.scrollTop, 500, "Scroll position ended up at 500");
+ SimpleTest.finish();
+ });
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/generic/test/test_scroll_behavior.html b/layout/generic/test/test_scroll_behavior.html
new file mode 100644
index 000000000..4508e84c3
--- /dev/null
+++ b/layout/generic/test/test_scroll_behavior.html
@@ -0,0 +1,309 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for CSSOM-View Smooth-Scroll DOM API Methods and MSD Animation</title>
+ <style>
+ #scroll_behavior_test_body {
+ width: 100000px;
+ height: 100000px;
+ }
+ .scroll_to_target {
+ position: absolute;
+ left: 20000px;
+ top: 10000px;
+ width: 200px;
+ height: 200px;
+ background-color: rgb(0, 0, 255);
+ }
+ </style>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ function clamp(val, minVal, maxVal) {
+ return Math.max(minVal, Math.min(maxVal, val));
+ }
+
+ window.addEventListener("load", function(event) {
+ if (event.target != document)
+ return;
+
+ // See bug 1062609 - these tests do not work with APZ yet. If APZ is
+ // enabled, end the tests early.
+ if (SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled")) {
+ todo(false, "This test does not yet work with APZ.");
+ SimpleTest.finish();
+ return;
+ }
+
+ SpecialPowers.pushPrefEnv(
+ { 'set': [['layout.css.scroll-behavior.enabled', true]] },
+ function () {
+ testScrollBehaviorInterruption(function() {
+ testScrollBehaviorFramerate(function() {
+ window.scrollTo(0,0);
+ SimpleTest.finish();
+ });
+ });
+ }
+ );
+ }, false);
+
+
+ function testScrollBehaviorInterruption(nextTest) {
+ // Take control of refresh driver
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0);
+ waitForAllPaintsFlushed(function() {
+
+ window.scrollTo(10, 9);
+ ok(window.scrollX == 10 && window.scrollY == 9,
+ "instant scroll-behavior must be synchronous when setting initial position");
+
+ window.scrollTo(15, 16);
+ ok(window.scrollX == 15 && window.scrollY == 16,
+ "instant scroll-behavior must be synchronous when setting new position");
+
+ window.scrollTo({left: 100, top: 200, behavior: 'smooth'});
+ ok(window.scrollX == 15 && window.scrollY == 16,
+ "smooth scroll-behavior must be asynchronous");
+
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100);
+ waitForAllPaintsFlushed(function() {
+ ok(window.scrollX != 15 && window.scrollY != 16
+ && window.scrollX != 100 && window.scrollY != 200,
+ "smooth scroll-behavior must be triggered by window.scrollTo");
+
+ window.scrollTo(50, 52);
+ ok(window.scrollX == 50 && window.scrollY == 52,
+ "instant scroll-behavior must interrupt smooth scroll-behavior animation");
+
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100);
+ waitForAllPaintsFlushed(function() {
+ ok(window.scrollX == 50 && window.scrollY == 52,
+ "smooth scroll-behavior animation must stop after being interrupted");
+
+ // Release control of refresh driver
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+ waitForAllPaintsFlushed(nextTest);
+ });
+ });
+ });
+ }
+
+ function testScrollBehaviorFramerate(nextTest) {
+ /**
+ * CSSOM-View scroll-behavior smooth scroll animations must produce the
+ * same results indendently of frame-rate:
+ *
+ * - Reference samples of scroll position for each frame are captured from
+ * a smooth scroll at 120fps for variations in X-Distance, Y-Distance.
+ * - Test samples are captured from an animation with the same parameters
+ * at varying framerates.
+ * - Variance in position at each sampled interval is compared to the
+ * 120fps reference. To pass the test, the position of each test
+ * sample must match the reference position with a tolerance of one test
+ * sample frame's range of motion. This range of motion is calculated
+ * by the position delta of the reference samples one test frame duration
+ * before and after.
+ * - The duration of the reference sample animation and the test sample
+ * animation must match within 1 frame to pass the test.
+ * - The simulation driving the animation must converge and stop on the
+ * destination position for the test to pass.
+ */
+
+ // Use 120hz for reference samples
+ var referenceFrameRate = 120;
+
+ var frameRates = [ 13, 60 ];
+ var deltas = [ {x: 0, y: 0},
+ {x: 1, y: 100},
+ {x: -100, y: 50000} ];
+
+ var deltaIndex = 0;
+
+ function testDeltas() {
+ if(deltaIndex >= deltas.length) {
+ nextTest();
+ return;
+ }
+ var deltaX = deltas[deltaIndex].x;
+ var deltaY = deltas[deltaIndex].y;
+ deltaIndex++;
+
+ // startX and startY must be at least as big as the greatest negative
+ // number in the deltas array in order to prevent the animation from
+ // being interrupted by scroll range boundaries.
+ var startX = 1000;
+ var startY = 1000;
+ var endX = startX + deltaX;
+ var endY = startY + deltaY;
+ var referenceTimeStep = Math.floor(1000 / referenceFrameRate);
+
+ sampleAnimation(startX, startY, endX, endY,
+ referenceTimeStep, function(refSamples) {
+
+ var referenceDuration = refSamples.length * referenceTimeStep; // ms
+
+ var frameRateIndex=0;
+
+ function testFrameRate() {
+ if(frameRateIndex < frameRates.length) {
+ var frameRate = frameRates[frameRateIndex++];
+ var testTimeStep = Math.floor(1000 / frameRate);
+
+ sampleAnimation(startX, startY, endX, endY,
+ testTimeStep, function(testSamples) {
+ var testDuration = testSamples.length * testTimeStep; // ms
+
+ // Variance in duration of animation must be accurate to within one
+ // frame interval
+ var durationVariance = Math.max(0, Math.abs(testDuration - referenceDuration) - testTimeStep);
+ is(durationVariance, 0, 'Smooth scroll animation duration must not '
+ + 'be framerate dependent at deltaX: ' + deltaX + ', deltaY: '
+ + deltaY + ', frameRate: ' + frameRate + 'fps');
+
+ var maxVariance = 0;
+ testSamples.forEach(function(sample, sampleIndex) {
+
+ var testToRef = refSamples.length / testSamples.length;
+ var refIndexThisFrame = clamp(Math.floor(sampleIndex * testToRef),
+ 0, refSamples.length - 1);
+ var refIndexPrevFrame = clamp(Math.floor((sampleIndex - 1) * testToRef),
+ 0, refSamples.length - 1);
+ var refIndexNextFrame = clamp(Math.floor((sampleIndex + 1) * testToRef),
+ 0, refSamples.length - 1);
+
+ var refSampleThisFrame = refSamples[refIndexThisFrame];
+ var refSamplePrevFrame = refSamples[refIndexPrevFrame];
+ var refSampleNextFrame = refSamples[refIndexNextFrame];
+
+ var refXMin = Math.min(refSamplePrevFrame[0],
+ refSampleThisFrame[0],
+ refSampleNextFrame[0]);
+
+ var refYMin = Math.min(refSamplePrevFrame[1],
+ refSampleThisFrame[1],
+ refSampleNextFrame[1]);
+
+ var refXMax = Math.max(refSamplePrevFrame[0],
+ refSampleThisFrame[0],
+ refSampleNextFrame[0]);
+
+ var refYMax = Math.max(refSamplePrevFrame[1],
+ refSampleThisFrame[1],
+ refSampleNextFrame[1]);
+
+ // Varience is expected to be at most 1 pixel beyond the range,
+ // due to integer rounding of pixel position.
+ var positionTolerance = 1; // 1 pixel
+
+ maxVariance = Math.max(maxVariance,
+ refXMin - sample[0] - positionTolerance,
+ sample[0] - refXMax - positionTolerance,
+ refYMin - sample[1] - positionTolerance,
+ sample[1] - refYMax - positionTolerance);
+ });
+
+ is(maxVariance, 0, 'Smooth scroll animated position must not be '
+ + 'framerate dependent at deltaX: ' + deltaX + ', deltaY: '
+ + deltaY + ', frameRate: ' + frameRate + 'fps');
+ testFrameRate();
+ });
+ } else {
+ waitForAllPaintsFlushed(testDeltas);
+ }
+ }
+
+ testFrameRate();
+
+ });
+ }
+
+ testDeltas();
+
+ }
+
+ function sampleAnimation(startX, startY, endX, endY, timeStep, callback) {
+ // The animation must be stopped at the destination position for
+ // minStoppedFrames consecutive frames to detect that the animation has
+ // completed.
+ var minStoppedFrames = 15; // 15 frames
+
+ // In case the simulation fails to converge, the test will time out after
+ // processing maxTime milliseconds of animation.
+ var maxTime = 10000; // 10 seconds
+
+ var positionSamples = [];
+
+ var frameCountAtDestination = 0;
+
+ // Take control of refresh driver so we can synthesize
+ // various frame rates
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0);
+ waitForAllPaintsFlushed(function() {
+
+ window.scrollTo(startX, startY);
+ window.scrollTo({left: endX, top: endY, behavior: 'smooth'});
+
+ var currentTime = 0; // ms
+
+ function advanceTime() {
+ if(currentTime < maxTime && frameCountAtDestination < 15) {
+
+ positionSamples.push([window.scrollX, window.scrollY]);
+
+ currentTime += timeStep;
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(timeStep);
+ waitForAllPaintsFlushed(function() {
+ if (window.scrollX == endX && window.scrollY == endY) {
+ frameCountAtDestination++;
+ } else {
+ frameCountAtDestination = 0;
+ }
+
+ advanceTime();
+ });
+
+ } else {
+ isnot(frameCountAtDestination, 0,
+ 'Smooth scrolls must always end at their destination '
+ + 'unless they are interrupted, at deltaX: '
+ + (endX - startX) + ', deltaY: ' + (endY - startY));
+
+ window.scrollTo(0, 0);
+
+ // Release control of refresh driver
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+
+ waitForAllPaintsFlushed(function() {
+
+ // We must not include the duplicated frames at the animation
+ // destination as the tests are dependant on the total duration of
+ // the animation to be accurate.
+ positionSamples.splice(1 - minStoppedFrames,
+ minStoppedFrames - 1);
+
+ callback(positionSamples);
+ });
+ }
+ }
+
+ advanceTime();
+
+ });
+ }
+
+ </script>
+</head>
+<body>
+<pre id="test">
+</pre>
+
+<div id="scroll_behavior_test_body">
+ <div id="scroll_to_target" class="scroll_to_target"></div>
+</body>
+</html>
diff --git a/layout/generic/test/test_scroll_position_iframe.html b/layout/generic/test/test_scroll_position_iframe.html
new file mode 100644
index 000000000..7dc03bcfc
--- /dev/null
+++ b/layout/generic/test/test_scroll_position_iframe.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1305579
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1305579</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1305579">Mozilla Bug 1305579</a>
+<p id="display"></p>
+<style>
+.off {
+ display:none;
+}
+</style>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function runtest() {
+ var iframe = document.getElementById("iframe");
+ iframe.contentDocument.scrollingElement.scrollTop = 50;
+ iframe.classList.toggle("off");
+ waitForAllPaintsFlushed(function() {
+ iframe.classList.toggle("off");
+ is(iframe.contentDocument.scrollingElement.scrollTop, 50, "scroll position restored");
+ SimpleTest.finish();
+ });
+}
+</script>
+<iframe onload="runtest()" id="iframe" class="" src="data:text/html,<p>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br></p>"></iframe><br>
+</body>
+</html>
diff --git a/layout/generic/test/test_scroll_position_restore.html b/layout/generic/test/test_scroll_position_restore.html
new file mode 100644
index 000000000..252515049
--- /dev/null
+++ b/layout/generic/test/test_scroll_position_restore.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1269539
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1269539</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1269539">Mozilla Bug 1269539</a>
+<p id="display"></p>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+var loadCount = 0;
+var childWin = window.open('file_scroll_position_restore.html', '_blank');
+
+function handleLoad() {
+ if (loadCount == 0) {
+ loadCount++;
+ childWin.scrollTo(0, childWin.scrollMaxY);
+ childWin.waitForAllPaintsFlushed(function() {
+ childWin.location.reload();
+ });
+ } else {
+ childWin.waitForAllPaintsFlushed(function() {
+ // Verify that the scroll position was retained
+ is(childWin.scrollY, childWin.scrollMaxY);
+ childWin.close();
+ SimpleTest.finish();
+ });
+ }
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/generic/test/test_selection_expanding.html b/layout/generic/test/test_selection_expanding.html
new file mode 100644
index 000000000..fad31a65d
--- /dev/null
+++ b/layout/generic/test/test_selection_expanding.html
@@ -0,0 +1,409 @@
+<!DOCTYPE>
+<html>
+<head>
+<title>selection expanding test</title>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<style type="text/css">
+ .testingDiv {
+ font-size: 16px;
+ width: 300px;
+ height: 140px;
+ background-color: white;
+ }
+ #fixedDiv1, #fixedDiv2 {
+ position: fixed;
+ right: 0;
+ overflow: scroll;
+ width: 200px;
+ }
+ #fixedDiv1 {
+ top: 0;
+ }
+ #fixedDiv2 {
+ top: 150px;
+ }
+ iframe, input, textarea {
+ font-size: 16px;
+ height: 16px;
+ width: 80px;
+ margin: 0;
+ padding: 0;
+ }
+ #xbl {
+ -moz-binding: url(selection_expanding_xbl.xml#binding);
+ }
+</style>
+
+</head>
+<body>
+<div id="div1" class="testingDiv">
+ aaaaaaa
+ <iframe id="iframe" src="data:text/html,<style type='text/css'>*{margin: 0; padding: 0; font-size: 16px;}</style><div>ffffff ffffff ffffff ffffff</div>"></iframe>
+ aaaaaaa aaaaaaa<br>aaaaaaa aaaaaaa aaaaaaa aaaaaaa<br>aaaaaaa
+</div>
+<div id="div2" class="testingDiv">
+ bbbbbbb
+ <input id="input" type="text" value="iiiiiiiii iiiiiiiii iiiiiiiii">
+ bbbbbbb bbbbbbb<br>bbbbbbb bbbbbbb bbbbbbb<br>bbbbbbb
+</div>
+<div id="div3" class="testingDiv">
+ ccccccc
+ <textarea id="textarea">tttttt tttttt tttttt</textarea>
+ ccccccc ccccccc<br>ccccccc ccccccc ccccccc ccccccc<br>ccccccc
+ <div id="fixedDiv1" class="testingDiv">
+ dddddd dddddd dddddd
+ </div>
+</div>
+<div id="xbl">
+ <p id="xbl_child">yyyyyyy yyyyyyy yyyyyyy</p>
+</div>
+<div id="fixedDiv2" class="testingDiv">
+ eeeeee eeeeee eeeeee
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var div1 = document.getElementById("div1");
+var div2 = document.getElementById("div2");
+var div3 = document.getElementById("div3");
+var xbl = document.getElementById("xbl");
+var xbl_child = document.getElementById("xbl_child");
+var fixedDiv1 = document.getElementById("fixedDiv1");
+var fixedDiv2 = document.getElementById("fixedDiv2");
+var iframe = document.getElementById("iframe");
+var input = document.getElementById("input");
+var textarea = SpecialPowers.wrap(document.getElementById("textarea"));
+
+function test()
+{
+ function getSelectionForEditor(aEditorElement)
+ {
+ const nsIDOMNSEditableElement = SpecialPowers.Ci.nsIDOMNSEditableElement;
+ return SpecialPowers.wrap(aEditorElement).editor.selection;
+ }
+
+ function clear()
+ {
+ synthesizeMouse(div1, 10, 5, { type: "mouseup" });
+ var sel = window.getSelection();
+ if (sel.rangeCount > 0)
+ sel.collapseToEnd();
+ sel = iframe.contentWindow.getSelection();
+ if (sel.rangeCount > 0)
+ sel.collapseToEnd();
+ sel = getSelectionForEditor(input);
+ if (sel.rangeCount > 0)
+ sel.collapseToEnd();
+ sel = getSelectionForEditor(textarea);
+ if (sel.rangeCount > 0)
+ sel.collapseToEnd();
+
+ div1.scrollTop = 0;
+ div1.scrollLeft = 0;
+ div2.scrollTop = 0;
+ div2.scrollLeft = 0;
+ div3.scrollTop = 0;
+ div3.scrollLeft = 0;
+ }
+
+ const kFalse = 0;
+ const kTrue = 1;
+ const kToDo = 2;
+
+ function check(aDiv1ShouldBeSelected,
+ aDiv2ShouldBeSelected,
+ aDiv3ShouldBeSelected,
+ aFixedDiv1ShouldBeSelected,
+ aXBLChildShouldBeSelected,
+ aFixedDiv2ShouldBeSelected,
+ aIFrameShouldBeSelected,
+ aInputShouldBeSelected,
+ aTextareaShouldBeSelected,
+ aTestingDescription)
+ {
+ function checkCharacter(aSelectedText,
+ aShouldBeIncludedCharacter,
+ aSouldBeSelected,
+ aElementName)
+ {
+ var boolvalue = aSouldBeSelected & kTrue;
+ var f = aSouldBeSelected & kToDo ? todo : ok;
+ var str = aSelectedText.replace('\n', '\\n');
+ if (boolvalue) {
+ f(aSelectedText.indexOf(aShouldBeIncludedCharacter) >= 0,
+ "The contents of " + aElementName +
+ " aren't selected (" + aTestingDescription +
+ "): Selected String: \"" + str + "\"");
+ } else {
+ f(aSelectedText.indexOf(aShouldBeIncludedCharacter) < 0,
+ "The contents of " + aElementName +
+ " are selected (" + aTestingDescription +
+ "): Selected String: \"" + str + "\"");
+ }
+ }
+
+ var sel = window.getSelection().toString();
+ checkCharacter(sel, "a", aDiv1ShouldBeSelected, "div1");
+ checkCharacter(sel, "b", aDiv2ShouldBeSelected, "div2");
+ checkCharacter(sel, "c", aDiv3ShouldBeSelected, "div3");
+ checkCharacter(sel, "y", aXBLChildShouldBeSelected, "xbl_child");
+ checkCharacter(sel, "d", aFixedDiv1ShouldBeSelected, "fixedDiv1");
+ checkCharacter(sel, "e", aFixedDiv2ShouldBeSelected, "fixedDiv2");
+
+ // iframe/input/xbl-bound contents must not be included on the parent
+ // selection.
+ checkCharacter(sel, "f", false, "iframe (checking on parent)");
+ checkCharacter(sel, "i", false, "input (checking on parent)");
+ checkCharacter(sel, "x", false, "XBL bound contents (checking on parent)");
+
+ var selInIFrame = iframe.contentWindow.getSelection().toString();
+ checkCharacter(selInIFrame, "f", aIFrameShouldBeSelected, "iframe");
+
+ var selInput = getSelectionForEditor(input).toString();
+ checkCharacter(selInput, "i", aInputShouldBeSelected, "input");
+ var selTextarea = getSelectionForEditor(textarea).toString();
+ checkCharacter(selTextarea, "t", aTextareaShouldBeSelected, "textarea");
+ }
+
+ // ***********************************************************
+ // Set all divs to overflow: auto;
+ const kOverflows = ["visible", "hidden", "scroll", "auto"];
+ for (var i = 0; i < kOverflows.length; i++) {
+ div1.style.overflow = kOverflows[i];
+ div2.style.overflow = kOverflows[i];
+ div3.style.overflow = kOverflows[i];
+
+ // ***********************************************************
+ // selection starting at div1
+ synthesizeMouse(div1, 30, 5, { type: "mousedown" });
+
+ // XXX if we move the mouse cursor to another document, the
+ // nsFrameSelection::HandleDrag method is called on the another document's.
+
+ // to iframe
+ synthesizeMouse(iframe, 30, 5, { type: "mousemove" });
+ check(kTrue | kToDo, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div1-iframe, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // XXX if the overflow is visible, synthesizeMouse with the input element
+ // or textarea element doesn't work fine.
+ var isVisibleTesting = kOverflows[i] == "visible";
+ var todoFlag = isVisibleTesting ? kToDo : 0;
+ // to input
+ synthesizeMouse(input, 30, 5, { type: "mousemove" });
+ check(kTrue | todoFlag, kTrue | todoFlag, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div1-input, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to textarea
+ synthesizeMouse(textarea, 30, 5, { type: "mousemove" });
+ check(kTrue | todoFlag, kTrue | todoFlag, kTrue | todoFlag,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div1-textarea, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to div2
+ synthesizeMouse(div2, 30, 5, { type: "mousemove" });
+ check(kTrue, kTrue, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div1-div2, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to div3
+ synthesizeMouse(div3, 30, 5, { type: "mousemove" });
+ check(kTrue, kTrue, kTrue,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div1-div3, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to fixedDiv1 (child of div3)
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousemove" });
+ check(kTrue, kTrue, kTrue,
+ kTrue, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div1-fixedDiv1, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to xbl_child
+ synthesizeMouse(xbl_child, 30, 5, { type: "mousemove" });
+ check(kTrue, kTrue, kTrue,
+ kTrue, kTrue, kFalse, kFalse, kFalse, kFalse,
+ "div1-xbl_child, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to fixedDiv2 (sibling of div*)
+ synthesizeMouse(fixedDiv2, 30, 5, { type: "mousemove" });
+ check(kTrue, kTrue, kTrue,
+ kTrue, kTrue, kTrue, kFalse, kFalse, kFalse,
+ "div1-fixedDiv2, all boxes are overflow: " + kOverflows[i] + ";");
+
+ clear();
+
+ // ***********************************************************
+ // selection starting at fixedDiv1
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousedown" });
+
+ // to xbl_child
+ synthesizeMouse(xbl_child, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kTrue, kTrue, kFalse, kFalse, kFalse, kFalse,
+ "fixedDiv1-xbl_child, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to fixedDiv2
+ synthesizeMouse(fixedDiv2, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kTrue, kTrue, kTrue, kFalse, kFalse, kFalse,
+ "fixedDiv1-fixedDiv2, all boxes are overflow: " + kOverflows[i] + ";");
+
+ clear();
+
+ // ***********************************************************
+ // selection starting at fixedDiv2
+ synthesizeMouse(fixedDiv2, 30, 5, { type: "mousedown" });
+
+ // to xbl_child
+ synthesizeMouse(xbl_child, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kTrue, kTrue, kFalse, kFalse, kFalse,
+ "fixedDiv2-xbl_child, all boxes are overflow: " + kOverflows[i] + ";");
+
+ // to fixedDiv1
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kTrue, kTrue, kTrue, kFalse, kFalse, kFalse,
+ "fixedDiv2-fixedDiv1, all boxes are overflow: " + kOverflows[i] + ";");
+
+ clear();
+
+ // ***********************************************************
+ div2.style.overflow = "visible";
+
+ // ***********************************************************
+ // selection starting at div2
+ synthesizeMouse(div2, 30, 5, { type: "mousedown" });
+
+ // to div3
+ synthesizeMouse(div3, 30, 5, { type: "mousemove" });
+ check(kFalse, kTrue, kTrue,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div2-div3, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ // to fixedDiv1 (child of div3)
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousemove" });
+ check(kFalse, kTrue, kTrue,
+ kTrue, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div2-fixedDiv1, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ // to xbl_child
+ synthesizeMouse(xbl_child, 30, 5, { type: "mousemove" });
+ check(kFalse, kTrue, kTrue,
+ kTrue, kTrue, kFalse, kFalse, kFalse, kFalse,
+ "div2-xbl_child, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ // to fixedDiv2 (sibling of div*)
+ synthesizeMouse(fixedDiv2, 30, 5, { type: "mousemove" });
+ check(kFalse, kTrue, kTrue,
+ kTrue, kTrue, kTrue, kFalse, kFalse, kFalse,
+ "div2-fixedDiv2, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ clear();
+
+ // ***********************************************************
+ // selection starting at div3
+ synthesizeMouse(div3, 30, 5, { type: "mousedown" });
+
+ // to div2
+ synthesizeMouse(div2, 30, 5, { type: "mousemove" });
+ check(kFalse, kTrue, kTrue,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div3-div2, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ // to fixedDiv1 (child of div3)
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kTrue,
+ kTrue, kFalse, kFalse, kFalse, kFalse, kFalse,
+ "div3-fixedDiv1, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ // to xbl_child
+ synthesizeMouse(xbl_child, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kTrue,
+ kTrue, kTrue, kFalse, kFalse, kFalse, kFalse,
+ "div3-xbl_child, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ // to fixedDiv2 (sibling of div*)
+ synthesizeMouse(fixedDiv2, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kTrue,
+ kTrue, kTrue, kTrue, kFalse, kFalse, kFalse,
+ "div3-fixedDiv2, div3 is overflow: " + kOverflows[i] +
+ ";, but div2 is overflow: visible;");
+
+ clear();
+ }
+
+ // ***********************************************************
+ // selection starting at iframe
+ synthesizeMouse(iframe, 30, 5, { type: "mousedown" });
+
+ // inside iframe
+ synthesizeMouse(iframe, 50, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kTrue, kFalse, kFalse,
+ "iframe-iframe");
+
+ // to div2
+ synthesizeMouse(div2, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kTrue, kFalse, kFalse,
+ "iframe-div2");
+
+ clear();
+
+ // ***********************************************************
+ // selection starting at input
+ synthesizeMouse(input, 20, 5, { type: "mousedown" });
+
+ // inside input
+ synthesizeMouse(input, 40, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kTrue, kFalse,
+ "input-input");
+
+ // to div3
+ synthesizeMouse(div3, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kTrue, kFalse,
+ "input-div3");
+
+ clear();
+
+ // ***********************************************************
+ // selection starting at textarea
+ synthesizeMouse(textarea, 30, 5, { type: "mousedown" });
+
+ // inside textarea
+ synthesizeMouse(textarea, 50, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kTrue,
+ "textarea-textarea");
+
+ // to div2
+ synthesizeMouse(div2, 30, 5, { type: "mousemove" });
+ check(kFalse, kFalse, kFalse,
+ kFalse, kFalse, kFalse, kFalse, kFalse, kTrue,
+ "textarea-div2");
+
+ clear();
+
+ SimpleTest.finish();
+}
+window.onload = function() { setTimeout(test, 0); };
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_selection_preventDefault.html b/layout/generic/test/test_selection_preventDefault.html
new file mode 100644
index 000000000..08e483a13
--- /dev/null
+++ b/layout/generic/test/test_selection_preventDefault.html
@@ -0,0 +1,175 @@
+<!DOCTYPE>
+<html>
+<head>
+<title>selection preventDefault test</title>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<style type="text/css">
+ #fixedDiv1 {
+ position: fixed;
+ right: 0;
+ overflow: scroll;
+ width: 200px;
+ top: 0;
+ }
+ input {
+ font-size: 16px;
+ height: 16px;
+ width: 80px;
+ margin: 0;
+ padding: 0;
+ }
+</style>
+
+</head>
+<body>
+<input id="input" type="text" value="iiiiiiiii iiiiiiiii iiiiiiiii">
+<div id="fixedDiv1" class="testingDiv">
+dddddd dddddd dddddd
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var fixedDiv1 = document.getElementById("fixedDiv1");
+var input = document.getElementById("input");
+
+function test()
+{
+ function getSelectionForEditor(aEditorElement)
+ {
+ const nsIDOMNSEditableElement =
+ SpecialPowers.Ci.nsIDOMNSEditableElement;
+ return SpecialPowers.wrap(aEditorElement)
+ .QueryInterface(nsIDOMNSEditableElement).editor.selection;
+ }
+
+ function clear()
+ {
+ var sel = window.getSelection();
+ if (sel.rangeCount > 0)
+ sel.collapseToEnd();
+ sel = getSelectionForEditor(input);
+ if (sel.rangeCount > 0)
+ sel.collapseToEnd();
+ }
+
+ const kFalse = 0;
+ const kTrue = 1;
+ const kToDo = 2;
+
+ function check(aFixedDiv1ShouldBeSelected,
+ aInputShouldBeSelected,
+ aTestingDescription)
+ {
+ function checkCharacter(aSelectedText,
+ aShouldBeIncludedCharacter,
+ aSouldBeSelected,
+ aElementName)
+ {
+ var boolvalue = aSouldBeSelected & kTrue;
+ var f = aSouldBeSelected & kToDo ? todo : ok;
+ var str = aSelectedText.replace('\n', '\\n');
+ if (boolvalue) {
+ f(aSelectedText.indexOf(aShouldBeIncludedCharacter) >= 0,
+ "The contents of " + aElementName +
+ " aren't selected (" + aTestingDescription +
+ "): Selected String: \"" + str + "\"");
+ } else {
+ f(aSelectedText.indexOf(aShouldBeIncludedCharacter) < 0,
+ "The contents of " + aElementName +
+ " are selected (" + aTestingDescription +
+ "): Selected String: \"" + str + "\"");
+ }
+ }
+
+ var sel = window.getSelection().toString();
+ checkCharacter(sel, "d", aFixedDiv1ShouldBeSelected, "fixedDiv1");
+
+ // input contents must not be included on the parent
+ // selection.
+ checkCharacter(sel, "i", false, "input (checking on parent)");
+
+ var selInput = getSelectionForEditor(input).toString();
+ checkCharacter(selInput, "i", aInputShouldBeSelected, "input");
+ }
+
+ function eventHandler(evt) {
+ evt.preventDefault();
+ }
+
+ // prevent default action on mousedown should prevent selection
+ fixedDiv1.addEventListener("mousedown", eventHandler);
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousedown" });
+ synthesizeMouse(fixedDiv1, 40, 5, { type: "mousemove" });
+ synthesizeMouse(fixedDiv1, 40, 5, { type: "mouseup" });
+ check(kFalse, kFalse, "fixedDiv1-fixedDiv1-mousedown");
+ clear();
+
+ input.addEventListener("mousedown", eventHandler);
+ synthesizeMouse(input, 20, 5, { type: "mousedown" });
+ synthesizeMouse(input, 40, 5, { type: "mousemove" });
+ synthesizeMouse(input, 40, 5, { type: "mouseup" });
+ check(kFalse, kFalse, "input-input-mousedown");
+ clear();
+
+ // clean up mousedown listener
+ [fixedDiv1, input].forEach(function(element) {
+ element.removeEventListener("mousedown", eventHandler);
+ });
+
+ // prevent default action on mouseup should not affect the selection state
+ fixedDiv1.addEventListener("mouseup", eventHandler);
+ synthesizeMouse(fixedDiv1, 30, 5, { type: "mousedown" });
+ synthesizeMouse(fixedDiv1, 40, 5, { type: "mousemove" });
+ synthesizeMouse(fixedDiv1, 40, 5, { type: "mouseup" });
+ check(kTrue, kFalse, "fixedDiv1-fixedDiv1-mouseup");
+ clear();
+
+ input.addEventListener("mouseup", eventHandler);
+ synthesizeMouse(input, 20, 5, { type: "mousedown" });
+ synthesizeMouse(input, 40, 5, { type: "mousemove" });
+ synthesizeMouse(input, 40, 5, { type: "mouseup" });
+ check(kFalse, kTrue, "input-input-mouseup");
+ clear();
+
+ [fixedDiv1, input].forEach(function(element) {
+ element.removeEventListener("mouseup", eventHandler);
+ });
+
+ // touchmove event should not affect the selection state
+ synthesizeTouch(fixedDiv1, 30, 5, { type: "touchstart" });
+ synthesizeTouch(fixedDiv1, 40, 5, { type: "touchmove" });
+ check(kFalse, kFalse, "fixedDiv1-fixedDiv1-touchmove");
+ synthesizeTouch(fixedDiv1, 40, 5, { type: "touchend" });
+ clear();
+
+ synthesizeTouch(input, 20, 5, { type: "touchstart" });
+ synthesizeTouch(input, 40, 5, { type: "touchmove" });
+ check(kFalse, kFalse, "input-input-touchmove");
+ synthesizeTouch(input, 40, 5, { type: "touchend" });
+ clear();
+
+ fixedDiv1.addEventListener("touchmove", eventHandler);
+ synthesizeTouch(fixedDiv1, 30, 5, { type: "touchstart" });
+ synthesizeTouch(fixedDiv1, 40, 5, { type: "touchmove" });
+ check(kFalse, kFalse, "fixedDiv1-fixedDiv1-touchmove-preventDefault");
+ synthesizeTouch(fixedDiv1, 40, 5, { type: "touchend" });
+ clear();
+
+ input.addEventListener("touchmove", eventHandler);
+ synthesizeTouch(input, 20, 5, { type: "touchstart" });
+ synthesizeTouch(input, 40, 5, { type: "touchmove" });
+ check(kFalse, kFalse, "input-input-touchmove-preventDefault");
+ synthesizeTouch(input, 40, 5, { type: "touchend" });
+ clear();
+
+ SimpleTest.finish();
+}
+window.onload = function() { setTimeout(test, 0); };
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_selection_splitText-normalize.html b/layout/generic/test/test_selection_splitText-normalize.html
new file mode 100644
index 000000000..40815b3e5
--- /dev/null
+++ b/layout/generic/test/test_selection_splitText-normalize.html
@@ -0,0 +1,173 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=191864
+-->
+<head>
+ <title>Test for Bug 191864</title>
+ <script type="application/javascript" src="/MochiKit/packed.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=191864">Mozilla Bug 191864</a>
+<p id="display">
+<span id="col1" style="float:left; height:800px; width:180px;"></span>
+<span id="col2" style="float:left; height:800px; width:180px;"></span>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+var tests = [
+ [ {}, [0,4], "012345678" ],
+ [ {}, [0,0], "012345678" ],
+ [ {}, [0,9], "012345678" ],
+ [ {startOffset:4}, [0,4], "012345678" ],
+ [ {startOffset:5}, [0,4], "012345678" ],
+ [ {startOffset:5,endOffset:6}, [0,4], "012345678" ],
+ [ {endOffset:5}, [0,4], "012345678" ],
+ [ {endOffset:4}, [0,4], "012345678" ],
+ [ {endOffset:3}, [0,4], "012345678" ],
+ [ {startOffset:1,endOffset:3}, [0,4], "012345678" ],
+ [ {startOffset:7,endOffset:7}, [0,4], "012345678" ],
+ [ {startOffset:4,endOffset:4}, [0,4], "012345678" ],
+ [ {endNode:1}, [0,4], "012345678", "" ],
+ [ {endNode:1}, [0,4], "01234567", "8" ],
+ [ {endNode:1}, [1,4], "0", "12345678" ],
+ [ {endNode:2}, [1,4], "0", "12345", "678" ],
+]
+
+function runtest(r,p,t) {
+ // create content
+ document.original_nodes = [];
+ for (let i = 2; i < t.length; ++i) {
+ c = document.createTextNode(t[i]);
+ p.appendChild(c);
+ document.original_nodes.push(c);
+ }
+
+ // setup the range
+ let sel = t[0]
+ let startNode = p.firstChild;
+ let startOffset = sel.startOffset === undefined ? 0 : sel.startOffset;
+ let endNode = sel.endNode === undefined ? startNode : p.childNodes[sel.endNode];
+ let endOffset = sel.endOffset === undefined ? endNode.length : sel.endOffset;
+ r.setStart(startNode, startOffset);
+ r.setEnd(endNode, endOffset);
+
+ // splitText
+ let split = t[1]
+ p.childNodes[split[0]].splitText(split[1])
+}
+function test_split(r,p,t) {
+ runtest(r,p,t);
+}
+function test_split_merge(r,p,t) {
+ runtest(r,p,t);
+ p.normalize();
+}
+</script>
+
+<script type="application/javascript;version=1.7">
+
+/** Test for Bug 191864 **/
+
+var results = [
+/* test_split */
+ [ {}, [ [0, "0123"], "45678" ]],
+ [ {}, [ [0, ""], "012345678" ]],
+ [ {endNode:0, endOffset:9}, [ [0, "012345678"], "" ]],
+ [ {startOffset:4}, [ [0, "0123"], "45678" ]],
+ [ {startNode:1, startOffset:1}, [ [0, "0123"], "45678" ]],
+ [ {startNode:1, startOffset:1, endOffset:2}, [ [0, "0123"], "45678" ]],
+ [ {endOffset:1}, [ [0, "0123"], "45678" ]],
+ [ {endNode:0}, [ [0, "0123"], "45678" ]],
+ [ {endNode:0, endOffset:3}, [ [0, "0123"], "45678" ]],
+ [ {startOffset:1, endNode:0, endOffset:3}, [ [0, "0123"], "45678" ]],
+ [ {startNode:1, startOffset:3, endOffset:3}, [ [0, "0123"], "45678" ]],
+ [ {startOffset:4, endNode:0}, [ [0, "0123"], "45678" ]],
+ [ {endNode:2, endOffset:0}, [ [0, "0123"], "45678", [1, ""] ]],
+ [ {endNode:2}, [ [0, "0123"], "4567", [1, "8"] ]],
+ [ {endNode:2}, [ [0, "0"], [1, "1234"], "5678" ]],
+ [ {endNode:3}, [ [0, "0"], [1, "1234"], "5", [2, "678"] ]],
+/* test_split_merge */
+ [ {}, [ [0, "012345678" ] ]],
+ [ {startParent:true}, [ "012345678" ]], /* splitText() creates an empty first child which is removed by normalize() */
+ [ {}, [ [0, "012345678" ] ]],
+ [ {startOffset:4}, [ [0, "012345678" ] ]],
+ [ {startOffset:5}, [ [0, "012345678" ] ]],
+ [ {startOffset:5,endOffset:6}, [ [0, "012345678" ] ]],
+ [ {endOffset:5}, [ [0, "012345678" ] ]],
+ [ {endOffset:4}, [ [0, "012345678" ] ]],
+ [ {endOffset:3}, [ [0, "012345678" ] ]],
+ [ {startOffset:1,endOffset:3}, [ [0, "012345678" ] ]],
+ [ {startOffset:7,endOffset:7}, [ [0, "012345678" ] ]],
+ [ {startOffset:4,endOffset:4}, [ [0, "012345678" ] ]],
+ [ {endParent:true}, [ [0, "012345678" ] ]],
+ [ {}, [ [0, "012345678" ] ]],
+ [ {}, [ [0, "012345678" ] ]],
+ [ {}, [ [0, "012345678" ] ]],
+]
+
+function verifyResults(r,p,i) {
+ let nodes = results[i][1];
+ is(p.childNodes.length, nodes.length, "same number of DOM nodes" + " (test " + i + ")");
+ for (let j = 0; j < nodes.length; ++j) {
+ let a = nodes[j];
+ let b = p.childNodes[j];
+ if (a instanceof Array) {
+ is(b, document.original_nodes[a[0]], "same node" + " (test " + i + " child " + j + ")");
+ is(b.textContent, a[1], "contents2" + " (test " + i + " child " + j + ")");
+ } else {
+ is(b.nodeType, Node.TEXT_NODE, "text node" + " (test " + i + " child " + j + ")");
+ is(b.textContent, a, "contents1" + " (test " + i + " child " + j + ")");
+ }
+ }
+ let sel = results[i][0];
+ if (sel.todo) {
+ alert(r.startContainer + '\n' + r.startOffset + '\n' + r.endContainer + '\n' + r.endOffset + '\n')
+ return;
+ }
+ let startNode = sel.startNode === undefined ? p.firstChild : p.childNodes[sel.startNode];
+ let startOffset = sel.startOffset === undefined ? 0 : sel.startOffset;
+ if (sel.startParent) { startNode = p; startOffset = 0; }
+ let endNode = sel.endNode === undefined ? p.childNodes[p.childNodes.length>1 ? 1 : 0] : p.childNodes[sel.endNode];
+ let endOffset = sel.endOffset === undefined ? endNode.length : sel.endOffset;
+ if (sel.endParent) { endNode = p; endOffset = 1; }
+ is(r.startContainer, startNode, "range start node" + " (test " + i + ")");
+ is(r.startOffset, startOffset, "range start offset" + " (test " + i + ")");
+ is(r.endContainer, endNode, "range end node" + " (test " + i + ")");
+ is(r.endOffset, endOffset, "range end offset" + " (test " + i + ")");
+}
+
+function runTest() {
+ let col1 = document.getElementById('col1');
+ let col2 = document.getElementById('col2');
+ for (let i=0; i < tests.length; ++i) {
+ let t = tests[i];
+ let p = document.createElement('p')
+ col1.appendChild(p);
+ let r = document.createRange();
+ test_split(r,p,t);
+ verifyResults(r,p,i);
+ }
+ for (let i=0; i < tests.length; ++i) {
+ let t = tests[i];
+ let p = document.createElement('p')
+ col2.appendChild(p);
+ let r = document.createRange();
+ test_split_merge(r,p,t);
+ verifyResults(r,p,i+tests.length);
+ }
+ SimpleTest.finish();
+}
+
+window.onload = function() { setTimeout(runTest, 0); };
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/generic/test/test_selection_touchevents.html b/layout/generic/test/test_selection_touchevents.html
new file mode 100644
index 000000000..5c444758a
--- /dev/null
+++ b/layout/generic/test/test_selection_touchevents.html
@@ -0,0 +1,57 @@
+<!DOCTYPE>
+<html>
+<head>
+<title>selection expanding test</title>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="div1" class="testingDiv">
+ aaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaa
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var div1 = document.getElementById("div1");
+
+function sendTouch(aType, aX, aY) {
+ var cwu = SpecialPowers.getDOMWindowUtils(window);
+ cwu.sendTouchEvent(aType, [0], [aX], [aY], [1], [1], [0], [1], 1, 0, true);
+}
+
+function test()
+{
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ var rect = div1.getBoundingClientRect();
+
+ // Position the caret using a fake mouse click
+ var Ci = SpecialPowers.Ci;
+ var cwu = SpecialPowers.getDOMWindowUtils(window);
+ cwu.sendMouseEventToWindow("mousedown", rect.left + rect.width/2, rect.top + rect.height/2, 0, 0, 0, true);
+ cwu.sendMouseEventToWindow("mouseup", rect.left + rect.width/2, rect.top + rect.height/2, 0, 0, 0, true);
+ var selectionController = SpecialPowers.wrap(window).
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebNavigation).
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsISelectionDisplay).
+ QueryInterface(Ci.nsISelectionController);
+
+ selectionController.wordMove(false, false);
+ selectionController.wordMove(true, true);
+ isnot(selection.rangeCount, 0, "Something should be selected");
+ var string = selection.toString();
+
+ sendTouch("touchstart", rect.right, rect.top + rect.height/2);
+ sendTouch("touchmove", rect.left, rect.top + rect.height/2);
+ sendTouch("touchend", rect.left, rect.top + rect.height/2);
+ is(selection.toString(), string, "touch events should not affect the selection");
+
+ SimpleTest.finish();
+}
+window.onload = function() { setTimeout(test, 0); };
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/generic/test/test_selection_underline.html b/layout/generic/test/test_selection_underline.html
new file mode 100644
index 000000000..61378fe45
--- /dev/null
+++ b/layout/generic/test/test_selection_underline.html
@@ -0,0 +1,350 @@
+<html>
+
+<head>
+ <title>Test for selection underline</title>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+
+// Canvas related code stolen from layout/base/tests/bidi_numeral_test.js which
+// stole from http://developer.mozilla.org/en/docs/Code_snippets:Canvas
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+var RemoteCanvas = function(aIFrame, aTest) {
+ this.iframe = aIFrame;
+ this.test = aTest;
+ this.snapshot = null;
+};
+
+RemoteCanvas.CANVAS_WIDTH = 200;
+RemoteCanvas.CANVAS_HEIGHT = 100;
+
+RemoteCanvas.prototype.isReference = function() {
+ return this.iframe && (this.iframe.id == "reference");
+}
+
+RemoteCanvas.prototype.load = function(callback) {
+ this.iframe.contentWindow.wrappedJSObject.init(this.test);
+ var me = this;
+ setTimeout(function () { me.remotePagePrepared(callback) }, 100);
+}
+
+RemoteCanvas.prototype.remotePagePrepared = function(callback) {
+ this.snapshot = snapshotWindow(this.iframe.contentWindow);
+ callback(this);
+}
+
+var gPrefs = [
+ {
+ name: "ui.SpellCheckerUnderline",
+ type: "char",
+ newValue: "#ff0000"
+ },
+ {
+ name: "ui.IMERawInputBackground",
+ type: "char",
+ newValue: "transparent"
+ },
+ {
+ name: "ui.IMERawInputForeground",
+ type: "char",
+ newValue: "#000000"
+ },
+ {
+ name: "ui.IMERawInputUnderline",
+ type: "char",
+ newValue: "#00ff00"
+ },
+ {
+ name: "ui.IMESelectedRawTextBackground",
+ type: "char",
+ newValue: "transparent"
+ },
+ {
+ name: "ui.IMESelectedRawTextForeground",
+ type: "char",
+ newValue: "#000000"
+ },
+ {
+ name: "ui.IMESelectedRawTextUnderline",
+ type: "char",
+ newValue: "#0000ff"
+ },
+ {
+ name: "ui.IMEConvertedTextBackground",
+ type: "char",
+ newValue: "transparent"
+ },
+ {
+ name: "ui.IMEConvertedTextForeground",
+ type: "char",
+ newValue: "#000000"
+ },
+ {
+ name: "ui.IMEConvertedTextUnderline",
+ type: "char",
+ newValue: "#ffff00"
+ },
+ {
+ name: "ui.IMESelectedConvertedTextBackground",
+ type: "char",
+ newValue: "transparent"
+ },
+ {
+ name: "ui.IMESelectedConvertedTextForeground",
+ type: "char",
+ newValue: "#000000"
+ },
+ {
+ name: "ui.IMESelectedConvertedTextUnderline",
+ type: "char",
+ newValue: "#00ffff"
+ },
+ {
+ name: "ui.SpellCheckerUnderlineStyle",
+ type: "int",
+ newValue: 0
+ },
+ {
+ name: "ui.IMERawInputUnderlineStyle",
+ type: "int",
+ newValue: 0
+ },
+ {
+ name: "ui.IMESelectedRawTextUnderlineStyle",
+ type: "int",
+ newValue: 0
+ },
+ {
+ name: "ui.IMEConvertedTextUnderlineStyle",
+ type: "int",
+ newValue: 0
+ },
+ {
+ name: "ui.IMESelectedConvertedTextUnderlineStyle",
+ type: "int",
+ newValue: 0
+ },
+ {
+ name: "ui.SpellCheckerUnderlineRelativeSize",
+ type: "float",
+ newValue: 1.0
+ },
+ {
+ name: "ui.IMEUnderlineRelativeSize",
+ type: "float",
+ newValue: 1.0
+ }
+];
+
+const nsISelectionController = Components.interfaces.nsISelectionController;
+
+var gSelectionIndex = -1;
+const kSelections = [
+ { type: nsISelectionController.SELECTION_SPELLCHECK,
+ typeName: "SpellCheck", isIME: false,
+ decorationColor: "#ff0000" },
+ { type: nsISelectionController.SELECTION_IME_RAWINPUT,
+ typeName: "IME-RawInput", isIME: true,
+ decorationColor: "#00ff00" },
+ { type: nsISelectionController.SELECTION_IME_SELECTEDRAWTEXT,
+ typeName: "IME-SelectedRawText", isIME: true,
+ decorationColor: "#0000ff" },
+ { type: nsISelectionController.SELECTION_IME_CONVERTEDTEXT,
+ typeName: "IME-ConvertedText", isIME: true,
+ decorationColor: "#ffff00" },
+ { type: nsISelectionController.SELECTION_IME_SELECTEDCONVERTEDTEXT,
+ typeName: "IME-SelectedConvertedText", isIME: true,
+ decorationColor: "#00ffff" },
+];
+
+const kFontName_Ahem = "AhemTest";
+const kFontName_MPlus = "mplusTest";
+
+var gFontIndex = 0;
+const kFonts = [
+ { family: kFontName_Ahem, defaultSize: 16 },
+ { family: kFontName_Ahem, defaultSize: 20 },
+ { family: kFontName_Ahem, defaultSize: 32 },
+ { family: kFontName_Ahem, defaultSize: 52 },
+
+ { family: kFontName_MPlus, defaultSize: 16 },
+ { family: kFontName_MPlus, defaultSize: 20 },
+ { family: kFontName_MPlus, defaultSize: 32 },
+ { family: kFontName_MPlus, defaultSize: 52 },
+];
+
+const kDecorationStyleNone = 0;
+const kDecorationStyleDotted = 1;
+const kDecorationStyleDashed = 2;
+const kDecorationStyleSolid = 3;
+const kDecorationStyleDouble = 4;
+const kDecorationStyleWavy = 5;
+
+var gDecorationIndex = 0;
+const kDecorations = [
+ { relativeSize: 1.0, style: kDecorationStyleNone, styleName: "-moz-none" },
+ { relativeSize: 1.0, style: kDecorationStyleSolid, styleName: "solid" },
+ { relativeSize: 1.0, style: kDecorationStyleDotted, styleName: "dotted" },
+ { relativeSize: 1.0, style: kDecorationStyleDashed, styleName: "dashed" },
+ { relativeSize: 1.0, style: kDecorationStyleDouble, styleName: "double" },
+ { relativeSize: 1.0, style: kDecorationStyleWavy, styleName: "wavy" },
+
+// XXX relativeSize 2.0 cannot be tested by CSS3 text-decoration
+
+];
+
+function IsD2DEnabled() {
+ var enabled = false;
+
+ try {
+ enabled = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).D2DEnabled;
+ } catch(e) {}
+
+ return enabled;
+}
+
+function getFuzz(test) {
+ // Only failing on Windows with Direct2D enabled, and only for 16 permutations.
+ if (IsD2DEnabled() &&
+ test.decoration.styleName == "solid" &&
+ test.decoration.relativeSize == "1" &&
+ test.font.family == "mplusTest" &&
+ test.selection.typeName != "SpellCheck") {
+ return { numDifferentPixels: 194, maxDifference: 1 };
+ }
+ return null;
+}
+
+function run()
+{
+ if (++gSelectionIndex == kSelections.length) {
+ if (++gFontIndex == kFonts.length) {
+ if (++gDecorationIndex == kDecorations.length) {
+ SimpleTest.finish();
+ cleanup();
+ return;
+ }
+ gFontIndex = 0;
+ }
+ gSelectionIndex = 0;
+ SpecialPowers.setIntPref("font.size.variable.x-western",
+ kFonts[gFontIndex].defaultSize);
+ }
+
+ var test = {
+ font: kFonts[gFontIndex],
+ decoration: kDecorations[gDecorationIndex],
+ selection: kSelections[gSelectionIndex],
+ };
+
+ SpecialPowers.setIntPref("ui.SpellCheckerUnderlineRelativeSize",
+ test.decoration.relativeSize * 100);
+ SpecialPowers.setIntPref("ui.IMEUnderlineRelativeSize",
+ test.decoration.relativeSize * 100);
+ SpecialPowers.setIntPref("ui.SpellCheckerUnderlineStyle",
+ test.decoration.style);
+ SpecialPowers.setIntPref("ui.IMERawInputUnderlineStyle",
+ test.decoration.style);
+ SpecialPowers.setIntPref("ui.IMESelectedRawTextUnderlineStyle",
+ test.decoration.style);
+ SpecialPowers.setIntPref("ui.IMEConvertedTextUnderlineStyle",
+ test.decoration.style);
+ SpecialPowers.setIntPref("ui.IMESelectedConvertedTextUnderlineStyle",
+ test.decoration.style);
+
+ SimpleTest.executeSoon(function () { doTest(test); });
+}
+
+function doTest(aTest)
+{
+
+ var canvases = [];
+ function callbackTestCanvas(canvas)
+ {
+ canvases.push(canvas);
+
+ if (canvases.length != 2)
+ return;
+
+ var result = !canvases[0].isReference() ? canvases[0] : canvases[1];
+ var reference = canvases[0].isReference() ? canvases[0] : canvases[1];
+
+ var description = "(selection: " + aTest.selection.typeName +
+ ", style: " + aTest.decoration.styleName +
+ ", relativeSize: " + aTest.decoration.relativeSize +
+ ", font: " + aTest.font.family +
+ ", default font size: " + aTest.font.defaultSize + ")";
+
+ // If the decoration line is thick and the descender of the text isn't
+ // enough for containing it, selection underline may be painted lower
+ // if it's possible. Then, we cannot test it with CSS3 text-decoration.
+ if (aTest.decoration.style == kDecorationStyleDouble ||
+ aTest.decoration.style == kDecorationStyleWavy) {
+ todo(false, "Rendering of" + description);
+ } else {
+ assertSnapshots(result.snapshot, reference.snapshot, true,
+ getFuzz(aTest), description, "");
+ }
+
+ canvases = [];
+
+ run();
+ }
+
+ var testCanvas = new RemoteCanvas(document.getElementById("result"), aTest);
+ testCanvas.load(callbackTestCanvas);
+
+ var refCanvas = new RemoteCanvas(document.getElementById("reference"), aTest);
+ refCanvas.load(callbackTestCanvas);
+}
+
+function onLoad()
+{
+ for (var i = 0; i < gPrefs.length; i++) {
+ if (gPrefs[i].type == "char") {
+ SpecialPowers.setCharPref(gPrefs[i].name, gPrefs[i].newValue);
+ } else if (gPrefs[i].type == "int") {
+ SpecialPowers.setIntPref(gPrefs[i].name, gPrefs[i].newValue);
+ } else if (gPrefs[i].type == "float") {
+ SpecialPowers.setIntPref(gPrefs[i].name, gPrefs[i].newValue * 100);
+ }
+ }
+
+ var iframe = document.getElementById("result");
+ iframe.width = RemoteCanvas.CANVAS_WIDTH + "px";
+ iframe.height = RemoteCanvas.CANVAS_HEIGHT + "px";
+ iframe = document.getElementById("reference");
+ iframe.width = RemoteCanvas.CANVAS_WIDTH + "px";
+ iframe.height = RemoteCanvas.CANVAS_HEIGHT + "px";
+
+ run();
+}
+
+function cleanup()
+{
+ SpecialPowers.clearUserPref("font.size.variable.x-western");
+ for (var i = 0; i < gPrefs.length; i++) {
+ SpecialPowers.clearUserPref(gPrefs[i].name);
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(onLoad, window);
+
+</script>
+
+</head>
+<body>
+
+<iframe src="frame_selection_underline.xhtml" id="result"></iframe>
+<iframe src="frame_selection_underline-ref.xhtml" id="reference"></iframe>
+<pre id="test">
+</pre>
+
+</body>
+</html>
diff --git a/layout/generic/test/test_taintedfilters.html b/layout/generic/test/test_taintedfilters.html
new file mode 100644
index 000000000..c3724fc70
--- /dev/null
+++ b/layout/generic/test/test_taintedfilters.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=941887
+-->
+<head>
+ <title>Test for Bug 941887</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ iframe {
+ width: 500px;
+ height: 300px;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=941887">Mozilla Bug 941887</a>
+<p id="display"></p>
+<div id="content">
+<iframe id="f1"></iframe>
+<iframe id="f2"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 941887 **/
+SimpleTest.waitForExplicitFinish();
+
+var f = [document.getElementById("f1"), document.getElementById("f2")];
+
+var testList = [
+ ["file_taintedfilters_feDisplacementMap-untainted-1.svg", "file_taintedfilters_feDisplacementMap-untainted-ref.svg"],
+
+ // Disabled until CORS for feImage is implemented.
+ // ["file_taintedfilters_feDisplacementMap-untainted-2.svg", "file_taintedfilters_feDisplacementMap-untainted-ref.svg"],
+
+ ["file_taintedfilters_feDisplacementMap-tainted-1.svg", "file_taintedfilters_feDisplacementMap-tainted-ref.svg"],
+ ["file_taintedfilters_feDisplacementMap-tainted-2.svg", "file_taintedfilters_feDisplacementMap-tainted-ref.svg"],
+ ["file_taintedfilters_feDisplacementMap-tainted-3.svg", "file_taintedfilters_feDisplacementMap-tainted-ref.svg"],
+];
+
+var currentTestIndex = 0;
+var currentTest = testList[0];
+var loaded = [false, false];
+
+function didLoadIframe(iframe, index) {
+ if (iframe.contentWindow.location.href == iframe.src) {
+ loaded[index] = true;
+ if (loaded[0] && loaded[1]) {
+ checkCurrentTest();
+ }
+ }
+}
+
+f[0].onload = function (e) { didLoadIframe(e.target, 0); }
+f[1].onload = function (e) { didLoadIframe(e.target, 1); }
+
+function loadCurrentTest() {
+ currentTest = testList[currentTestIndex];
+ f[0].contentWindow.stop();
+ f[0].src = currentTest[0];
+ f[1].contentWindow.stop();
+ f[1].src = currentTest[1];
+ loaded = [false, false];
+}
+
+function okEqualSnapshots(c1, c2, msg) {
+ var [correct, c1url, c2url] = compareSnapshots(c1, c2, true);
+ if (correct) {
+ ok(true, msg);
+ } else {
+ ok(false, msg + "\nTEST: " + c1url + "\nREFERENCE: " + c1url);
+ }
+}
+
+function checkCurrentTest() {
+ okEqualSnapshots(snapshotWindow(f[0].contentWindow),
+ snapshotWindow(f[1].contentWindow),
+ currentTest[0] + " and " + currentTest[1] + " should match.");
+
+ currentTestIndex++;
+
+ if (currentTestIndex < testList.length)
+ loadCurrentTest();
+ else
+ SimpleTest.finish();
+}
+
+loadCurrentTest();
+
+</script>
+</pre>
+</body>
+</html>