browser/base/content/test/general/browser_aboutHome.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 /* Any copyright is dedicated to the Public Domain.
     2  * http://creativecommons.org/publicdomain/zero/1.0/
     3  */
     5 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
     6   "resource://gre/modules/Promise.jsm");
     7 XPCOMUtils.defineLazyModuleGetter(this, "Task",
     8   "resource://gre/modules/Task.jsm");
     9 XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
    10   "resource:///modules/AboutHome.jsm");
    12 let gRightsVersion = Services.prefs.getIntPref("browser.rights.version");
    14 registerCleanupFunction(function() {
    15   // Ensure we don't pollute prefs for next tests.
    16   Services.prefs.clearUserPref("network.cookies.cookieBehavior");
    17   Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
    18   Services.prefs.clearUserPref("browser.rights.override");
    19   Services.prefs.clearUserPref("browser.rights." + gRightsVersion + ".shown");
    20 });
    22 let gTests = [
    24 {
    25   desc: "Check that clearing cookies does not clear storage",
    26   setup: function ()
    27   {
    28     Cc["@mozilla.org/observer-service;1"]
    29       .getService(Ci.nsIObserverService)
    30       .notifyObservers(null, "cookie-changed", "cleared");
    31   },
    32   run: function (aSnippetsMap)
    33   {
    34     isnot(aSnippetsMap.get("snippets-last-update"), null,
    35           "snippets-last-update should have a value");
    36   }
    37 },
    39 {
    40   desc: "Check default snippets are shown",
    41   setup: function () { },
    42   run: function ()
    43   {
    44     let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
    45     let snippetsElt = doc.getElementById("snippets");
    46     ok(snippetsElt, "Found snippets element")
    47     is(snippetsElt.getElementsByTagName("span").length, 1,
    48        "A default snippet is present.");
    49   }
    50 },
    52 {
    53   desc: "Check default snippets are shown if snippets are invalid xml",
    54   setup: function (aSnippetsMap)
    55   {
    56     // This must be some incorrect xhtml code.
    57     aSnippetsMap.set("snippets", "<p><b></p></b>");
    58   },
    59   run: function (aSnippetsMap)
    60   {
    61     let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
    63     let snippetsElt = doc.getElementById("snippets");
    64     ok(snippetsElt, "Found snippets element");
    65     is(snippetsElt.getElementsByTagName("span").length, 1,
    66        "A default snippet is present.");
    68     aSnippetsMap.delete("snippets");
    69   }
    70 },
    72 {
    73   desc: "Check that search engine logo has alt text",
    74   setup: function () { },
    75   run: function ()
    76   {
    77     let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
    79     let searchEngineLogoElt = doc.getElementById("searchEngineLogo");
    80     ok(searchEngineLogoElt, "Found search engine logo");
    82     let altText = searchEngineLogoElt.alt;
    83     ok(typeof altText == "string" && altText.length > 0,
    84        "Search engine logo's alt text is a nonempty string");
    86     isnot(altText, "undefined",
    87           "Search engine logo's alt text shouldn't be the string 'undefined'");
    88   }
    89 },
    91 // Disabled on Linux for intermittent issues with FHR, see Bug 945667.
    92 {
    93   desc: "Check that performing a search fires a search event and records to " +
    94         "Firefox Health Report.",
    95   setup: function () { },
    96   run: function () {
    97     // Skip this test on Linux.
    98     if (navigator.platform.indexOf("Linux") == 0) { return; }
   100     try {
   101       let cm = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
   102       cm.getCategoryEntry("healthreport-js-provider-default", "SearchesProvider");
   103     } catch (ex) {
   104       // Health Report disabled, or no SearchesProvider.
   105       return Promise.resolve();
   106     }
   108     let numSearchesBefore = 0;
   109     let searchEventDeferred = Promise.defer();
   110     let doc = gBrowser.contentDocument;
   111     let engineName = doc.documentElement.getAttribute("searchEngineName");
   113     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
   114       let data = JSON.parse(e.detail);
   115       is(data.engineName, engineName, "Detail is search engine name");
   117       // We use executeSoon() to ensure that this code runs after the
   118       // count has been updated in browser.js, since it uses the same
   119       // event.
   120       executeSoon(function () {
   121         getNumberOfSearches(engineName).then(num => {
   122           is(num, numSearchesBefore + 1, "One more search recorded.");
   123           searchEventDeferred.resolve();
   124         });
   125       });
   126     }, true, true);
   128     // Get the current number of recorded searches.
   129     let searchStr = "a search";
   130     getNumberOfSearches(engineName).then(num => {
   131       numSearchesBefore = num;
   133       info("Perform a search.");
   134       doc.getElementById("searchText").value = searchStr;
   135       doc.getElementById("searchSubmit").click();
   136     });
   138     let expectedURL = Services.search.currentEngine.
   139                       getSubmission(searchStr, null, "homepage").
   140                       uri.spec;
   141     let loadPromise = waitForDocLoadAndStopIt(expectedURL);
   143     return Promise.all([searchEventDeferred.promise, loadPromise]);
   144   }
   145 },
   147 {
   148   desc: "Check snippets map is cleared if cached version is old",
   149   setup: function (aSnippetsMap)
   150   {
   151     aSnippetsMap.set("snippets", "test");
   152     aSnippetsMap.set("snippets-cached-version", 0);
   153   },
   154   run: function (aSnippetsMap)
   155   {
   156     ok(!aSnippetsMap.has("snippets"), "snippets have been properly cleared");
   157     ok(!aSnippetsMap.has("snippets-cached-version"),
   158        "cached-version has been properly cleared");
   159   }
   160 },
   162 {
   163   desc: "Check cached snippets are shown if cached version is current",
   164   setup: function (aSnippetsMap)
   165   {
   166     aSnippetsMap.set("snippets", "test");
   167   },
   168   run: function (aSnippetsMap)
   169   {
   170     let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
   172     let snippetsElt = doc.getElementById("snippets");
   173     ok(snippetsElt, "Found snippets element");
   174     is(snippetsElt.innerHTML, "test", "Cached snippet is present.");
   176     is(aSnippetsMap.get("snippets"), "test", "snippets still cached");
   177     is(aSnippetsMap.get("snippets-cached-version"),
   178        AboutHomeUtils.snippetsVersion,
   179        "cached-version is correct");
   180     ok(aSnippetsMap.has("snippets-last-update"), "last-update still exists");
   181   }
   182 },
   184 {
   185   desc: "Check if the 'Know Your Rights default snippet is shown when 'browser.rights.override' pref is set",
   186   beforeRun: function ()
   187   {
   188     Services.prefs.setBoolPref("browser.rights.override", false);
   189   },
   190   setup: function () { },
   191   run: function (aSnippetsMap)
   192   {
   193     let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
   194     let showRights = AboutHomeUtils.showKnowYourRights;
   196     ok(showRights, "AboutHomeUtils.showKnowYourRights should be TRUE");
   198     let snippetsElt = doc.getElementById("snippets");
   199     ok(snippetsElt, "Found snippets element");
   200     is(snippetsElt.getElementsByTagName("a")[0].href, "about:rights", "Snippet link is present.");
   202     Services.prefs.clearUserPref("browser.rights.override");
   203   }
   204 },
   206 {
   207   desc: "Check if the 'Know Your Rights default snippet is NOT shown when 'browser.rights.override' pref is NOT set",
   208   beforeRun: function ()
   209   {
   210     Services.prefs.setBoolPref("browser.rights.override", true);
   211   },
   212   setup: function () { },
   213   run: function (aSnippetsMap)
   214   {
   215     let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
   216     let rightsData = AboutHomeUtils.knowYourRightsData;
   218     ok(!rightsData, "AboutHomeUtils.knowYourRightsData should be FALSE");
   220     let snippetsElt = doc.getElementById("snippets");
   221     ok(snippetsElt, "Found snippets element");
   222     ok(snippetsElt.getElementsByTagName("a")[0].href != "about:rights", "Snippet link should not point to about:rights.");
   224     Services.prefs.clearUserPref("browser.rights.override");
   225   }
   226 },
   228 {
   229   desc: "Check that the search UI/ action is updated when the search engine is changed",
   230   setup: function() {},
   231   run: function()
   232   {
   233     let currEngine = Services.search.currentEngine;
   234     let unusedEngines = [].concat(Services.search.getVisibleEngines()).filter(x => x != currEngine);
   235     let searchbar = document.getElementById("searchbar");
   237     function checkSearchUI(engine) {
   238       let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
   239       let searchText = doc.getElementById("searchText");
   240       let logoElt = doc.getElementById("searchEngineLogo");
   241       let engineName = doc.documentElement.getAttribute("searchEngineName");
   243       is(engineName, engine.name, "Engine name should've been updated");
   245       if (!logoElt.parentNode.hidden) {
   246         is(logoElt.alt, engineName, "Alt text of logo image should match search engine name")
   247       } else {
   248         is(searchText.placeholder, engineName, "Placeholder text should match search engine name");
   249       }
   250     }
   251     // Do a sanity check that all attributes are correctly set to begin with
   252     checkSearchUI(currEngine);
   254     let deferred = Promise.defer();
   255     promiseBrowserAttributes(gBrowser.selectedTab).then(function() {
   256       // Test if the update propagated
   257       checkSearchUI(unusedEngines[0]);
   258       searchbar.currentEngine = currEngine;
   259       deferred.resolve();
   260     });
   262     // The following cleanup function will set currentEngine back to the previous
   263     // engine if we fail to do so above.
   264     registerCleanupFunction(function() {
   265       searchbar.currentEngine = currEngine;
   266     });
   267     // Set the current search engine to an unused one
   268     searchbar.currentEngine = unusedEngines[0];
   269     searchbar.select();
   270     return deferred.promise;
   271   }
   272 },
   274 {
   275   desc: "Check POST search engine support",
   276   setup: function() {},
   277   run: function()
   278   {
   279     let deferred = Promise.defer();
   280     let currEngine = Services.search.defaultEngine;
   281     let searchObserver = function search_observer(aSubject, aTopic, aData) {
   282       let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
   283       info("Observer: " + aData + " for " + engine.name);
   285       if (aData != "engine-added")
   286         return;
   288       if (engine.name != "POST Search")
   289         return;
   291       // Ready to execute the tests!
   292       let needle = "Search for something awesome.";
   293       let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
   294       let searchText = document.getElementById("searchText");
   296       // We're about to change the search engine. Once the change has
   297       // propagated to the about:home content, we want to perform a search.
   298       let mutationObserver = new MutationObserver(function (mutations) {
   299         for (let mutation of mutations) {
   300           if (mutation.attributeName == "searchEngineName") {
   301             searchText.value = needle;
   302             searchText.focus();
   303             EventUtils.synthesizeKey("VK_RETURN", {});
   304           }
   305         }
   306       });
   307       mutationObserver.observe(document.documentElement, { attributes: true });
   309       // Change the search engine, triggering the observer above.
   310       Services.search.defaultEngine = engine;
   312       registerCleanupFunction(function() {
   313         mutationObserver.disconnect();
   314         Services.search.removeEngine(engine);
   315         Services.search.defaultEngine = currEngine;
   316       });
   319       // When the search results load, check them for correctness.
   320       waitForLoad(function() {
   321         let loadedText = gBrowser.contentDocument.body.textContent;
   322         ok(loadedText, "search page loaded");
   323         is(loadedText, "searchterms=" + escape(needle.replace(/\s/g, "+")),
   324            "Search text should arrive correctly");
   325         deferred.resolve();
   326       });
   327     };
   328     Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false);
   329     registerCleanupFunction(function () {
   330       Services.obs.removeObserver(searchObserver, "browser-search-engine-modified");
   331     });
   332     Services.search.addEngine("http://test:80/browser/browser/base/content/test/general/POSTSearchEngine.xml",
   333                               Ci.nsISearchEngine.DATA_XML, null, false);
   334     return deferred.promise;
   335   }
   336 },
   338 {
   339   desc: "Make sure that a page can't imitate about:home",
   340   setup: function () { },
   341   run: function (aSnippetsMap)
   342   {
   343     let deferred = Promise.defer();
   345     let browser = gBrowser.selectedTab.linkedBrowser;
   346     waitForLoad(() => {
   347       let button = browser.contentDocument.getElementById("settings");
   348       ok(button, "Found settings button in test page");
   349       button.click();
   351       // It may take a few turns of the event loop before the window
   352       // is displayed, so we wait.
   353       function check(n) {
   354         let win = Services.wm.getMostRecentWindow("Browser:Preferences");
   355         ok(!win, "Preferences window not showing");
   356         if (win) {
   357           win.close();
   358         }
   360         if (n > 0) {
   361           executeSoon(() => check(n-1));
   362         } else {
   363           deferred.resolve();
   364         }
   365       }
   367       check(5);
   368     });
   370     browser.loadURI("https://example.com/browser/browser/base/content/test/general/test_bug959531.html");
   371     return deferred.promise;
   372   }
   373 },
   375 ];
   377 function test()
   378 {
   379   waitForExplicitFinish();
   380   requestLongerTimeout(2);
   381   ignoreAllUncaughtExceptions();
   383   Task.spawn(function () {
   384     for (let test of gTests) {
   385       info(test.desc);
   387       if (test.beforeRun)
   388         yield test.beforeRun();
   390       // Create a tab to run the test.
   391       let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
   393       // Add an event handler to modify the snippets map once it's ready.
   394       let snippetsPromise = promiseSetupSnippetsMap(tab, test.setup);
   396       // Start loading about:home and wait for it to complete.
   397       yield promiseTabLoadEvent(tab, "about:home", "AboutHomeLoadSnippetsSucceeded");
   399       // This promise should already be resolved since the page is done,
   400       // but we still want to get the snippets map out of it.
   401       let snippetsMap = yield snippetsPromise;
   403       info("Running test");
   404       yield test.run(snippetsMap);
   405       info("Cleanup");
   406       gBrowser.removeCurrentTab();
   407     }
   408   }).then(finish, ex => {
   409     ok(false, "Unexpected Exception: " + ex);
   410     finish();
   411   });
   412 }
   414 /**
   415  * Starts a load in an existing tab and waits for it to finish (via some event).
   416  *
   417  * @param aTab
   418  *        The tab to load into.
   419  * @param aUrl
   420  *        The url to load.
   421  * @param aEvent
   422  *        The load event type to wait for.  Defaults to "load".
   423  * @return {Promise} resolved when the event is handled.
   424  */
   425 function promiseTabLoadEvent(aTab, aURL, aEventType="load")
   426 {
   427   let deferred = Promise.defer();
   428   info("Wait tab event: " + aEventType);
   429   aTab.linkedBrowser.addEventListener(aEventType, function load(event) {
   430     if (event.originalTarget != aTab.linkedBrowser.contentDocument ||
   431         event.target.location.href == "about:blank") {
   432       info("skipping spurious load event");
   433       return;
   434     }
   435     aTab.linkedBrowser.removeEventListener(aEventType, load, true);
   436     info("Tab event received: " + aEventType);
   437     deferred.resolve();
   438   }, true, true);
   439   aTab.linkedBrowser.loadURI(aURL);
   440   return deferred.promise;
   441 }
   443 /**
   444  * Cleans up snippets and ensures that by default we don't try to check for
   445  * remote snippets since that may cause network bustage or slowness.
   446  *
   447  * @param aTab
   448  *        The tab containing about:home.
   449  * @param aSetupFn
   450  *        The setup function to be run.
   451  * @return {Promise} resolved when the snippets are ready.  Gets the snippets map.
   452  */
   453 function promiseSetupSnippetsMap(aTab, aSetupFn)
   454 {
   455   let deferred = Promise.defer();
   456   info("Waiting for snippets map");
   457   aTab.linkedBrowser.addEventListener("AboutHomeLoadSnippets", function load(event) {
   458     aTab.linkedBrowser.removeEventListener("AboutHomeLoadSnippets", load, true);
   460     let cw = aTab.linkedBrowser.contentWindow.wrappedJSObject;
   461     // The snippets should already be ready by this point. Here we're
   462     // just obtaining a reference to the snippets map.
   463     cw.ensureSnippetsMapThen(function (aSnippetsMap) {
   464       info("Got snippets map: " +
   465            "{ last-update: " + aSnippetsMap.get("snippets-last-update") +
   466            ", cached-version: " + aSnippetsMap.get("snippets-cached-version") +
   467            " }");
   468       // Don't try to update.
   469       aSnippetsMap.set("snippets-last-update", Date.now());
   470       aSnippetsMap.set("snippets-cached-version", AboutHomeUtils.snippetsVersion);
   471       // Clear snippets.
   472       aSnippetsMap.delete("snippets");
   473       aSetupFn(aSnippetsMap);
   474       deferred.resolve(aSnippetsMap);
   475     });
   476   }, true, true);
   477   return deferred.promise;
   478 }
   480 /**
   481  * Waits for the attributes being set by browser.js.
   482  *
   483  * @param aTab
   484  *        The tab containing about:home.
   485  * @return {Promise} resolved when the attributes are ready.
   486  */
   487 function promiseBrowserAttributes(aTab)
   488 {
   489   let deferred = Promise.defer();
   491   let docElt = aTab.linkedBrowser.contentDocument.documentElement;
   492   let observer = new MutationObserver(function (mutations) {
   493     for (let mutation of mutations) {
   494       info("Got attribute mutation: " + mutation.attributeName +
   495                                     " from " + mutation.oldValue);
   496       // Now we just have to wait for the last attribute.
   497       if (mutation.attributeName == "searchEngineName") {
   498         info("Remove attributes observer");
   499         observer.disconnect();
   500         // Must be sure to continue after the page mutation observer.
   501         executeSoon(function() deferred.resolve());
   502         break;
   503       }
   504     }
   505   });
   506   info("Add attributes observer");
   507   observer.observe(docElt, { attributes: true });
   509   return deferred.promise;
   510 }
   512 /**
   513  * Retrieves the number of about:home searches recorded for the current day.
   514  *
   515  * @param aEngineName
   516  *        name of the setup search engine.
   517  *
   518  * @return {Promise} Returns a promise resolving to the number of searches.
   519  */
   520 function getNumberOfSearches(aEngineName) {
   521   let reporter = Components.classes["@mozilla.org/datareporting/service;1"]
   522                                    .getService()
   523                                    .wrappedJSObject
   524                                    .healthReporter;
   525   ok(reporter, "Health Reporter instance available.");
   527   return reporter.onInit().then(function onInit() {
   528     let provider = reporter.getProvider("org.mozilla.searches");
   529     ok(provider, "Searches provider is available.");
   531     let m = provider.getMeasurement("counts", 3);
   532     return m.getValues().then(data => {
   533       let now = new Date();
   534       let yday = new Date(now);
   535       yday.setDate(yday.getDate() - 1);
   537       // Add the number of searches recorded yesterday to the number of searches
   538       // recorded today. This makes the test not fail intermittently when it is
   539       // run at midnight and we accidentally compare the number of searches from
   540       // different days. Tests are always run with an empty profile so there
   541       // are no searches from yesterday, normally. Should the test happen to run
   542       // past midnight we make sure to count them in as well.
   543       return getNumberOfSearchesByDate(aEngineName, data, now) +
   544              getNumberOfSearchesByDate(aEngineName, data, yday);
   545     });
   546   });
   547 }
   549 function getNumberOfSearchesByDate(aEngineName, aData, aDate) {
   550   if (aData.days.hasDay(aDate)) {
   551     let id = Services.search.getEngineByName(aEngineName).identifier;
   553     let day = aData.days.getDay(aDate);
   554     let field = id + ".abouthome";
   556     if (day.has(field)) {
   557       return day.get(field) || 0;
   558     }
   559   }
   561   return 0; // No records found.
   562 }
   564 function waitForLoad(cb) {
   565   let browser = gBrowser.selectedBrowser;
   566   browser.addEventListener("load", function listener() {
   567     if (browser.currentURI.spec == "about:blank")
   568       return;
   569     info("Page loaded: " + browser.currentURI.spec);
   570     browser.removeEventListener("load", listener, true);
   572     cb();
   573   }, true);
   574 }

mercurial