|
1 <!DOCTYPE HTML> |
|
2 <html> |
|
3 <!-- |
|
4 https://bugzilla.mozilla.org/show_bug.cgi?id=421640 |
|
5 --> |
|
6 <head> |
|
7 <title>Test for Bug 396392</title> |
|
8 <meta http-equiv="content-type" content="text/html; charset=UTF-8"> |
|
9 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
|
10 <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> |
|
11 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> |
|
12 </head> |
|
13 <body> |
|
14 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=396392">Mozilla Bug Range getClientRects and getBoundingClientRect</a> |
|
15 <div id="content" style="font-family:monospace;font-size:12px;width:100px"> |
|
16 <p>000000<span>0</span></p><div>00000<span>0</span></div><p>0000<span>0000</span>0000</p><div><span>000000000000 00000000000000 000000</span></div><div>000000000000 00000000000003 100305</div> |
|
17 </div> |
|
18 <div id="mixeddir" style="font-family:monospace;font-size:12px;width:100px"><span>english <bdo id="bdo" dir="rtl">rtl-overide english</bdo> word</span></div> |
|
19 <div id="mixeddir2" style="font-family:monospace;font-size:12px"><span>english <bdo id="bdo2" dir="rtl">rtl-override english</bdo> word</span></div> |
|
20 <pre id="test"> |
|
21 <script class="testbody" type="text/javascript"> |
|
22 |
|
23 var isLTR=true; |
|
24 function isEmptyRect(rect, name) { |
|
25 name = (isLTR ? 'isLTR ' : 'isRTL ') + name; |
|
26 is(rect.left, 0, name+'empty rect should have left = 0'); |
|
27 is(rect.right, 0, name+'empty rect should have right = 0'); |
|
28 is(rect.top, 0, name+'empty rect should have top = 0'); |
|
29 is(rect.bottom, 0, name+'empty rect should have bottom = 0'); |
|
30 is(rect.width, 0, name+'empty rect should have width = 0'); |
|
31 is(rect.height, 0, name+'empty rect should have height = 0'); |
|
32 } |
|
33 |
|
34 function isEmptyRectList(rectlist, name) { |
|
35 name = (isLTR ? 'isLTR ' : 'isRTL ') + name; |
|
36 is(rectlist.length, 0, name + 'empty rectlist should have zero rects'); |
|
37 } |
|
38 |
|
39 // round coordinates to the nearest 1/256 of a pixel |
|
40 function roundCoord(x) { |
|
41 return Math.round(x * 256) / 256; |
|
42 } |
|
43 |
|
44 function _getRect(r) { |
|
45 if (r.length) //array |
|
46 return "{left:"+roundCoord(r[0])+",right:"+roundCoord(r[1])+ |
|
47 ",top:" +roundCoord(r[2])+",bottom:"+roundCoord(r[3])+ |
|
48 ",width:"+roundCoord(r[4])+",height:"+roundCoord(r[5])+"}"; |
|
49 else |
|
50 return "{left:"+roundCoord(r.left)+",right:"+roundCoord(r.right)+ |
|
51 ",top:"+roundCoord(r.top)+",bottom:"+roundCoord(r.bottom)+ |
|
52 ",width:"+roundCoord(r.width)+",height:"+roundCoord(r.height)+"}"; |
|
53 } |
|
54 |
|
55 function runATest(obj) { |
|
56 var range = document.createRange(); |
|
57 try { |
|
58 range.setStart(obj.range[0],obj.range[1]); |
|
59 if (obj.range.length>2) { |
|
60 range.setEnd(obj.range[2]||obj.range[0], obj.range[3]); |
|
61 } |
|
62 //test getBoundingClientRect() |
|
63 var rect = range.getBoundingClientRect(); |
|
64 var testname = (isLTR ? 'isLTR ' : 'isRTL ') + |
|
65 'range.getBoundingClientRect for ' + obj.name; |
|
66 if (obj.rect) { |
|
67 is(_getRect(rect),_getRect(obj.rect), testname); |
|
68 } else { |
|
69 isEmptyRect(rect,testname+": "); |
|
70 } |
|
71 //test getClientRects() |
|
72 var rectlist = range.getClientRects(); |
|
73 testname = (isLTR ? 'isLTR ' : 'isRTL ') + |
|
74 'range.getClientRects for '+obj.name; |
|
75 if (!obj.rectList) { |
|
76 //rectList is not specified, use obj.rect to figure out rectList |
|
77 obj.rectList = obj.rect?[obj.rect]:[]; |
|
78 } |
|
79 if (!obj.rectList.length) { |
|
80 isEmptyRectList(rectlist, testname+": "); |
|
81 } else { |
|
82 is(rectlist.length, obj.rectList.length, testname+' should return '+obj.rectList.length+' rects.'); |
|
83 if(!obj.rectList.forEach){ |
|
84 //convert RectList to a real array |
|
85 obj.rectList=Array.prototype.slice.call(obj.rectList, 0); |
|
86 } |
|
87 obj.rectList.forEach(function(rect,i) { |
|
88 is(_getRect(rectlist[i]),_getRect(rect),testname+": item at "+i); |
|
89 }); |
|
90 } |
|
91 } finally { |
|
92 range.detach(); |
|
93 } |
|
94 } |
|
95 /** Test for Bug 396392 **/ |
|
96 function doTest(){ |
|
97 var root = document.getElementById('content'); |
|
98 var firstP = root.firstElementChild, spanInFirstP = firstP.childNodes[1], |
|
99 firstDiv = root.childNodes[2], spanInFirstDiv = firstDiv.childNodes[1], |
|
100 secondP = root.childNodes[3], spanInSecondP = secondP.childNodes[1], |
|
101 secondDiv = root.childNodes[4], spanInSecondDiv = secondDiv.firstChild, |
|
102 thirdDiv = root.childNodes[5]; |
|
103 var firstPRect = firstP.getBoundingClientRect(), |
|
104 spanInFirstPRect = spanInFirstP.getBoundingClientRect(), |
|
105 firstDivRect = firstDiv.getBoundingClientRect(), |
|
106 spanInFirstDivRect = spanInFirstDiv.getBoundingClientRect(), |
|
107 secondPRect = secondP.getBoundingClientRect(), |
|
108 secondDivRect = secondDiv.getBoundingClientRect(), |
|
109 spanInSecondPRect = spanInSecondP.getBoundingClientRect(), |
|
110 spanInSecondDivRect = spanInSecondDiv.getBoundingClientRect(), |
|
111 spanInSecondDivRectList = spanInSecondDiv.getClientRects(); |
|
112 var widthPerchar = spanInSecondPRect.width / spanInSecondP.firstChild.length; |
|
113 var testcases = [ |
|
114 {name:'nodesNotInDocument', range:[document.createTextNode('abc'), 1], |
|
115 rect:null}, |
|
116 {name:'collapsedInBlockNode', range:[firstP, 2], rect:null}, |
|
117 {name:'collapsedAtBeginningOfTextNode', range:[firstP.firstChild, 0], |
|
118 rect:[spanInFirstPRect.left - 6 * widthPerchar, |
|
119 spanInFirstPRect.left - 6 * widthPerchar, spanInFirstPRect.top, |
|
120 spanInFirstPRect.bottom, 0, spanInFirstPRect.height]}, |
|
121 {name:'collapsedWithinTextNode', range:[firstP.firstChild, 1], |
|
122 rect:[spanInFirstPRect.left - 5 * widthPerchar, |
|
123 spanInFirstPRect.left - 5 * widthPerchar, |
|
124 spanInFirstPRect.top, spanInFirstPRect.bottom, 0, spanInFirstPRect.height]}, |
|
125 {name:'collapsedAtEndOfTextNode', range:[firstP.firstChild, 6], |
|
126 rect:[spanInFirstPRect.left, spanInFirstPRect.left, |
|
127 spanInFirstPRect.top, spanInFirstPRect.bottom, 0, spanInFirstPRect.height]}, |
|
128 {name:'singleBlockNode', range:[root, 1, root, 2], rect:firstPRect}, |
|
129 {name:'twoBlockNodes', range:[root, 1, root, 3], |
|
130 rect:[firstPRect.left, firstPRect.right, firstPRect.top, |
|
131 firstDivRect.bottom, firstPRect.width, |
|
132 firstDivRect.bottom - firstPRect.top], |
|
133 rectList:[firstPRect, firstDivRect]}, |
|
134 {name:'endOfTextNodeToEndOfAnotherTextNodeInAnotherBlock', |
|
135 range:[spanInFirstP.firstChild, 1, firstDiv.firstChild, 5], |
|
136 rect:[spanInFirstDivRect.left - 5*widthPerchar, spanInFirstDivRect.left, |
|
137 spanInFirstDivRect.top, spanInFirstDivRect.bottom, 5 * widthPerchar, |
|
138 spanInFirstDivRect.height]}, |
|
139 {name:'startOfTextNodeToStartOfAnotherTextNodeInAnotherBlock', |
|
140 range:[spanInFirstP.firstChild, 0, firstDiv.firstChild, 0], |
|
141 rect:[spanInFirstPRect.left, spanInFirstPRect.left + widthPerchar, spanInFirstPRect.top, |
|
142 spanInFirstPRect.bottom, widthPerchar, spanInFirstPRect.height]}, |
|
143 {name:'endPortionOfATextNode', range:[firstP.firstChild, 3, |
|
144 firstP.firstChild, 6], |
|
145 rect:[spanInFirstPRect.left - 3*widthPerchar, spanInFirstPRect.left, |
|
146 spanInFirstPRect.top, spanInFirstPRect.bottom, 3*widthPerchar, spanInFirstPRect.height]}, |
|
147 {name:'startPortionOfATextNode', range:[firstP.firstChild, 0, |
|
148 firstP.firstChild, 3], |
|
149 rect:[spanInFirstPRect.left - 6*widthPerchar, |
|
150 spanInFirstPRect.left - 3*widthPerchar, spanInFirstPRect.top, |
|
151 spanInFirstPRect.bottom, 3 * widthPerchar, spanInFirstPRect.height]}, |
|
152 {name:'spanTextNodes', range:[secondP.firstChild, 1, secondP.lastChild, 1], |
|
153 rect:[spanInSecondPRect.left - 3*widthPerchar, spanInSecondPRect.right + |
|
154 widthPerchar, spanInSecondPRect.top, spanInSecondPRect.bottom, |
|
155 spanInSecondPRect.width + 4*widthPerchar, spanInSecondPRect.height], |
|
156 rectList:[[spanInSecondPRect.left - 3*widthPerchar, spanInSecondPRect.left, |
|
157 spanInSecondPRect.top, spanInSecondPRect.bottom, 3 * widthPerchar, |
|
158 spanInSecondPRect.height], |
|
159 spanInSecondPRect, |
|
160 [spanInSecondPRect.right, spanInSecondPRect.right + widthPerchar, |
|
161 spanInSecondPRect.top, spanInSecondPRect.bottom, widthPerchar, |
|
162 spanInSecondPRect.height]]} |
|
163 ]; |
|
164 testcases.forEach(runATest); |
|
165 |
|
166 // testcases that have different ranges in LTR and RTL |
|
167 var directionDependentTestcases; |
|
168 if (isLTR) { |
|
169 directionDependentTestcases = [ |
|
170 {name:'spanAcrossLines',range:[spanInSecondDiv.firstChild, 1, spanInSecondDiv.firstChild, 30], |
|
171 rect: spanInSecondDivRect, |
|
172 rectList:[[spanInSecondDivRectList[0].left+widthPerchar, |
|
173 spanInSecondDivRectList[0].right, spanInSecondDivRectList[0].top, |
|
174 spanInSecondDivRectList[0].bottom, spanInSecondDivRectList[0].width - widthPerchar, |
|
175 spanInSecondDivRectList[0].height], |
|
176 spanInSecondDivRectList[1], |
|
177 [spanInSecondDivRectList[2].left, |
|
178 spanInSecondDivRectList[2].right - 4 * widthPerchar, spanInSecondDivRectList[2].top, |
|
179 spanInSecondDivRectList[2].bottom, |
|
180 spanInSecondDivRectList[2].width - 4 * widthPerchar, |
|
181 spanInSecondDivRectList[2].height]]}, |
|
182 {name:'textAcrossLines',range:[thirdDiv.firstChild, 13, thirdDiv.firstChild, 28], |
|
183 rect: [spanInSecondDivRectList[1].left, spanInSecondDivRectList[1].right, |
|
184 spanInSecondDivRectList[1].top + secondDivRect.height, |
|
185 spanInSecondDivRectList[1].bottom + secondDivRect.height, |
|
186 spanInSecondDivRectList[1].width, spanInSecondDivRectList[1].height]} |
|
187 ]; |
|
188 } else { |
|
189 directionDependentTestcases = [ |
|
190 {name:'spanAcrossLines',range:[spanInSecondDiv.firstChild, 1, spanInSecondDiv.firstChild, 30], |
|
191 rect: spanInSecondDivRect, |
|
192 rectList:[[spanInSecondDivRectList[0].left+widthPerchar, |
|
193 spanInSecondDivRectList[0].right, spanInSecondDivRectList[0].top, |
|
194 spanInSecondDivRectList[0].bottom, spanInSecondDivRectList[0].width - widthPerchar, |
|
195 spanInSecondDivRectList[0].height], |
|
196 spanInSecondDivRectList[1], |
|
197 spanInSecondDivRectList[2], |
|
198 spanInSecondDivRectList[3], |
|
199 [spanInSecondDivRectList[4].left, |
|
200 spanInSecondDivRectList[4].right - 4 * widthPerchar, |
|
201 spanInSecondDivRectList[4].top, |
|
202 spanInSecondDivRectList[4].bottom, |
|
203 spanInSecondDivRectList[4].width - 4 * widthPerchar, |
|
204 spanInSecondDivRectList[4].height]]}, |
|
205 {name:'textAcrossLines',range:[thirdDiv.firstChild, 13, thirdDiv.firstChild, 28], |
|
206 rect: [spanInSecondDivRectList[2].left, spanInSecondDivRectList[2].right, |
|
207 spanInSecondDivRectList[2].top + secondDivRect.height, |
|
208 spanInSecondDivRectList[2].bottom + secondDivRect.height, |
|
209 spanInSecondDivRectList[2].width, spanInSecondDivRectList[2].height], |
|
210 rectList:[[spanInSecondDivRectList[2].left, spanInSecondDivRectList[2].right, |
|
211 spanInSecondDivRectList[2].top + secondDivRect.height, |
|
212 spanInSecondDivRectList[2].bottom + secondDivRect.height, |
|
213 spanInSecondDivRectList[2].width, spanInSecondDivRectList[2].height], |
|
214 [spanInSecondDivRectList[2].left, spanInSecondDivRectList[2].left, |
|
215 spanInSecondDivRectList[2].top + secondDivRect.height, |
|
216 spanInSecondDivRectList[2].bottom + secondDivRect.height, |
|
217 0, spanInSecondDivRectList[2].height]]} |
|
218 ]; |
|
219 } |
|
220 directionDependentTestcases.forEach(runATest); |
|
221 } |
|
222 function testMixedDir(){ |
|
223 var root = document.getElementById('mixeddir'); |
|
224 var firstSpan = root.firstElementChild, firstSpanRect=firstSpan.getBoundingClientRect(), |
|
225 firstSpanRectList = firstSpan.getClientRects(); |
|
226 runATest({name:'mixeddir',range:[firstSpan.firstChild,0,firstSpan.lastChild,firstSpan.lastChild.length], |
|
227 rect: firstSpanRect, rectList:firstSpanRectList}); |
|
228 |
|
229 root = document.getElementById('mixeddir2'); |
|
230 firstSpan = root.firstElementChild; |
|
231 firstSpanRect = firstSpan.getBoundingClientRect(); |
|
232 bdo = document.getElementById('bdo2'); |
|
233 bdoRect=bdo.getBoundingClientRect(); |
|
234 var widthPerChar = bdoRect.width / bdo.firstChild.length; |
|
235 runATest({name:'mixeddirPartial', range:[firstSpan.firstChild, 3, |
|
236 bdo.firstChild, 7], |
|
237 rect: [firstSpanRect.left + 3*widthPerChar, bdoRect.right, |
|
238 bdoRect.top, bdoRect.bottom, |
|
239 (firstSpan.firstChild.length + bdo.firstChild.length - 3) * |
|
240 widthPerChar, |
|
241 bdoRect.height], |
|
242 rectList:[[firstSpanRect.left + 3*widthPerChar, |
|
243 bdoRect.left, |
|
244 firstSpanRect.top, firstSpanRect.bottom, |
|
245 (firstSpan.firstChild.length - 3) * widthPerChar, |
|
246 firstSpanRect.height], |
|
247 [bdoRect.right - 7 * widthPerChar, bdoRect.right, |
|
248 bdoRect.top, bdoRect.bottom, |
|
249 7*widthPerChar, bdoRect.height]]}); |
|
250 } |
|
251 function test(){ |
|
252 //test ltr |
|
253 doTest(); |
|
254 testMixedDir(); |
|
255 |
|
256 isLTR = false; |
|
257 var root = document.getElementById('content'); |
|
258 root.dir = 'rtl'; |
|
259 |
|
260 //test rtl |
|
261 doTest(); |
|
262 testMixedDir(); |
|
263 |
|
264 SimpleTest.finish(); |
|
265 } |
|
266 |
|
267 window.onload = function() { |
|
268 SimpleTest.waitForExplicitFinish(); |
|
269 setTimeout(test, 0); |
|
270 }; |
|
271 |
|
272 </script> |
|
273 </pre> |
|
274 </body> |
|
275 </html> |