|
1 var scrollbarWidth = 17, scrollbarHeight = 17; |
|
2 |
|
3 function testElements(baseid, callback) |
|
4 { |
|
5 scrollbarWidth = scrollbarHeight = gcs($("scrollbox-test"), "width"); |
|
6 |
|
7 var elements = $(baseid).getElementsByTagName("*"); |
|
8 for (var t = 0; t < elements.length; t++) { |
|
9 var element = elements[t]; |
|
10 testElement(element); |
|
11 } |
|
12 |
|
13 var nonappended = document.createElement("div"); |
|
14 nonappended.id = "nonappended"; |
|
15 nonappended.setAttribute("_offsetParent", "null"); |
|
16 testElement(nonappended); |
|
17 |
|
18 checkScrolledElement($("scrollbox"), $("scrollchild")); |
|
19 |
|
20 var div = $("noscroll"); |
|
21 div.scrollLeft = 10; |
|
22 div.scrollTop = 10; |
|
23 is(element.scrollLeft, 0, element.id + " scrollLeft after nonscroll"); |
|
24 is(element.scrollTop, 0, element.id + " scrollTop after nonscroll"); |
|
25 |
|
26 callback(); |
|
27 } |
|
28 |
|
29 function toNearestAppunit(v) |
|
30 { |
|
31 // 60 appunits per CSS pixel; round result to the nearest appunit |
|
32 return Math.round(v*60)/60; |
|
33 } |
|
34 |
|
35 function isEqualAppunits(a, b, msg) |
|
36 { |
|
37 is(toNearestAppunit(a), toNearestAppunit(b), msg); |
|
38 } |
|
39 |
|
40 function testElement(element) |
|
41 { |
|
42 var offsetParent = element.getAttribute("_offsetParent"); |
|
43 offsetParent = $(offsetParent == "null" ? null: (offsetParent ? offsetParent : "body")); |
|
44 |
|
45 var borderLeft = gcs(element, "borderLeftWidth"); |
|
46 var borderTop = gcs(element, "borderTopWidth"); |
|
47 var borderRight = gcs(element, "borderRightWidth"); |
|
48 var borderBottom = gcs(element, "borderBottomWidth"); |
|
49 var paddingLeft = gcs(element, "paddingLeft"); |
|
50 var paddingTop = gcs(element, "paddingTop"); |
|
51 var paddingRight = gcs(element, "paddingRight"); |
|
52 var paddingBottom = gcs(element, "paddingBottom"); |
|
53 var width = gcs(element, "width"); |
|
54 var height = gcs(element, "height"); |
|
55 |
|
56 if (element instanceof HTMLElement) |
|
57 checkOffsetState(element, -10000, -10000, |
|
58 borderLeft + paddingLeft + width + paddingRight + borderRight, |
|
59 borderTop + paddingTop + height + paddingBottom + borderBottom, |
|
60 offsetParent, element.id); |
|
61 |
|
62 var scrollWidth, scrollHeight, clientWidth, clientHeight; |
|
63 var doScrollCheck = true; |
|
64 if (element.id == "scrollbox") { |
|
65 var lastchild = $("lastline"); |
|
66 scrollWidth = lastchild.getBoundingClientRect().width + paddingLeft + paddingRight; |
|
67 var top = element.firstChild.getBoundingClientRect().top; |
|
68 var bottom = element.lastChild.getBoundingClientRect().bottom; |
|
69 var contentsHeight = bottom - top; |
|
70 scrollHeight = contentsHeight + paddingTop + paddingBottom; |
|
71 clientWidth = paddingLeft + width + paddingRight - scrollbarWidth; |
|
72 clientHeight = paddingTop + height + paddingBottom - scrollbarHeight; |
|
73 } else { |
|
74 clientWidth = paddingLeft + width + paddingRight; |
|
75 clientHeight = paddingTop + height + paddingBottom; |
|
76 if (element.id == "overflow-visible") { |
|
77 scrollWidth = 200; |
|
78 scrollHeight = 201; |
|
79 } else if (element.scrollWidth > clientWidth || |
|
80 element.scrollHeight > clientHeight) { |
|
81 // The element overflows. Don't check scrollWidth/scrollHeight since the |
|
82 // above calculation is not correct. |
|
83 doScrollCheck = false; |
|
84 } else { |
|
85 scrollWidth = clientWidth; |
|
86 scrollHeight = clientHeight; |
|
87 } |
|
88 } |
|
89 |
|
90 if (doScrollCheck) { |
|
91 if (element instanceof SVGElement) |
|
92 checkScrollState(element, 0, 0, 0, 0, element.id); |
|
93 else |
|
94 checkScrollState(element, 0, 0, scrollWidth, scrollHeight, element.id); |
|
95 } |
|
96 |
|
97 if (element instanceof SVGElement) |
|
98 checkClientState(element, 0, 0, 0, 0, element.id); |
|
99 else |
|
100 checkClientState(element, borderLeft, borderTop, clientWidth, clientHeight, element.id); |
|
101 |
|
102 var boundingrect = element.getBoundingClientRect(); |
|
103 isEqualAppunits(boundingrect.width, borderLeft + paddingLeft + width + paddingRight + borderRight, |
|
104 element.id + " bounding rect width"); |
|
105 isEqualAppunits(boundingrect.height, borderTop + paddingTop + height + paddingBottom + borderBottom, |
|
106 element.id + " bounding rect height"); |
|
107 isEqualAppunits(boundingrect.right - boundingrect.left, boundingrect.width, |
|
108 element.id + " bounding rect right"); |
|
109 isEqualAppunits(boundingrect.bottom - boundingrect.top, boundingrect.height, |
|
110 element.id + " bounding rect bottom"); |
|
111 |
|
112 var rects = element.getClientRects(); |
|
113 if (element.id == "div-displaynone" || element.id == "nonappended") { |
|
114 is(rects.length, 0, element.id + " getClientRects empty"); |
|
115 } |
|
116 else { |
|
117 is(rects[0].left, boundingrect.left, element.id + " getClientRects left"); |
|
118 is(rects[0].top, boundingrect.top, element.id + " getClientRects top"); |
|
119 is(rects[0].right, boundingrect.right, element.id + " getClientRects right"); |
|
120 is(rects[0].bottom, boundingrect.bottom, element.id + " getClientRects bottom"); |
|
121 } |
|
122 } |
|
123 |
|
124 function checkScrolledElement(element, child) |
|
125 { |
|
126 var elemrect = element.getBoundingClientRect(); |
|
127 var childrect = child.getBoundingClientRect(); |
|
128 |
|
129 var topdiff = childrect.top - elemrect.top; |
|
130 |
|
131 element.scrollTop = 20; |
|
132 is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll"); |
|
133 is(element.scrollTop, 20, element.id + " scrollTop after vertical scroll"); |
|
134 // If the viewport has been transformed, then we might have scrolled to a subpixel value |
|
135 // that's slightly different from what we requested. After rounding, however, it should |
|
136 // be the same. |
|
137 is(Math.round(childrect.top - child.getBoundingClientRect().top), 20, "child position after vertical scroll"); |
|
138 |
|
139 element.scrollTop = 0; |
|
140 is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll reset"); |
|
141 is(element.scrollTop, 0, element.id + " scrollTop after vertical scroll reset"); |
|
142 // Scrolling back to the top should work precisely. |
|
143 is(child.getBoundingClientRect().top, childrect.top, "child position after vertical scroll reset"); |
|
144 |
|
145 element.scrollTop = 10; |
|
146 element.scrollTop = -30; |
|
147 is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll negative"); |
|
148 is(element.scrollTop, 0, element.id + " scrollTop after vertical scroll negative"); |
|
149 is(child.getBoundingClientRect().top, childrect.top, "child position after vertical scroll negative"); |
|
150 |
|
151 element.scrollLeft = 18; |
|
152 is(element.scrollLeft, 18, element.id + " scrollLeft after horizontal scroll"); |
|
153 is(element.scrollTop, 0, element.id + " scrollTop after horizontal scroll"); |
|
154 is(Math.round(childrect.left - child.getBoundingClientRect().left), 18, "child position after horizontal scroll"); |
|
155 |
|
156 element.scrollLeft = -30; |
|
157 is(element.scrollLeft, 0, element.id + " scrollLeft after horizontal scroll reset"); |
|
158 is(element.scrollTop, 0, element.id + " scrollTop after horizontal scroll reset"); |
|
159 is(child.getBoundingClientRect().left, childrect.left, "child position after horizontal scroll reset"); |
|
160 } |
|
161 |
|
162 function checkOffsetState(element, left, top, width, height, parent, testname) |
|
163 { |
|
164 checkCoords(element, "offset", left, top, width, height, testname); |
|
165 is(element.offsetParent, parent, testname + " offsetParent"); |
|
166 } |
|
167 |
|
168 function checkScrollState(element, left, top, width, height, testname) |
|
169 { |
|
170 checkCoords(element, "scroll", left, top, width, height, testname); |
|
171 } |
|
172 |
|
173 function checkClientState(element, left, top, width, height, testname) |
|
174 { |
|
175 checkCoords(element, "client", left, top, width, height, testname); |
|
176 } |
|
177 |
|
178 function checkCoord(element, type, val, testname) |
|
179 { |
|
180 if (val != -10000) |
|
181 is(element[type], Math.round(val), testname + " " + type); |
|
182 } |
|
183 |
|
184 function checkCoords(element, type, left, top, width, height, testname) |
|
185 { |
|
186 checkCoord(element, type + "Left", left, testname); |
|
187 checkCoord(element, type + "Top", top, testname); |
|
188 checkCoord(element, type + "Width", width, testname); |
|
189 checkCoord(element, type + "Height", height, testname); |
|
190 |
|
191 if (element instanceof SVGElement) |
|
192 return; |
|
193 |
|
194 if (element.id == "outerpopup" && !element.parentNode.open) // closed popup |
|
195 return; |
|
196 |
|
197 if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements |
|
198 ok(element[type + "Width"] == 0 && element[type + "Height"] == 0, |
|
199 element.id + " has zero " + type + " width and height"); |
|
200 } |
|
201 |
|
202 function gcs(element, prop) |
|
203 { |
|
204 var propVal = (element instanceof SVGElement && (prop == "width" || prop == "height")) ? |
|
205 element.getAttribute(prop) : getComputedStyle(element, "")[prop]; |
|
206 if (propVal == "auto") |
|
207 return 0; |
|
208 return parseFloat(propVal); |
|
209 } |