Wed, 31 Dec 2014 06:09:35 +0100
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 }