browser/base/content/test/social/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial