addon-sdk/source/test/test-page-mod.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 "use strict";
     6 const { PageMod } = require("sdk/page-mod");
     7 const { testPageMod, handleReadyState } = require("./pagemod-test-helpers");
     8 const { Loader } = require('sdk/test/loader');
     9 const tabs = require("sdk/tabs");
    10 const { setTimeout } = require("sdk/timers");
    11 const { Cc, Ci, Cu } = require("chrome");
    12 const {
    13   open,
    14   getFrames,
    15   getMostRecentBrowserWindow,
    16   getInnerId
    17 } = require('sdk/window/utils');
    18 const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
    19 const xulApp = require("sdk/system/xul-app");
    20 const { isPrivateBrowsingSupported } = require('sdk/self');
    21 const { isPrivate } = require('sdk/private-browsing');
    22 const { openWebpage } = require('./private-browsing/helper');
    23 const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
    24 const promise = require("sdk/core/promise");
    25 const { pb } = require('./private-browsing/helper');
    26 const { URL } = require("sdk/url");
    27 const { LoaderWithHookedConsole } = require('sdk/test/loader');
    29 const { waitUntil } = require("sdk/test/utils");
    30 const data = require("./fixtures");
    32 const { gDevToolsExtensions } = Cu.import("resource://gre/modules/devtools/DevToolsExtensions.jsm", {});
    34 const testPageURI = data.url("test.html");
    36 // The following adds Debugger constructor to the global namespace.
    37 const { addDebuggerToGlobal } =
    38   Cu.import('resource://gre/modules/jsdebugger.jsm', {});
    39 addDebuggerToGlobal(this);
    41 function Isolate(worker) {
    42   return "(" + worker + ")()";
    43 }
    45 /* Tests for the PageMod APIs */
    47 exports.testPageMod1 = function(assert, done) {
    48   let mods = testPageMod(assert, done, "about:", [{
    49       include: /about:/,
    50       contentScriptWhen: 'end',
    51       contentScript: 'new ' + function WorkerScope() {
    52         window.document.body.setAttribute("JEP-107", "worked");
    53       },
    54       onAttach: function() {
    55         assert.equal(this, mods[0], "The 'this' object is the page mod.");
    56       }
    57     }],
    58     function(win, done) {
    59       assert.equal(
    60         win.document.body.getAttribute("JEP-107"),
    61         "worked",
    62         "PageMod.onReady test"
    63       );
    64       done();
    65     }
    66   );
    67 };
    69 exports.testPageMod2 = function(assert, done) {
    70   testPageMod(assert, done, "about:", [{
    71       include: "about:*",
    72       contentScript: [
    73         'new ' + function contentScript() {
    74           window.AUQLUE = function() { return 42; }
    75           try {
    76             window.AUQLUE()
    77           }
    78           catch(e) {
    79             throw new Error("PageMod scripts executed in order");
    80           }
    81           document.documentElement.setAttribute("first", "true");
    82         },
    83         'new ' + function contentScript() {
    84           document.documentElement.setAttribute("second", "true");
    85         }
    86       ]
    87     }], function(win, done) {
    88       assert.equal(win.document.documentElement.getAttribute("first"),
    89                        "true",
    90                        "PageMod test #2: first script has run");
    91       assert.equal(win.document.documentElement.getAttribute("second"),
    92                        "true",
    93                        "PageMod test #2: second script has run");
    94       assert.equal("AUQLUE" in win, false,
    95                        "PageMod test #2: scripts get a wrapped window");
    96       done();
    97     });
    98 };
   100 exports.testPageModIncludes = function(assert, done) {
   101   var asserts = [];
   102   function createPageModTest(include, expectedMatch) {
   103     // Create an 'onload' test function...
   104     asserts.push(function(test, win) {
   105       var matches = include in win.localStorage;
   106       assert.ok(expectedMatch ? matches : !matches,
   107                   "'" + include + "' match test, expected: " + expectedMatch);
   108     });
   109     // ...and corresponding PageMod options
   110     return {
   111       include: include,
   112       contentScript: 'new ' + function() {
   113         self.on("message", function(msg) {
   114           window.localStorage[msg] = true;
   115         });
   116       },
   117       // The testPageMod callback with test assertions is called on 'end',
   118       // and we want this page mod to be attached before it gets called,
   119       // so we attach it on 'start'.
   120       contentScriptWhen: 'start',
   121       onAttach: function(worker) {
   122         worker.postMessage(this.include[0]);
   123       }
   124     };
   125   }
   127   testPageMod(assert, done, testPageURI, [
   128       createPageModTest("*", false),
   129       createPageModTest("*.google.com", false),
   130       createPageModTest("resource:*", true),
   131       createPageModTest("resource:", false),
   132       createPageModTest(testPageURI, true)
   133     ],
   134     function (win, done) {
   135       waitUntil(function () win.localStorage[testPageURI],
   136                      testPageURI + " page-mod to be executed")
   137           .then(function () {
   138             asserts.forEach(function(fn) {
   139               fn(assert, win);
   140             });
   141             done();
   142           });
   143     }
   144     );
   145 };
   147 exports.testPageModValidationAttachTo = function(assert) {
   148   [{ val: 'top', type: 'string "top"' },
   149    { val: 'frame', type: 'string "frame"' },
   150    { val: ['top', 'existing'], type: 'array with "top" and "existing"' },
   151    { val: ['frame', 'existing'], type: 'array with "frame" and "existing"' },
   152    { val: ['top'], type: 'array with "top"' },
   153    { val: ['frame'], type: 'array with "frame"' },
   154    { val: undefined, type: 'undefined' }].forEach((attachTo) => {
   155     new PageMod({ attachTo: attachTo.val, include: '*.validation111' });
   156     assert.pass("PageMod() does not throw when attachTo is " + attachTo.type);
   157   });
   159   [{ val: 'existing', type: 'string "existing"' },
   160    { val: ['existing'], type: 'array with "existing"' },
   161    { val: 'not-legit', type: 'string with "not-legit"' },
   162    { val: ['not-legit'], type: 'array with "not-legit"' },
   163    { val: {}, type: 'object' }].forEach((attachTo) => {
   164     assert.throws(() =>
   165       new PageMod({ attachTo: attachTo.val, include: '*.validation111' }),
   166       /The `attachTo` option/,
   167       "PageMod() throws when 'attachTo' option is " + attachTo.type + ".");
   168   });
   169 };
   171 exports.testPageModValidationInclude = function(assert) {
   172   [{ val: undefined, type: 'undefined' },
   173    { val: {}, type: 'object' },
   174    { val: [], type: 'empty array'},
   175    { val: [/regexp/, 1], type: 'array with non string/regexp' },
   176    { val: 1, type: 'number' }].forEach((include) => {
   177     assert.throws(() => new PageMod({ include: include.val }),
   178       /The `include` option must always contain atleast one rule/,
   179       "PageMod() throws when 'include' option is " + include.type + ".");
   180   });
   182   [{ val: '*.validation111', type: 'string' },
   183    { val: /validation111/, type: 'regexp' },
   184    { val: ['*.validation111'], type: 'array with length > 0'}].forEach((include) => {
   185     new PageMod({ include: include.val });
   186     assert.pass("PageMod() does not throw when include option is " + include.type);
   187   });
   188 };
   190 /* Tests for internal functions. */
   191 exports.testCommunication1 = function(assert, done) {
   192   let workerDone = false,
   193       callbackDone = null;
   195   testPageMod(assert, done, "about:", [{
   196       include: "about:*",
   197       contentScriptWhen: 'end',
   198       contentScript: 'new ' + function WorkerScope() {
   199         self.on('message', function(msg) {
   200           document.body.setAttribute('JEP-107', 'worked');
   201           self.postMessage(document.body.getAttribute('JEP-107'));
   202         })
   203       },
   204       onAttach: function(worker) {
   205         worker.on('error', function(e) {
   206           assert.fail('Errors where reported');
   207         });
   208         worker.on('message', function(value) {
   209           assert.equal(
   210             "worked",
   211             value,
   212             "test comunication"
   213           );
   214           workerDone = true;
   215           if (callbackDone)
   216             callbackDone();
   217         });
   218         worker.postMessage('do it!')
   219       }
   220     }],
   221     function(win, done) {
   222       (callbackDone = function() {
   223         if (workerDone) {
   224           assert.equal(
   225             'worked',
   226             win.document.body.getAttribute('JEP-107'),
   227             'attribute should be modified'
   228           );
   229           done();
   230         }
   231       })();
   232     }
   233   );
   234 };
   236 exports.testCommunication2 = function(assert, done) {
   237   let callbackDone = null,
   238       window;
   240   testPageMod(assert, done, "about:license", [{
   241       include: "about:*",
   242       contentScriptWhen: 'start',
   243       contentScript: 'new ' + function WorkerScope() {
   244         document.documentElement.setAttribute('AUQLUE', 42);
   245         window.addEventListener('load', function listener() {
   246           self.postMessage('onload');
   247         }, false);
   248         self.on("message", function() {
   249           self.postMessage(document.documentElement.getAttribute("test"))
   250         });
   251       },
   252       onAttach: function(worker) {
   253         worker.on('error', function(e) {
   254           assert.fail('Errors where reported');
   255         });
   256         worker.on('message', function(msg) {
   257           if ('onload' == msg) {
   258             assert.equal(
   259               '42',
   260               window.document.documentElement.getAttribute('AUQLUE'),
   261               'PageMod scripts executed in order'
   262             );
   263             window.document.documentElement.setAttribute('test', 'changes in window');
   264             worker.postMessage('get window.test')
   265           } else {
   266             assert.equal(
   267               'changes in window',
   268               msg,
   269               'PageMod test #2: second script has run'
   270             )
   271             callbackDone();
   272           }
   273         });
   274       }
   275     }],
   276     function(win, done) {
   277       window = win;
   278       callbackDone = done;
   279     }
   280   );
   281 };
   283 exports.testEventEmitter = function(assert, done) {
   284   let workerDone = false,
   285       callbackDone = null;
   287   testPageMod(assert, done, "about:", [{
   288       include: "about:*",
   289       contentScript: 'new ' + function WorkerScope() {
   290         self.port.on('addon-to-content', function(data) {
   291           self.port.emit('content-to-addon', data);
   292         });
   293       },
   294       onAttach: function(worker) {
   295         worker.on('error', function(e) {
   296           assert.fail('Errors were reported : '+e);
   297         });
   298         worker.port.on('content-to-addon', function(value) {
   299           assert.equal(
   300             "worked",
   301             value,
   302             "EventEmitter API works!"
   303           );
   304           if (callbackDone)
   305             callbackDone();
   306           else
   307             workerDone = true;
   308         });
   309         worker.port.emit('addon-to-content', 'worked');
   310       }
   311     }],
   312     function(win, done) {
   313       if (workerDone)
   314         done();
   315       else
   316         callbackDone = done;
   317     }
   318   );
   319 };
   321 // Execute two concurrent page mods on same document to ensure that their
   322 // JS contexts are different
   323 exports.testMixedContext = function(assert, done) {
   324   let doneCallback = null;
   325   let messages = 0;
   326   let modObject = {
   327     include: "data:text/html;charset=utf-8,",
   328     contentScript: 'new ' + function WorkerScope() {
   329       // Both scripts will execute this,
   330       // context is shared if one script see the other one modification.
   331       let isContextShared = "sharedAttribute" in document;
   332       self.postMessage(isContextShared);
   333       document.sharedAttribute = true;
   334     },
   335     onAttach: function(w) {
   336       w.on("message", function (isContextShared) {
   337         if (isContextShared) {
   338           assert.fail("Page mod contexts are mixed.");
   339           doneCallback();
   340         }
   341         else if (++messages == 2) {
   342           assert.pass("Page mod contexts are different.");
   343           doneCallback();
   344         }
   345       });
   346     }
   347   };
   348   testPageMod(assert, done, "data:text/html;charset=utf-8,", [modObject, modObject],
   349     function(win, done) {
   350       doneCallback = done;
   351     }
   352   );
   353 };
   355 exports.testHistory = function(assert, done) {
   356   // We need a valid url in order to have a working History API.
   357   // (i.e do not work on data: or about: pages)
   358   // Test bug 679054.
   359   let url = data.url("test-page-mod.html");
   360   let callbackDone = null;
   361   testPageMod(assert, done, url, [{
   362       include: url,
   363       contentScriptWhen: 'end',
   364       contentScript: 'new ' + function WorkerScope() {
   365         history.pushState({}, "", "#");
   366         history.replaceState({foo: "bar"}, "", "#");
   367         self.postMessage(history.state);
   368       },
   369       onAttach: function(worker) {
   370         worker.on('message', function (data) {
   371           assert.equal(JSON.stringify(data), JSON.stringify({foo: "bar"}),
   372                            "History API works!");
   373           callbackDone();
   374         });
   375       }
   376     }],
   377     function(win, done) {
   378       callbackDone = done;
   379     }
   380   );
   381 };
   383 exports.testRelatedTab = function(assert, done) {
   384   let tab;
   385   let pageMod = new PageMod({
   386     include: "about:*",
   387     onAttach: function(worker) {
   388       assert.ok(!!worker.tab, "Worker.tab exists");
   389       assert.equal(tab, worker.tab, "Worker.tab is valid");
   390       pageMod.destroy();
   391       tab.close(done);
   392     }
   393   });
   395   tabs.open({
   396     url: "about:",
   397     onOpen: function onOpen(t) {
   398       tab = t;
   399     }
   400   });
   401 };
   403 exports.testRelatedTabNoRequireTab = function(assert, done) {
   404   let loader = Loader(module);
   405   let tab;
   406   let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2");
   407   let { PageMod } = loader.require("sdk/page-mod");
   408   let pageMod = new PageMod({
   409     include: url,
   410     onAttach: function(worker) {
   411       assert.equal(worker.tab.url, url, "Worker.tab.url is valid");
   412       worker.tab.close(function() {
   413         pageMod.destroy();
   414         loader.unload();
   415         done();
   416       });
   417     }
   418   });
   420   tabs.open(url);
   421 };
   423 exports.testRelatedTabNoOtherReqs = function(assert, done) {
   424   let loader = Loader(module);
   425   let { PageMod } = loader.require("sdk/page-mod");
   426   let pageMod = new PageMod({
   427     include: "about:blank?testRelatedTabNoOtherReqs",
   428     onAttach: function(worker) {
   429       assert.ok(!!worker.tab, "Worker.tab exists");
   430       pageMod.destroy();
   431       worker.tab.close(function() {
   432         worker.destroy();
   433         loader.unload();
   434         done();
   435       });
   436     }
   437   });
   439   tabs.open({
   440     url: "about:blank?testRelatedTabNoOtherReqs"
   441   });
   442 };
   444 exports.testWorksWithExistingTabs = function(assert, done) {
   445   let url = "data:text/html;charset=utf-8," + encodeURI("Test unique document");
   446   let { PageMod } = require("sdk/page-mod");
   447   tabs.open({
   448     url: url,
   449     onReady: function onReady(tab) {
   450       let pageModOnExisting = new PageMod({
   451         include: url,
   452         attachTo: ["existing", "top", "frame"],
   453         onAttach: function(worker) {
   454           assert.ok(!!worker.tab, "Worker.tab exists");
   455           assert.equal(tab, worker.tab, "A worker has been created on this existing tab");
   457           setTimeout(function() {
   458             pageModOnExisting.destroy();
   459             pageModOffExisting.destroy();
   460             tab.close(done);
   461           }, 0);
   462         }
   463       });
   465       let pageModOffExisting = new PageMod({
   466         include: url,
   467         onAttach: function(worker) {
   468           assert.fail("pageModOffExisting page-mod should not have attached to anything");
   469         }
   470       });
   471     }
   472   });
   473 };
   475 exports.testExistingFrameDoesntMatchInclude = function(assert, done) {
   476   let iframeURL = 'data:text/html;charset=utf-8,UNIQUE-TEST-STRING-42';
   477   let iframe = '<iframe src="' + iframeURL + '" />';
   478   let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(iframe);
   479   tabs.open({
   480     url: url,
   481     onReady: function onReady(tab) {
   482       let pagemod = new PageMod({
   483         include: url,
   484         attachTo: ['existing', 'frame'],
   485         onAttach: function() {
   486           assert.fail("Existing iframe URL doesn't match include, must not attach to anything");
   487         }
   488       });
   489       setTimeout(function() {
   490         assert.pass("PageMod didn't attach to anything")
   491         pagemod.destroy();
   492         tab.close(done);
   493       }, 250);
   494     }
   495   });
   496 };
   498 exports.testExistingOnlyFrameMatchesInclude = function(assert, done) {
   499   let iframeURL = 'data:text/html;charset=utf-8,UNIQUE-TEST-STRING-43';
   500   let iframe = '<iframe src="' + iframeURL + '" />';
   501   let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(iframe);
   502   tabs.open({
   503     url: url,
   504     onReady: function onReady(tab) {
   505       let pagemod = new PageMod({
   506         include: iframeURL,
   507         attachTo: ['existing', 'frame'],
   508         onAttach: function(worker) {
   509           assert.equal(iframeURL, worker.url,
   510               "PageMod attached to existing iframe when only it matches include rules");
   511           pagemod.destroy();
   512           tab.close(done);
   513         }
   514       });
   515     }
   516   });
   517 };
   519 exports.testContentScriptWhenDefault = function(assert) {
   520   let pagemod = PageMod({include: '*'});
   522   assert.equal(pagemod.contentScriptWhen, 'end', "Default contentScriptWhen is 'end'");
   523   pagemod.destroy();
   524 }
   526 // test timing for all 3 contentScriptWhen options (start, ready, end)
   527 // for new pages, or tabs opened after PageMod is created
   528 exports.testContentScriptWhenForNewTabs = function(assert, done) {
   529   const url = "data:text/html;charset=utf-8,testContentScriptWhenForNewTabs";
   531   let count = 0;
   533   handleReadyState(url, 'start', {
   534     onLoading: (tab) => {
   535       assert.pass("PageMod is attached while document is loading");
   536       if (++count === 3)
   537         tab.close(done);
   538     },
   539     onInteractive: () => assert.fail("onInteractive should not be called with 'start'."),
   540     onComplete: () => assert.fail("onComplete should not be called with 'start'."),
   541   });
   543   handleReadyState(url, 'ready', {
   544     onInteractive: (tab) => {
   545       assert.pass("PageMod is attached while document is interactive");
   546       if (++count === 3)
   547         tab.close(done);
   548     },
   549     onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
   550     onComplete: () => assert.fail("onComplete should not be called with 'ready'."),
   551   });
   553   handleReadyState(url, 'end', {
   554     onComplete: (tab) => {
   555       assert.pass("PageMod is attached when document is complete");
   556       if (++count === 3)
   557         tab.close(done);
   558     },
   559     onLoading: () => assert.fail("onLoading should not be called with 'end'."),
   560     onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
   561   });
   563   tabs.open(url);
   564 }
   566 // test timing for all 3 contentScriptWhen options (start, ready, end)
   567 // for PageMods created right as the tab is created (in tab.onOpen)
   568 exports.testContentScriptWhenOnTabOpen = function(assert, done) {
   569   const url = "data:text/html;charset=utf-8,testContentScriptWhenOnTabOpen";
   571   tabs.open({
   572     url: url,
   573     onOpen: function(tab) {
   574       let count = 0;
   576       handleReadyState(url, 'start', {
   577         onLoading: () => {
   578           assert.pass("PageMod is attached while document is loading");
   579           if (++count === 3)
   580             tab.close(done);
   581         },
   582         onInteractive: () => assert.fail("onInteractive should not be called with 'start'."),
   583         onComplete: () => assert.fail("onComplete should not be called with 'start'."),
   584       });
   586       handleReadyState(url, 'ready', {
   587         onInteractive: () => {
   588           assert.pass("PageMod is attached while document is interactive");
   589           if (++count === 3)
   590             tab.close(done);
   591         },
   592         onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
   593         onComplete: () => assert.fail("onComplete should not be called with 'ready'."),
   594       });
   596       handleReadyState(url, 'end', {
   597         onComplete: () => {
   598           assert.pass("PageMod is attached when document is complete");
   599           if (++count === 3)
   600             tab.close(done);
   601         },
   602         onLoading: () => assert.fail("onLoading should not be called with 'end'."),
   603         onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
   604       });
   606     }
   607   });
   608 }
   610 // test timing for all 3 contentScriptWhen options (start, ready, end)
   611 // for PageMods created while the tab is interactive (in tab.onReady)
   612 exports.testContentScriptWhenOnTabReady = function(assert, done) {
   613   const url = "data:text/html;charset=utf-8,testContentScriptWhenOnTabReady";
   615   tabs.open({
   616     url: url,
   617     onReady: function(tab) {
   618       let count = 0;
   620       handleReadyState(url, 'start', {
   621         onInteractive: () => {
   622           assert.pass("PageMod is attached while document is interactive");
   623           if (++count === 3)
   624             tab.close(done);
   625         },
   626         onLoading: () => assert.fail("onLoading should not be called with 'start'."),
   627         onComplete: () => assert.fail("onComplete should not be called with 'start'."),
   628       });
   630       handleReadyState(url, 'ready', {
   631         onInteractive: () => {
   632           assert.pass("PageMod is attached while document is interactive");
   633           if (++count === 3)
   634             tab.close(done);
   635         },
   636         onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
   637         onComplete: () => assert.fail("onComplete should not be called with 'ready'."),
   638       });
   640       handleReadyState(url, 'end', {
   641         onComplete: () => {
   642           assert.pass("PageMod is attached when document is complete");
   643           if (++count === 3)
   644             tab.close(done);
   645         },
   646         onLoading: () => assert.fail("onLoading should not be called with 'end'."),
   647         onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
   648       });
   650     }
   651   });
   652 }
   654 // test timing for all 3 contentScriptWhen options (start, ready, end)
   655 // for PageMods created after a tab has completed loading (in tab.onLoad)
   656 exports.testContentScriptWhenOnTabLoad = function(assert, done) {
   657   const url = "data:text/html;charset=utf-8,testContentScriptWhenOnTabLoad";
   659   tabs.open({
   660     url: url,
   661     onLoad: function(tab) {
   662       let count = 0;
   664       handleReadyState(url, 'start', {
   665         onComplete: () => {
   666           assert.pass("PageMod is attached when document is complete");
   667           if (++count === 3)
   668             tab.close(done);
   669         },
   670         onLoading: () => assert.fail("onLoading should not be called with 'start'."),
   671         onInteractive: () => assert.fail("onInteractive should not be called with 'start'."),
   672       });
   674       handleReadyState(url, 'ready', {
   675         onComplete: () => {
   676           assert.pass("PageMod is attached when document is complete");
   677           if (++count === 3)
   678             tab.close(done);
   679         },
   680         onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
   681         onInteractive: () => assert.fail("onInteractive should not be called with 'ready'."),
   682       });
   684       handleReadyState(url, 'end', {
   685         onComplete: () => {
   686           assert.pass("PageMod is attached when document is complete");
   687           if (++count === 3)
   688             tab.close(done);
   689         },
   690         onLoading: () => assert.fail("onLoading should not be called with 'end'."),
   691         onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
   692       });
   694     }
   695   });
   696 }
   698 exports.testTabWorkerOnMessage = function(assert, done) {
   699   let { browserWindows } = require("sdk/windows");
   700   let tabs = require("sdk/tabs");
   701   let { PageMod } = require("sdk/page-mod");
   703   let url1 = "data:text/html;charset=utf-8,<title>tab1</title><h1>worker1.tab</h1>";
   704   let url2 = "data:text/html;charset=utf-8,<title>tab2</title><h1>worker2.tab</h1>";
   705   let worker1 = null;
   707   let mod = PageMod({
   708     include: "data:text/html*",
   709     contentScriptWhen: "ready",
   710     contentScript: "self.postMessage('#1');",
   711     onAttach: function onAttach(worker) {
   712       worker.on("message", function onMessage() {
   713         this.tab.attach({
   714           contentScriptWhen: "ready",
   715           contentScript: "self.postMessage({ url: window.location.href, title: document.title });",
   716           onMessage: function onMessage(data) {
   717             assert.equal(this.tab.url, data.url, "location is correct");
   718             assert.equal(this.tab.title, data.title, "title is correct");
   719             if (this.tab.url === url1) {
   720               worker1 = this;
   721               tabs.open({ url: url2, inBackground: true });
   722             }
   723             else if (this.tab.url === url2) {
   724               mod.destroy();
   725               worker1.tab.close(function() {
   726                 worker1.destroy();
   727                 worker.tab.close(function() {
   728                   worker.destroy();
   729                   done();
   730                 });
   731               });
   732             }
   733           }
   734         });
   735       });
   736     }
   737   });
   739   tabs.open(url1);
   740 };
   742 exports.testAutomaticDestroy = function(assert, done) {
   743   let loader = Loader(module);
   745   let pageMod = loader.require("sdk/page-mod").PageMod({
   746     include: "about:*",
   747     contentScriptWhen: "start",
   748     onAttach: function(w) {
   749       assert.fail("Page-mod should have been detroyed during module unload");
   750     }
   751   });
   753   // Unload the page-mod module so that our page mod is destroyed
   754   loader.unload();
   756   // Then create a second tab to ensure that it is correctly destroyed
   757   let tabs = require("sdk/tabs");
   758   tabs.open({
   759     url: "about:",
   760     onReady: function onReady(tab) {
   761       assert.pass("check automatic destroy");
   762       tab.close(done);
   763     }
   764   });
   765 };
   767 exports.testAttachToTabsOnly = function(assert, done) {
   768   let { PageMod } = require('sdk/page-mod');
   769   let openedTab = null; // Tab opened in openTabWithIframe()
   770   let workerCount = 0;
   772   let mod = PageMod({
   773     include: 'data:text/html*',
   774     contentScriptWhen: 'start',
   775     contentScript: '',
   776     onAttach: function onAttach(worker) {
   777       if (worker.tab === openedTab) {
   778         if (++workerCount == 3) {
   779           assert.pass('Succesfully applied to tab documents and its iframe');
   780           worker.destroy();
   781           mod.destroy();
   782           openedTab.close(done);
   783         }
   784       }
   785       else {
   786         assert.fail('page-mod attached to a non-tab document');
   787       }
   788     }
   789   });
   791   function openHiddenFrame() {
   792     assert.pass('Open iframe in hidden window');
   793     let hiddenFrames = require('sdk/frame/hidden-frame');
   794     let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
   795       onReady: function () {
   796         let element = this.element;
   797         element.addEventListener('DOMContentLoaded', function onload() {
   798           element.removeEventListener('DOMContentLoaded', onload, false);
   799           hiddenFrames.remove(hiddenFrame);
   801           if (!xulApp.is("Fennec")) {
   802             openToplevelWindow();
   803           }
   804           else {
   805             openBrowserIframe();
   806           }
   807         }, false);
   808         element.setAttribute('src', 'data:text/html;charset=utf-8,foo');
   809       }
   810     }));
   811   }
   813   function openToplevelWindow() {
   814     assert.pass('Open toplevel window');
   815     let win = open('data:text/html;charset=utf-8,bar');
   816     win.addEventListener('DOMContentLoaded', function onload() {
   817       win.removeEventListener('DOMContentLoaded', onload, false);
   818       win.close();
   819       openBrowserIframe();
   820     }, false);
   821   }
   823   function openBrowserIframe() {
   824     assert.pass('Open iframe in browser window');
   825     let window = require('sdk/deprecated/window-utils').activeBrowserWindow;
   826     let document = window.document;
   827     let iframe = document.createElement('iframe');
   828     iframe.setAttribute('type', 'content');
   829     iframe.setAttribute('src', 'data:text/html;charset=utf-8,foobar');
   830     iframe.addEventListener('DOMContentLoaded', function onload() {
   831       iframe.removeEventListener('DOMContentLoaded', onload, false);
   832       iframe.parentNode.removeChild(iframe);
   833       openTabWithIframes();
   834     }, false);
   835     document.documentElement.appendChild(iframe);
   836   }
   838   // Only these three documents will be accepted by the page-mod
   839   function openTabWithIframes() {
   840     assert.pass('Open iframes in a tab');
   841     let subContent = '<iframe src="data:text/html;charset=utf-8,sub frame" />'
   842     let content = '<iframe src="data:text/html;charset=utf-8,' +
   843                   encodeURIComponent(subContent) + '" />';
   844     require('sdk/tabs').open({
   845       url: 'data:text/html;charset=utf-8,' + encodeURIComponent(content),
   846       onOpen: function onOpen(tab) {
   847         openedTab = tab;
   848       }
   849     });
   850   }
   852   openHiddenFrame();
   853 };
   855 exports['test111 attachTo [top]'] = function(assert, done) {
   856   let { PageMod } = require('sdk/page-mod');
   858   let subContent = '<iframe src="data:text/html;charset=utf-8,sub frame" />'
   859   let content = '<iframe src="data:text/html;charset=utf-8,' +
   860                 encodeURIComponent(subContent) + '" />';
   861   let topDocumentURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(content)
   863   let workerCount = 0;
   865   let mod = PageMod({
   866     include: 'data:text/html*',
   867     contentScriptWhen: 'start',
   868     contentScript: 'self.postMessage(document.location.href);',
   869     attachTo: ['top'],
   870     onAttach: function onAttach(worker) {
   871       if (++workerCount == 1) {
   872         worker.on('message', function (href) {
   873           assert.equal(href, topDocumentURL,
   874                            "worker on top level document only");
   875           let tab = worker.tab;
   876           worker.destroy();
   877           mod.destroy();
   878           tab.close(done);
   879         });
   880       }
   881       else {
   882         assert.fail('page-mod attached to a non-top document');
   883       }
   884     }
   885   });
   887   require('sdk/tabs').open(topDocumentURL);
   888 };
   890 exports['test111 attachTo [frame]'] = function(assert, done) {
   891   let { PageMod } = require('sdk/page-mod');
   893   let subFrameURL = 'data:text/html;charset=utf-8,subframe';
   894   let subContent = '<iframe src="' + subFrameURL + '" />';
   895   let frameURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(subContent);
   896   let content = '<iframe src="' + frameURL + '" />';
   897   let topDocumentURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(content)
   899   let workerCount = 0, messageCount = 0;
   901   function onMessage(href) {
   902     if (href == frameURL)
   903       assert.pass("worker on first frame");
   904     else if (href == subFrameURL)
   905       assert.pass("worker on second frame");
   906     else
   907       assert.fail("worker on unexpected document: " + href);
   908     this.destroy();
   909     if (++messageCount == 2) {
   910       mod.destroy();
   911       require('sdk/tabs').activeTab.close(done);
   912     }
   913   }
   914   let mod = PageMod({
   915     include: 'data:text/html*',
   916     contentScriptWhen: 'start',
   917     contentScript: 'self.postMessage(document.location.href);',
   918     attachTo: ['frame'],
   919     onAttach: function onAttach(worker) {
   920       if (++workerCount <= 2) {
   921         worker.on('message', onMessage);
   922       }
   923       else {
   924         assert.fail('page-mod attached to a non-frame document');
   925       }
   926     }
   927   });
   929   require('sdk/tabs').open(topDocumentURL);
   930 };
   932 exports.testContentScriptOptionsOption = function(assert, done) {
   933   let callbackDone = null;
   934   testPageMod(assert, done, "about:", [{
   935       include: "about:*",
   936       contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
   937       contentScriptWhen: "end",
   938       contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
   939       onAttach: function(worker) {
   940         worker.on('message', function(msg) {
   941           assert.equal( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
   942           assert.equal( typeof msg[1], 'object', 'object as contentScriptOptions' );
   943           assert.equal( msg[1].a, true, 'boolean in contentScriptOptions' );
   944           assert.equal( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
   945           assert.equal( msg[1].c, 'string', 'string in contentScriptOptions' );
   946           callbackDone();
   947         });
   948       }
   949     }],
   950     function(win, done) {
   951       callbackDone = done;
   952     }
   953   );
   954 };
   956 exports.testPageModCss = function(assert, done) {
   957   let [pageMod] = testPageMod(assert, done,
   958     'data:text/html;charset=utf-8,<div style="background: silver">css test</div>', [{
   959       include: ["*", "data:*"],
   960       contentStyle: "div { height: 100px; }",
   961       contentStyleFile: data.url("css-include-file.css")
   962     }],
   963     function(win, done) {
   964       let div = win.document.querySelector("div");
   965       assert.equal(
   966         div.clientHeight,
   967         100,
   968         "PageMod contentStyle worked"
   969       );
   970       assert.equal(
   971        div.offsetHeight,
   972         120,
   973         "PageMod contentStyleFile worked"
   974       );
   975       done();
   976     }
   977   );
   978 };
   980 exports.testPageModCssList = function(assert, done) {
   981   let [pageMod] = testPageMod(assert, done,
   982     'data:text/html;charset=utf-8,<div style="width:320px; max-width: 480px!important">css test</div>', [{
   983       include: "data:*",
   984       contentStyleFile: [
   985         // Highlight evaluation order in this list
   986         "data:text/css;charset=utf-8,div { border: 1px solid black; }",
   987         "data:text/css;charset=utf-8,div { border: 10px solid black; }",
   988         // Highlight evaluation order between contentStylesheet & contentStylesheetFile
   989         "data:text/css;charset=utf-8s,div { height: 1000px; }",
   990         // Highlight precedence between the author and user style sheet
   991         "data:text/css;charset=utf-8,div { width: 200px; max-width: 640px!important}",
   992       ],
   993       contentStyle: [
   994         "div { height: 10px; }",
   995         "div { height: 100px; }"
   996       ]
   997     }],
   998     function(win, done) {
   999       let div = win.document.querySelector("div"),
  1000           style = win.getComputedStyle(div);
  1002       assert.equal(
  1003        div.clientHeight,
  1004         100,
  1005         "PageMod contentStyle list works and is evaluated after contentStyleFile"
  1006       );
  1008       assert.equal(
  1009         div.offsetHeight,
  1010         120,
  1011         "PageMod contentStyleFile list works"
  1012       );
  1014       assert.equal(
  1015         style.width,
  1016         "320px",
  1017         "PageMod add-on author/page author style sheet precedence works"
  1018       );
  1020       assert.equal(
  1021         style.maxWidth,
  1022         "480px",
  1023         "PageMod add-on author/page author style sheet precedence with !important works"
  1024       );
  1026       done();
  1028   );
  1029 };
  1031 exports.testPageModCssDestroy = function(assert, done) {
  1032   let [pageMod] = testPageMod(assert, done,
  1033     'data:text/html;charset=utf-8,<div style="width:200px">css test</div>', [{
  1034       include: "data:*",
  1035       contentStyle: "div { width: 100px!important; }"
  1036     }],
  1038     function(win, done) {
  1039       let div = win.document.querySelector("div"),
  1040           style = win.getComputedStyle(div);
  1042       assert.equal(
  1043         style.width,
  1044         "100px",
  1045         "PageMod contentStyle worked"
  1046       );
  1048       pageMod.destroy();
  1049       assert.equal(
  1050         style.width,
  1051         "200px",
  1052         "PageMod contentStyle is removed after destroy"
  1053       );
  1055       done();
  1058   );
  1059 };
  1061 exports.testPageModCssAutomaticDestroy = function(assert, done) {
  1062   let loader = Loader(module);
  1064   let pageMod = loader.require("sdk/page-mod").PageMod({
  1065     include: "data:*",
  1066     contentStyle: "div { width: 100px!important; }"
  1067   });
  1069   tabs.open({
  1070     url: "data:text/html;charset=utf-8,<div style='width:200px'>css test</div>",
  1072     onReady: function onReady(tab) {
  1073       let browserWindow = getMostRecentBrowserWindow();
  1074       let win = getTabContentWindow(getActiveTab(browserWindow));
  1076       let div = win.document.querySelector("div");
  1077       let style = win.getComputedStyle(div);
  1079       assert.equal(
  1080         style.width,
  1081         "100px",
  1082         "PageMod contentStyle worked"
  1083       );
  1085       loader.unload();
  1087       assert.equal(
  1088         style.width,
  1089         "200px",
  1090         "PageMod contentStyle is removed after loader's unload"
  1091       );
  1093       tab.close(done);
  1095   });
  1096 };
  1099 exports.testPageModTimeout = function(assert, done) {
  1100   let tab = null
  1101   let loader = Loader(module);
  1102   let { PageMod } = loader.require("sdk/page-mod");
  1104   let mod = PageMod({
  1105     include: "data:*",
  1106     contentScript: Isolate(function() {
  1107       var id = setTimeout(function() {
  1108         self.port.emit("fired", id)
  1109       }, 10)
  1110       self.port.emit("scheduled", id);
  1111     }),
  1112     onAttach: function(worker) {
  1113       worker.port.on("scheduled", function(id) {
  1114         assert.pass("timer was scheduled")
  1115         worker.port.on("fired", function(data) {
  1116           assert.equal(id, data, "timer was fired")
  1117           tab.close(function() {
  1118             worker.destroy()
  1119             loader.unload()
  1120             done()
  1121           });
  1122         })
  1123       })
  1125   });
  1127   tabs.open({
  1128     url: "data:text/html;charset=utf-8,timeout",
  1129     onReady: function($) { tab = $ }
  1130   })
  1134 exports.testPageModcancelTimeout = function(assert, done) {
  1135   let tab = null
  1136   let loader = Loader(module);
  1137   let { PageMod } = loader.require("sdk/page-mod");
  1139   let mod = PageMod({
  1140     include: "data:*",
  1141     contentScript: Isolate(function() {
  1142       var id1 = setTimeout(function() {
  1143         self.port.emit("failed")
  1144       }, 10)
  1145       var id2 = setTimeout(function() {
  1146         self.port.emit("timeout")
  1147       }, 100)
  1148       clearTimeout(id1)
  1149     }),
  1150     onAttach: function(worker) {
  1151       worker.port.on("failed", function() {
  1152         assert.fail("cancelled timeout fired")
  1153       })
  1154       worker.port.on("timeout", function(id) {
  1155         assert.pass("timer was scheduled")
  1156         tab.close(function() {
  1157           worker.destroy();
  1158           mod.destroy();
  1159           loader.unload();
  1160           done();
  1161         });
  1162       })
  1164   });
  1166   tabs.open({
  1167     url: "data:text/html;charset=utf-8,cancell timeout",
  1168     onReady: function($) { tab = $ }
  1169   })
  1172 exports.testExistingOnFrames = function(assert, done) {
  1173   let subFrameURL = 'data:text/html;charset=utf-8,testExistingOnFrames-sub-frame';
  1174   let subIFrame = '<iframe src="' + subFrameURL + '" />'
  1175   let iFrameURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(subIFrame)
  1176   let iFrame = '<iframe src="' + iFrameURL + '" />';
  1177   let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(iFrame);
  1179   // we want all urls related to the test here, and not just the iframe urls
  1180   // because we need to fail if the test is applied to the top window url.
  1181   let urls = [url, iFrameURL, subFrameURL];
  1183   let counter = 0;
  1184   let tab = openTab(getMostRecentBrowserWindow(), url);
  1185   let window = getTabContentWindow(tab);
  1187   function wait4Iframes() {
  1188     if (window.document.readyState != "complete" ||
  1189         getFrames(window).length != 2) {
  1190       return;
  1193     let pagemodOnExisting = PageMod({
  1194       include: ["*", "data:*"],
  1195       attachTo: ["existing", "frame"],
  1196       contentScriptWhen: 'ready',
  1197       onAttach: function(worker) {
  1198         // need to ignore urls that are not part of the test, because other
  1199         // tests are not closing their tabs when they complete..
  1200         if (urls.indexOf(worker.url) == -1)
  1201           return;
  1203         assert.notEqual(url,
  1204                             worker.url,
  1205                             'worker should not be attached to the top window');
  1207         if (++counter < 2) {
  1208           // we can rely on this order in this case because we are sure that
  1209           // the frames being tested have completely loaded
  1210           assert.equal(iFrameURL, worker.url, '1st attach is for top frame');
  1212         else if (counter > 2) {
  1213           assert.fail('applied page mod too many times');
  1215         else {
  1216           assert.equal(subFrameURL, worker.url, '2nd attach is for sub frame');
  1217           // need timeout because onAttach is called before the constructor returns
  1218           setTimeout(function() {
  1219             pagemodOnExisting.destroy();
  1220             pagemodOffExisting.destroy();
  1221             closeTab(tab);
  1222             done();
  1223           }, 0);
  1226     });
  1228     let pagemodOffExisting = PageMod({
  1229       include: ["*", "data:*"],
  1230       attachTo: ["frame"],
  1231       contentScriptWhen: 'ready',
  1232       onAttach: function(mod) {
  1233         assert.fail('pagemodOffExisting page-mod should not have been attached');
  1235     });
  1238   window.addEventListener("load", wait4Iframes, false);
  1239 };
  1241 exports.testIFramePostMessage = function(assert, done) {
  1242   let count = 0;
  1244   tabs.open({
  1245     url: data.url("test-iframe.html"),
  1246     onReady: function(tab) {
  1247       var worker = tab.attach({
  1248         contentScriptFile: data.url('test-iframe.js'),
  1249         contentScript: 'var iframePath = \'' + data.url('test-iframe-postmessage.html') + '\'',
  1250         onMessage: function(msg) {
  1251           assert.equal(++count, 1);
  1252           assert.equal(msg.first, 'a string');
  1253           assert.ok(msg.second[1], "array");
  1254           assert.equal(typeof msg.third, 'object');
  1256           worker.destroy();
  1257           tab.close(done);
  1259       });
  1261   });
  1262 };
  1264 exports.testEvents = function(assert, done) {
  1265   let content = "<script>\n new " + function DocumentScope() {
  1266     window.addEventListener("ContentScriptEvent", function () {
  1267       window.receivedEvent = true;
  1268     }, false);
  1269   } + "\n</script>";
  1270   let url = "data:text/html;charset=utf-8," + encodeURIComponent(content);
  1271   testPageMod(assert, done, url, [{
  1272       include: "data:*",
  1273       contentScript: 'new ' + function WorkerScope() {
  1274         let evt = document.createEvent("Event");
  1275         evt.initEvent("ContentScriptEvent", true, true);
  1276         document.body.dispatchEvent(evt);
  1278     }],
  1279     function(win, done) {
  1280       assert.ok(
  1281         win.receivedEvent,
  1282         "Content script sent an event and document received it"
  1283       );
  1284       done();
  1286   );
  1287 };
  1289 exports["test page-mod on private tab"] = function (assert, done) {
  1290   let fail = assert.fail.bind(assert);
  1292   let privateUri = "data:text/html;charset=utf-8," +
  1293                    "<iframe src=\"data:text/html;charset=utf-8,frame\" />";
  1294   let nonPrivateUri = "data:text/html;charset=utf-8,non-private";
  1296   let pageMod = new PageMod({
  1297     include: "data:*",
  1298     onAttach: function(worker) {
  1299       if (isTabPBSupported || isWindowPBSupported) {
  1300         // When PB isn't supported, the page-mod will apply to all document
  1301         // as all of them will be non-private
  1302         assert.equal(worker.tab.url,
  1303                          nonPrivateUri,
  1304                          "page-mod should only attach to the non-private tab");
  1307       assert.ok(!isPrivate(worker),
  1308                   "The worker is really non-private");
  1309       assert.ok(!isPrivate(worker.tab),
  1310                   "The document is really non-private");
  1311       pageMod.destroy();
  1313       page1.close().
  1314         then(page2.close).
  1315         then(done, fail);
  1317   });
  1319   let page1, page2;
  1320   page1 = openWebpage(privateUri, true);
  1321   page1.ready.then(function() {
  1322     page2 = openWebpage(nonPrivateUri, false);
  1323   }, fail);
  1326 exports["test page-mod on private tab in global pb"] = function (assert, done) {
  1327   if (!isGlobalPBSupported) {
  1328     assert.pass();
  1329     return done();
  1332   let privateUri = "data:text/html;charset=utf-8," +
  1333                    "<iframe%20src=\"data:text/html;charset=utf-8,frame\"/>";
  1335   let pageMod = new PageMod({
  1336     include: privateUri,
  1337     onAttach: function(worker) {
  1338       assert.equal(worker.tab.url,
  1339                        privateUri,
  1340                        "page-mod should attach");
  1341       assert.equal(isPrivateBrowsingSupported,
  1342                        false,
  1343                        "private browsing is not supported");
  1344       assert.ok(isPrivate(worker),
  1345                   "The worker is really non-private");
  1346       assert.ok(isPrivate(worker.tab),
  1347                   "The document is really non-private");
  1348       pageMod.destroy();
  1350       worker.tab.close(function() {
  1351         pb.once('stop', function() {
  1352           assert.pass('global pb stop');
  1353           done();
  1354         });
  1355         pb.deactivate();
  1356       });
  1358   });
  1360   let page1;
  1361   pb.once('start', function() {
  1362     assert.pass('global pb start');
  1363     tabs.open({ url: privateUri });
  1364   });
  1365   pb.activate();
  1368 // Bug 699450: Calling worker.tab.close() should not lead to exception
  1369 exports.testWorkerTabClose = function(assert, done) {
  1370   let callbackDone;
  1371   testPageMod(assert, done, "about:", [{
  1372       include: "about:",
  1373       contentScript: '',
  1374       onAttach: function(worker) {
  1375         assert.pass("The page-mod was attached");
  1377         worker.tab.close(function () {
  1378           // On Fennec, tab is completely destroyed right after close event is
  1379           // dispatch, so we need to wait for the next event loop cycle to
  1380           // check for tab nulliness.
  1381           setTimeout(function () {
  1382             assert.ok(!worker.tab,
  1383                         "worker.tab should be null right after tab.close()");
  1384             callbackDone();
  1385           }, 0);
  1386         });
  1388     }],
  1389     function(win, done) {
  1390       callbackDone = done;
  1392   );
  1393 };
  1395 exports.testDebugMetadata = function(assert, done) {
  1396   let dbg = new Debugger;
  1397   let globalDebuggees = [];
  1398   dbg.onNewGlobalObject = function(global) {
  1399     globalDebuggees.push(global);
  1402   let mods = testPageMod(assert, done, "about:", [{
  1403       include: "about:",
  1404       contentScriptWhen: "start",
  1405       contentScript: "null;",
  1406     }], function(win, done) {
  1407       assert.ok(globalDebuggees.some(function(global) {
  1408         try {
  1409           let metadata = Cu.getSandboxMetadata(global.unsafeDereference());
  1410           return metadata && metadata.addonID && metadata.SDKContentScript &&
  1411                  metadata['inner-window-id'] == getInnerId(win);
  1412         } catch(e) {
  1413           // Some of the globals might not be Sandbox instances and thus
  1414           // will cause getSandboxMetadata to fail.
  1415           return false;
  1417       }), "one of the globals is a content script");
  1418       done();
  1420   );
  1421 };
  1423 exports.testDevToolsExtensionsGetContentGlobals = function(assert, done) {
  1424   let mods = testPageMod(assert, done, "about:", [{
  1425       include: "about:",
  1426       contentScriptWhen: "start",
  1427       contentScript: "null;",
  1428     }], function(win, done) {
  1429       assert.equal(gDevToolsExtensions.getContentGlobals({ 'inner-window-id': getInnerId(win) }).length, 1);
  1430       done();
  1432   );
  1433 };
  1435 exports.testDetachOnDestroy = function(assert, done) {
  1436   let tab;
  1437   const TEST_URL = 'data:text/html;charset=utf-8,detach';
  1438   const loader = Loader(module);
  1439   const { PageMod } = loader.require('sdk/page-mod');
  1441   let mod1 = PageMod({
  1442     include: TEST_URL,
  1443     contentScript: Isolate(function() {
  1444       self.port.on('detach', function(reason) {
  1445         window.document.body.innerHTML += '!' + reason;
  1446       });
  1447     }),
  1448     onAttach: worker => {
  1449       assert.pass('attach[1] happened');
  1451       worker.on('detach', _ => setTimeout(_ => {
  1452         assert.pass('detach happened');
  1454         let mod2 = PageMod({
  1455           attachTo: [ 'existing', 'top' ],
  1456           include: TEST_URL,
  1457           contentScript: Isolate(function() {
  1458             self.port.on('test', _ => {
  1459               self.port.emit('result', { result: window.document.body.innerHTML});
  1460             });
  1461           }),
  1462           onAttach: worker => {
  1463             assert.pass('attach[2] happened');
  1464             worker.port.once('result', ({ result }) => {
  1465               assert.equal(result, 'detach!', 'the body.innerHTML is as expected');
  1466               mod1.destroy();
  1467               mod2.destroy();
  1468               loader.unload();
  1469               tab.close(done);
  1470             });
  1471             worker.port.emit('test');
  1473         });
  1474       }));
  1476       worker.destroy();
  1478   });
  1480   tabs.open({
  1481     url: TEST_URL,
  1482     onOpen: t => tab = t
  1483   })
  1486 exports.testDetachOnUnload = function(assert, done) {
  1487   let tab;
  1488   const TEST_URL = 'data:text/html;charset=utf-8,detach';
  1489   const loader = Loader(module);
  1490   const { PageMod } = loader.require('sdk/page-mod');
  1492   let mod1 = PageMod({
  1493     include: TEST_URL,
  1494     contentScript: Isolate(function() {
  1495       self.port.on('detach', function(reason) {
  1496         window.document.body.innerHTML += '!' + reason;
  1497       });
  1498     }),
  1499     onAttach: worker => {
  1500       assert.pass('attach[1] happened');
  1502       worker.on('detach', _ => setTimeout(_ => {
  1503         assert.pass('detach happened');
  1505         let mod2 = require('sdk/page-mod').PageMod({
  1506           attachTo: [ 'existing', 'top' ],
  1507           include: TEST_URL,
  1508           contentScript: Isolate(function() {
  1509             self.port.on('test', _ => {
  1510               self.port.emit('result', { result: window.document.body.innerHTML});
  1511             });
  1512           }),
  1513           onAttach: worker => {
  1514             assert.pass('attach[2] happened');
  1515             worker.port.once('result', ({ result }) => {
  1516               assert.equal(result, 'detach!shutdown', 'the body.innerHTML is as expected');
  1517               mod2.destroy();
  1518               tab.close(done);
  1519             });
  1520             worker.port.emit('test');
  1522         });
  1523       }));
  1525       loader.unload('shutdown');
  1527   });
  1529   tabs.open({
  1530     url: TEST_URL,
  1531     onOpen: t => tab = t
  1532   })
  1535 exports.testConsole = function(assert, done) {
  1536   let innerID;
  1537   const TEST_URL = 'data:text/html;charset=utf-8,console';
  1538   const { loader } = LoaderWithHookedConsole(module, onMessage);
  1539   const { PageMod } = loader.require('sdk/page-mod');
  1540   const system = require("sdk/system/events");
  1542   let seenMessage = false;
  1543   function onMessage(type, msg, msgID) {
  1544     seenMessage = true;
  1545     innerID = msgID;
  1548   let mod = PageMod({
  1549     include: TEST_URL,
  1550     contentScriptWhen: "ready",
  1551     contentScript: Isolate(function() {
  1552       console.log("Hello from the page mod");
  1553       self.port.emit("done");
  1554     }),
  1555     onAttach: function(worker) {
  1556       worker.port.on("done", function() {
  1557         let window = getTabContentWindow(tab);
  1558         let id = getInnerId(window);
  1559         assert.ok(seenMessage, "Should have seen the console message");
  1560         assert.equal(innerID, id, "Should have seen the right inner ID");
  1561         closeTab(tab);
  1562         done();
  1563       });
  1564     },
  1565   });
  1567   let tab = openTab(getMostRecentBrowserWindow(), TEST_URL);
  1570 exports.testSyntaxErrorInContentScript = function(assert, done) {
  1571   const url = "data:text/html;charset=utf-8,testSyntaxErrorInContentScript";
  1572   let hitError = null;
  1573   let attached = false;
  1575   testPageMod(assert, done, url, [{
  1576       include: url,
  1577       contentScript: 'console.log(23',
  1579       onAttach: function() {
  1580         attached = true;
  1581       },
  1583       onError: function(e) {
  1584         hitError = e;
  1586     }],
  1588     function(win, done) {
  1589       assert.ok(attached, "The worker was attached.");
  1590       assert.notStrictEqual(hitError, null, "The syntax error was reported.");
  1591       if (hitError)
  1592         assert.equal(hitError.name, "SyntaxError", "The error thrown should be a SyntaxError");
  1593       done();
  1595   );
  1596 };
  1598 require('sdk/test').run(exports);

mercurial