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.

     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/. */
     5 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
     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");
    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 }
    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 }
    48 let gURLsNotRemembered = [];
    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 }
    65 function defaultFinishChecks() {
    66   checkProviderPrefsEmpty(true);
    67   finish();
    68 }
    70 function runSocialTestWithProvider(manifest, callback, finishcallback) {
    71   let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
    73   let manifests = Array.isArray(manifest) ? manifest : [manifest];
    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   }
    93   info("runSocialTestWithProvider: " + manifests.toSource());
    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   }
   126   let providersAdded = 0;
   127   let firstProvider;
   129   manifests.forEach(function (m) {
   130     SocialService.addProvider(m, function(provider) {
   132       providersAdded++;
   133       info("runSocialTestWithProvider: provider added");
   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       }
   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 }
   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");
   161   if (cbPreTest === undefined) {
   162     cbPreTest = function(cb) {cb()};
   163   }
   164   if (cbPostTest === undefined) {
   165     cbPostTest = function(cb) {cb()};
   166   }
   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 }
   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;
   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   }
   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?");
   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   ];
   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   }
   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?");
   285   // and report on overall success of failure of the various checks here.
   286   is(numGoodTests, numTests, "The Social UI tests succeeded.")
   287 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   395 // chat test help functions
   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");
   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 }
   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 }
   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);
   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   }
   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 }
   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 }
   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 }
   575 function closeAllChats() {
   576   let chatbar = window.SocialChatBar.chatbar;
   577   chatbar.removeAll();
   578 }

mercurial