Thu, 22 Jan 2015 13:21:57 +0100
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();
1027 }
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();
1057 }
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);
1094 }
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 })
1124 }
1125 });
1127 tabs.open({
1128 url: "data:text/html;charset=utf-8,timeout",
1129 onReady: function($) { tab = $ }
1130 })
1131 }
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 })
1163 }
1164 });
1166 tabs.open({
1167 url: "data:text/html;charset=utf-8,cancell timeout",
1168 onReady: function($) { tab = $ }
1169 })
1170 }
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;
1191 }
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');
1211 }
1212 else if (counter > 2) {
1213 assert.fail('applied page mod too many times');
1214 }
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);
1224 }
1225 }
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');
1234 }
1235 });
1236 }
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);
1258 }
1259 });
1260 }
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);
1277 }
1278 }],
1279 function(win, done) {
1280 assert.ok(
1281 win.receivedEvent,
1282 "Content script sent an event and document received it"
1283 );
1284 done();
1285 }
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");
1305 }
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);
1316 }
1317 });
1319 let page1, page2;
1320 page1 = openWebpage(privateUri, true);
1321 page1.ready.then(function() {
1322 page2 = openWebpage(nonPrivateUri, false);
1323 }, fail);
1324 }
1326 exports["test page-mod on private tab in global pb"] = function (assert, done) {
1327 if (!isGlobalPBSupported) {
1328 assert.pass();
1329 return done();
1330 }
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 });
1357 }
1358 });
1360 let page1;
1361 pb.once('start', function() {
1362 assert.pass('global pb start');
1363 tabs.open({ url: privateUri });
1364 });
1365 pb.activate();
1366 }
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 });
1387 }
1388 }],
1389 function(win, done) {
1390 callbackDone = done;
1391 }
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);
1400 }
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;
1416 }
1417 }), "one of the globals is a content script");
1418 done();
1419 }
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();
1431 }
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');
1472 }
1473 });
1474 }));
1476 worker.destroy();
1477 }
1478 });
1480 tabs.open({
1481 url: TEST_URL,
1482 onOpen: t => tab = t
1483 })
1484 }
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');
1521 }
1522 });
1523 }));
1525 loader.unload('shutdown');
1526 }
1527 });
1529 tabs.open({
1530 url: TEST_URL,
1531 onOpen: t => tab = t
1532 })
1533 }
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;
1546 }
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);
1568 }
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;
1585 }
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();
1594 }
1595 );
1596 };
1598 require('sdk/test').run(exports);