|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
6 |
|
7 XPCOMUtils.defineLazyModuleGetter(this, "Promise", |
|
8 "resource://gre/modules/Promise.jsm"); |
|
9 XPCOMUtils.defineLazyModuleGetter(this, "Task", |
|
10 "resource://gre/modules/Task.jsm"); |
|
11 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", |
|
12 "resource://gre/modules/PlacesUtils.jsm"); |
|
13 |
|
14 function waitForCondition(condition, nextTest, errorMsg) { |
|
15 var tries = 0; |
|
16 var interval = setInterval(function() { |
|
17 if (tries >= 30) { |
|
18 ok(false, errorMsg); |
|
19 moveOn(); |
|
20 } |
|
21 var conditionPassed; |
|
22 try { |
|
23 conditionPassed = condition(); |
|
24 } catch (e) { |
|
25 ok(false, e + "\n" + e.stack); |
|
26 conditionPassed = false; |
|
27 } |
|
28 if (conditionPassed) { |
|
29 moveOn(); |
|
30 } |
|
31 tries++; |
|
32 }, 100); |
|
33 var moveOn = function() { clearInterval(interval); nextTest(); }; |
|
34 } |
|
35 |
|
36 // Check that a specified (string) URL hasn't been "remembered" (ie, is not |
|
37 // in history, will not appear in about:newtab or auto-complete, etc.) |
|
38 function promiseSocialUrlNotRemembered(url) { |
|
39 let deferred = Promise.defer(); |
|
40 let uri = Services.io.newURI(url, null, null); |
|
41 PlacesUtils.asyncHistory.isURIVisited(uri, function(aURI, aIsVisited) { |
|
42 ok(!aIsVisited, "social URL " + url + " should not be in global history"); |
|
43 deferred.resolve(); |
|
44 }); |
|
45 return deferred.promise; |
|
46 } |
|
47 |
|
48 let gURLsNotRemembered = []; |
|
49 |
|
50 |
|
51 function checkProviderPrefsEmpty(isError) { |
|
52 let MANIFEST_PREFS = Services.prefs.getBranch("social.manifest."); |
|
53 let prefs = MANIFEST_PREFS.getChildList("", []); |
|
54 let c = 0; |
|
55 for (let pref of prefs) { |
|
56 if (MANIFEST_PREFS.prefHasUserValue(pref)) { |
|
57 info("provider [" + pref + "] manifest left installed from previous test"); |
|
58 c++; |
|
59 } |
|
60 } |
|
61 is(c, 0, "all provider prefs uninstalled from previous test"); |
|
62 is(Social.providers.length, 0, "all providers uninstalled from previous test " + Social.providers.length); |
|
63 } |
|
64 |
|
65 function defaultFinishChecks() { |
|
66 checkProviderPrefsEmpty(true); |
|
67 finish(); |
|
68 } |
|
69 |
|
70 function runSocialTestWithProvider(manifest, callback, finishcallback) { |
|
71 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService; |
|
72 |
|
73 let manifests = Array.isArray(manifest) ? manifest : [manifest]; |
|
74 |
|
75 // Check that none of the provider's content ends up in history. |
|
76 function finishCleanUp() { |
|
77 ok(!SocialSidebar.provider, "no provider in sidebar"); |
|
78 SessionStore.setWindowValue(window, "socialSidebar", ""); |
|
79 for (let i = 0; i < manifests.length; i++) { |
|
80 let m = manifests[i]; |
|
81 for (let what of ['sidebarURL', 'workerURL', 'iconURL']) { |
|
82 if (m[what]) { |
|
83 yield promiseSocialUrlNotRemembered(m[what]); |
|
84 } |
|
85 }; |
|
86 } |
|
87 for (let i = 0; i < gURLsNotRemembered.length; i++) { |
|
88 yield promiseSocialUrlNotRemembered(gURLsNotRemembered[i]); |
|
89 } |
|
90 gURLsNotRemembered = []; |
|
91 } |
|
92 |
|
93 info("runSocialTestWithProvider: " + manifests.toSource()); |
|
94 |
|
95 let finishCount = 0; |
|
96 function finishIfDone(callFinish) { |
|
97 finishCount++; |
|
98 if (finishCount == manifests.length) |
|
99 Task.spawn(finishCleanUp).then(finishcallback || defaultFinishChecks); |
|
100 } |
|
101 function removeAddedProviders(cleanup) { |
|
102 manifests.forEach(function (m) { |
|
103 // If we're "cleaning up", don't call finish when done. |
|
104 let callback = cleanup ? function () {} : finishIfDone; |
|
105 // Similarly, if we're cleaning up, catch exceptions from removeProvider |
|
106 let removeProvider = SocialService.removeProvider.bind(SocialService); |
|
107 if (cleanup) { |
|
108 removeProvider = function (origin, cb) { |
|
109 try { |
|
110 SocialService.removeProvider(origin, cb); |
|
111 } catch (ex) { |
|
112 // Ignore "provider doesn't exist" errors. |
|
113 if (ex.message.indexOf("SocialService.removeProvider: no provider with origin") == 0) |
|
114 return; |
|
115 info("Failed to clean up provider " + origin + ": " + ex); |
|
116 } |
|
117 } |
|
118 } |
|
119 removeProvider(m.origin, callback); |
|
120 }); |
|
121 } |
|
122 function finishSocialTest(cleanup) { |
|
123 removeAddedProviders(cleanup); |
|
124 } |
|
125 |
|
126 let providersAdded = 0; |
|
127 let firstProvider; |
|
128 |
|
129 manifests.forEach(function (m) { |
|
130 SocialService.addProvider(m, function(provider) { |
|
131 |
|
132 providersAdded++; |
|
133 info("runSocialTestWithProvider: provider added"); |
|
134 |
|
135 // we want to set the first specified provider as the UI's provider |
|
136 if (provider.origin == manifests[0].origin) { |
|
137 firstProvider = provider; |
|
138 } |
|
139 |
|
140 // If we've added all the providers we need, call the callback to start |
|
141 // the tests (and give it a callback it can call to finish them) |
|
142 if (providersAdded == manifests.length) { |
|
143 registerCleanupFunction(function () { |
|
144 finishSocialTest(true); |
|
145 }); |
|
146 waitForCondition(function() provider.enabled, |
|
147 function() { |
|
148 info("provider has been enabled"); |
|
149 callback(finishSocialTest); |
|
150 }, "providers added and enabled"); |
|
151 } |
|
152 }); |
|
153 }); |
|
154 } |
|
155 |
|
156 function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) { |
|
157 let testIter = Iterator(tests); |
|
158 let providersAtStart = Social.providers.length; |
|
159 info("runSocialTests: start test run with " + providersAtStart + " providers"); |
|
160 |
|
161 if (cbPreTest === undefined) { |
|
162 cbPreTest = function(cb) {cb()}; |
|
163 } |
|
164 if (cbPostTest === undefined) { |
|
165 cbPostTest = function(cb) {cb()}; |
|
166 } |
|
167 |
|
168 function runNextTest() { |
|
169 let name, func; |
|
170 try { |
|
171 [name, func] = testIter.next(); |
|
172 } catch (err if err instanceof StopIteration) { |
|
173 // out of items: |
|
174 (cbFinish || defaultFinishChecks)(); |
|
175 is(providersAtStart, Social.providers.length, |
|
176 "runSocialTests: finish test run with " + Social.providers.length + " providers"); |
|
177 return; |
|
178 } |
|
179 // We run on a timeout as the frameworker also makes use of timeouts, so |
|
180 // this helps keep the debug messages sane. |
|
181 executeSoon(function() { |
|
182 function cleanupAndRunNextTest() { |
|
183 info("sub-test " + name + " complete"); |
|
184 cbPostTest(runNextTest); |
|
185 } |
|
186 cbPreTest(function() { |
|
187 info("pre-test: starting with " + Social.providers.length + " providers"); |
|
188 info("sub-test " + name + " starting"); |
|
189 try { |
|
190 func.call(tests, cleanupAndRunNextTest); |
|
191 } catch (ex) { |
|
192 ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack); |
|
193 cleanupAndRunNextTest(); |
|
194 } |
|
195 }) |
|
196 }); |
|
197 } |
|
198 runNextTest(); |
|
199 } |
|
200 |
|
201 // A fairly large hammer which checks all aspects of the SocialUI for |
|
202 // internal consistency. |
|
203 function checkSocialUI(win) { |
|
204 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService; |
|
205 win = win || window; |
|
206 let doc = win.document; |
|
207 let enabled = win.SocialUI.enabled; |
|
208 let active = Social.providers.length > 0 && !win.SocialUI._chromeless && |
|
209 !PrivateBrowsingUtils.isWindowPrivate(win); |
|
210 let sidebarEnabled = win.SocialSidebar.provider ? enabled : false; |
|
211 |
|
212 // if we have enabled providers, we should also have instances of those |
|
213 // providers |
|
214 if (SocialService.hasEnabledProviders) { |
|
215 ok(Social.providers.length > 0, "providers are enabled"); |
|
216 } else { |
|
217 is(Social.providers.length, 0, "providers are not enabled"); |
|
218 } |
|
219 |
|
220 // some local helpers to avoid log-spew for the many checks made here. |
|
221 let numGoodTests = 0, numTests = 0; |
|
222 function _ok(what, msg) { |
|
223 numTests++; |
|
224 if (!ok) |
|
225 ok(what, msg) |
|
226 else |
|
227 ++numGoodTests; |
|
228 } |
|
229 function _is(a, b, msg) { |
|
230 numTests++; |
|
231 if (a != b) |
|
232 is(a, b, msg) |
|
233 else |
|
234 ++numGoodTests; |
|
235 } |
|
236 function isbool(a, b, msg) { |
|
237 _is(!!a, !!b, msg); |
|
238 } |
|
239 isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?"); |
|
240 isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?"); |
|
241 isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?"); |
|
242 |
|
243 let contextMenus = [ |
|
244 { |
|
245 type: "link", |
|
246 id: "context-marklinkMenu", |
|
247 label: "social.marklinkMenu.label" |
|
248 }, |
|
249 { |
|
250 type: "page", |
|
251 id: "context-markpageMenu", |
|
252 label: "social.markpageMenu.label" |
|
253 } |
|
254 ]; |
|
255 |
|
256 for (let c of contextMenus) { |
|
257 let leMenu = document.getElementById(c.id); |
|
258 let parent, menus; |
|
259 let markProviders = SocialMarks.getProviders(); |
|
260 if (markProviders.length > SocialMarks.MENU_LIMIT) { |
|
261 // menus should be in a submenu, not in the top level of the context menu |
|
262 parent = leMenu.firstChild; |
|
263 menus = document.getElementsByClassName("context-mark" + c.type); |
|
264 _is(menus.length, 0, "menu's are not in main context menu\n"); |
|
265 menus = parent.childNodes; |
|
266 _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider"); |
|
267 } else { |
|
268 // menus should be in the top level of the context menu, not in a submenu |
|
269 parent = leMenu.parentNode; |
|
270 menus = document.getElementsByClassName("context-mark" + c.type); |
|
271 _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider"); |
|
272 menus = leMenu.firstChild.childNodes; |
|
273 _is(menus.length, 0, "menu's are not in context submenu\n"); |
|
274 } |
|
275 for (let m of menus) |
|
276 _is(m.parentNode, parent, "menu has correct parent"); |
|
277 } |
|
278 |
|
279 // and for good measure, check all the social commands. |
|
280 isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?"); |
|
281 isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?"); |
|
282 isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?"); |
|
283 isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?"); |
|
284 |
|
285 // and report on overall success of failure of the various checks here. |
|
286 is(numGoodTests, numTests, "The Social UI tests succeeded.") |
|
287 } |
|
288 |
|
289 function waitForNotification(topic, cb) { |
|
290 function observer(subject, topic, data) { |
|
291 Services.obs.removeObserver(observer, topic); |
|
292 cb(); |
|
293 } |
|
294 Services.obs.addObserver(observer, topic, false); |
|
295 } |
|
296 |
|
297 // blocklist testing |
|
298 function updateBlocklist(aCallback) { |
|
299 var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"] |
|
300 .getService(Ci.nsITimerCallback); |
|
301 var observer = function() { |
|
302 Services.obs.removeObserver(observer, "blocklist-updated"); |
|
303 if (aCallback) |
|
304 executeSoon(aCallback); |
|
305 }; |
|
306 Services.obs.addObserver(observer, "blocklist-updated", false); |
|
307 blocklistNotifier.notify(null); |
|
308 } |
|
309 |
|
310 var _originalTestBlocklistURL = null; |
|
311 function setAndUpdateBlocklist(aURL, aCallback) { |
|
312 if (!_originalTestBlocklistURL) |
|
313 _originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url"); |
|
314 Services.prefs.setCharPref("extensions.blocklist.url", aURL); |
|
315 updateBlocklist(aCallback); |
|
316 } |
|
317 |
|
318 function resetBlocklist(aCallback) { |
|
319 // XXX - this has "forked" from the head.js helpers in our parent directory :( |
|
320 // But let's reuse their blockNoPlugins.xml. Later, we should arrange to |
|
321 // use their head.js helpers directly |
|
322 let noBlockedURL = "http://example.com/browser/browser/base/content/test/plugins/blockNoPlugins.xml"; |
|
323 setAndUpdateBlocklist(noBlockedURL, function() { |
|
324 Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL); |
|
325 if (aCallback) |
|
326 aCallback(); |
|
327 }); |
|
328 } |
|
329 |
|
330 function setManifestPref(name, manifest) { |
|
331 let string = Cc["@mozilla.org/supports-string;1"]. |
|
332 createInstance(Ci.nsISupportsString); |
|
333 string.data = JSON.stringify(manifest); |
|
334 Services.prefs.setComplexValue(name, Ci.nsISupportsString, string); |
|
335 } |
|
336 |
|
337 function getManifestPrefname(aManifest) { |
|
338 // is same as the generated name in SocialServiceInternal.getManifestPrefname |
|
339 let originUri = Services.io.newURI(aManifest.origin, null, null); |
|
340 return "social.manifest." + originUri.hostPort.replace('.','-'); |
|
341 } |
|
342 |
|
343 function setBuiltinManifestPref(name, manifest) { |
|
344 // we set this as a default pref, it must not be a user pref |
|
345 manifest.builtin = true; |
|
346 let string = Cc["@mozilla.org/supports-string;1"]. |
|
347 createInstance(Ci.nsISupportsString); |
|
348 string.data = JSON.stringify(manifest); |
|
349 Services.prefs.getDefaultBranch(null).setComplexValue(name, Ci.nsISupportsString, string); |
|
350 // verify this is set on the default branch |
|
351 let stored = Services.prefs.getComplexValue(name, Ci.nsISupportsString).data; |
|
352 is(stored, string.data, "manifest '"+name+"' stored in default prefs"); |
|
353 // don't dirty our manifest, we'll need it without this flag later |
|
354 delete manifest.builtin; |
|
355 // verify we DO NOT have a user-level pref |
|
356 ok(!Services.prefs.prefHasUserValue(name), "manifest '"+name+"' is not in user-prefs"); |
|
357 } |
|
358 |
|
359 function resetBuiltinManifestPref(name) { |
|
360 Services.prefs.getDefaultBranch(null).deleteBranch(name); |
|
361 is(Services.prefs.getDefaultBranch(null).getPrefType(name), |
|
362 Services.prefs.PREF_INVALID, "default manifest removed"); |
|
363 } |
|
364 |
|
365 function addTab(url, callback) { |
|
366 let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true}); |
|
367 tab.linkedBrowser.addEventListener("load", function tabLoad(event) { |
|
368 tab.linkedBrowser.removeEventListener("load", tabLoad, true); |
|
369 executeSoon(function() {callback(tab)}); |
|
370 }, true); |
|
371 } |
|
372 |
|
373 function selectBrowserTab(tab, callback) { |
|
374 if (gBrowser.selectedTab == tab) { |
|
375 executeSoon(function() {callback(tab)}); |
|
376 return; |
|
377 } |
|
378 gBrowser.tabContainer.addEventListener("TabSelect", function onTabSelect() { |
|
379 gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect, false); |
|
380 is(gBrowser.selectedTab, tab, "browser tab is selected"); |
|
381 executeSoon(function() {callback(tab)}); |
|
382 }); |
|
383 gBrowser.selectedTab = tab; |
|
384 } |
|
385 |
|
386 function loadIntoTab(tab, url, callback) { |
|
387 tab.linkedBrowser.addEventListener("load", function tabLoad(event) { |
|
388 tab.linkedBrowser.removeEventListener("load", tabLoad, true); |
|
389 executeSoon(function() {callback(tab)}); |
|
390 }, true); |
|
391 tab.linkedBrowser.loadURI(url); |
|
392 } |
|
393 |
|
394 |
|
395 // chat test help functions |
|
396 |
|
397 // And lots of helpers for the resize tests. |
|
398 function get3ChatsForCollapsing(mode, cb) { |
|
399 // We make one chat, then measure its size. We then resize the browser to |
|
400 // ensure a second can be created fully visible but a third can not - then |
|
401 // create the other 2. first will will be collapsed, second fully visible |
|
402 // and the third also visible and the "selected" one. |
|
403 // To make our life easier we don't go via the worker and ports so we get |
|
404 // more control over creation *and* to make the code much simpler. We |
|
405 // assume the worker/port stuff is individually tested above. |
|
406 let chatbar = window.SocialChatBar.chatbar; |
|
407 let chatWidth = undefined; |
|
408 let num = 0; |
|
409 is(chatbar.childNodes.length, 0, "chatbar starting empty"); |
|
410 is(chatbar.menupopup.childNodes.length, 0, "popup starting empty"); |
|
411 |
|
412 makeChat(mode, "first chat", function() { |
|
413 // got the first one. |
|
414 checkPopup(); |
|
415 ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible"); |
|
416 // we kinda cheat here and get the width of the first chat, assuming |
|
417 // that all future chats will have the same width when open. |
|
418 chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat); |
|
419 let desired = chatWidth * 2.5; |
|
420 resizeWindowToChatAreaWidth(desired, function(sizedOk) { |
|
421 ok(sizedOk, "can't do any tests without this width"); |
|
422 checkPopup(); |
|
423 makeChat(mode, "second chat", function() { |
|
424 is(chatbar.childNodes.length, 2, "now have 2 chats"); |
|
425 checkPopup(); |
|
426 // and create the third. |
|
427 makeChat(mode, "third chat", function() { |
|
428 is(chatbar.childNodes.length, 3, "now have 3 chats"); |
|
429 checkPopup(); |
|
430 // XXX - this is a hacky implementation detail around the order of |
|
431 // the chats. Ideally things would be a little more sane wrt the |
|
432 // other in which the children were created. |
|
433 let second = chatbar.childNodes[2]; |
|
434 let first = chatbar.childNodes[1]; |
|
435 let third = chatbar.childNodes[0]; |
|
436 ok(first.collapsed && !second.collapsed && !third.collapsed, "collapsed state as promised"); |
|
437 is(chatbar.selectedChat, third, "third is selected as promised") |
|
438 info("have 3 chats for collapse testing - starting actual test..."); |
|
439 cb(first, second, third); |
|
440 }, mode); |
|
441 }, mode); |
|
442 }); |
|
443 }, mode); |
|
444 } |
|
445 |
|
446 function makeChat(mode, uniqueid, cb) { |
|
447 info("making a chat window '" + uniqueid +"'"); |
|
448 let provider = SocialSidebar.provider; |
|
449 const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html"; |
|
450 let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) { |
|
451 info("chat window has opened"); |
|
452 // we can't callback immediately or we might close the chat during |
|
453 // this event which upsets the implementation - it is only 1/2 way through |
|
454 // handling the load event. |
|
455 chat.document.title = uniqueid; |
|
456 executeSoon(cb); |
|
457 }, mode); |
|
458 if (!isOpened) { |
|
459 ok(false, "unable to open chat window, no provider? more failures to come"); |
|
460 executeSoon(cb); |
|
461 } |
|
462 } |
|
463 |
|
464 function checkPopup() { |
|
465 // popup only showing if any collapsed popup children. |
|
466 let chatbar = window.SocialChatBar.chatbar; |
|
467 let numCollapsed = 0; |
|
468 for (let chat of chatbar.childNodes) { |
|
469 if (chat.collapsed) { |
|
470 numCollapsed += 1; |
|
471 // and it have a menuitem weakmap |
|
472 is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item"); |
|
473 } else { |
|
474 ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item"); |
|
475 } |
|
476 } |
|
477 is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state"); |
|
478 is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children"); |
|
479 // todo - check each individual elt is what we expect? |
|
480 } |
|
481 // Resize the main window so the chat area's boxObject is |desired| wide. |
|
482 // Does a callback passing |true| if the window is now big enough or false |
|
483 // if we couldn't resize large enough to satisfy the test requirement. |
|
484 function resizeWindowToChatAreaWidth(desired, cb, count = 0) { |
|
485 let current = window.SocialChatBar.chatbar.getBoundingClientRect().width; |
|
486 let delta = desired - current; |
|
487 info(count + ": resizing window so chat area is " + desired + " wide, currently it is " |
|
488 + current + ". Screen avail is " + window.screen.availWidth |
|
489 + ", current outer width is " + window.outerWidth); |
|
490 |
|
491 // WTF? Sometimes we will get fractional values due to the - err - magic |
|
492 // of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference. |
|
493 let widthDeltaCloseEnough = function(d) { |
|
494 return Math.abs(d) < 2; |
|
495 } |
|
496 |
|
497 // attempting to resize by (0,0), unsurprisingly, doesn't cause a resize |
|
498 // event - so just callback saying all is well. |
|
499 if (widthDeltaCloseEnough(delta)) { |
|
500 info(count + ": skipping this as screen width is close enough"); |
|
501 executeSoon(function() { |
|
502 cb(true); |
|
503 }); |
|
504 return; |
|
505 } |
|
506 // On lo-res screens we may already be maxed out but still smaller than the |
|
507 // requested size, so asking to resize up also will not cause a resize event. |
|
508 // So just callback now saying the test must be skipped. |
|
509 if (window.screen.availWidth - window.outerWidth < delta) { |
|
510 info(count + ": skipping this as screen available width is less than necessary"); |
|
511 executeSoon(function() { |
|
512 cb(false); |
|
513 }); |
|
514 return; |
|
515 } |
|
516 function resize_handler(event) { |
|
517 // we did resize - but did we get far enough to be able to continue? |
|
518 let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width; |
|
519 let sizedOk = widthDeltaCloseEnough(newSize - desired); |
|
520 if (!sizedOk) |
|
521 return; |
|
522 window.removeEventListener("resize", resize_handler, true); |
|
523 info(count + ": resized window width is " + newSize); |
|
524 executeSoon(function() { |
|
525 cb(sizedOk); |
|
526 }); |
|
527 } |
|
528 // Otherwise we request resize and expect a resize event |
|
529 window.addEventListener("resize", resize_handler, true); |
|
530 window.resizeBy(delta, 0); |
|
531 } |
|
532 |
|
533 function resizeAndCheckWidths(first, second, third, checks, cb) { |
|
534 if (checks.length == 0) { |
|
535 cb(); // nothing more to check! |
|
536 return; |
|
537 } |
|
538 let count = checks.length; |
|
539 let [width, numExpectedVisible, why] = checks.shift(); |
|
540 info("<< Check " + count + ": " + why); |
|
541 info(count + ": " + "resizing window to " + width + ", expect " + numExpectedVisible + " visible items"); |
|
542 resizeWindowToChatAreaWidth(width, function(sizedOk) { |
|
543 checkPopup(); |
|
544 ok(sizedOk, count+": window resized correctly"); |
|
545 function collapsedObserver(r, m) { |
|
546 if ([first, second, third].filter(function(item) !item.collapsed).length == numExpectedVisible) { |
|
547 if (m) { |
|
548 m.disconnect(); |
|
549 } |
|
550 ok(true, count + ": " + "correct number of chats visible"); |
|
551 info(">> Check " + count); |
|
552 executeSoon(function() { |
|
553 resizeAndCheckWidths(first, second, third, checks, cb); |
|
554 }); |
|
555 } |
|
556 } |
|
557 let m = new MutationObserver(collapsedObserver); |
|
558 m.observe(first, {attributes: true }); |
|
559 m.observe(second, {attributes: true }); |
|
560 m.observe(third, {attributes: true }); |
|
561 // and just in case we are already at the right size, explicitly call the |
|
562 // observer. |
|
563 collapsedObserver(undefined, m); |
|
564 }, count); |
|
565 } |
|
566 |
|
567 function getPopupWidth() { |
|
568 let popup = window.SocialChatBar.chatbar.menupopup; |
|
569 ok(!popup.parentNode.collapsed, "asking for popup width when it is visible"); |
|
570 let cs = document.defaultView.getComputedStyle(popup.parentNode); |
|
571 let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight); |
|
572 return popup.parentNode.getBoundingClientRect().width + margins; |
|
573 } |
|
574 |
|
575 function closeAllChats() { |
|
576 let chatbar = window.SocialChatBar.chatbar; |
|
577 chatbar.removeAll(); |
|
578 } |