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