mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-b2g-inbound
This commit is contained in:
commit
3366347b08
@ -50,7 +50,8 @@ function PlainTextConsole(print, innerID) {
|
||||
prefix: self.name + ": ",
|
||||
maxLogLevel: logLevel,
|
||||
dump: print,
|
||||
innerID: innerID
|
||||
innerID: innerID,
|
||||
consoleID: "addon/" + self.id
|
||||
};
|
||||
let console = new ConsoleAPI(consoleOptions);
|
||||
|
||||
|
@ -292,7 +292,7 @@ const ContentWorker = Object.freeze({
|
||||
"function, which works the same. Replace calls to `on()` " +
|
||||
"with calls to `self.on()`" +
|
||||
"For more info on `self.on`, see " +
|
||||
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
|
||||
"<https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage>.");
|
||||
return self.on.apply(null, arguments);
|
||||
};
|
||||
|
||||
@ -309,7 +309,7 @@ const ContentWorker = Object.freeze({
|
||||
"definitions with calls to `self.on('message', " +
|
||||
"function (data){})`. " +
|
||||
"For more info on `self.on`, see " +
|
||||
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
|
||||
"<https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage>.");
|
||||
onMessage = v;
|
||||
if (typeof onMessage == "function")
|
||||
self.on("message", onMessage);
|
||||
|
@ -20,6 +20,7 @@ const events = require('../system/events');
|
||||
const { getInnerId } = require("../window/utils");
|
||||
const { WorkerSandbox } = require('./sandbox');
|
||||
const { getTabForWindow } = require('../tabs/helpers');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
|
||||
// A weak map of workers to hold private attributes that
|
||||
// should not be exposed
|
||||
@ -37,7 +38,7 @@ const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
|
||||
/**
|
||||
* Message-passing facility for communication between code running
|
||||
* in the content and add-on process.
|
||||
* @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
|
||||
*/
|
||||
const Worker = Class({
|
||||
implements: [EventTarget],
|
||||
@ -186,6 +187,8 @@ detach.define(Worker, function (worker, reason) {
|
||||
model.inited = false;
|
||||
});
|
||||
|
||||
isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
|
||||
|
||||
/**
|
||||
* Tells content worker to unload itself and
|
||||
* removes all the references from itself.
|
||||
|
@ -11,7 +11,7 @@ const { Trait } = require('../deprecated/traits');
|
||||
const { iteratorSymbol } = require('../util/iteration');
|
||||
|
||||
/**
|
||||
* @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/util_list
|
||||
*/
|
||||
const Iterable = Trait.compose({
|
||||
/**
|
||||
|
@ -393,7 +393,7 @@ const WorkerSandbox = EventEmitter.compose({
|
||||
/**
|
||||
* Message-passing facility for communication between code running
|
||||
* in the content and add-on process.
|
||||
* @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
|
||||
*/
|
||||
const Worker = EventEmitter.compose({
|
||||
on: Trait.required,
|
||||
|
@ -11,6 +11,8 @@ module.metadata = {
|
||||
const memory = require('./memory');
|
||||
var timer = require("../timers");
|
||||
var cfxArgs = require("@test/options");
|
||||
const { getTabs, getURI } = require("../tabs/utils");
|
||||
const { windows, isBrowser } = require("../window/utils");
|
||||
|
||||
exports.findAndRunTests = function findAndRunTests(options) {
|
||||
var TestFinder = require("./unit-test-finder").TestFinder;
|
||||
@ -278,14 +280,39 @@ TestRunner.prototype = {
|
||||
this.failed++;
|
||||
this.test.failed++;
|
||||
}
|
||||
|
||||
|
||||
let wins = windows(null, { includePrivate: true });
|
||||
let tabs = [];
|
||||
for (let win of wins.filter(isBrowser)) {
|
||||
for (let tab of getTabs(win)) {
|
||||
tabs.push(tab);
|
||||
}
|
||||
}
|
||||
|
||||
if (wins.length != 1)
|
||||
this.fail("Should not be any unexpected windows open");
|
||||
if (tabs.length != 1)
|
||||
this.fail("Should not be any unexpected tabs open");
|
||||
if (tabs.length != 1 || wins.length != 1) {
|
||||
console.log("Windows open:");
|
||||
for (let win of wins) {
|
||||
if (isBrowser(win)) {
|
||||
tabs = getTabs(win);
|
||||
console.log(win.location + " - " + tabs.map(getURI).join(", "));
|
||||
}
|
||||
else {
|
||||
console.log(win.location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.testRunSummary.push({
|
||||
name: this.test.name,
|
||||
passed: this.test.passed,
|
||||
failed: this.test.failed,
|
||||
errors: [error for (error in this.test.errors)].join(", ")
|
||||
});
|
||||
|
||||
|
||||
if (this.onDone !== null) {
|
||||
var onDone = this.onDone;
|
||||
var self = this;
|
||||
|
@ -25,7 +25,7 @@ const { windowIterator } = require('./deprecated/window-utils');
|
||||
const { isBrowser, getFrames } = require('./window/utils');
|
||||
const { getTabs, getTabContentWindow, getTabForContentWindow,
|
||||
getURI: getTabURI } = require('./tabs/utils');
|
||||
const { ignoreWindow } = require('sdk/private-browsing/utils');
|
||||
const { ignoreWindow } = require('./private-browsing/utils');
|
||||
const { Style } = require("./stylesheet/style");
|
||||
const { attach, detach } = require("./content/mod");
|
||||
const { has, hasAny } = require("./util/array");
|
||||
@ -167,22 +167,8 @@ function onContentWindow({ subject: document }) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns all tabs on all currently opened windows
|
||||
function getAllTabs() {
|
||||
let tabs = [];
|
||||
// Iterate over all chrome windows
|
||||
for (let window in windowIterator()) {
|
||||
if (!isBrowser(window))
|
||||
continue;
|
||||
tabs = tabs.concat(getTabs(window));
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
|
||||
function applyOnExistingDocuments (mod) {
|
||||
let tabs = getAllTabs();
|
||||
|
||||
tabs.forEach(function (tab) {
|
||||
getTabs().forEach(tab => {
|
||||
// Fake a newly created document
|
||||
let window = getTabContentWindow(tab);
|
||||
if (has(mod.attachTo, "top") && mod.include.matchesAny(getTabURI(tab)))
|
||||
|
@ -165,7 +165,7 @@ function register(factory) {
|
||||
throw new Error("xpcom.register() expect a Factory instance.\n" +
|
||||
"Please refactor your code to new xpcom module if you" +
|
||||
" are repacking an addon from SDK <= 1.5:\n" +
|
||||
"https://addons.mozilla.org/en-US/developers/docs/sdk/latest/packages/api-utils/xpcom.html");
|
||||
"https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/platform_xpcom");
|
||||
}
|
||||
|
||||
registerFactory(factory.id, factory.description, factory.contract, factory);
|
||||
|
@ -7,13 +7,10 @@ module.metadata = {
|
||||
"stability": "stable"
|
||||
};
|
||||
|
||||
const { Ci } = require('chrome');
|
||||
const { setMode, getMode, on: onStateChange, isPermanentPrivateBrowsing } = require('./private-browsing/utils');
|
||||
const { isWindowPrivate } = require('./window/utils');
|
||||
const { setMode, getMode, on: onStateChange, isPrivate } = require('./private-browsing/utils');
|
||||
const { emit, on, once, off } = require('./event/core');
|
||||
const { when: unload } = require('./system/unload');
|
||||
const { deprecateUsage, deprecateFunction, deprecateEvent } = require('./util/deprecate');
|
||||
const { getOwnerWindow } = require('./private-browsing/window/utils');
|
||||
const { deprecateFunction, deprecateEvent } = require('./util/deprecate');
|
||||
|
||||
onStateChange('start', function onStart() {
|
||||
emit(exports, 'start');
|
||||
@ -39,48 +36,7 @@ exports.removeListener = deprecateEvents(function removeListener(type, listener)
|
||||
off(exports, type, listener);
|
||||
});
|
||||
|
||||
exports.isPrivate = function(thing) {
|
||||
// if thing is defined, and we can find a window for it
|
||||
// then check if the window is private
|
||||
if (!!thing) {
|
||||
// if the thing is a window, and the window is private
|
||||
// then return true
|
||||
if (isWindowPrivate(thing)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// does the thing have an associated tab?
|
||||
// page-mod instances do..
|
||||
if (thing.tab) {
|
||||
let tabWindow = getOwnerWindow(thing.tab);
|
||||
if (tabWindow) {
|
||||
let isThingPrivate = isWindowPrivate(tabWindow);
|
||||
if (isThingPrivate)
|
||||
return isThingPrivate;
|
||||
}
|
||||
}
|
||||
|
||||
// can we find an associated window?
|
||||
let window = getOwnerWindow(thing);
|
||||
if (window)
|
||||
return isWindowPrivate(window);
|
||||
|
||||
try {
|
||||
let { isChannelPrivate } = thing.QueryInterface(Ci.nsIPrivateBrowsingChannel);
|
||||
if (isChannelPrivate)
|
||||
return true;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// check if the post pwpb, global pb service is enabled.
|
||||
if (isPermanentPrivateBrowsing())
|
||||
return true;
|
||||
|
||||
// if we get here, and global private browsing
|
||||
// is available, and it is true, then return
|
||||
// true otherwise false is returned here
|
||||
return getMode();
|
||||
};
|
||||
exports.isPrivate = isPrivate;
|
||||
|
||||
function deprecateEvents(func) deprecateEvent(
|
||||
func,
|
||||
|
@ -16,6 +16,7 @@ const { deprecateFunction } = require('../util/deprecate');
|
||||
const { isOneOf, is, satisfiesVersion, version } = require('../system/xul-app');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const { isPrivateBrowsingSupported } = require('../self');
|
||||
const { dispatcher } = require("../util/dispatcher");
|
||||
|
||||
let deferredEmit = defer(emit);
|
||||
let pbService;
|
||||
@ -53,10 +54,11 @@ let isWindowPBSupported = exports.isWindowPBSupported =
|
||||
let isTabPBSupported = exports.isTabPBSupported =
|
||||
!pbService && !!PrivateBrowsingUtils && is('Fennec') && satisfiesVersion(version, '>=20.0*');
|
||||
|
||||
exports.isPermanentPrivateBrowsing = function() {
|
||||
function isPermanentPrivateBrowsing() {
|
||||
return !!(PrivateBrowsingUtils && PrivateBrowsingUtils.permanentPrivateBrowsing);
|
||||
}
|
||||
|
||||
exports.isPermanentPrivateBrowsing = isPermanentPrivateBrowsing;
|
||||
|
||||
function ignoreWindow(window) {
|
||||
return !isPrivateBrowsingSupported && isWindowPrivate(window) && !isGlobalPBSupported;
|
||||
}
|
||||
@ -90,14 +92,25 @@ exports.setMode = deprecateFunction(
|
||||
);
|
||||
|
||||
let getMode = function getMode(chromeWin) {
|
||||
if (isWindowPrivate(chromeWin))
|
||||
if (chromeWin !== undefined && isWindowPrivate(chromeWin))
|
||||
return true;
|
||||
|
||||
// default
|
||||
return pbService ? pbService.privateBrowsingEnabled : false;
|
||||
return isGlobalPrivateBrowsing();
|
||||
};
|
||||
exports.getMode = getMode;
|
||||
|
||||
function isGlobalPrivateBrowsing() {
|
||||
return pbService ? pbService.privateBrowsingEnabled : false;
|
||||
}
|
||||
|
||||
const isPrivate = dispatcher("isPrivate");
|
||||
isPrivate.when(isPermanentPrivateBrowsing, _ => true);
|
||||
isPrivate.when(x => x instanceof Ci.nsIDOMWindow, isWindowPrivate);
|
||||
isPrivate.when(x => Ci.nsIPrivateBrowsingChannel && x instanceof Ci.nsIPrivateBrowsingChannel, x => x.isChannelPrivate);
|
||||
isPrivate.define(isGlobalPrivateBrowsing);
|
||||
exports.isPrivate = isPrivate;
|
||||
|
||||
exports.on = on.bind(null, exports);
|
||||
|
||||
// Make sure listeners are cleaned up.
|
||||
|
@ -1,33 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
module.metadata = {
|
||||
'stability': 'unstable'
|
||||
};
|
||||
|
||||
const privateNS = require('../../core/namespace').ns();
|
||||
|
||||
function getOwnerWindow(thing) {
|
||||
try {
|
||||
// check for and return associated window
|
||||
let fn = (privateNS(thing.prototype) || privateNS(thing) || {}).getOwnerWindow;
|
||||
|
||||
if (fn)
|
||||
return fn.apply(fn, [thing].concat(arguments));
|
||||
}
|
||||
// stuff like numbers and strings throw errors with namespaces
|
||||
catch(e) {}
|
||||
// default
|
||||
return undefined;
|
||||
}
|
||||
getOwnerWindow.define = function(Type, fn) {
|
||||
privateNS(Type.prototype).getOwnerWindow = fn;
|
||||
}
|
||||
|
||||
getOwnerWindow.implement = function(instance, fn) {
|
||||
privateNS(instance).getOwnerWindow = fn;
|
||||
}
|
||||
|
||||
exports.getOwnerWindow = getOwnerWindow;
|
@ -11,7 +11,8 @@ const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabConten
|
||||
getTabForBrowser,
|
||||
setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
|
||||
const { emit } = require('../event/core');
|
||||
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const { when: unload } = require('../system/unload');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { EVENTS } = require('./events');
|
||||
@ -240,6 +241,6 @@ function onTabClose(event) {
|
||||
cleanupTab(this);
|
||||
};
|
||||
|
||||
getPBOwnerWindow.define(Tab, function(tab) {
|
||||
return getTabContentWindow(tabNS(tab).tab);
|
||||
isPrivate.implement(Tab, tab => {
|
||||
return isWindowPrivate(getTabContentWindow(tabNS(tab).tab));
|
||||
});
|
||||
|
@ -12,11 +12,13 @@ const { getThumbnailURIForWindow } = require("../content/thumbnail");
|
||||
const { getFaviconURIForLocation } = require("../io/data");
|
||||
const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
|
||||
getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
|
||||
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const viewNS = require('../core/namespace').ns();
|
||||
const { deprecateUsage } = require('../util/deprecate');
|
||||
const { getURL } = require('../url/utils');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { observer } = require('./observer');
|
||||
|
||||
// Array of the inner instances of all the wrapped tabs.
|
||||
const TABS = [];
|
||||
@ -64,8 +66,8 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
this.pin();
|
||||
|
||||
viewNS(this._public).tab = this._tab;
|
||||
getPBOwnerWindow.implement(this._public, getChromeTab);
|
||||
viewFor.implement(this._public, getTabView);
|
||||
isPrivate.implement(this._public, tab => isWindowPrivate(getChromeTab(tab)));
|
||||
|
||||
// Add tabs to getURL method
|
||||
getURL.implement(this._public, (function (obj) this._public.url).bind(this));
|
||||
@ -256,8 +258,12 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (callback)
|
||||
this.once(EVENTS.close.name, callback);
|
||||
if (callback) {
|
||||
if (this.window.tabs.activeTab && (this.window.tabs.activeTab.id == this.id))
|
||||
observer.once('select', callback);
|
||||
else
|
||||
this.once(EVENTS.close.name, callback);
|
||||
}
|
||||
this._window.gBrowser.removeTab(this._tab);
|
||||
},
|
||||
/**
|
||||
|
@ -81,9 +81,9 @@ exports.getTabContainer = getTabContainer;
|
||||
*/
|
||||
function getTabs(window) {
|
||||
if (arguments.length === 0) {
|
||||
return getWindows().filter(isBrowser).reduce(function(tabs, window) {
|
||||
return tabs.concat(getTabs(window))
|
||||
}, []);
|
||||
return getWindows().
|
||||
filter(isBrowser).
|
||||
reduce((tabs, window) => tabs.concat(getTabs(window)), []);
|
||||
}
|
||||
|
||||
// fennec
|
||||
@ -91,7 +91,7 @@ function getTabs(window) {
|
||||
return window.BrowserApp.tabs;
|
||||
|
||||
// firefox - default
|
||||
return Array.slice(getTabContainer(window).children);
|
||||
return Array.filter(getTabContainer(window).children, function(t) !t.closing);
|
||||
}
|
||||
exports.getTabs = getTabs;
|
||||
|
||||
|
@ -9,11 +9,10 @@ const { on, off, once } = require('../event/core');
|
||||
const { method } = require('../lang/functional');
|
||||
const { getWindowTitle } = require('./utils');
|
||||
const unload = require('../system/unload');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { deprecateUsage } = require('../util/deprecate');
|
||||
|
||||
const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("sdk/tabs") instead';
|
||||
|
||||
@ -38,18 +37,11 @@ const BrowserWindow = Class({
|
||||
get activeTab() require('../tabs').activeTab,
|
||||
on: method(on),
|
||||
removeListener: method(off),
|
||||
once: method(once),
|
||||
get isPrivateBrowsing() {
|
||||
deprecateUsage('`browserWindow.isPrivateBrowsing` is deprecated, please ' +
|
||||
'consider using ' +
|
||||
'`require("sdk/private-browsing").isPrivate(browserWindow)` ' +
|
||||
'instead.');
|
||||
return isWindowPrivate(windowNS(this).window);
|
||||
}
|
||||
once: method(once)
|
||||
});
|
||||
exports.BrowserWindow = BrowserWindow;
|
||||
|
||||
const getWindowView = window => windowNS(window).window;
|
||||
|
||||
getPBOwnerWindow.define(BrowserWindow, getWindowView);
|
||||
isPrivate.define(BrowserWindow, window => isWindowPrivate(windowNS(this).window));
|
||||
viewFor.define(BrowserWindow, getWindowView);
|
||||
|
@ -10,18 +10,18 @@ const { Cc, Ci, Cr } = require('chrome'),
|
||||
{ WindowTabs, WindowTabTracker } = require('./tabs-firefox'),
|
||||
{ WindowDom } = require('./dom'),
|
||||
{ WindowLoader } = require('./loader'),
|
||||
{ isBrowser, getWindowDocShell, windows: windowIterator } = require('../window/utils'),
|
||||
{ isBrowser, getWindowDocShell,
|
||||
windows: windowIterator, isWindowPrivate } = require('../window/utils'),
|
||||
{ Options } = require('../tabs/common'),
|
||||
apiUtils = require('../deprecated/api-utils'),
|
||||
unload = require('../system/unload'),
|
||||
windowUtils = require('../deprecated/window-utils'),
|
||||
{ WindowTrackerTrait } = windowUtils,
|
||||
{ ns } = require('../core/namespace'),
|
||||
{ observer: windowObserver } = require('./observer'),
|
||||
{ getOwnerWindow } = require('../private-browsing/window/utils');
|
||||
{ observer: windowObserver } = require('./observer');
|
||||
const { windowNS } = require('../window/namespace');
|
||||
const { isPrivateBrowsingSupported } = require('../self');
|
||||
const { ignoreWindow } = require('sdk/private-browsing/utils');
|
||||
const { ignoreWindow, isPrivate } = require('sdk/private-browsing/utils');
|
||||
const { viewFor } = require('../view/core');
|
||||
|
||||
/**
|
||||
@ -76,7 +76,8 @@ const BrowserWindowTrait = Trait.compose(
|
||||
this._load();
|
||||
|
||||
windowNS(this._public).window = this._window;
|
||||
getOwnerWindow.implement(this._public, getChromeWindow);
|
||||
|
||||
isPrivate.implement(this._public, window => isWindowPrivate(getChromeWindow(window)));
|
||||
viewFor.implement(this._public, getChromeWindow);
|
||||
|
||||
return this;
|
||||
|
@ -40,7 +40,7 @@ const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
|
||||
getService(Ci.nsIObserverService);
|
||||
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
const { Reflect } = Cu.import("resource://gre/modules/reflect.jsm", {});
|
||||
const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm");
|
||||
const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
|
||||
const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
|
||||
|
||||
// Define some shortcuts.
|
||||
@ -56,7 +56,7 @@ const NODE_MODULES = ["assert", "buffer_ieee754", "buffer", "child_process", "cl
|
||||
|
||||
const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
|
||||
'Functionality provided by Components may be available in an SDK\n' +
|
||||
'module: https://jetpack.mozillalabs.com/sdk/latest/docs/ \n\n' +
|
||||
'module: https://developer.mozilla.org/en-US/Add-ons/SDK \n\n' +
|
||||
'However, if you still need to import Components, you may use the\n' +
|
||||
'`chrome` module\'s properties for shortcuts to Component properties:\n\n' +
|
||||
'Shortcuts: \n' +
|
||||
@ -688,6 +688,10 @@ exports.unload = unload;
|
||||
// If `resolve` does not returns `uri` string exception will be thrown by
|
||||
// an associated `require` call.
|
||||
const Loader = iced(function Loader(options) {
|
||||
let console = new ConsoleAPI({
|
||||
consoleID: options.id ? "addon/" + options.id : ""
|
||||
});
|
||||
|
||||
let {
|
||||
modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative
|
||||
} = override({
|
||||
|
@ -264,10 +264,11 @@ parser_groups = (
|
||||
cmds=['test', 'run', 'xpi', 'testex',
|
||||
'testpkgs', 'testall'])),
|
||||
(("", "--e10s",), dict(dest="enable_e10s",
|
||||
help="enable out-of-process Jetpacks",
|
||||
help="enable remote windows",
|
||||
action="store_true",
|
||||
default=False,
|
||||
cmds=['test', 'run', 'testex', 'testpkgs'])),
|
||||
cmds=['test', 'run', 'testex', 'testpkgs',
|
||||
'testaddons', 'testcfx', 'testall'])),
|
||||
(("", "--logfile",), dict(dest="logfile",
|
||||
help="log console output to file",
|
||||
metavar=None,
|
||||
@ -902,6 +903,8 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
|
||||
if options.addons is not None:
|
||||
options.addons = options.addons.split(",")
|
||||
|
||||
enable_e10s = options.enable_e10s or target_cfg.get('e10s', False)
|
||||
|
||||
try:
|
||||
retval = run_app(harness_root_dir=app_extension_dir,
|
||||
manifest_rdf=manifest_rdf,
|
||||
@ -924,7 +927,8 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
|
||||
is_running_tests=(command == "test"),
|
||||
overload_modules=options.overload_modules,
|
||||
bundle_sdk=options.bundle_sdk,
|
||||
pkgdir=options.pkgdir)
|
||||
pkgdir=options.pkgdir,
|
||||
enable_e10s=enable_e10s)
|
||||
except ValueError, e:
|
||||
print ""
|
||||
print "A given cfx option has an inappropriate value:"
|
||||
|
@ -418,7 +418,8 @@ def run_app(harness_root_dir, manifest_rdf, harness_options,
|
||||
is_running_tests=False,
|
||||
overload_modules=False,
|
||||
bundle_sdk=True,
|
||||
pkgdir=""):
|
||||
pkgdir="",
|
||||
enable_e10s=False):
|
||||
if binary:
|
||||
binary = os.path.expanduser(binary)
|
||||
|
||||
@ -430,6 +431,9 @@ def run_app(harness_root_dir, manifest_rdf, harness_options,
|
||||
cmdargs = []
|
||||
preferences = dict(DEFAULT_COMMON_PREFS)
|
||||
|
||||
if enable_e10s:
|
||||
preferences['browser.tabs.remote.autostart'] = True
|
||||
|
||||
# For now, only allow running on Mobile with --force-mobile argument
|
||||
if app_type in ["fennec", "fennec-on-device"] and not enable_mobile:
|
||||
print """
|
||||
|
@ -60,7 +60,7 @@ def welcome():
|
||||
print ("Your SDK may not work properly.")
|
||||
return
|
||||
|
||||
print ("Welcome to the Add-on SDK. For the docs, visit https://addons.mozilla.org/en-US/developers/docs/sdk/latest/")
|
||||
print ("Welcome to the Add-on SDK. For the docs, visit https://developer.mozilla.org/en-US/Add-ons/SDK")
|
||||
|
||||
if __name__ == '__main__':
|
||||
welcome()
|
||||
|
@ -0,0 +1,10 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
8
addon-sdk/source/test/addons/chrome/data/panel.js
Normal file
8
addon-sdk/source/test/addons/chrome/data/panel.js
Normal file
@ -0,0 +1,8 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
self.port.on('echo', _ => {
|
||||
self.port.emit('echo', '');
|
||||
});
|
@ -7,6 +7,8 @@ const { Cu, Cc, Ci } = require('chrome');
|
||||
const Request = require('sdk/request').Request;
|
||||
const { WindowTracker } = require('sdk/deprecated/window-utils');
|
||||
const { close, open } = require('sdk/window/helpers');
|
||||
const { data } = require('sdk/self');
|
||||
const { Panel } = require('sdk/panel');
|
||||
|
||||
const XUL_URL = 'chrome://test/content/new-window.xul'
|
||||
|
||||
@ -65,4 +67,26 @@ exports.testChromeLocale = function(assert) {
|
||||
'locales en-US folder was copied correctly');
|
||||
}
|
||||
|
||||
exports.testChromeInPanel = function(assert, done) {
|
||||
let panel = Panel({
|
||||
contentURL: 'chrome://test/content/panel.html',
|
||||
contentScriptWhen: 'start',
|
||||
contentScriptFile: data.url('panel.js')
|
||||
});
|
||||
panel.once('show', _ => {
|
||||
assert.pass('panel shown');
|
||||
panel.port.once('echo', _ => {
|
||||
assert.pass('got echo');
|
||||
panel.once('hide', _ => {
|
||||
panel.destroy();
|
||||
assert.pass('panel is destroyed');
|
||||
done();
|
||||
});
|
||||
panel.hide();
|
||||
});
|
||||
panel.port.emit('echo');
|
||||
});
|
||||
panel.show();
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
29
addon-sdk/source/test/addons/e10s/lib/main.js
Normal file
29
addon-sdk/source/test/addons/e10s/lib/main.js
Normal file
@ -0,0 +1,29 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const { get: getPref } = require('sdk/preferences/service');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { openTab, closeTab, getBrowserForTab } = require('sdk/tabs/utils');
|
||||
|
||||
exports.testRemotePrefIsSet = function(assert) {
|
||||
assert.ok(getPref('browser.tabs.remote.autostart'),
|
||||
"Electrolysis remote tabs pref should be set");
|
||||
}
|
||||
|
||||
exports.testTabIsRemote = function(assert, done) {
|
||||
const url = 'data:text/html,test-tab-is-remote';
|
||||
let tab = openTab(getMostRecentBrowserWindow(), url);
|
||||
assert.ok(tab.getAttribute('remote'), "The new tab should be remote");
|
||||
|
||||
// can't simply close a remote tab before it is loaded, bug 1006043
|
||||
let mm = getBrowserForTab(tab).messageManager;
|
||||
mm.addMessageListener(7, function() {
|
||||
closeTab(tab);
|
||||
done();
|
||||
})
|
||||
mm.loadFrameScript('data:,sendAsyncMessage(7)', false);
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
10
addon-sdk/source/test/addons/e10s/package.json
Normal file
10
addon-sdk/source/test/addons/e10s/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "e10s-flag",
|
||||
"title": "e10s-flag",
|
||||
"id": "jid1-DYaXFHAPlHwbgw",
|
||||
"description": "a basic e10s test add-on",
|
||||
"author": "Tomislav Jovanovic",
|
||||
"license": "MPL 2.0",
|
||||
"version": "0.1",
|
||||
"e10s": true
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
const { Cu } = require('chrome');
|
||||
const { PageMod } = require('sdk/page-mod');
|
||||
const tabs = require('sdk/tabs');
|
||||
const { closeTab } = require('sdk/tabs/utils');
|
||||
const promise = require('sdk/core/promise')
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { data } = require('sdk/self');
|
||||
@ -47,6 +48,7 @@ exports.testDebugger = function(assert, done) {
|
||||
then(_ => { assert.pass('testDebuggerStatement called') }).
|
||||
then(closeConnection).
|
||||
then(_ => { assert.pass('closeConnection called') }).
|
||||
then(_ => { tab.close() }).
|
||||
then(done).
|
||||
then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
|
@ -6,6 +6,7 @@
|
||||
const { Cu } = require('chrome');
|
||||
const { PageMod } = require('sdk/page-mod');
|
||||
const tabs = require('sdk/tabs');
|
||||
const { closeTab } = require('sdk/tabs/utils');
|
||||
const promise = require('sdk/core/promise')
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { data } = require('sdk/self');
|
||||
@ -54,6 +55,7 @@ exports.testDebugger = function(assert, done) {
|
||||
then(_ => { assert.pass('testDebuggerStatement called') }).
|
||||
then(closeConnection).
|
||||
then(_ => { assert.pass('closeConnection called') }).
|
||||
then(_ => { tab.close() }).
|
||||
then(done).
|
||||
then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
|
@ -8,7 +8,6 @@ const { isPrivateBrowsingSupported } = require('sdk/self');
|
||||
const tabs = require('sdk/tabs');
|
||||
const { browserWindows: windows } = require('sdk/windows');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
|
||||
const { is } = require('sdk/system/xul-app');
|
||||
const { isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
|
||||
|
||||
@ -19,47 +18,6 @@ exports.testIsPrivateBrowsingTrue = function(assert) {
|
||||
'isPrivateBrowsingSupported property is true');
|
||||
};
|
||||
|
||||
// test tab.open with isPrivate: true
|
||||
// test isPrivate on a tab
|
||||
// test getOwnerWindow on windows and tabs
|
||||
exports.testGetOwnerWindow = function(assert, done) {
|
||||
let window = windows.activeWindow;
|
||||
let chromeWindow = getOwnerWindow(window);
|
||||
assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
|
||||
|
||||
tabs.open({
|
||||
url: 'about:blank',
|
||||
isPrivate: true,
|
||||
onOpen: function(tab) {
|
||||
// test that getOwnerWindow works as expected
|
||||
if (is('Fennec')) {
|
||||
assert.notStrictEqual(chromeWindow, getOwnerWindow(tab));
|
||||
assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
|
||||
}
|
||||
else {
|
||||
if (isWindowPBSupported) {
|
||||
assert.notStrictEqual(chromeWindow,
|
||||
getOwnerWindow(tab),
|
||||
'associated window is not the same for window and window\'s tab');
|
||||
}
|
||||
else {
|
||||
assert.strictEqual(chromeWindow,
|
||||
getOwnerWindow(tab),
|
||||
'associated window is the same for window and window\'s tab');
|
||||
}
|
||||
}
|
||||
|
||||
let pbSupported = isTabPBSupported || isWindowPBSupported;
|
||||
|
||||
// test that the tab is private if it should be
|
||||
assert.equal(isPrivate(tab), pbSupported);
|
||||
assert.equal(isPrivate(getOwnerWindow(tab)), pbSupported);
|
||||
|
||||
tab.close(function() done());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// test that it is possible to open a private tab
|
||||
exports.testTabOpenPrivate = function(assert, done) {
|
||||
tabs.open({
|
||||
@ -68,10 +26,7 @@ exports.testTabOpenPrivate = function(assert, done) {
|
||||
onReady: function(tab) {
|
||||
assert.equal(tab.url, TAB_URL, 'opened correct tab');
|
||||
assert.equal(isPrivate(tab), (isWindowPBSupported || isTabPBSupported));
|
||||
|
||||
tab.close(function() {
|
||||
done();
|
||||
});
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -84,10 +39,7 @@ exports.testTabOpenPrivateDefault = function(assert, done) {
|
||||
onReady: function(tab) {
|
||||
assert.equal(tab.url, TAB_URL, 'opened correct tab');
|
||||
assert.equal(isPrivate(tab), false);
|
||||
|
||||
tab.close(function() {
|
||||
done();
|
||||
});
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -100,10 +52,7 @@ exports.testTabOpenPrivateOffExplicit = function(assert, done) {
|
||||
onReady: function(tab) {
|
||||
assert.equal(tab.url, TAB_URL, 'opened correct tab');
|
||||
assert.equal(isPrivate(tab), false);
|
||||
|
||||
tab.close(function() {
|
||||
done();
|
||||
});
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -121,10 +70,7 @@ if (!is('Fennec')) {
|
||||
tab.once('ready', function() {
|
||||
assert.equal(tab.url, TAB_URL, 'opened correct tab');
|
||||
assert.equal(isPrivate(tab), isWindowPBSupported, 'tab is private');
|
||||
|
||||
window.close(function() {
|
||||
done();
|
||||
});
|
||||
window.close(done);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -86,25 +86,6 @@ function open(url, options) {
|
||||
return promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the Active Tab
|
||||
*/
|
||||
function close(window) {
|
||||
let { promise, resolve } = defer();
|
||||
|
||||
if (window && typeof(window.close) === "function") {
|
||||
closeWindow(window).then(function() resolve());
|
||||
}
|
||||
else {
|
||||
// Here we assuming that the most recent browser window is the one we're
|
||||
// doing the test, and the active tab is the one we just opened.
|
||||
closeTab(getActiveTab(getMostRecentBrowserWindow()));
|
||||
resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the window given and return a promise, that will be resolved with the
|
||||
* content window after a small delay.
|
||||
@ -249,7 +230,7 @@ exports["test PWPB Selection Listener"] = function(assert, done) {
|
||||
|
||||
assert.equal(selection.text, "fo");
|
||||
|
||||
close(window).
|
||||
closeWindow(window).
|
||||
then(loader.unload).
|
||||
then(done).
|
||||
then(null, assert.fail);
|
||||
@ -279,7 +260,7 @@ exports["test PWPB Textarea OnSelect Listener"] = function(assert, done) {
|
||||
focus(window).then(function() {
|
||||
assert.equal(selection.text, "noodles");
|
||||
|
||||
close(window).
|
||||
closeWindow(window).
|
||||
then(loader.unload).
|
||||
then(done).
|
||||
then(null, assert.fail);
|
||||
@ -298,7 +279,7 @@ exports["test PWPB Single DOM Selection"] = function(assert, done) {
|
||||
|
||||
open(URL, {private: true, title: "PWPB Single DOM Selection"}).
|
||||
then(selectFirstDiv).
|
||||
then(focus).then(function() {
|
||||
then(focus).then(function(window) {
|
||||
assert.equal(selection.isContiguous, true,
|
||||
"selection.isContiguous with single DOM Selection works.");
|
||||
|
||||
@ -321,7 +302,9 @@ exports["test PWPB Single DOM Selection"] = function(assert, done) {
|
||||
|
||||
assert.equal(selectionCount, 1,
|
||||
"One iterable selection");
|
||||
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
|
||||
|
||||
return closeWindow(window);
|
||||
}).then(loader.unload).then(done).then(null, assert.fail);
|
||||
}
|
||||
|
||||
exports["test PWPB Textarea Selection"] = function(assert, done) {
|
||||
@ -331,7 +314,7 @@ exports["test PWPB Textarea Selection"] = function(assert, done) {
|
||||
open(URL, {private: true, title: "PWPB Textarea Listener"}).
|
||||
then(selectTextarea).
|
||||
then(focus).
|
||||
then(function() {
|
||||
then(function(window) {
|
||||
|
||||
assert.equal(selection.isContiguous, true,
|
||||
"selection.isContiguous with Textarea Selection works.");
|
||||
@ -355,7 +338,9 @@ exports["test PWPB Textarea Selection"] = function(assert, done) {
|
||||
|
||||
assert.equal(selectionCount, 1,
|
||||
"One iterable selection");
|
||||
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
|
||||
|
||||
return closeWindow(window);
|
||||
}).then(loader.unload).then(done).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done) {
|
||||
@ -365,7 +350,7 @@ exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done)
|
||||
open(URL, {private: true, title: "PWPB Set HTML in Multiple DOM Selection"}).
|
||||
then(selectAllDivs).
|
||||
then(focus).
|
||||
then(function() {
|
||||
then(function(window) {
|
||||
let html = "<span>b<b>a</b>r</span>";
|
||||
|
||||
let expectedText = ["bar", "and"];
|
||||
@ -393,7 +378,9 @@ exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done)
|
||||
|
||||
assert.equal(selectionCount, 2,
|
||||
"Two iterable selections");
|
||||
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
|
||||
|
||||
return closeWindow(window);
|
||||
}).then(loader.unload).then(done).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
|
||||
@ -403,7 +390,7 @@ exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
|
||||
open(URL, {private: true, title: "test PWPB Set Text in Textarea Selection"}).
|
||||
then(selectTextarea).
|
||||
then(focus).
|
||||
then(function() {
|
||||
then(function(window) {
|
||||
|
||||
let text = "bar";
|
||||
|
||||
@ -429,7 +416,8 @@ exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
|
||||
assert.equal(selectionCount, 1,
|
||||
"One iterable selection");
|
||||
|
||||
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
|
||||
return closeWindow(window);
|
||||
}).then(loader.unload).then(done).then(null, assert.fail);
|
||||
};
|
||||
|
||||
// If the platform doesn't support the PBPW, we're replacing PBPW tests
|
||||
|
@ -1,9 +1,11 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const tabs = require('sdk/tabs');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const pbUtils = require('sdk/private-browsing/utils');
|
||||
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
|
||||
|
||||
exports.testPrivateTabsAreListed = function (assert, done) {
|
||||
let originalTabCount = tabs.length;
|
||||
@ -12,7 +14,6 @@ exports.testPrivateTabsAreListed = function (assert, done) {
|
||||
url: 'about:blank',
|
||||
isPrivate: true,
|
||||
onOpen: function(tab) {
|
||||
let win = getOwnerWindow(tab);
|
||||
// PWPB case
|
||||
if (pbUtils.isWindowPBSupported || pbUtils.isTabPBSupported) {
|
||||
assert.ok(isPrivate(tab), "tab is private");
|
||||
|
@ -1,8 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const tabs = require('sdk/tabs');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
|
||||
const { promise: windowPromise, close, focus } = require('sdk/window/helpers');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
|
||||
@ -16,9 +18,6 @@ exports.testOpenTabWithPrivateActiveWindowNoIsPrivateOption = function(assert, d
|
||||
url: 'about:blank',
|
||||
onOpen: function(tab) {
|
||||
assert.ok(isPrivate(tab), 'new tab is private');
|
||||
assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
|
||||
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
|
||||
|
||||
close(window).then(done).then(null, assert.fail);
|
||||
}
|
||||
})
|
||||
@ -35,9 +34,6 @@ exports.testOpenTabWithNonPrivateActiveWindowNoIsPrivateOption = function(assert
|
||||
url: 'about:blank',
|
||||
onOpen: function(tab) {
|
||||
assert.equal(isPrivate(tab), false, 'new tab is not private');
|
||||
assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
|
||||
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
|
||||
|
||||
close(window).then(done).then(null, assert.fail);
|
||||
}
|
||||
})
|
||||
@ -55,9 +51,6 @@ exports.testOpenTabWithPrivateActiveWindowWithIsPrivateOptionTrue = function(ass
|
||||
isPrivate: true,
|
||||
onOpen: function(tab) {
|
||||
assert.ok(isPrivate(tab), 'new tab is private');
|
||||
assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
|
||||
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
|
||||
|
||||
close(window).then(done).then(null, assert.fail);
|
||||
}
|
||||
})
|
||||
@ -75,9 +68,6 @@ exports.testOpenTabWithNonPrivateActiveWindowWithIsPrivateOptionFalse = function
|
||||
isPrivate: false,
|
||||
onOpen: function(tab) {
|
||||
assert.equal(isPrivate(tab), false, 'new tab is not private');
|
||||
assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
|
||||
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
|
||||
|
||||
close(window).then(done).then(null, assert.fail);
|
||||
}
|
||||
})
|
||||
|
@ -3,15 +3,10 @@
|
||||
const { Ci } = require('chrome');
|
||||
const { openTab, closeTab } = require('sdk/tabs/utils');
|
||||
const { browserWindows } = require('sdk/windows');
|
||||
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
|
||||
exports.testIsPrivateOnTab = function(assert) {
|
||||
let window = browserWindows.activeWindow;
|
||||
|
||||
let chromeWindow = getOwnerWindow(window);
|
||||
|
||||
assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
|
||||
assert.ok(!isPrivate(chromeWindow), 'the top level window is not private');
|
||||
|
||||
let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
|
||||
|
@ -6,7 +6,8 @@
|
||||
const { Cc, Ci } = require('chrome');
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
|
||||
const { viewFor } = require('sdk/view/core');
|
||||
const { getOwnerWindow } = require('sdk/tabs/utils');
|
||||
const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { open, focus, close } = require('sdk/window/helpers');
|
||||
const tabs = require('sdk/tabs');
|
||||
@ -165,12 +166,12 @@ exports.testTabPropertiesInNewWindow = function(assert, done) {
|
||||
});
|
||||
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils');
|
||||
let { viewFor } = loader.require('sdk/view/core');
|
||||
|
||||
let count = 0;
|
||||
function onReadyOrLoad (tab) {
|
||||
if (count++) {
|
||||
close(getOwnerWindow(tab)).then(done).then(null, assert.fail);
|
||||
close(getOwnerWindow(viewFor(tab))).then(done).then(null, assert.fail);
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,6 +369,33 @@ exports.testTabMove = function(assert, done) {
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports.testIgnoreClosing = function(assert, done) {
|
||||
let originalWindow = browserWindows.activeWindow;
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,foobar";
|
||||
|
||||
assert.equal(tabs.length, 2, "should be two windows open each with one tab");
|
||||
|
||||
tabs.on('ready', function onReady(tab) {
|
||||
tabs.removeListener('ready', onReady);
|
||||
|
||||
let win = tab.window;
|
||||
assert.equal(win.tabs.length, 2, "should be two tabs in the new window");
|
||||
assert.equal(tabs.length, 3, "should be three tabs in total");
|
||||
|
||||
tab.close(function() {
|
||||
assert.equal(win.tabs.length, 1, "should be one tab in the new window");
|
||||
assert.equal(tabs.length, 2, "should be two tabs in total");
|
||||
|
||||
originalWindow.once("activate", done);
|
||||
close(window);
|
||||
});
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: open tab with default options
|
||||
exports.testOpen = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
@ -413,6 +441,8 @@ exports.testPinUnpin = function(assert, done) {
|
||||
|
||||
// TEST: open tab in background
|
||||
exports.testInBackground = function(assert, done) {
|
||||
assert.equal(tabs.length, 1, "Should be one tab");
|
||||
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let activeUrl = tabs.activeTab.url;
|
||||
let url = "data:text/html;charset=utf-8,background";
|
||||
@ -441,7 +471,7 @@ exports.testOpenInNewWindow = function(assert, done) {
|
||||
url: url,
|
||||
inNewWindow: true,
|
||||
onReady: function(tab) {
|
||||
let newWindow = getOwnerWindow(tab);
|
||||
let newWindow = getOwnerWindow(viewFor(tab));
|
||||
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
|
||||
|
||||
onFocus(newWindow).then(function() {
|
||||
@ -466,7 +496,7 @@ exports.testOpenInNewWindowOnOpen = function(assert, done) {
|
||||
url: url,
|
||||
inNewWindow: true,
|
||||
onOpen: function(tab) {
|
||||
let newWindow = getOwnerWindow(tab);
|
||||
let newWindow = getOwnerWindow(viewFor(tab));
|
||||
|
||||
onFocus(newWindow).then(function() {
|
||||
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
|
||||
@ -942,9 +972,11 @@ exports['test unique tab ids'] = function(assert, done) {
|
||||
var one = openWindow(), two = openWindow();
|
||||
all([one, two]).then(function(results) {
|
||||
assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal.");
|
||||
results[0].win.close();
|
||||
results[1].win.close();
|
||||
done();
|
||||
results[0].win.close(function() {
|
||||
results[1].win.close(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ exports.testExecOptionsEnvironment = function (assert, done) {
|
||||
'receives environment option');
|
||||
done();
|
||||
});
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports.testExecOptionsTimeout = function (assert, done) {
|
||||
@ -104,7 +104,7 @@ exports.testExecOptionsTimeout = function (assert, done) {
|
||||
child.off('close', closeHandler);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports.testExecFileCallbackSuccess = function (assert, done) {
|
||||
@ -116,7 +116,7 @@ exports.testExecFileCallbackSuccess = function (assert, done) {
|
||||
assert.equal(stdout.trim(), '--myargs -j -s'.trim(), 'passes in correct arguments');
|
||||
done();
|
||||
});
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports.testExecFileCallbackError = function (assert, done) {
|
||||
@ -143,7 +143,7 @@ exports.testExecFileOptionsEnvironment = function (assert, done) {
|
||||
'receives environment option');
|
||||
done();
|
||||
});
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports.testExecFileOptionsTimeout = function (assert, done) {
|
||||
@ -181,7 +181,7 @@ exports.testExecFileOptionsTimeout = function (assert, done) {
|
||||
child.off('close', closeHandler);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -204,7 +204,7 @@ exports.testExecFileOptionsMaxBufferLargeStdOut = function (assert, done) {
|
||||
});
|
||||
stdoutChild.on('exit', exitHandler);
|
||||
stdoutChild.on('close', closeHandler);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
|
||||
function exitHandler (code, signal) {
|
||||
assert.equal(code, null, 'Exit code is null in exit handler');
|
||||
@ -239,7 +239,7 @@ exports.testExecFileOptionsMaxBufferLargeStdOErr = function (assert, done) {
|
||||
});
|
||||
stderrChild.on('exit', exitHandler);
|
||||
stderrChild.on('close', closeHandler);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
|
||||
function exitHandler (code, signal) {
|
||||
assert.equal(code, null, 'Exit code is null in exit handler');
|
||||
@ -280,7 +280,7 @@ exports.testExecFileOptionsMaxBufferSmallStdOut = function (assert, done) {
|
||||
});
|
||||
stdoutChild.on('exit', exitHandler);
|
||||
stdoutChild.on('close', closeHandler);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
|
||||
function exitHandler (code, signal) {
|
||||
// Sometimes the buffer limit is hit before the process closes successfully
|
||||
@ -331,7 +331,7 @@ exports.testExecFileOptionsMaxBufferSmallStdErr = function (assert, done) {
|
||||
});
|
||||
stderrChild.on('exit', exitHandler);
|
||||
stderrChild.on('close', closeHandler);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
|
||||
function exitHandler (code, signal) {
|
||||
// Sometimes the buffer limit is hit before the process closes successfully
|
||||
@ -377,7 +377,7 @@ exports.testChildExecFileKillSignal = function (assert, done) {
|
||||
assert.equal(err.signal, 'beepbeep', 'correctly used custom killSignal');
|
||||
done();
|
||||
});
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
};
|
||||
|
||||
exports.testChildProperties = function (assert, done) {
|
||||
@ -390,8 +390,7 @@ exports.testChildProperties = function (assert, done) {
|
||||
assert.ok(true, 'Windows environment does not have `pid`');
|
||||
else
|
||||
assert.ok(child.pid > 0, 'Child has a pid');
|
||||
done();
|
||||
});
|
||||
}).then(done, assert.fail);
|
||||
};
|
||||
|
||||
exports.testChildStdinStreamLarge = function (assert, done) {
|
||||
@ -509,7 +508,7 @@ exports.testSpawnOptions = function (assert, done) {
|
||||
|
||||
envChild.on('close', envClose);
|
||||
cwdChild.on('close', cwdClose);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
|
||||
function envClose () {
|
||||
assert.equal(envStdout.trim(), 'my-value-test', 'spawn correctly passed in ENV');
|
||||
|
@ -90,8 +90,7 @@ function comparePixelImages(imageA, imageB, callback) {
|
||||
compared = pixels;
|
||||
this.emit("draw-image", imageB);
|
||||
} else {
|
||||
callback(compared === pixels);
|
||||
tab.close()
|
||||
tab.close(callback.bind(null, compared === pixels))
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -986,7 +986,7 @@ exports['test panel CSS'] = function(assert, done) {
|
||||
getActiveView(panel).querySelector('iframe').contentWindow;
|
||||
|
||||
let panel = Panel({
|
||||
contentURL: 'data:text/html;charset=utf-8,' +
|
||||
contentURL: 'data:text/html;charset=utf-8,' +
|
||||
'<div style="background: silver">css test</div>',
|
||||
contentStyle: 'div { height: 100px; }',
|
||||
contentStyleFile: CSS_URL,
|
||||
@ -999,7 +999,7 @@ exports['test panel CSS'] = function(assert, done) {
|
||||
|
||||
loader.unload();
|
||||
done();
|
||||
}).then(null, assert.fail);
|
||||
}).then(null, assert.fail);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1016,7 +1016,7 @@ exports['test panel CSS list'] = function(assert, done) {
|
||||
getActiveView(panel).querySelector('iframe').contentWindow;
|
||||
|
||||
let panel = Panel({
|
||||
contentURL: 'data:text/html;charset=utf-8,' +
|
||||
contentURL: 'data:text/html;charset=utf-8,' +
|
||||
'<div style="width:320px; max-width: 480px!important">css test</div>',
|
||||
contentStyleFile: [
|
||||
// Highlight evaluation order in this list
|
||||
@ -1049,8 +1049,7 @@ exports['test panel CSS list'] = function(assert, done) {
|
||||
'add-on author/page author stylesheet !important precedence works');
|
||||
|
||||
loader.unload();
|
||||
done();
|
||||
}).then(null, assert.fail);
|
||||
}).then(done, assert.fail);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1065,12 +1064,12 @@ if (isWindowPBSupported) {
|
||||
toolbar: true,
|
||||
chrome: true,
|
||||
private: true
|
||||
} }).then(function(window) {
|
||||
} }).then(window => {
|
||||
assert.ok(isPrivate(window), 'window is private');
|
||||
assert.equal(getWindow(window.gBrowser), null, 'private window elements returns null');
|
||||
assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'non-private window elements returns window');
|
||||
close(window).then(done);
|
||||
})
|
||||
return window;
|
||||
}).then(close).then(done).then(null, assert.fail);
|
||||
}
|
||||
}
|
||||
else if (isGlobalPBSupported) {
|
||||
@ -1084,7 +1083,7 @@ else if (isGlobalPBSupported) {
|
||||
open(null, { features: {
|
||||
toolbar: true,
|
||||
chrome: true
|
||||
} }).then(function(window) {
|
||||
} }).then(window => {
|
||||
assert.ok(isPrivate(window), 'window is private');
|
||||
assert.equal(getWindow(window.gBrowser), window, 'private window elements returns window');
|
||||
assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'active window elements returns window');
|
||||
|
@ -12,7 +12,6 @@ const { isWindowPrivate } = winUtils;
|
||||
const { isPrivateBrowsingSupported } = require('sdk/self');
|
||||
const { is } = require('sdk/system/xul-app');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
|
||||
const { LoaderWithHookedConsole } = require("sdk/test/loader");
|
||||
const { getMode, isGlobalPBSupported,
|
||||
isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
|
||||
@ -84,34 +83,6 @@ exports.testIsPrivateBrowsingFalseDefault = function(assert) {
|
||||
'isPrivateBrowsingSupported property is false by default');
|
||||
};
|
||||
|
||||
exports.testGetOwnerWindow = function(assert, done) {
|
||||
let window = windows.activeWindow;
|
||||
let chromeWindow = getOwnerWindow(window);
|
||||
assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
|
||||
|
||||
tabs.open({
|
||||
url: 'about:blank',
|
||||
isPrivate: true,
|
||||
onOpen: function(tab) {
|
||||
// test that getOwnerWindow works as expected
|
||||
if (is('Fennec')) {
|
||||
assert.notStrictEqual(chromeWindow, getOwnerWindow(tab));
|
||||
assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
|
||||
}
|
||||
else {
|
||||
assert.strictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab');
|
||||
}
|
||||
|
||||
// test that the tab is not private
|
||||
// private flag should be ignored by default
|
||||
assert.ok(!isPrivate(tab));
|
||||
assert.ok(!isPrivate(getOwnerWindow(tab)));
|
||||
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testNSIPrivateBrowsingChannel = function(assert) {
|
||||
let channel = Services.io.newChannel("about:blank", null, null);
|
||||
channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);
|
||||
|
@ -28,7 +28,7 @@ const tabs = require("sdk/tabs");
|
||||
const { setTabURL } = require("sdk/tabs/utils");
|
||||
const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
|
||||
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
|
||||
const { open: openNewWindow } = require("sdk/window/helpers");
|
||||
const { open: openNewWindow, close: closeWindow } = require("sdk/window/helpers");
|
||||
const { Loader } = require("sdk/test/loader");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
const { Cu } = require("chrome");
|
||||
@ -698,13 +698,13 @@ exports["test Selection Listener"] = function(assert, done) {
|
||||
|
||||
selection.once("select", function() {
|
||||
assert.equal(selection.text, "fo");
|
||||
close();
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
|
||||
open(URL).then(selectContentFirstDiv).
|
||||
then(dispatchSelectionEvent).
|
||||
then(close).
|
||||
then(loader.unload, assert.fail);
|
||||
then(dispatchSelectionEvent, assert.fail);
|
||||
};
|
||||
|
||||
exports["test Textarea OnSelect Listener"] = function(assert, done) {
|
||||
@ -713,13 +713,13 @@ exports["test Textarea OnSelect Listener"] = function(assert, done) {
|
||||
|
||||
selection.once("select", function() {
|
||||
assert.equal(selection.text, "noodles");
|
||||
close();
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
|
||||
open(URL).then(selectTextarea).
|
||||
then(dispatchOnSelectEvent).
|
||||
then(close).
|
||||
then(loader.unload, assert.fail);
|
||||
then(dispatchOnSelectEvent, assert.fail);
|
||||
};
|
||||
|
||||
exports["test Selection listener removed on unload"] = function(assert, done) {
|
||||
@ -769,14 +769,14 @@ exports["test Selection Listener on existing document"] = function(assert, done)
|
||||
|
||||
selection.once("select", function() {
|
||||
assert.equal(selection.text, "fo");
|
||||
close();
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
|
||||
return window;
|
||||
}).then(selectContentFirstDiv).
|
||||
then(dispatchSelectionEvent).
|
||||
then(close).
|
||||
then(loader.unload, assert.fail);
|
||||
then(dispatchSelectionEvent, assert.fail);
|
||||
};
|
||||
|
||||
|
||||
@ -788,14 +788,14 @@ exports["test Textarea OnSelect Listener on existing document"] = function(asser
|
||||
|
||||
selection.once("select", function() {
|
||||
assert.equal(selection.text, "noodles");
|
||||
close();
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
|
||||
return window;
|
||||
}).then(selectTextarea).
|
||||
then(dispatchOnSelectEvent).
|
||||
then(close).
|
||||
then(loader.unload, assert.fail);
|
||||
then(dispatchOnSelectEvent, assert.fail);
|
||||
};
|
||||
|
||||
exports["test Selection Listener on document reload"] = function(assert, done) {
|
||||
@ -804,15 +804,15 @@ exports["test Selection Listener on document reload"] = function(assert, done) {
|
||||
|
||||
selection.once("select", function() {
|
||||
assert.equal(selection.text, "fo");
|
||||
close();
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
|
||||
open(URL).
|
||||
then(reload).
|
||||
then(selectContentFirstDiv).
|
||||
then(dispatchSelectionEvent).
|
||||
then(close).
|
||||
then(loader.unload, assert.fail);
|
||||
then(dispatchSelectionEvent, assert.fail);
|
||||
};
|
||||
|
||||
exports["test Textarea OnSelect Listener on document reload"] = function(assert, done) {
|
||||
@ -821,15 +821,15 @@ exports["test Textarea OnSelect Listener on document reload"] = function(assert,
|
||||
|
||||
selection.once("select", function() {
|
||||
assert.equal(selection.text, "noodles");
|
||||
close();
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
|
||||
open(URL).
|
||||
then(reload).
|
||||
then(selectTextarea).
|
||||
then(dispatchOnSelectEvent).
|
||||
then(close).
|
||||
then(loader.unload, assert.fail);
|
||||
then(dispatchOnSelectEvent, assert.fail);
|
||||
};
|
||||
|
||||
exports["test Selection Listener on frame"] = function(assert, done) {
|
||||
@ -884,7 +884,7 @@ exports["test PBPW Selection Listener"] = function(assert, done) {
|
||||
open(URL, {private: true}).
|
||||
then(selectContentFirstDiv).
|
||||
then(dispatchSelectionEvent).
|
||||
then(close).
|
||||
then(closeWindow).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
};
|
||||
@ -902,7 +902,7 @@ exports["test PBPW Textarea OnSelect Listener"] = function(assert, done) {
|
||||
open(URL, {private: true}).
|
||||
then(selectTextarea).
|
||||
then(dispatchOnSelectEvent).
|
||||
then(close).
|
||||
then(closeWindow).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
};
|
||||
@ -931,7 +931,7 @@ exports["test PBPW Single DOM Selection"] = function(assert, done) {
|
||||
"No iterable selection in PBPW");
|
||||
|
||||
return window;
|
||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
||||
}).then(closeWindow).then(loader.unload).then(done, assert.fail);
|
||||
};
|
||||
|
||||
exports["test PBPW Textarea Selection"] = function(assert, done) {
|
||||
@ -964,7 +964,7 @@ exports["test PBPW Textarea Selection"] = function(assert, done) {
|
||||
"No iterable selection in PBPW");
|
||||
|
||||
return window;
|
||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
||||
}).then(closeWindow).then(loader.unload).then(done, assert.fail);
|
||||
};
|
||||
|
||||
// TODO: test Selection Listener on long-held connection (Bug 661884)
|
||||
|
@ -172,4 +172,4 @@ exports["test modelFor(xulTab)"] = (assert, done) => {
|
||||
});
|
||||
};
|
||||
|
||||
require("test").run(exports);
|
||||
require("sdk/test").run(exports);
|
||||
|
@ -563,33 +563,34 @@ exports.testDestroyEdgeCaseBug = function(assert, done) {
|
||||
|
||||
sidebar.show();
|
||||
assert.pass('showing the sidebar');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.testClickingACheckedMenuitem = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testClickingACheckedMenuitem';
|
||||
let window = getMostRecentBrowserWindow();
|
||||
const testName = 'testClickingACheckedMenuitem';
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
url: 'data:text/html;charset=utf-8,'+testName,
|
||||
});
|
||||
assert.pass('sidebar was created');
|
||||
|
||||
sidebar.show().then(function() {
|
||||
assert.pass('the show callback works');
|
||||
open().then(focus).then(window => {
|
||||
return sidebar.show().then(_ => {
|
||||
assert.pass('the show callback works');
|
||||
|
||||
sidebar.once('hide', function() {
|
||||
assert.pass('clicking the menuitem after the sidebar has shown hides it.');
|
||||
sidebar.destroy();
|
||||
done();
|
||||
sidebar.once('hide', _ => {
|
||||
assert.pass('clicking the menuitem after the sidebar has shown hides it.');
|
||||
sidebar.destroy();
|
||||
close(window).then(done, assert.fail);
|
||||
});
|
||||
|
||||
let menuitem = window.document.getElementById(makeID(sidebar.id));
|
||||
simulateCommand(menuitem);
|
||||
});
|
||||
|
||||
let menuitem = window.document.getElementById(makeID(sidebar.id));
|
||||
simulateCommand(menuitem);
|
||||
});
|
||||
}).catch(assert.fail);
|
||||
};
|
||||
|
||||
exports.testTitleSetter = function(assert, done) {
|
||||
|
@ -90,37 +90,30 @@ exports['test close on unload'] = function(assert) {
|
||||
};
|
||||
|
||||
exports.testWindowTracker = function(assert, done) {
|
||||
var myWindow;
|
||||
var finished = false;
|
||||
var myWindow = makeEmptyWindow();
|
||||
assert.pass('window was created');
|
||||
|
||||
var delegate = {
|
||||
onTrack: function(window) {
|
||||
if (window == myWindow) {
|
||||
assert.pass("onTrack() called with our test window");
|
||||
timer.setTimeout(function() myWindow.close());
|
||||
}
|
||||
},
|
||||
onUntrack: function(window) {
|
||||
if (window == myWindow) {
|
||||
assert.pass("onUntrack() called with our test window");
|
||||
timer.setTimeout(function() {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
myWindow = null;
|
||||
wt.unload();
|
||||
done();
|
||||
}
|
||||
else {
|
||||
assert.fail("finishTest() called multiple times.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
myWindow.addEventListener("load", function onload() {
|
||||
myWindow.removeEventListener("load", onload, false);
|
||||
assert.pass("test window has opened");
|
||||
|
||||
// test bug 638007 (new is optional), using new
|
||||
var wt = new windowUtils.WindowTracker(delegate);
|
||||
myWindow = makeEmptyWindow();
|
||||
// test bug 638007 (new is optional), using new
|
||||
var wt = new windowUtils.WindowTracker({
|
||||
onTrack: window => {
|
||||
if (window === myWindow) {
|
||||
assert.pass("onTrack() called with our test window");
|
||||
close(window);
|
||||
}
|
||||
},
|
||||
onUntrack: window => {
|
||||
if (window === myWindow) {
|
||||
assert.pass("onUntrack() called with our test window");
|
||||
wt.unload();
|
||||
timer.setTimeout(done);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
};
|
||||
|
||||
exports['test window watcher untracker'] = function(assert, done) {
|
||||
|
@ -119,8 +119,8 @@
|
||||
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
|
||||
<command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
|
||||
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
||||
<command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
|
||||
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
|
||||
<command id="Chat:Focus" oncommand="Cu.import('resource:///modules/Chat.jsm', {}).Chat.focus(window);"/>
|
||||
</commandset>
|
||||
|
||||
<commandset id="placesCommands">
|
||||
@ -380,7 +380,15 @@
|
||||
#endif
|
||||
|
||||
<!--<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>-->
|
||||
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
|
||||
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Chat:Focus"
|
||||
#ifdef XP_MACOSX
|
||||
# Sadly the devtools uses shift-accel-c on non-mac and alt-accel-c everywhere else
|
||||
# So we just use the other
|
||||
modifiers="accel,shift"
|
||||
#else
|
||||
modifiers="accel,alt"
|
||||
#endif
|
||||
/>
|
||||
|
||||
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
// the "exported" symbols
|
||||
let SocialUI,
|
||||
SocialChatBar,
|
||||
SocialFlyout,
|
||||
SocialMarks,
|
||||
SocialShare,
|
||||
@ -173,7 +172,6 @@ SocialUI = {
|
||||
_providersChanged: function() {
|
||||
SocialSidebar.clearProviderMenus();
|
||||
SocialSidebar.update();
|
||||
SocialChatBar.update();
|
||||
SocialShare.populateProviderMenu();
|
||||
SocialStatus.populateToolbarPalette();
|
||||
SocialMarks.populateToolbarPalette();
|
||||
@ -297,45 +295,6 @@ SocialUI = {
|
||||
}
|
||||
}
|
||||
|
||||
SocialChatBar = {
|
||||
get chatbar() {
|
||||
return document.getElementById("pinnedchats");
|
||||
},
|
||||
// Whether the chatbar is available for this window. Note that in full-screen
|
||||
// mode chats are available, but not shown.
|
||||
get isAvailable() {
|
||||
return SocialUI.enabled;
|
||||
},
|
||||
// Does this chatbar have any chats (whether minimized, collapsed or normal)
|
||||
get hasChats() {
|
||||
return !!this.chatbar.firstElementChild;
|
||||
},
|
||||
openChat: function(aProvider, aURL, aCallback, aMode) {
|
||||
this.update();
|
||||
if (!this.isAvailable)
|
||||
return false;
|
||||
this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
|
||||
// We only want to focus the chat if it is as a result of user input.
|
||||
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (dwu.isHandlingUserInput)
|
||||
this.chatbar.focus();
|
||||
return true;
|
||||
},
|
||||
update: function() {
|
||||
let command = document.getElementById("Social:FocusChat");
|
||||
if (!this.isAvailable) {
|
||||
this.chatbar.hidden = command.hidden = true;
|
||||
} else {
|
||||
this.chatbar.hidden = command.hidden = false;
|
||||
}
|
||||
command.setAttribute("disabled", command.hidden ? "true" : "false");
|
||||
},
|
||||
focus: function SocialChatBar_focus() {
|
||||
this.chatbar.focus();
|
||||
}
|
||||
}
|
||||
|
||||
SocialFlyout = {
|
||||
get panel() {
|
||||
return document.getElementById("social-flyout-panel");
|
||||
|
@ -1061,9 +1061,6 @@ var gBrowserInit = {
|
||||
LightweightThemeListener.init();
|
||||
WebrtcIndicator.init();
|
||||
|
||||
// Ensure login manager is up and running.
|
||||
Services.logins;
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (gMultiProcessBrowser)
|
||||
TabCrashReporter.init();
|
||||
@ -6056,7 +6053,7 @@ function GetSearchFieldBookmarkData(node) {
|
||||
|
||||
|
||||
function AddKeywordForSearchField() {
|
||||
bookmarkData = GetSearchFieldBookmarkData(document.popupNode);
|
||||
bookmarkData = GetSearchFieldBookmarkData(gContextMenu.target);
|
||||
|
||||
PlacesUIUtils.showBookmarkDialog({ action: "add"
|
||||
, type: "bookmark"
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
<xul:panel id="sponsored-panel" orient="vertical" type="arrow">
|
||||
<xul:description id="sponsored-panel-release-descr">&newtab.sponsored.release.message;</xul:description>
|
||||
<xul:description id="sponsored-panel-trial-descr">&newtab.sponsored.trial.message;</xul:description>
|
||||
<xul:description id="sponsored-panel-trial-descr">&newtab.sponsored.trial.message2;</xul:description>
|
||||
<xul:label class="text-link"
|
||||
href="https://support.mozilla.org/kb/how-do-sponsored-tiles-work"
|
||||
value="&newtab.panel.link.text;" />
|
||||
|
@ -30,50 +30,43 @@
|
||||
|
||||
<implementation implements="nsIDOMEventListener">
|
||||
<constructor><![CDATA[
|
||||
let Social = Components.utils.import("resource:///modules/Social.jsm", {}).Social;
|
||||
this.content.__defineGetter__("popupnotificationanchor",
|
||||
() => document.getAnonymousElementByAttribute(this, "anonid", "notification-icon"));
|
||||
Social.setErrorListener(this.content, function(aBrowser) {
|
||||
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
|
||||
encodeURIComponent(aBrowser.getAttribute("origin")),
|
||||
null, null, null, null);
|
||||
});
|
||||
|
||||
if (!this.chatbar) {
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "minimize").hidden = true;
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "close").hidden = true;
|
||||
}
|
||||
let contentWindow = this.contentWindow;
|
||||
// process this._callbacks, then set to null so the chatbox creator
|
||||
// knows to make new callbacks immediately.
|
||||
if (this._callbacks) {
|
||||
for (let callback of this._callbacks) {
|
||||
callback(this);
|
||||
}
|
||||
this._callbacks = null;
|
||||
}
|
||||
this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
|
||||
if (event.target != this.contentDocument)
|
||||
return;
|
||||
this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
|
||||
this.isActive = !this.minimized;
|
||||
// process this._callbacks, then set to null so the chatbox creator
|
||||
// knows to make new callbacks immediately.
|
||||
if (this._callbacks) {
|
||||
for (let callback of this._callbacks) {
|
||||
if (callback)
|
||||
callback(contentWindow);
|
||||
}
|
||||
this._callbacks = null;
|
||||
}
|
||||
|
||||
// content can send a socialChatActivity event to have the UI update.
|
||||
let chatActivity = function() {
|
||||
this.setAttribute("activity", true);
|
||||
if (this.chatbar)
|
||||
this.chatbar.updateTitlebar(this);
|
||||
}.bind(this);
|
||||
contentWindow.addEventListener("socialChatActivity", chatActivity);
|
||||
contentWindow.addEventListener("unload", function unload() {
|
||||
contentWindow.removeEventListener("unload", unload);
|
||||
contentWindow.removeEventListener("socialChatActivity", chatActivity);
|
||||
});
|
||||
this._deferredChatLoaded.resolve(this);
|
||||
}, true);
|
||||
if (this.src)
|
||||
this.setAttribute("src", this.src);
|
||||
]]></constructor>
|
||||
|
||||
<field name="_deferredChatLoaded" readonly="true">
|
||||
Promise.defer();
|
||||
</field>
|
||||
|
||||
<property name="promiseChatLoaded">
|
||||
<getter>
|
||||
return this._deferredChatLoaded.promise;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<field name="content" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "content");
|
||||
</field>
|
||||
@ -133,7 +126,7 @@
|
||||
this.contentDocument.documentElement.dispatchEvent(evt);
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
|
||||
<method name="showNotifications">
|
||||
<body><![CDATA[
|
||||
PopupNotifications._reshowNotifications(this.content.popupnotificationanchor,
|
||||
@ -148,15 +141,7 @@
|
||||
aTarget.src = this.src;
|
||||
aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
|
||||
aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
|
||||
this.content.socialErrorListener.remove();
|
||||
aTarget.content.socialErrorListener.remove();
|
||||
this.content.swapDocShells(aTarget.content);
|
||||
Social.setErrorListener(this.content, function(aBrowser) {}); // 'this' will be destroyed soon.
|
||||
Social.setErrorListener(aTarget.content, function(aBrowser) {
|
||||
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
|
||||
encodeURIComponent(aBrowser.getAttribute("origin")),
|
||||
null, null, null, null);
|
||||
});
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -186,31 +171,40 @@
|
||||
|
||||
<method name="swapWindows">
|
||||
<body><![CDATA[
|
||||
let provider = Social._getProviderFromOrigin(this.content.getAttribute("origin"));
|
||||
let deferred = Promise.defer();
|
||||
let title = this.getAttribute("label");
|
||||
if (this.chatbar) {
|
||||
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }, win => {
|
||||
win.document.title = provider.name;
|
||||
});
|
||||
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }).then(
|
||||
chatbox => {
|
||||
chatbox.contentWindow.document.title = title;
|
||||
deferred.resolve(chatbox);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// attach this chatbox to the topmost browser window
|
||||
let findChromeWindowForChats = Cu.import("resource://gre/modules/MozSocialAPI.jsm").findChromeWindowForChats;
|
||||
let win = findChromeWindowForChats();
|
||||
let chatbar = win.SocialChatBar.chatbar;
|
||||
chatbar.openChat(provider, "about:blank", win => {
|
||||
let cb = chatbar.selectedChat;
|
||||
this.swapDocShells(cb);
|
||||
let Chat = Cu.import("resource:///modules/Chat.jsm").Chat;
|
||||
let win = Chat.findChromeWindowForChats();
|
||||
let chatbar = win.document.getElementById("pinnedchats");
|
||||
let origin = this.content.getAttribute("origin");
|
||||
let cb = chatbar.openChat(origin, title, "about:blank");
|
||||
cb.promiseChatLoaded.then(
|
||||
() => {
|
||||
this.swapDocShells(cb);
|
||||
|
||||
// chatboxForURL is a map of URL -> chatbox used to avoid opening
|
||||
// duplicate chat windows. Ensure reattached chat windows aren't
|
||||
// registered with about:blank as their URL, otherwise reattaching
|
||||
// more than one chat window isn't possible.
|
||||
chatbar.chatboxForURL.delete("about:blank");
|
||||
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
|
||||
// chatboxForURL is a map of URL -> chatbox used to avoid opening
|
||||
// duplicate chat windows. Ensure reattached chat windows aren't
|
||||
// registered with about:blank as their URL, otherwise reattaching
|
||||
// more than one chat window isn't possible.
|
||||
chatbar.chatboxForURL.delete("about:blank");
|
||||
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
|
||||
|
||||
chatbar.focus();
|
||||
this.close();
|
||||
});
|
||||
chatbar.focus();
|
||||
this.close();
|
||||
deferred.resolve(cb);
|
||||
}
|
||||
);
|
||||
}
|
||||
return deferred.promise;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -510,7 +504,6 @@
|
||||
<method name="_remove">
|
||||
<parameter name="aChatbox"/>
|
||||
<body><![CDATA[
|
||||
aChatbox.content.socialErrorListener.remove();
|
||||
this.removeChild(aChatbox);
|
||||
// child might have been collapsed.
|
||||
let menuitem = this.menuitemMap.get(aChatbox);
|
||||
@ -522,22 +515,12 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="removeAll">
|
||||
<body><![CDATA[
|
||||
this.selectedChat = null;
|
||||
while (this.firstElementChild) {
|
||||
this._remove(this.firstElementChild);
|
||||
}
|
||||
// and the nub/popup must also die.
|
||||
this.nub.collapsed = true;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="openChat">
|
||||
<parameter name="aProvider"/>
|
||||
<parameter name="aOrigin"/>
|
||||
<parameter name="aTitle"/>
|
||||
<parameter name="aURL"/>
|
||||
<parameter name="aCallback"/>
|
||||
<parameter name="aMode"/>
|
||||
<parameter name="aCallback"/>
|
||||
<body><![CDATA[
|
||||
let cb = this.chatboxForURL.get(aURL);
|
||||
if (cb) {
|
||||
@ -546,30 +529,35 @@
|
||||
this.showChat(cb, aMode);
|
||||
if (aCallback) {
|
||||
if (cb._callbacks == null) {
|
||||
// DOMContentLoaded has already fired, so callback now.
|
||||
aCallback(cb.contentWindow);
|
||||
// Chatbox has already been created, so callback now.
|
||||
aCallback(cb);
|
||||
} else {
|
||||
// DOMContentLoaded for this chat is yet to fire...
|
||||
// Chatbox is yet to have bindings created...
|
||||
cb._callbacks.push(aCallback);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return cb;
|
||||
}
|
||||
this.chatboxForURL.delete(aURL);
|
||||
}
|
||||
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
|
||||
// _callbacks is a javascript property instead of a <field> as it
|
||||
// must exist before the (possibly delayed) bindings are created.
|
||||
cb._callbacks = [aCallback];
|
||||
cb._callbacks = [];
|
||||
if (aCallback) {
|
||||
// _callbacks is a javascript property instead of a <field> as it
|
||||
// must exist before the (possibly delayed) bindings are created.
|
||||
cb._callbacks.push(aCallback);
|
||||
}
|
||||
// src also a javascript property; the src attribute is set in the ctor.
|
||||
cb.src = aURL;
|
||||
if (aMode == "minimized")
|
||||
cb.setAttribute("minimized", "true");
|
||||
cb.setAttribute("origin", aProvider.origin);
|
||||
cb.setAttribute("origin", aOrigin);
|
||||
cb.setAttribute("label", aTitle);
|
||||
this.insertBefore(cb, this.firstChild);
|
||||
this.selectedChat = cb;
|
||||
this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
|
||||
this.resize();
|
||||
return cb;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -648,12 +636,14 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- Moves a chatbox to a new window. -->
|
||||
<!-- Moves a chatbox to a new window. Returns a promise that is resolved
|
||||
once the move to the other window is complete.
|
||||
-->
|
||||
<method name="detachChatbox">
|
||||
<parameter name="aChatbox"/>
|
||||
<parameter name="aOptions"/>
|
||||
<parameter name="aCallback"/>
|
||||
<body><![CDATA[
|
||||
let deferred = Promise.defer();
|
||||
let options = "";
|
||||
for (let name in aOptions)
|
||||
options += "," + name + "=" + aOptions[name];
|
||||
@ -669,9 +659,9 @@
|
||||
let otherChatbox = otherWin.document.getElementById("chatter");
|
||||
aChatbox.swapDocShells(otherChatbox);
|
||||
aChatbox.close();
|
||||
if (aCallback)
|
||||
aCallback(otherWin);
|
||||
deferred.resolve(otherChatbox);
|
||||
}, true);
|
||||
return deferred.promise;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -750,11 +740,12 @@
|
||||
let top = Math.min(Math.max(eY, sY.value),
|
||||
sY.value + sHeight.value - winHeight);
|
||||
|
||||
let provider = Social._getProviderFromOrigin(draggedChat.content.getAttribute("origin"));
|
||||
this.detachChatbox(draggedChat, { screenX: left, screenY: top }, win => {
|
||||
win.document.title = provider.name;
|
||||
});
|
||||
|
||||
let title = draggedChat.content.getAttribute("title");
|
||||
this.detachChatbox(draggedChat, { screenX: left, screenY: top }).then(
|
||||
chatbox => {
|
||||
chatbox.contentWindow.document.title = title;
|
||||
}
|
||||
);
|
||||
event.stopPropagation();
|
||||
]]></handler>
|
||||
</handlers>
|
||||
|
8
browser/base/content/test/chat/browser.ini
Normal file
8
browser/base/content/test/chat/browser.ini
Normal file
@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
chat.html
|
||||
|
||||
[browser_chatwindow.js]
|
||||
[browser_focus.js]
|
||||
[browser_tearoff.js]
|
135
browser/base/content/test/chat/browser_chatwindow.js
Normal file
135
browser/base/content/test/chat/browser_chatwindow.js
Normal file
@ -0,0 +1,135 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let chatbar = document.getElementById("pinnedchats");
|
||||
|
||||
add_chat_task(function* testOpenCloseChat() {
|
||||
let chatbox = yield promiseOpenChat("http://example.com");
|
||||
Assert.strictEqual(chatbox, chatbar.selectedChat);
|
||||
// we requested a "normal" chat, so shouldn't be minimized
|
||||
Assert.ok(!chatbox.minimized, "chat is not minimized");
|
||||
Assert.equal(chatbar.childNodes.length, 1, "should be 1 chat open");
|
||||
|
||||
|
||||
// now request the same URL again - we should get the same chat.
|
||||
let chatbox2 = yield promiseOpenChat("http://example.com");
|
||||
Assert.strictEqual(chatbox2, chatbox, "got the same chat");
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
|
||||
chatbox.toggle();
|
||||
is(chatbox.minimized, true, "chat is now minimized");
|
||||
// was no other chat to select, so selected becomes null.
|
||||
is(chatbar.selectedChat, null);
|
||||
|
||||
// We check the content gets an unload event as we close it.
|
||||
let promiseClosed = promiseOneEvent(chatbox.content, "unload", true);
|
||||
chatbox.close();
|
||||
yield promiseClosed;
|
||||
});
|
||||
|
||||
// In this case we open a chat minimized, then request the same chat again
|
||||
// without specifying minimized. On that second call the chat should open,
|
||||
// selected, and no longer minimized.
|
||||
add_chat_task(function* testMinimized() {
|
||||
let chatbox = yield promiseOpenChat("http://example.com", "minimized");
|
||||
Assert.strictEqual(chatbox, chatbar.selectedChat);
|
||||
Assert.ok(chatbox.minimized, "chat is minimized");
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
yield promiseOpenChat("http://example.com");
|
||||
Assert.ok(!chatbox.minimized, false, "chat is no longer minimized");
|
||||
});
|
||||
|
||||
// open enough chats to overflow the window, then check
|
||||
// if the menupopup is visible
|
||||
add_chat_task(function* testManyChats() {
|
||||
Assert.ok(chatbar.menupopup.parentNode.collapsed, "popup nub collapsed at start");
|
||||
// we should *never* find a test box that needs more than this to cause
|
||||
// an overflow!
|
||||
let maxToOpen = 20;
|
||||
let numOpened = 0;
|
||||
for (let i = 0; i < maxToOpen; i++) {
|
||||
yield promiseOpenChat("http://example.com#" + i);
|
||||
if (!chatbar.menupopup.parentNode.collapsed) {
|
||||
info("the menu popup appeared");
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
|
||||
});
|
||||
|
||||
// Check that closeAll works as expected.
|
||||
add_chat_task(function* testOpenTwiceCallbacks() {
|
||||
yield promiseOpenChat("http://example.com#1");
|
||||
yield promiseOpenChat("http://example.com#2");
|
||||
yield promiseOpenChat("http://test2.example.com");
|
||||
Assert.equal(numChatsInWindow(window), 3, "should be 3 chats open");
|
||||
Chat.closeAll("http://example.com");
|
||||
Assert.equal(numChatsInWindow(window), 1, "should have closed 2 chats");
|
||||
Chat.closeAll("http://test2.example.com");
|
||||
Assert.equal(numChatsInWindow(window), 0, "should have closed last chat");
|
||||
});
|
||||
|
||||
// Check that when we open the same chat twice, the callbacks are called back
|
||||
// twice.
|
||||
add_chat_task(function* testOpenTwiceCallbacks() {
|
||||
yield promiseOpenChatCallback("http://example.com");
|
||||
yield promiseOpenChatCallback("http://example.com");
|
||||
});
|
||||
|
||||
// Bug 817782 - check chats work in new top-level windows.
|
||||
add_chat_task(function* testSecondTopLevelWindow() {
|
||||
const chatUrl = "http://example.com";
|
||||
let secondWindow = OpenBrowserWindow();
|
||||
yield promiseOneEvent(secondWindow, "load");
|
||||
yield promiseOpenChat(chatUrl);
|
||||
// the chat was created - let's make sure it was created in the second window.
|
||||
Assert.equal(numChatsInWindow(window), 0, "main window has no chats");
|
||||
Assert.equal(numChatsInWindow(secondWindow), 1, "second window has 1 chat");
|
||||
secondWindow.close();
|
||||
});
|
||||
|
||||
// Test that chats are created in the correct window.
|
||||
add_chat_task(function* testChatWindowChooser() {
|
||||
let chat = yield promiseOpenChat("http://example.com");
|
||||
Assert.equal(numChatsInWindow(window), 1, "first window has the chat");
|
||||
// create a second window - this will be the "most recent" and will
|
||||
// therefore be the window that hosts the new chat (see bug 835111)
|
||||
let secondWindow = OpenBrowserWindow();
|
||||
yield promiseOneEvent(secondWindow, "load");
|
||||
Assert.equal(numChatsInWindow(secondWindow), 0, "second window starts with no chats");
|
||||
yield promiseOpenChat("http://example.com#2");
|
||||
Assert.equal(numChatsInWindow(secondWindow), 1, "second window now has chats");
|
||||
Assert.equal(numChatsInWindow(window), 1, "first window still has 1 chat");
|
||||
chat.close();
|
||||
Assert.equal(numChatsInWindow(window), 0, "first window now has no chats");
|
||||
// now open another chat - it should still open in the second.
|
||||
yield promiseOpenChat("http://example.com#3");
|
||||
Assert.equal(numChatsInWindow(window), 0, "first window still has no chats");
|
||||
Assert.equal(numChatsInWindow(secondWindow), 2, "second window has both chats");
|
||||
|
||||
// focus the first window, and open yet another chat - it
|
||||
// should open in the first window.
|
||||
window.focus();
|
||||
yield promiseWaitForFocus();
|
||||
chat = yield promiseOpenChat("http://example.com#4");
|
||||
Assert.equal(numChatsInWindow(window), 1, "first window got new chat");
|
||||
chat.close();
|
||||
Assert.equal(numChatsInWindow(window), 0, "first window has no chats");
|
||||
|
||||
let privateWindow = OpenBrowserWindow({private: true});
|
||||
yield promiseOneEvent(privateWindow, "load")
|
||||
|
||||
// open a last chat - the focused window can't accept
|
||||
// chats (it's a private window), so the chat should open
|
||||
// in the window that was selected before. This is known
|
||||
// to be broken on Linux.
|
||||
chat = yield promiseOpenChat("http://example.com#5");
|
||||
let os = Services.appinfo.OS;
|
||||
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
|
||||
let fn = BROKEN_WM_Z_ORDER ? todo : ok;
|
||||
fn(numChatsInWindow(window) == 1, "first window got the chat");
|
||||
chat.close();
|
||||
privateWindow.close();
|
||||
secondWindow.close();
|
||||
});
|
230
browser/base/content/test/chat/browser_focus.js
Normal file
230
browser/base/content/test/chat/browser_focus.js
Normal file
@ -0,0 +1,230 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Tests the focus functionality.
|
||||
|
||||
const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
|
||||
|
||||
// Is the currently opened tab focused?
|
||||
function isTabFocused() {
|
||||
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
|
||||
return Services.focus.focusedWindow == tabb.contentWindow;
|
||||
}
|
||||
|
||||
// Is the specified chat focused?
|
||||
function isChatFocused(chat) {
|
||||
return chat.chatbar._isChatFocused(chat);
|
||||
}
|
||||
|
||||
let chatbar = document.getElementById("pinnedchats");
|
||||
|
||||
function* setUp() {
|
||||
// Note that (probably) due to bug 604289, if a tab is focused but the
|
||||
// focused element is null, our chat windows can "steal" focus. This is
|
||||
// avoided if we explicitly focus an element in the tab.
|
||||
// So we load a page with an <input> field and focus that before testing.
|
||||
let html = '<input id="theinput"><button id="chat-opener"></button>';
|
||||
let url = "data:text/html;charset=utf-8," + encodeURI(html);
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
|
||||
yield promiseOneEvent(tab.linkedBrowser, "load", true);
|
||||
tab.linkedBrowser.contentDocument.getElementById("theinput").focus();
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
}
|
||||
|
||||
// Test default focus - not user input.
|
||||
add_chat_task(function* testDefaultFocus() {
|
||||
yield setUp();
|
||||
let chat = yield promiseOpenChat("http://example.com");
|
||||
// we used the default focus behaviour, which means that because this was
|
||||
// not the direct result of user action the chat should not be focused.
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
Assert.ok(isTabFocused(), "the tab should remain focused.");
|
||||
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
|
||||
});
|
||||
|
||||
// Test default focus via user input.
|
||||
add_chat_task(function* testDefaultFocus() {
|
||||
yield setUp();
|
||||
let tab = gBrowser.selectedTab;
|
||||
let deferred = Promise.defer();
|
||||
let button = tab.linkedBrowser.contentDocument.getElementById("chat-opener");
|
||||
button.addEventListener("click", function onclick() {
|
||||
button.removeEventListener("click", onclick);
|
||||
promiseOpenChat("http://example.com").then(
|
||||
chat => deferred.resolve(chat)
|
||||
);
|
||||
})
|
||||
// Note we must use synthesizeMouseAtCenter() rather than calling
|
||||
// .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
|
||||
// to be true.
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, button.ownerDocument.defaultView);
|
||||
let chat = yield deferred.promise;
|
||||
|
||||
// we use the default focus behaviour but the chat was opened via user input,
|
||||
// so the chat should be focused.
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
Assert.ok(!isTabFocused(), "the tab should have lost focus.");
|
||||
Assert.ok(isChatFocused(chat), "the chat should have got focus.");
|
||||
});
|
||||
|
||||
// We explicitly ask for the chat to be focused.
|
||||
add_chat_task(function* testExplicitFocus() {
|
||||
yield setUp();
|
||||
let chat = yield promiseOpenChat("http://example.com", undefined, true);
|
||||
// we use the default focus behaviour, which means that because this was
|
||||
// not the direct result of user action the chat should not be focused.
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
Assert.ok(!isTabFocused(), "the tab should have lost focus.");
|
||||
Assert.ok(isChatFocused(chat), "the chat should have got focus.");
|
||||
});
|
||||
|
||||
// Open a minimized chat via default focus behaviour - it will open and not
|
||||
// have focus. Then open the same chat without 'minimized' - it will be
|
||||
// restored but should still not have grabbed focus.
|
||||
add_chat_task(function* testNoFocusOnAutoRestore() {
|
||||
yield setUp();
|
||||
let chat = yield promiseOpenChat("http://example.com", "minimized");
|
||||
Assert.ok(chat.minimized, "chat is minimized");
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
Assert.ok(isTabFocused(), "the tab should remain focused.");
|
||||
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
|
||||
yield promiseOpenChat("http://example.com");
|
||||
Assert.ok(!chat.minimized, "chat should be restored");
|
||||
Assert.ok(isTabFocused(), "the tab should remain focused.");
|
||||
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
|
||||
});
|
||||
|
||||
// Here we open a chat, which will not be focused. Then we minimize it and
|
||||
// restore it via a titlebar clock - it should get focus at that point.
|
||||
add_chat_task(function* testFocusOnExplicitRestore() {
|
||||
yield setUp();
|
||||
let chat = yield promiseOpenChat("http://example.com");
|
||||
Assert.ok(!chat.minimized, "chat should have been opened restored");
|
||||
Assert.ok(isTabFocused(), "the tab should remain focused.");
|
||||
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
|
||||
chat.minimized = true;
|
||||
Assert.ok(isTabFocused(), "tab should still be focused");
|
||||
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
|
||||
|
||||
let promise = promiseOneEvent(chat.contentWindow, "focus");
|
||||
// pretend we clicked on the titlebar
|
||||
chat.onTitlebarClick({button: 0});
|
||||
yield promise; // wait for focus event.
|
||||
Assert.ok(!chat.minimized, "chat should have been restored");
|
||||
Assert.ok(isChatFocused(chat), "chat should be focused");
|
||||
Assert.strictEqual(chat, chatbar.selectedChat, "chat is marked selected");
|
||||
});
|
||||
|
||||
// Open 2 chats and give 1 focus. Minimize the focused one - the second
|
||||
// should get focus.
|
||||
add_chat_task(function* testMinimizeFocused() {
|
||||
yield setUp();
|
||||
let chat1 = yield promiseOpenChat("http://example.com#1");
|
||||
let chat2 = yield promiseOpenChat("http://example.com#2");
|
||||
Assert.equal(numChatsInWindow(window), 2, "2 chats open");
|
||||
Assert.strictEqual(chatbar.selectedChat, chat2, "chat2 is selected");
|
||||
let promise = promiseOneEvent(chat1.contentWindow, "focus");
|
||||
chatbar.selectedChat = chat1;
|
||||
chatbar.focus();
|
||||
yield promise; // wait for chat1 to get focus.
|
||||
Assert.strictEqual(chat1, chatbar.selectedChat, "chat1 is marked selected");
|
||||
Assert.notStrictEqual(chat2, chatbar.selectedChat, "chat2 is not marked selected");
|
||||
promise = promiseOneEvent(chat2.contentWindow, "focus");
|
||||
chat1.minimized = true;
|
||||
yield promise; // wait for chat2 to get focus.
|
||||
Assert.notStrictEqual(chat1, chatbar.selectedChat, "chat1 is not marked selected");
|
||||
Assert.strictEqual(chat2, chatbar.selectedChat, "chat2 is marked selected");
|
||||
});
|
||||
|
||||
// Open 2 chats, select and focus the second. Pressing the TAB key should
|
||||
// cause focus to move between all elements in our chat window before moving
|
||||
// to the next chat window.
|
||||
add_chat_task(function* testTab() {
|
||||
yield setUp();
|
||||
|
||||
function sendTabAndWaitForFocus(chat, eltid) {
|
||||
let doc = chat.contentDocument;
|
||||
EventUtils.sendKey("tab");
|
||||
// ideally we would use the 'focus' event here, but that doesn't work
|
||||
// as expected for the iframe - the iframe itself never gets the focus
|
||||
// event (apparently the sub-document etc does.)
|
||||
// So just poll for the correct element getting focus...
|
||||
let deferred = Promise.defer();
|
||||
let tries = 0;
|
||||
let interval = setInterval(function() {
|
||||
if (tries >= 30) {
|
||||
clearInterval(interval);
|
||||
deferred.reject("never got focus");
|
||||
return;
|
||||
}
|
||||
tries ++;
|
||||
let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
|
||||
if (doc.activeElement == elt) {
|
||||
clearInterval(interval);
|
||||
deferred.resolve();
|
||||
}
|
||||
info("retrying wait for focus: " + tries);
|
||||
info("(the active element is " + doc.activeElement + "/" + doc.activeElement.getAttribute("id") + ")");
|
||||
}, 100);
|
||||
info("waiting for element " + eltid + " to get focus");
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let chat1 = yield promiseOpenChat(CHAT_URL + "#1");
|
||||
let chat2 = yield promiseOpenChat(CHAT_URL + "#2");
|
||||
chatbar.selectedChat = chat2;
|
||||
let promise = promiseOneEvent(chat2.contentWindow, "focus");
|
||||
chatbar.focus();
|
||||
info("waiting for second chat to get focus");
|
||||
yield promise;
|
||||
|
||||
// Our chats have 3 focusable elements, so it takes 4 TABs to move
|
||||
// to the new chat.
|
||||
yield sendTabAndWaitForFocus(chat2, "input1");
|
||||
Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
|
||||
"first input field has focus");
|
||||
Assert.ok(isChatFocused(chat2), "new chat still focused after first tab");
|
||||
|
||||
yield sendTabAndWaitForFocus(chat2, "input2");
|
||||
Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
|
||||
Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
|
||||
"second input field has focus");
|
||||
|
||||
yield sendTabAndWaitForFocus(chat2, "iframe");
|
||||
Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
|
||||
Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "iframe",
|
||||
"iframe has focus");
|
||||
|
||||
// this tab now should move to the next chat, but focus the
|
||||
// document element itself (hence the null eltid)
|
||||
yield sendTabAndWaitForFocus(chat1, null);
|
||||
Assert.ok(isChatFocused(chat1), "first chat is focused");
|
||||
});
|
||||
|
||||
// Open a chat and focus an element other than the first. Move focus to some
|
||||
// other item (the tab itself in this case), then focus the chatbar - the
|
||||
// same element that was previously focused should still have focus.
|
||||
add_chat_task(function* testFocusedElement() {
|
||||
yield setUp();
|
||||
|
||||
// open a chat with focus requested.
|
||||
let chat = yield promiseOpenChat(CHAT_URL, undefined, true);
|
||||
|
||||
chat.contentDocument.getElementById("input2").focus();
|
||||
|
||||
// set focus to the tab.
|
||||
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
|
||||
let promise = promiseOneEvent(tabb.contentWindow, "focus");
|
||||
Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
|
||||
yield promise;
|
||||
|
||||
promise = promiseOneEvent(chat.contentWindow, "focus");
|
||||
chatbar.focus();
|
||||
yield promise;
|
||||
|
||||
Assert.equal(chat.contentDocument.activeElement.getAttribute("id"), "input2",
|
||||
"correct input field still has focus");
|
||||
});
|
128
browser/base/content/test/chat/browser_tearoff.js
Normal file
128
browser/base/content/test/chat/browser_tearoff.js
Normal file
@ -0,0 +1,128 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let chatbar = document.getElementById("pinnedchats");
|
||||
|
||||
function promiseNewWindowLoaded() {
|
||||
let deferred = Promise.defer();
|
||||
Services.wm.addListener({
|
||||
onWindowTitleChange: function() {},
|
||||
onCloseWindow: function(xulwindow) {},
|
||||
onOpenWindow: function(xulwindow) {
|
||||
var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
Services.wm.removeListener(this);
|
||||
// wait for load to ensure the window is ready for us to test
|
||||
domwindow.addEventListener("load", function _load(event) {
|
||||
let doc = domwindow.document;
|
||||
if (event.target != doc)
|
||||
return;
|
||||
domwindow.removeEventListener("load", _load);
|
||||
deferred.resolve(domwindow);
|
||||
});
|
||||
},
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_chat_task(function* testTearoffChat() {
|
||||
let chatbox = yield promiseOpenChat("http://example.com");
|
||||
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
|
||||
|
||||
let chatDoc = chatbox.contentDocument;
|
||||
let chatTitle = chatDoc.title;
|
||||
|
||||
Assert.equal(chatbox.getAttribute("label"), chatTitle,
|
||||
"the new chatbox should show the title of the chat window");
|
||||
|
||||
// mutate the chat document a bit before we tear it off.
|
||||
let div = chatDoc.createElement("div");
|
||||
div.setAttribute("id", "testdiv");
|
||||
div.setAttribute("test", "1");
|
||||
chatDoc.body.appendChild(div);
|
||||
|
||||
// chatbox is open, lets detach. The new chat window will be caught in
|
||||
// the window watcher below
|
||||
let promise = promiseNewWindowLoaded();
|
||||
|
||||
let swap = document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
|
||||
swap.click();
|
||||
|
||||
// and wait for the new window.
|
||||
let domwindow = yield promise;
|
||||
|
||||
Assert.equal(domwindow.document.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
|
||||
Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
|
||||
|
||||
// get the chatbox from the new window.
|
||||
chatbox = domwindow.document.getElementById("chatter")
|
||||
Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
|
||||
|
||||
div = chatbox.contentDocument.getElementById("testdiv");
|
||||
Assert.equal(div.getAttribute("test"), "1", "docshell should have been swapped");
|
||||
div.setAttribute("test", "2");
|
||||
|
||||
// swap the window back to the chatbar
|
||||
promise = promiseOneEvent(domwindow, "unload");
|
||||
swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
|
||||
swap.click();
|
||||
|
||||
yield promise;
|
||||
|
||||
Assert.equal(numChatsInWindow(window), 1, "chat should be docked back in the window");
|
||||
chatbox = chatbar.selectedChat;
|
||||
Assert.equal(chatbox.getAttribute("label"), chatTitle,
|
||||
"the new chatbox should show the title of the chat window again");
|
||||
|
||||
div = chatbox.contentDocument.getElementById("testdiv");
|
||||
Assert.equal(div.getAttribute("test"), "2", "docshell should have been swapped");
|
||||
});
|
||||
|
||||
// Similar test but with 2 chats.
|
||||
add_chat_task(function* testReattachTwice() {
|
||||
let chatbox1 = yield promiseOpenChat("http://example.com#1");
|
||||
let chatbox2 = yield promiseOpenChat("http://example.com#2");
|
||||
Assert.equal(numChatsInWindow(window), 2, "both chats should be docked in the window");
|
||||
|
||||
info("chatboxes are open, detach from window");
|
||||
let promise = promiseNewWindowLoaded();
|
||||
document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
|
||||
let domwindow1 = yield promise;
|
||||
chatbox1 = domwindow1.document.getElementById("chatter");
|
||||
Assert.equal(numChatsInWindow(window), 1, "only second chat should be docked in the window");
|
||||
|
||||
promise = promiseNewWindowLoaded();
|
||||
document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
|
||||
let domwindow2 = yield promise;
|
||||
chatbox2 = domwindow2.document.getElementById("chatter");
|
||||
Assert.equal(numChatsInWindow(window), 0, "should be no docked chats");
|
||||
|
||||
promise = promiseOneEvent(domwindow2, "unload");
|
||||
domwindow2.document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
|
||||
yield promise;
|
||||
Assert.equal(numChatsInWindow(window), 1, "one chat should be docked back in the window");
|
||||
|
||||
promise = promiseOneEvent(domwindow1, "unload");
|
||||
domwindow1.document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
|
||||
yield promise;
|
||||
Assert.equal(numChatsInWindow(window), 2, "both chats should be docked back in the window");
|
||||
});
|
||||
|
||||
// Check that Chat.closeAll() also closes detached windows.
|
||||
add_chat_task(function* testCloseAll() {
|
||||
let chatbox1 = yield promiseOpenChat("http://example.com#1");
|
||||
let chatbox2 = yield promiseOpenChat("http://example.com#2");
|
||||
|
||||
let promise = promiseNewWindowLoaded();
|
||||
document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
|
||||
let domwindow = yield promise;
|
||||
chatbox1 = domwindow.document.getElementById("chatter");
|
||||
|
||||
let promiseWindowUnload = promiseOneEvent(domwindow, "unload");
|
||||
|
||||
Assert.equal(numChatsInWindow(window), 1, "second chat should still be docked");
|
||||
Chat.closeAll("http://example.com");
|
||||
yield promiseWindowUnload;
|
||||
Assert.equal(numChatsInWindow(window), 0, "should be no chats left");
|
||||
});
|
14
browser/base/content/test/chat/chat.html
Normal file
14
browser/base/content/test/chat/chat.html
Normal file
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>test chat window</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>This is a test chat window.</p>
|
||||
<!-- a couple of input fields to help with focus testing -->
|
||||
<input id="input1"/>
|
||||
<input id="input2"/>
|
||||
<!-- an iframe here so this one page generates multiple load events -->
|
||||
<iframe id="iframe" src="data:text/plain:this is an iframe"></iframe>
|
||||
</body>
|
||||
</html>
|
91
browser/base/content/test/chat/head.js
Normal file
91
browser/base/content/test/chat/head.js
Normal file
@ -0,0 +1,91 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Utility functions for Chat tests.
|
||||
|
||||
let Chat = Cu.import("resource:///modules/Chat.jsm", {}).Chat;
|
||||
|
||||
function promiseOpenChat(url, mode, focus) {
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
let origin = uri.prePath;
|
||||
let title = origin;
|
||||
let deferred = Promise.defer();
|
||||
// we just through a few hoops to ensure the content document is fully
|
||||
// loaded, otherwise tests that rely on that content may intermittently fail.
|
||||
let callback = function(chatbox) {
|
||||
if (chatbox.contentDocument.readyState == "complete") {
|
||||
// already loaded.
|
||||
deferred.resolve(chatbox);
|
||||
return;
|
||||
}
|
||||
chatbox.addEventListener("load", function onload(event) {
|
||||
if (event.target != chatbox.contentDocument || chatbox.contentDocument.location.href == "about:blank") {
|
||||
return;
|
||||
}
|
||||
chatbox.removeEventListener("load", onload, true);
|
||||
deferred.resolve(chatbox);
|
||||
}, true);
|
||||
}
|
||||
let chatbox = Chat.open(null, origin, title, url, mode, focus, callback);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Opens a chat, returns a promise resolved when the chat callback fired.
|
||||
function promiseOpenChatCallback(url, mode) {
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
let origin = uri.prePath;
|
||||
let title = origin;
|
||||
let deferred = Promise.defer();
|
||||
let callback = deferred.resolve;
|
||||
Chat.open(null, origin, title, url, mode, undefined, callback);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Opens a chat, returns the chat window's promise which fires when the chat
|
||||
// starts loading.
|
||||
function promiseOneEvent(target, eventName, capture) {
|
||||
let deferred = Promise.defer();
|
||||
target.addEventListener(eventName, function handler(event) {
|
||||
target.removeEventListener(eventName, handler, capture);
|
||||
deferred.resolve();
|
||||
}, capture);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Return the number of chats in a browser window.
|
||||
function numChatsInWindow(win) {
|
||||
let chatbar = win.document.getElementById("pinnedchats");
|
||||
return chatbar.childElementCount;
|
||||
}
|
||||
|
||||
function promiseWaitForFocus() {
|
||||
let deferred = Promise.defer();
|
||||
waitForFocus(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// A simple way to clean up after each test.
|
||||
function add_chat_task(genFunction) {
|
||||
add_task(function* () {
|
||||
info("Starting chat test " + genFunction.name);
|
||||
try {
|
||||
yield genFunction();
|
||||
} finally {
|
||||
info("Finished chat test " + genFunction.name + " - cleaning up.");
|
||||
// close all docked chats.
|
||||
while (chatbar.childNodes.length) {
|
||||
chatbar.childNodes[0].close();
|
||||
}
|
||||
// and non-docked chats.
|
||||
let winEnum = Services.wm.getEnumerator("Social:Chat");
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let win = winEnum.getNext();
|
||||
if (win.closed) {
|
||||
continue;
|
||||
}
|
||||
win.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -26,7 +26,6 @@ support-files =
|
||||
|
||||
[browser_addons.js]
|
||||
[browser_blocklist.js]
|
||||
[browser_chat_tearoff.js]
|
||||
[browser_defaults.js]
|
||||
[browser_share.js]
|
||||
[browser_social_activation.js]
|
||||
|
@ -1,308 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2); // only debug builds seem to need more time...
|
||||
waitForExplicitFinish();
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
|
||||
};
|
||||
|
||||
let postSubTest = function(cb) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
ok(chats.children.length == 0, "no chatty children left behind");
|
||||
cb();
|
||||
};
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
SocialSidebar.show();
|
||||
ok(SocialSidebar.provider, "sidebar provider exists");
|
||||
runSocialTests(tests, undefined, postSubTest, function() {
|
||||
finishcb();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var tests = {
|
||||
testTearoffChat: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let chatTitle;
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-sidebar-message":
|
||||
port.postMessage({topic: "test-chatbox-open"});
|
||||
break;
|
||||
case "got-chatbox-visibility":
|
||||
// chatbox is open, lets detach. The new chat window will be caught in
|
||||
// the window watcher below
|
||||
let doc = chats.selectedChat.contentDocument;
|
||||
// This message is (sometimes!) received a second time
|
||||
// before we start our tests from the onCloseWindow
|
||||
// callback.
|
||||
if (doc.location == "about:blank")
|
||||
return;
|
||||
chatTitle = doc.title;
|
||||
ok(chats.selectedChat.getAttribute("label") == chatTitle,
|
||||
"the new chatbox should show the title of the chat window");
|
||||
let div = doc.createElement("div");
|
||||
div.setAttribute("id", "testdiv");
|
||||
div.setAttribute("test", "1");
|
||||
doc.body.appendChild(div);
|
||||
let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap");
|
||||
swap.click();
|
||||
port.close();
|
||||
break;
|
||||
case "got-chatbox-message":
|
||||
ok(true, "got chatbox message");
|
||||
ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
|
||||
chats.selectedChat.toggle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Services.wm.addListener({
|
||||
onWindowTitleChange: function() {},
|
||||
onCloseWindow: function(xulwindow) {},
|
||||
onOpenWindow: function(xulwindow) {
|
||||
var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
Services.wm.removeListener(this);
|
||||
// wait for load to ensure the window is ready for us to test
|
||||
domwindow.addEventListener("load", function _load(event) {
|
||||
let doc = domwindow.document;
|
||||
if (event.target != doc)
|
||||
return;
|
||||
|
||||
domwindow.removeEventListener("load", _load, false);
|
||||
|
||||
domwindow.addEventListener("unload", function _close(event) {
|
||||
if (event.target != doc)
|
||||
return;
|
||||
domwindow.removeEventListener("unload", _close, false);
|
||||
info("window has been closed");
|
||||
waitForCondition(function() {
|
||||
return chats.selectedChat && chats.selectedChat.contentDocument &&
|
||||
chats.selectedChat.contentDocument.readyState == "complete";
|
||||
},function () {
|
||||
ok(chats.selectedChat, "should have a chatbox in our window again");
|
||||
ok(chats.selectedChat.getAttribute("label") == chatTitle,
|
||||
"the new chatbox should show the title of the chat window again");
|
||||
let testdiv = chats.selectedChat.contentDocument.getElementById("testdiv");
|
||||
is(testdiv.getAttribute("test"), "2", "docshell should have been swapped");
|
||||
chats.selectedChat.close();
|
||||
waitForCondition(function() {
|
||||
return chats.children.length == 0;
|
||||
},function () {
|
||||
next();
|
||||
});
|
||||
});
|
||||
}, false);
|
||||
|
||||
is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
|
||||
// window is loaded, but the docswap does not happen until after load,
|
||||
// and we have no event to wait on, so we'll wait for document state
|
||||
// to be ready
|
||||
let chatbox = doc.getElementById("chatter");
|
||||
waitForCondition(function() {
|
||||
return chats.selectedChat == null &&
|
||||
chatbox.contentDocument &&
|
||||
chatbox.contentDocument.readyState == "complete";
|
||||
},function() {
|
||||
ok(chatbox.getAttribute("label") == chatTitle,
|
||||
"detached window should show the title of the chat window");
|
||||
let testdiv = chatbox.contentDocument.getElementById("testdiv");
|
||||
is(testdiv.getAttribute("test"), "1", "docshell should have been swapped");
|
||||
testdiv.setAttribute("test", "2");
|
||||
// swap the window back to the chatbar
|
||||
let swap = doc.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
|
||||
swap.click();
|
||||
}, domwindow);
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
port.postMessage({topic: "test-init", data: { id: 1 }});
|
||||
},
|
||||
|
||||
testCloseOnLogout: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
port.postMessage({topic: "test-init"});
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-chatbox-visibility":
|
||||
// chatbox is open, lets detach. The new chat window will be caught in
|
||||
// the window watcher below
|
||||
let doc = chats.selectedChat.contentDocument;
|
||||
// This message is (sometimes!) received a second time
|
||||
// before we start our tests from the onCloseWindow
|
||||
// callback.
|
||||
if (doc.location == "about:blank")
|
||||
return;
|
||||
info("chatbox is open, detach from window");
|
||||
let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap");
|
||||
swap.click();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Services.wm.addListener({
|
||||
onWindowTitleChange: function() {},
|
||||
onCloseWindow: function(xulwindow) {},
|
||||
onOpenWindow: function(xulwindow) {
|
||||
let domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
Services.wm.removeListener(this);
|
||||
// wait for load to ensure the window is ready for us to test, make sure
|
||||
// we're not getting called for about:blank
|
||||
domwindow.addEventListener("load", function _load(event) {
|
||||
let doc = domwindow.document;
|
||||
if (event.target != doc)
|
||||
return;
|
||||
domwindow.removeEventListener("load", _load, false);
|
||||
|
||||
domwindow.addEventListener("unload", function _close(event) {
|
||||
if (event.target != doc)
|
||||
return;
|
||||
domwindow.removeEventListener("unload", _close, false);
|
||||
ok(true, "window has been closed");
|
||||
next();
|
||||
}, false);
|
||||
|
||||
is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
|
||||
// window is loaded, but the docswap does not happen until after load,
|
||||
// and we have no event to wait on, so we'll wait for document state
|
||||
// to be ready
|
||||
let chatbox = doc.getElementById("chatter");
|
||||
waitForCondition(function() {
|
||||
return chats.children.length == 0 &&
|
||||
chatbox.contentDocument &&
|
||||
chatbox.contentDocument.readyState == "complete";
|
||||
},function() {
|
||||
// logout, we should get unload next
|
||||
port.postMessage({topic: "test-logout"});
|
||||
port.close();
|
||||
}, domwindow);
|
||||
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
port.postMessage({topic: "test-worker-chat", data: chatUrl});
|
||||
},
|
||||
|
||||
testReattachTwice: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
let chatBoxCount = 0, reattachCount = 0;
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
port.postMessage({topic: "test-init"});
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-chatbox-visibility":
|
||||
// chatbox is open, lets detach. The new chat window will be caught in
|
||||
// the window watcher below
|
||||
let doc = chats.selectedChat.contentDocument;
|
||||
// This message is (sometimes!) received a second time
|
||||
// before we start our tests from the onCloseWindow
|
||||
// callback.
|
||||
if (doc.location == "about:blank")
|
||||
return;
|
||||
if (++chatBoxCount != 2) {
|
||||
// open the second chat window
|
||||
port.postMessage({topic: "test-worker-chat", data: chatUrl + "?id=2"});
|
||||
return;
|
||||
}
|
||||
info("chatbox is open, detach from window");
|
||||
let chat1 = chats.firstChild;
|
||||
let chat2 = chat1.nextSibling;
|
||||
document.getAnonymousElementByAttribute(chat1, "anonid", "swap").click();
|
||||
document.getAnonymousElementByAttribute(chat2, "anonid", "swap").click();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let firstChatWindowDoc;
|
||||
Services.wm.addListener({
|
||||
onWindowTitleChange: function() {},
|
||||
onCloseWindow: function(xulwindow) {},
|
||||
onOpenWindow: function(xulwindow) {
|
||||
let listener = this;
|
||||
let domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
// wait for load to ensure the window is ready for us to test, make sure
|
||||
// we're not getting called for about:blank
|
||||
domwindow.addEventListener("load", function _load(event) {
|
||||
let doc = domwindow.document;
|
||||
if (event.target != doc)
|
||||
return;
|
||||
domwindow.removeEventListener("load", _load, false);
|
||||
|
||||
domwindow.addEventListener("unload", function _close(event) {
|
||||
if (event.target != doc)
|
||||
return;
|
||||
domwindow.removeEventListener("unload", _close, false);
|
||||
ok(true, "window has been closed");
|
||||
waitForCondition(function() {
|
||||
return chats.selectedChat && chats.selectedChat.contentDocument &&
|
||||
chats.selectedChat.contentDocument.readyState == "complete";
|
||||
}, function () {
|
||||
++reattachCount;
|
||||
if (reattachCount == 1) {
|
||||
info("reattaching second chat window");
|
||||
let chatbox = firstChatWindowDoc.getElementById("chatter");
|
||||
firstChatWindowDoc.getAnonymousElementByAttribute(chatbox, "anonid", "swap").click();
|
||||
firstChatWindowDoc = null;
|
||||
}
|
||||
else if (reattachCount == 2) {
|
||||
is(chats.children.length, 2, "both chat windows should be reattached");
|
||||
chats.removeAll();
|
||||
waitForCondition(() => chats.children.length == 0, function () {
|
||||
info("no chat window left");
|
||||
is(chats.chatboxForURL.size, 0, "chatboxForURL map should be empty");
|
||||
next();
|
||||
});
|
||||
}
|
||||
}, "waited too long for the window to reattach");
|
||||
}, false);
|
||||
|
||||
is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
|
||||
if (!firstChatWindowDoc) {
|
||||
firstChatWindowDoc = doc;
|
||||
return;
|
||||
}
|
||||
Services.wm.removeListener(listener);
|
||||
|
||||
// window is loaded, but the docswap does not happen until after load,
|
||||
// and we have no event to wait on, so we'll wait for document state
|
||||
// to be ready
|
||||
let chatbox = doc.getElementById("chatter");
|
||||
waitForCondition(function() {
|
||||
return chats.children.length == 0 &&
|
||||
chatbox.contentDocument &&
|
||||
chatbox.contentDocument.readyState == "complete";
|
||||
},function() {
|
||||
info("reattaching chat window");
|
||||
doc.getAnonymousElementByAttribute(chatbox, "anonid", "swap").click();
|
||||
}, "waited too long for the chat window to be detached");
|
||||
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
port.postMessage({topic: "test-worker-chat", data: chatUrl + "?id=1"});
|
||||
}
|
||||
};
|
@ -44,6 +44,10 @@ function openChat(provider, callback) {
|
||||
gURLsNotRemembered.push(url);
|
||||
}
|
||||
|
||||
function windowHasChats(win) {
|
||||
return !!getChatBar().firstElementChild;
|
||||
}
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2); // only debug builds seem to need more time...
|
||||
waitForExplicitFinish();
|
||||
@ -107,85 +111,6 @@ var tests = {
|
||||
}
|
||||
port.postMessage({topic: "test-init", data: { id: 1 }});
|
||||
},
|
||||
testOpenMinimized: function(next) {
|
||||
// In this case the sidebar opens a chat (without specifying minimized).
|
||||
// We then minimize it and have the sidebar reopen the chat (again without
|
||||
// minimized). On that second call the chat should open and no longer
|
||||
// be minimized.
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
let seen_opened = false;
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "test-init-done":
|
||||
port.postMessage({topic: "test-chatbox-open"});
|
||||
break;
|
||||
case "chatbox-opened":
|
||||
is(e.data.result, "ok", "the sidebar says it got a chatbox");
|
||||
if (!seen_opened) {
|
||||
// first time we got the opened message, so minimize the chat then
|
||||
// re-request the same chat to be opened - we should get the
|
||||
// message again and the chat should be restored.
|
||||
ok(!chats.selectedChat.minimized, "chat not initially minimized")
|
||||
chats.selectedChat.minimized = true
|
||||
seen_opened = true;
|
||||
port.postMessage({topic: "test-chatbox-open"});
|
||||
} else {
|
||||
// This is the second time we've seen this message - there should
|
||||
// be exactly 1 chat open and it should no longer be minimized.
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
ok(!chats.selectedChat.minimized, "chat no longer minimized")
|
||||
chats.selectedChat.close();
|
||||
is(chats.selectedChat, null, "should only have been one chat open");
|
||||
port.close();
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "test-init", data: { id: 1 }});
|
||||
},
|
||||
testManyChats: function(next) {
|
||||
// open enough chats to overflow the window, then check
|
||||
// if the menupopup is visible
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
ok(port, "provider has a port");
|
||||
ok(chats.menupopup.parentNode.collapsed, "popup nub collapsed at start");
|
||||
port.postMessage({topic: "test-init"});
|
||||
// we should *never* find a test box that needs more than this to cause
|
||||
// an overflow!
|
||||
let maxToOpen = 20;
|
||||
let numOpened = 0;
|
||||
let maybeOpenAnother = function() {
|
||||
if (numOpened++ >= maxToOpen) {
|
||||
ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
|
||||
closeAllChats();
|
||||
next();
|
||||
}
|
||||
port.postMessage({topic: "test-chatbox-open", data: { id: numOpened }});
|
||||
}
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-chatbox-message":
|
||||
if (!chats.menupopup.parentNode.collapsed) {
|
||||
maybeOpenAnother();
|
||||
break;
|
||||
}
|
||||
ok(true, "popup nub became visible");
|
||||
// close our chats now
|
||||
while (chats.selectedChat) {
|
||||
chats.selectedChat.close();
|
||||
}
|
||||
ok(!chats.selectedChat, "chats are all closed");
|
||||
port.close();
|
||||
next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
maybeOpenAnother();
|
||||
},
|
||||
testWorkerChatWindow: function(next) {
|
||||
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
@ -239,61 +164,10 @@ var tests = {
|
||||
}
|
||||
port.postMessage({topic: "test-init", data: { id: 1 }});
|
||||
},
|
||||
testSameChatCallbacks: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
let seen_opened = false;
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "test-init-done":
|
||||
port.postMessage({topic: "test-chatbox-open"});
|
||||
break;
|
||||
case "chatbox-opened":
|
||||
is(e.data.result, "ok", "the sidebar says it got a chatbox");
|
||||
if (seen_opened) {
|
||||
// This is the second time we've seen this message - there should
|
||||
// be exactly 1 chat open.
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
chats.selectedChat.close();
|
||||
is(chats.selectedChat, null, "should only have been one chat open");
|
||||
port.close();
|
||||
next();
|
||||
} else {
|
||||
// first time we got the opened message, so re-request the same
|
||||
// chat to be opened - we should get the message again.
|
||||
seen_opened = true;
|
||||
port.postMessage({topic: "test-chatbox-open"});
|
||||
}
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "test-init", data: { id: 1 }});
|
||||
},
|
||||
|
||||
// check removeAll does the right thing
|
||||
testRemoveAll: function(next, mode) {
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
port.postMessage({topic: "test-init"});
|
||||
get3ChatsForCollapsing(mode || "normal", function() {
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
chatbar.removeAll();
|
||||
// should be no evidence of any chats left.
|
||||
is(chatbar.childNodes.length, 0, "should be no chats left");
|
||||
checkPopup();
|
||||
is(chatbar.selectedChat, null, "nothing should be selected");
|
||||
is(chatbar.chatboxForURL.size, 0, "chatboxForURL map should be empty");
|
||||
port.close();
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
testRemoveAllMinimized: function(next) {
|
||||
this.testRemoveAll(next, "minimized");
|
||||
},
|
||||
|
||||
// Check what happens when you close the only visible chat.
|
||||
testCloseOnlyVisible: function(next) {
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
let chatbar = getChatBar();
|
||||
let chatWidth = undefined;
|
||||
let num = 0;
|
||||
is(chatbar.childNodes.length, 0, "chatbar starting empty");
|
||||
@ -331,7 +205,7 @@ var tests = {
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
port.postMessage({topic: "test-init"});
|
||||
get3ChatsForCollapsing("normal", function(first, second, third) {
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
let chatbar = getChatBar();
|
||||
chatbar.showChat(first);
|
||||
ok(!first.collapsed, "first should no longer be collapsed");
|
||||
ok(second.collapsed || third.collapsed, false, "one of the others should be collapsed");
|
||||
@ -341,61 +215,6 @@ var tests = {
|
||||
});
|
||||
},
|
||||
|
||||
testActivity: function(next) {
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
port.postMessage({topic: "test-init"});
|
||||
get3ChatsForCollapsing("normal", function(first, second, third) {
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
is(chatbar.selectedChat, third, "third chat should be selected");
|
||||
ok(!chatbar.selectedChat.hasAttribute("activity"), "third chat should have no activity");
|
||||
// send an activity message to the second.
|
||||
ok(!second.hasAttribute("activity"), "second chat should have no activity");
|
||||
let chat2 = second.content;
|
||||
let evt = chat2.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent("socialChatActivity", true, true, {});
|
||||
chat2.contentDocument.documentElement.dispatchEvent(evt);
|
||||
// second should have activity.
|
||||
ok(second.hasAttribute("activity"), "second chat should now have activity");
|
||||
// select the second - it should lose "activity"
|
||||
chatbar.selectedChat = second;
|
||||
ok(!second.hasAttribute("activity"), "second chat should no longer have activity");
|
||||
// Now try the first - it is collapsed, so the 'nub' also gets activity attr.
|
||||
ok(!first.hasAttribute("activity"), "first chat should have no activity");
|
||||
let chat1 = first.content;
|
||||
let evt = chat1.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent("socialChatActivity", true, true, {});
|
||||
chat1.contentDocument.documentElement.dispatchEvent(evt);
|
||||
ok(first.hasAttribute("activity"), "first chat should now have activity");
|
||||
ok(chatbar.nub.hasAttribute("activity"), "nub should also have activity");
|
||||
// first is collapsed, so use openChat to get it.
|
||||
chatbar.openChat(SocialSidebar.provider, first.getAttribute("src"));
|
||||
ok(!first.hasAttribute("activity"), "first chat should no longer have activity");
|
||||
// The nub should lose the activity flag here too
|
||||
todo(!chatbar.nub.hasAttribute("activity"), "Bug 806266 - nub should no longer have activity");
|
||||
// TODO: tests for bug 806266 should arrange to have 2 chats collapsed
|
||||
// then open them checking the nub is updated correctly.
|
||||
// Now we will go and change the embedded browser in the second chat and
|
||||
// ensure the activity magic still works (ie, check that the unload for
|
||||
// the browser didn't cause our event handlers to be removed.)
|
||||
ok(!second.hasAttribute("activity"), "second chat should have no activity");
|
||||
let subiframe = chat2.contentDocument.getElementById("iframe");
|
||||
subiframe.contentWindow.addEventListener("unload", function subunload() {
|
||||
subiframe.contentWindow.removeEventListener("unload", subunload);
|
||||
// ensure all other unload listeners have fired.
|
||||
executeSoon(function() {
|
||||
let evt = chat2.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent("socialChatActivity", true, true, {});
|
||||
chat2.contentDocument.documentElement.dispatchEvent(evt);
|
||||
ok(second.hasAttribute("activity"), "second chat still has activity after unloading sub-iframe");
|
||||
closeAllChats();
|
||||
port.close();
|
||||
next();
|
||||
})
|
||||
})
|
||||
subiframe.setAttribute("src", "data:text/plain:new location for iframe");
|
||||
});
|
||||
},
|
||||
|
||||
testOnlyOneCallback: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
@ -413,7 +232,7 @@ var tests = {
|
||||
case "pong":
|
||||
executeSoon(function() {
|
||||
is(numOpened, 1, "only got one open message");
|
||||
chats.removeAll();
|
||||
chats.selectedChat.close();
|
||||
port.close();
|
||||
next();
|
||||
});
|
||||
@ -422,86 +241,6 @@ var tests = {
|
||||
port.postMessage({topic: "test-init", data: { id: 1 }});
|
||||
},
|
||||
|
||||
testSecondTopLevelWindow: function(next) {
|
||||
// Bug 817782 - check chats work in new top-level windows.
|
||||
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
let secondWindow;
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "test-init-done") {
|
||||
secondWindow = OpenBrowserWindow();
|
||||
secondWindow.addEventListener("load", function loadListener() {
|
||||
secondWindow.removeEventListener("load", loadListener);
|
||||
port.postMessage({topic: "test-worker-chat", data: chatUrl});
|
||||
});
|
||||
} else if (e.data.topic == "got-chatbox-message") {
|
||||
// the chat was created - let's make sure it was created in the second window.
|
||||
is(secondWindow.SocialChatBar.chatbar.childElementCount, 1);
|
||||
secondWindow.close();
|
||||
next();
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "test-init"});
|
||||
},
|
||||
|
||||
testChatWindowChooser: function(next) {
|
||||
// Tests that when a worker creates a chat, it is opened in the correct
|
||||
// window.
|
||||
// open a chat (it will open in the main window)
|
||||
ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
|
||||
openChat(SocialSidebar.provider, function() {
|
||||
ok(window.SocialChatBar.hasChats, "first window has the chat");
|
||||
// create a second window - this will be the "most recent" and will
|
||||
// therefore be the window that hosts the new chat (see bug 835111)
|
||||
let secondWindow = OpenBrowserWindow();
|
||||
secondWindow.addEventListener("load", function loadListener() {
|
||||
secondWindow.removeEventListener("load", loadListener);
|
||||
ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
|
||||
openChat(SocialSidebar.provider, function() {
|
||||
ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
|
||||
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
|
||||
window.SocialChatBar.chatbar.removeAll();
|
||||
// now open another chat - it should still open in the second.
|
||||
openChat(SocialSidebar.provider, function() {
|
||||
ok(!window.SocialChatBar.hasChats, "first window has no chats");
|
||||
ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
|
||||
|
||||
// focus the first window, and open yet another chat - it
|
||||
// should open in the first window.
|
||||
waitForFocus(function() {
|
||||
openChat(SocialSidebar.provider, function() {
|
||||
ok(window.SocialChatBar.hasChats, "first window has chats");
|
||||
window.SocialChatBar.chatbar.removeAll();
|
||||
ok(!window.SocialChatBar.hasChats, "first window has no chats");
|
||||
|
||||
let privateWindow = OpenBrowserWindow({private: true});
|
||||
privateWindow.addEventListener("load", function loadListener() {
|
||||
privateWindow.removeEventListener("load", loadListener);
|
||||
|
||||
// open a last chat - the focused window can't accept
|
||||
// chats (it's a private window), so the chat should open
|
||||
// in the window that was selected before. This is known
|
||||
// to be broken on Linux.
|
||||
openChat(SocialSidebar.provider, function() {
|
||||
let os = Services.appinfo.OS;
|
||||
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
|
||||
let fn = BROKEN_WM_Z_ORDER ? todo : ok;
|
||||
fn(window.SocialChatBar.hasChats, "first window has a chat");
|
||||
window.SocialChatBar.chatbar.removeAll();
|
||||
|
||||
privateWindow.close();
|
||||
secondWindow.close();
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
window.focus();
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
},
|
||||
testMultipleProviderChat: function(next) {
|
||||
// test incomming chats from all providers
|
||||
openChat(Social.providers[0], function() {
|
||||
@ -517,7 +256,7 @@ var tests = {
|
||||
port.postMessage({topic: "test-logout"});
|
||||
waitForCondition(function() chats.children.length == Social.providers.length - 1,
|
||||
function() {
|
||||
chats.removeAll();
|
||||
closeAllChats();
|
||||
waitForCondition(function() chats.children.length == 0,
|
||||
function() {
|
||||
ok(!chats.selectedChat, "multiprovider chats are all closed");
|
||||
|
@ -9,7 +9,7 @@ function isTabFocused() {
|
||||
}
|
||||
|
||||
function isChatFocused(chat) {
|
||||
return SocialChatBar.chatbar._isChatFocused(chat);
|
||||
return getChatBar()._isChatFocused(chat);
|
||||
}
|
||||
|
||||
function openChatViaUser() {
|
||||
@ -32,7 +32,7 @@ function openChatViaSidebarMessage(port, data, callback) {
|
||||
function openChatViaWorkerMessage(port, data, callback) {
|
||||
// sadly there is no message coming back to tell us when the chat has
|
||||
// been opened, so we wait until one appears.
|
||||
let chatbar = SocialChatBar.chatbar;
|
||||
let chatbar = getChatBar();
|
||||
let numExpected = chatbar.childElementCount + 1;
|
||||
port.postMessage({topic: "test-worker-chat", data: data});
|
||||
waitForCondition(function() chatbar.childElementCount == numExpected,
|
||||
@ -40,12 +40,13 @@ function openChatViaWorkerMessage(port, data, callback) {
|
||||
// so the child has been added, but we don't know if it
|
||||
// has been intialized - re-request it and the callback
|
||||
// means it's done. Minimized, same as the worker.
|
||||
SocialChatBar.openChat(SocialSidebar.provider,
|
||||
data,
|
||||
function() {
|
||||
callback();
|
||||
},
|
||||
"minimized");
|
||||
chatbar.openChat(SocialSidebar.provider.origin,
|
||||
SocialSidebar.provider.name,
|
||||
data,
|
||||
"minimized",
|
||||
function() {
|
||||
callback();
|
||||
});
|
||||
},
|
||||
"No new chat appeared");
|
||||
}
|
||||
@ -109,7 +110,7 @@ function test() {
|
||||
waitForCondition(function() isTabFocused(), cb, "tab should have focus");
|
||||
}
|
||||
let postSubTest = function(cb) {
|
||||
window.SocialChatBar.chatbar.removeAll();
|
||||
closeAllChats();
|
||||
cb();
|
||||
}
|
||||
// and run the tests.
|
||||
@ -132,21 +133,22 @@ var tests = {
|
||||
// Then we do it again - should still not be focused.
|
||||
// Then we perform a user-initiated request - it should get focus.
|
||||
testNoFocusWhenViaWorker: function(next) {
|
||||
let chatbar = getChatBar();
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
|
||||
ok(true, "got chatbox message");
|
||||
is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open");
|
||||
is(chatbar.childElementCount, 1, "exactly 1 chat open");
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
// re-request the same chat via a message.
|
||||
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
|
||||
is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open");
|
||||
is(chatbar.childElementCount, 1, "still exactly 1 chat open");
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
// re-request the same chat via user event.
|
||||
openChatViaUser();
|
||||
waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
|
||||
waitForCondition(function() isChatFocused(chatbar.selectedChat),
|
||||
function() {
|
||||
is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open");
|
||||
is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat should be selected");
|
||||
is(chatbar.childElementCount, 1, "still exactly 1 chat open");
|
||||
is(chatbar.selectedChat, chatbar.firstElementChild, "chat should be selected");
|
||||
next();
|
||||
}, "chat should be focused");
|
||||
});
|
||||
@ -158,204 +160,14 @@ var tests = {
|
||||
// click. This should cause the new chat to be opened and focused.
|
||||
testFocusWhenViaUser: function(next) {
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
let chatbar = getChatBar();
|
||||
openChatViaUser();
|
||||
ok(SocialChatBar.chatbar.firstElementChild, "chat opened");
|
||||
waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
|
||||
ok(chatbar.firstElementChild, "chat opened");
|
||||
waitForCondition(function() isChatFocused(chatbar.selectedChat),
|
||||
function() {
|
||||
is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat is selected");
|
||||
is(chatbar.selectedChat, chatbar.firstElementChild, "chat is selected");
|
||||
next();
|
||||
}, "chat should be focused");
|
||||
});
|
||||
},
|
||||
|
||||
// Open a chat via the worker - it will open and not have focus.
|
||||
// Then open the same chat via a sidebar message - it will be restored but
|
||||
// should still not have grabbed focus.
|
||||
testNoFocusOnAutoRestore: function(next) {
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1";
|
||||
let chatbar = SocialChatBar.chatbar;
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaWorkerMessage(port, chatUrl, function() {
|
||||
is(chatbar.childElementCount, 1, "exactly 1 chat open");
|
||||
// bug 865086 opening minimized still sets the window as selected
|
||||
todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected");
|
||||
ok(isTabFocused(), "tab should be focused");
|
||||
openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() {
|
||||
is(chatbar.childElementCount, 1, "still 1 chat open");
|
||||
ok(!chatbar.firstElementChild.minimized, "chat no longer minimized");
|
||||
// bug 865086 because we marked it selected on open, it still is
|
||||
todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected");
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Here we open a chat, which will not be focused. Then we minimize it and
|
||||
// restore it via a titlebar clock - it should get focus at that point.
|
||||
testFocusOnExplicitRestore: function(next) {
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
|
||||
ok(true, "got chatbox message");
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
let chatbox = SocialChatBar.chatbar.firstElementChild;
|
||||
ok(chatbox, "chat opened");
|
||||
chatbox.minimized = true;
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
// pretend we clicked on the titlebar
|
||||
chatbox.onTitlebarClick({button: 0});
|
||||
waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
|
||||
function() {
|
||||
ok(!chatbox.minimized, "chat should have been restored");
|
||||
ok(isChatFocused(chatbox), "chat should be focused");
|
||||
is(chatbox, SocialChatBar.chatbar.selectedChat, "chat is marked selected");
|
||||
next();
|
||||
}, "chat should have focus");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Open 2 chats and give 1 focus. Minimize the focused one - the second
|
||||
// should get focus.
|
||||
testMinimizeFocused: function(next) {
|
||||
let chatbar = SocialChatBar.chatbar;
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() {
|
||||
let chat1 = chatbar.firstElementChild;
|
||||
openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() {
|
||||
is(chatbar.childElementCount, 2, "exactly 2 chats open");
|
||||
let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
|
||||
chatbar.selectedChat = chat1;
|
||||
chatbar.focus();
|
||||
waitForCondition(function() isChatFocused(chat1),
|
||||
function() {
|
||||
is(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is marked selected");
|
||||
isnot(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is not marked selected");
|
||||
chat1.minimized = true;
|
||||
waitForCondition(function() isChatFocused(chat2),
|
||||
function() {
|
||||
// minimizing the chat with focus should give it to another.
|
||||
isnot(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is not marked selected");
|
||||
is(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is marked selected");
|
||||
next();
|
||||
}, "chat2 should have focus");
|
||||
}, "chat1 should have focus");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Open 2 chats, select (but not focus) one, then re-request it be
|
||||
// opened via a message. Focus should not move.
|
||||
testReopenNonFocused: function(next) {
|
||||
let chatbar = SocialChatBar.chatbar;
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaSidebarMessage(port, {id: 1}, function() {
|
||||
let chat1 = chatbar.firstElementChild;
|
||||
openChatViaSidebarMessage(port, {id: 2}, function() {
|
||||
let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
|
||||
chatbar.selectedChat = chat2;
|
||||
// tab still has focus
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
// re-request the first.
|
||||
openChatViaSidebarMessage(port, {id: 1}, function() {
|
||||
is(chatbar.selectedChat, chat1, "chat1 now selected");
|
||||
ok(isTabFocused(), "tab should still be focused");
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Open 2 chats, select and focus the second. Pressing the TAB key should
|
||||
// cause focus to move between all elements in our chat window before moving
|
||||
// to the next chat window.
|
||||
testTab: function(next) {
|
||||
function sendTabAndWaitForFocus(chat, eltid, callback) {
|
||||
// ideally we would use the 'focus' event here, but that doesn't work
|
||||
// as expected for the iframe - the iframe itself never gets the focus
|
||||
// event (apparently the sub-document etc does.)
|
||||
// So just poll for the correct element getting focus...
|
||||
let doc = chat.contentDocument;
|
||||
EventUtils.sendKey("tab");
|
||||
waitForCondition(function() {
|
||||
let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
|
||||
return doc.activeElement == elt;
|
||||
}, callback, "element " + eltid + " never got focus");
|
||||
}
|
||||
|
||||
let chatbar = SocialChatBar.chatbar;
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaSidebarMessage(port, {id: 1}, function() {
|
||||
let chat1 = chatbar.firstElementChild;
|
||||
openChatViaSidebarMessage(port, {id: 2}, function() {
|
||||
let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
|
||||
chatbar.selectedChat = chat2;
|
||||
chatbar.focus();
|
||||
waitForCondition(function() isChatFocused(chatbar.selectedChat),
|
||||
function() {
|
||||
// Our chats have 3 focusable elements, so it takes 4 TABs to move
|
||||
// to the new chat.
|
||||
sendTabAndWaitForFocus(chat2, "input1", function() {
|
||||
is(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
|
||||
"first input field has focus");
|
||||
ok(isChatFocused(chat2), "new chat still focused after first tab");
|
||||
sendTabAndWaitForFocus(chat2, "input2", function() {
|
||||
ok(isChatFocused(chat2), "new chat still focused after tab");
|
||||
is(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
|
||||
"second input field has focus");
|
||||
sendTabAndWaitForFocus(chat2, "iframe", function() {
|
||||
ok(isChatFocused(chat2), "new chat still focused after tab");
|
||||
is(chat2.contentDocument.activeElement.getAttribute("id"), "iframe",
|
||||
"iframe has focus");
|
||||
// this tab now should move to the next chat, but focus the
|
||||
// document element itself (hence the null eltid)
|
||||
sendTabAndWaitForFocus(chat1, null, function() {
|
||||
ok(isChatFocused(chat1), "first chat is focused");
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, "chat should have focus");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Open a chat and focus an element other than the first. Move focus to some
|
||||
// other item (the tab itself in this case), then focus the chatbar - the
|
||||
// same element that was previously focused should still have focus.
|
||||
testFocusedElement: function(next) {
|
||||
let chatbar = SocialChatBar.chatbar;
|
||||
startTestAndWaitForSidebar(function(port) {
|
||||
openChatViaUser();
|
||||
let chat = chatbar.firstElementChild;
|
||||
// need to wait for the content to load before we can focus it.
|
||||
chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
|
||||
chat.removeEventListener("DOMContentLoaded", DOMContentLoaded);
|
||||
chat.contentDocument.getElementById("input2").focus();
|
||||
waitForCondition(function() isChatFocused(chat),
|
||||
function() {
|
||||
is(chat.contentDocument.activeElement.getAttribute("id"), "input2",
|
||||
"correct input field has focus");
|
||||
// set focus to the tab.
|
||||
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
|
||||
Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
|
||||
waitForCondition(function() isTabFocused(),
|
||||
function() {
|
||||
chatbar.focus();
|
||||
waitForCondition(function() isChatFocused(chat),
|
||||
function() {
|
||||
is(chat.contentDocument.activeElement.getAttribute("id"), "input2",
|
||||
"correct input field still has focus");
|
||||
next();
|
||||
}, "chat took focus");
|
||||
}, "tab has focus");
|
||||
}, "chat took focus");
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -9,6 +9,8 @@ function gc() {
|
||||
wu.garbageCollect();
|
||||
}
|
||||
|
||||
let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
|
||||
|
||||
// Support for going on and offline.
|
||||
// (via browser/base/content/test/browser_bookmark_titles.js)
|
||||
let origProxyType = Services.prefs.getIntPref('network.proxy.type');
|
||||
@ -42,9 +44,10 @@ function openPanel(url, panelCallback, loadCallback) {
|
||||
|
||||
function openChat(url, panelCallback, loadCallback) {
|
||||
// open a chat window
|
||||
SocialChatBar.openChat(SocialSidebar.provider, url, panelCallback);
|
||||
SocialChatBar.chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
|
||||
SocialChatBar.chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
|
||||
let chatbar = getChatBar();
|
||||
openChatWindow(null, SocialSidebar.provider, url, panelCallback);
|
||||
chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
|
||||
chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
|
||||
loadCallback();
|
||||
}, true);
|
||||
}
|
||||
@ -154,7 +157,7 @@ var tests = {
|
||||
|
||||
testChatWindow: function(next) {
|
||||
let panelCallbackCount = 0;
|
||||
// go offline and open a flyout.
|
||||
// go offline and open a chat.
|
||||
goOffline();
|
||||
openChat(
|
||||
"https://example.com/browser/browser/base/content/test/social/social_chat.html",
|
||||
@ -164,7 +167,7 @@ var tests = {
|
||||
function() { // the "load" callback.
|
||||
executeSoon(function() {
|
||||
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
|
||||
let chat = SocialChatBar.chatbar.selectedChat;
|
||||
let chat = getChatBar().selectedChat;
|
||||
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||
function() {
|
||||
chat.close();
|
||||
@ -174,5 +177,36 @@ var tests = {
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
testChatWindowAfterTearOff: function(next) {
|
||||
// Ensure that the error listener survives the chat window being detached.
|
||||
let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
let panelCallbackCount = 0;
|
||||
// open a chat while we are still online.
|
||||
openChat(
|
||||
url,
|
||||
null,
|
||||
function() { // the "load" callback.
|
||||
executeSoon(function() {
|
||||
let chat = getChatBar().selectedChat;
|
||||
is(chat.contentDocument.location.href, url, "correct url loaded");
|
||||
// toggle to a detached window.
|
||||
chat.swapWindows().then(
|
||||
chat => {
|
||||
// now go offline and reload the chat - about:socialerror should be loaded.
|
||||
goOffline();
|
||||
chat.contentDocument.location.reload();
|
||||
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||
function() {
|
||||
chat.close();
|
||||
next();
|
||||
},
|
||||
"error page didn't appear");
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -237,8 +237,6 @@ function checkSocialUI(win) {
|
||||
_is(!!a, !!b, msg);
|
||||
}
|
||||
isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?");
|
||||
isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?");
|
||||
isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?");
|
||||
|
||||
let contextMenus = [
|
||||
{
|
||||
@ -279,8 +277,6 @@ function checkSocialUI(win) {
|
||||
// and for good measure, check all the social commands.
|
||||
isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?");
|
||||
isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
|
||||
isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?");
|
||||
isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
|
||||
|
||||
// and report on overall success of failure of the various checks here.
|
||||
is(numGoodTests, numTests, "The Social UI tests succeeded.")
|
||||
@ -403,7 +399,7 @@ function get3ChatsForCollapsing(mode, cb) {
|
||||
// To make our life easier we don't go via the worker and ports so we get
|
||||
// more control over creation *and* to make the code much simpler. We
|
||||
// assume the worker/port stuff is individually tested above.
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
let chatbar = getChatBar();
|
||||
let chatWidth = undefined;
|
||||
let num = 0;
|
||||
is(chatbar.childNodes.length, 0, "chatbar starting empty");
|
||||
@ -447,23 +443,21 @@ function makeChat(mode, uniqueid, cb) {
|
||||
info("making a chat window '" + uniqueid +"'");
|
||||
let provider = SocialSidebar.provider;
|
||||
const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
|
||||
// Note that we use promiseChatLoaded instead of the callback to ensure the
|
||||
// content has started loading.
|
||||
let chatbox = getChatBar().openChat(provider.origin, provider.name,
|
||||
chatUrl + "?id=" + uniqueid, mode);
|
||||
chatbox.promiseChatLoaded.then(
|
||||
() => {
|
||||
info("chat window has opened");
|
||||
// we can't callback immediately or we might close the chat during
|
||||
// this event which upsets the implementation - it is only 1/2 way through
|
||||
// handling the load event.
|
||||
chat.document.title = uniqueid;
|
||||
executeSoon(cb);
|
||||
}, mode);
|
||||
if (!isOpened) {
|
||||
ok(false, "unable to open chat window, no provider? more failures to come");
|
||||
executeSoon(cb);
|
||||
}
|
||||
chatbox.contentDocument.title = uniqueid;
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
function checkPopup() {
|
||||
// popup only showing if any collapsed popup children.
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
let chatbar = getChatBar();
|
||||
let numCollapsed = 0;
|
||||
for (let chat of chatbar.childNodes) {
|
||||
if (chat.collapsed) {
|
||||
@ -482,7 +476,7 @@ function checkPopup() {
|
||||
// Does a callback passing |true| if the window is now big enough or false
|
||||
// if we couldn't resize large enough to satisfy the test requirement.
|
||||
function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
|
||||
let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
|
||||
let current = getChatBar().getBoundingClientRect().width;
|
||||
let delta = desired - current;
|
||||
info(count + ": resizing window so chat area is " + desired + " wide, currently it is "
|
||||
+ current + ". Screen avail is " + window.screen.availWidth
|
||||
@ -515,7 +509,7 @@ function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
|
||||
}
|
||||
function resize_handler(event) {
|
||||
// we did resize - but did we get far enough to be able to continue?
|
||||
let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
|
||||
let newSize = getChatBar().getBoundingClientRect().width;
|
||||
let sizedOk = widthDeltaCloseEnough(newSize - desired);
|
||||
if (!sizedOk)
|
||||
return;
|
||||
@ -563,8 +557,13 @@ function resizeAndCheckWidths(first, second, third, checks, cb) {
|
||||
}, count);
|
||||
}
|
||||
|
||||
function getChatBar() {
|
||||
return document.getElementById("pinnedchats");
|
||||
}
|
||||
|
||||
function getPopupWidth() {
|
||||
let popup = window.SocialChatBar.chatbar.menupopup;
|
||||
let chatbar = getChatBar();
|
||||
let popup = chatbar.menupopup;
|
||||
ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
|
||||
let cs = document.defaultView.getComputedStyle(popup.parentNode);
|
||||
let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
|
||||
@ -572,6 +571,8 @@ function getPopupWidth() {
|
||||
}
|
||||
|
||||
function closeAllChats() {
|
||||
let chatbar = window.SocialChatBar.chatbar;
|
||||
chatbar.removeAll();
|
||||
let chatbar = getChatBar();
|
||||
while (chatbar.selectedChat) {
|
||||
chatbar.selectedChat.close();
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ MOCHITEST_CHROME_MANIFESTS += [
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'content/test/chat/browser.ini',
|
||||
'content/test/general/browser.ini',
|
||||
'content/test/newtab/browser.ini',
|
||||
'content/test/plugins/browser.ini',
|
||||
|
@ -758,7 +758,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The corresponding item.
|
||||
*/
|
||||
_onBreakpointRemoved: function(aItem) {
|
||||
dumpn("Finalizing breakpoint item: " + aItem);
|
||||
dumpn("Finalizing breakpoint item: " + aItem.stringify());
|
||||
|
||||
// Destroy the context menu for the breakpoint.
|
||||
let contextMenu = aItem.attachment.popup;
|
||||
@ -2215,6 +2215,7 @@ WatchExpressionsView.prototype = Heritage.extend(WidgetMethods, {
|
||||
_createItemView: function(aExpression) {
|
||||
let container = document.createElement("hbox");
|
||||
container.className = "list-widget-item dbg-expression";
|
||||
container.setAttribute("align", "center");
|
||||
|
||||
let arrowNode = document.createElement("hbox");
|
||||
arrowNode.className = "dbg-expression-arrow";
|
||||
|
@ -491,7 +491,7 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The corresponding item.
|
||||
*/
|
||||
_onStackframeRemoved: function(aItem) {
|
||||
dumpn("Finalizing stackframe item: " + aItem);
|
||||
dumpn("Finalizing stackframe item: " + aItem.stringify());
|
||||
|
||||
// Remove the mirrored item in the classic list.
|
||||
let depth = aItem.attachment.depth;
|
||||
|
@ -574,10 +574,16 @@ Item.prototype = {
|
||||
|
||||
/**
|
||||
* Returns a string representing the object.
|
||||
* Avoid using `toString` to avoid accidental JSONification.
|
||||
* @return string
|
||||
*/
|
||||
toString: function() {
|
||||
return this._value + " :: " + this._target + " :: " + this.attachment;
|
||||
stringify: function() {
|
||||
return JSON.stringify({
|
||||
value: this._value,
|
||||
target: this._target + "",
|
||||
prebuiltNode: this._prebuiltNode + "",
|
||||
attachment: this.attachment
|
||||
}, null, 2);
|
||||
},
|
||||
|
||||
_value: "",
|
||||
|
@ -9,5 +9,5 @@
|
||||
<!ENTITY newtab.undo.restoreButton "Restore All.">
|
||||
<!ENTITY newtab.undo.closeTooltip "Hide">
|
||||
<!ENTITY newtab.sponsored.release.message "This Sponsor site was suggested because we hoped you’d find it interesting and because it supports Mozilla’s mission.">
|
||||
<!ENTITY newtab.sponsored.trial.message "This site was suggested because we hoped you’d find it interesting and because it supports Mozilla’s mission.">
|
||||
<!ENTITY newtab.sponsored.trial.message2 "This site was suggested because we hoped you’d find it interesting and because it supports Mozilla’s mission.">
|
||||
<!ENTITY newtab.panel.link.text "Learn more…">
|
||||
|
@ -160,8 +160,6 @@ var BrowserUI = {
|
||||
Util.dumpLn("* delay load started...");
|
||||
window.removeEventListener("UIReadyDelayed", delayedInit, false);
|
||||
|
||||
// Login Manager and Form History initialization
|
||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
||||
|
||||
try {
|
||||
|
191
browser/modules/Chat.jsm
Normal file
191
browser/modules/Chat.jsm
Normal file
@ -0,0 +1,191 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// A module for working with chat windows.
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Chat"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
// A couple of internal helper function.
|
||||
function isWindowChromeless(win) {
|
||||
// XXX - stolen from browser-social.js, but there's no obvious place to
|
||||
// put this so it can be shared.
|
||||
|
||||
// Is this a popup window that doesn't want chrome shown?
|
||||
let docElem = win.document.documentElement;
|
||||
// extrachrome is not restored during session restore, so we need
|
||||
// to check for the toolbar as well.
|
||||
let chromeless = docElem.getAttribute("chromehidden").contains("extrachrome") ||
|
||||
docElem.getAttribute('chromehidden').contains("toolbar");
|
||||
return chromeless;
|
||||
}
|
||||
|
||||
function isWindowGoodForChats(win) {
|
||||
return !win.closed &&
|
||||
!!win.document.getElementById("pinnedchats") &&
|
||||
!isWindowChromeless(win) &&
|
||||
!PrivateBrowsingUtils.isWindowPrivate(win);
|
||||
}
|
||||
|
||||
function getChromeWindow(contentWin) {
|
||||
return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
}
|
||||
|
||||
/*
|
||||
* The exported Chat object
|
||||
*/
|
||||
|
||||
let Chat = {
|
||||
/**
|
||||
* Open a new chatbox.
|
||||
*
|
||||
* @param contentWindow [optional]
|
||||
* The content window that requested this chat. May be null.
|
||||
* @param origin
|
||||
* The origin for the chat. This is primarily used as an identifier
|
||||
* to help identify all chats from the same provider.
|
||||
* @param title
|
||||
* The title to be used if a new chat window is created.
|
||||
* @param url
|
||||
* The URL for the that. Should be under the origin. If an existing
|
||||
* chatbox exists with the same URL, it will be reused and returned.
|
||||
* @param mode [optional]
|
||||
* May be undefined or 'minimized'
|
||||
* @param focus [optional]
|
||||
* Indicates if the chatbox should be focused. If undefined the chat
|
||||
* will be focused if the window is currently handling user input (ie,
|
||||
* if the chat is being opened as a direct result of user input)
|
||||
|
||||
* @return A chatbox binding. This binding has a number of promises which
|
||||
* can be used to determine when the chatbox is being created and
|
||||
* has loaded. Will return null if no chat can be created (Which
|
||||
* should only happen in edge-cases)
|
||||
*/
|
||||
open: function(contentWindow, origin, title, url, mode, focus, callback) {
|
||||
let chromeWindow = this.findChromeWindowForChats(contentWindow);
|
||||
if (!chromeWindow) {
|
||||
Cu.reportError("Failed to open a chat window - no host window could be found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
let chatbar = chromeWindow.document.getElementById("pinnedchats");
|
||||
chatbar.hidden = false;
|
||||
let chatbox = chatbar.openChat(origin, title, url, mode, callback);
|
||||
// getAttention is ignored if the target window is already foreground, so
|
||||
// we can call it unconditionally.
|
||||
chromeWindow.getAttention();
|
||||
// If focus is undefined we want automatic focus handling, and only focus
|
||||
// if a direct result of user action.
|
||||
if (focus === undefined) {
|
||||
let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
focus = dwu.isHandlingUserInput;
|
||||
}
|
||||
if (focus) {
|
||||
chatbar.focus();
|
||||
}
|
||||
return chatbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Close all chats from the specified origin.
|
||||
*
|
||||
* @param origin
|
||||
* The origin from which all chats should be closed.
|
||||
*/
|
||||
closeAll: function(origin) {
|
||||
// close all attached chat windows
|
||||
let winEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let win = winEnum.getNext();
|
||||
let chatbar = win.document.getElementById("pinnedchats");
|
||||
if (!chatbar)
|
||||
continue;
|
||||
let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
|
||||
[c.close() for (c of chats)];
|
||||
}
|
||||
|
||||
// close all standalone chat windows
|
||||
winEnum = Services.wm.getEnumerator("Social:Chat");
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let win = winEnum.getNext();
|
||||
if (win.closed)
|
||||
continue;
|
||||
let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
|
||||
if (origin == chatOrigin)
|
||||
win.close();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the chatbar associated with a window
|
||||
*
|
||||
* @param window
|
||||
*/
|
||||
focus: function(win) {
|
||||
let chatbar = win.document.getElementById("pinnedchats");
|
||||
if (chatbar && !chatbar.hidden) {
|
||||
chatbar.focus();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// This is exported as socialchat.xml needs to find a window when a chat
|
||||
// is re-docked.
|
||||
findChromeWindowForChats: function(preferredWindow) {
|
||||
if (preferredWindow) {
|
||||
preferredWindow = getChromeWindow(preferredWindow);
|
||||
if (isWindowGoodForChats(preferredWindow)) {
|
||||
return preferredWindow;
|
||||
}
|
||||
}
|
||||
// no good - we just use the "most recent" browser window which can host
|
||||
// chats (we used to try and "group" all chats in the same browser window,
|
||||
// but that didn't work out so well - see bug 835111
|
||||
|
||||
// Try first the most recent window as getMostRecentWindow works
|
||||
// even on platforms where getZOrderDOMWindowEnumerator is broken
|
||||
// (ie. Linux). This will handle most cases, but won't work if the
|
||||
// foreground window is a popup.
|
||||
|
||||
let mostRecent = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (isWindowGoodForChats(mostRecent))
|
||||
return mostRecent;
|
||||
|
||||
let topMost, enumerator;
|
||||
// *sigh* - getZOrderDOMWindowEnumerator is broken except on Mac and
|
||||
// Windows. We use BROKEN_WM_Z_ORDER as that is what some other code uses
|
||||
// and a few bugs recommend searching mxr for this symbol to identify the
|
||||
// workarounds - we want this code to be hit in such searches.
|
||||
let os = Services.appinfo.OS;
|
||||
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
|
||||
if (BROKEN_WM_Z_ORDER) {
|
||||
// this is oldest to newest and no way to change the order.
|
||||
enumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
} else {
|
||||
// here we explicitly ask for bottom-to-top so we can use the same logic
|
||||
// where BROKEN_WM_Z_ORDER is true.
|
||||
enumerator = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
|
||||
}
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let win = enumerator.getNext();
|
||||
if (!win.closed && isWindowGoodForChats(win))
|
||||
topMost = win;
|
||||
}
|
||||
return topMost;
|
||||
},
|
||||
}
|
@ -9,6 +9,7 @@ TEST_DIRS += ['test']
|
||||
EXTRA_JS_MODULES += [
|
||||
'BrowserNewTabPreloader.jsm',
|
||||
'BrowserUITelemetry.jsm',
|
||||
'Chat.jsm',
|
||||
'ContentClick.jsm',
|
||||
'ContentLinkHandler.jsm',
|
||||
'ContentSearch.jsm',
|
||||
|
@ -280,10 +280,11 @@
|
||||
}
|
||||
|
||||
.dbg-expression-arrow {
|
||||
background: url(commandline-icon.png);
|
||||
background-position: 16px;
|
||||
width: 16px;
|
||||
height: auto;
|
||||
height: 16px;
|
||||
margin: 2px;
|
||||
background: -moz-image-rect(url(commandline-icon.png), 0, 32, 16, 16);
|
||||
}
|
||||
|
||||
.dbg-expression-input {
|
||||
@ -294,8 +295,8 @@
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.theme-dark .dbg-expression-button {
|
||||
|
@ -814,7 +814,7 @@ class Automation(object):
|
||||
xrePath = None, certPath = None,
|
||||
debuggerInfo = None, symbolsPath = None,
|
||||
timeout = -1, maxTime = None, onLaunch = None,
|
||||
webapprtChrome = False, hide_subtests=None, screenshotOnFail=False):
|
||||
webapprtChrome = False, screenshotOnFail=False):
|
||||
"""
|
||||
Run the app, log the duration it took to execute, return the status code.
|
||||
Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
class nsIDocShell;
|
||||
class nsString;
|
||||
class nsCString;
|
||||
class nsIClassInfo;
|
||||
class nsIIOService;
|
||||
class nsIStringBundle;
|
||||
@ -139,9 +139,13 @@ private:
|
||||
inline void
|
||||
ScriptSecurityPrefChanged();
|
||||
|
||||
inline void
|
||||
AddSitesToFileURIWhitelist(const nsCString& aSiteList);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mSystemPrincipal;
|
||||
bool mPrefInitialized;
|
||||
bool mIsJavaScriptEnabled;
|
||||
nsTArray<nsCOMPtr<nsIURI>> mFileURIWhitelist;
|
||||
|
||||
// This machinery controls new-style domain policies. The old-style
|
||||
// policy machinery will be removed soon.
|
||||
|
@ -722,6 +722,14 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
|
||||
&hasFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (hasFlags) {
|
||||
// Allow domains that were whitelisted in the prefs. In 99.9% of cases,
|
||||
// this array is empty.
|
||||
for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) {
|
||||
if (SecurityCompareURIs(mFileURIWhitelist[i], sourceURI)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// resource: and chrome: are equivalent, securitywise
|
||||
// That's bogus!! Fix this. But watch out for
|
||||
// the view-source stylesheet?
|
||||
@ -1142,6 +1150,7 @@ const char sFileOriginPolicyPrefName[] =
|
||||
static const char* kObservedPrefs[] = {
|
||||
sJSEnabledPrefName,
|
||||
sFileOriginPolicyPrefName,
|
||||
"capability.policy.",
|
||||
nullptr
|
||||
};
|
||||
|
||||
@ -1150,18 +1159,8 @@ NS_IMETHODIMP
|
||||
nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
|
||||
const char16_t* aMessage)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
NS_ConvertUTF16toUTF8 messageStr(aMessage);
|
||||
const char *message = messageStr.get();
|
||||
|
||||
static const char jsPrefix[] = "javascript.";
|
||||
static const char securityPrefix[] = "security.";
|
||||
if ((PL_strncmp(message, jsPrefix, sizeof(jsPrefix)-1) == 0) ||
|
||||
(PL_strncmp(message, securityPrefix, sizeof(securityPrefix)-1) == 0) )
|
||||
{
|
||||
ScriptSecurityPrefChanged();
|
||||
}
|
||||
return rv;
|
||||
ScriptSecurityPrefChanged();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
@ -1270,26 +1269,98 @@ nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
|
||||
return static_cast<nsSystemPrincipal*>(sysprin);
|
||||
}
|
||||
|
||||
struct IsWhitespace {
|
||||
static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
|
||||
};
|
||||
struct IsWhitespaceOrComma {
|
||||
static bool Test(char aChar) { return aChar == ',' || NS_IsAsciiWhitespace(aChar); };
|
||||
};
|
||||
|
||||
template <typename Predicate>
|
||||
uint32_t SkipPast(const nsCString& str, uint32_t base)
|
||||
{
|
||||
while (base < str.Length() && Predicate::Test(str[base])) {
|
||||
++base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
uint32_t SkipUntil(const nsCString& str, uint32_t base)
|
||||
{
|
||||
while (base < str.Length() && !Predicate::Test(str[base])) {
|
||||
++base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
inline void
|
||||
nsScriptSecurityManager::ScriptSecurityPrefChanged()
|
||||
{
|
||||
// JavaScript defaults to enabled in failure cases.
|
||||
mIsJavaScriptEnabled = true;
|
||||
|
||||
sStrictFileOriginPolicy = true;
|
||||
|
||||
nsresult rv;
|
||||
if (!mPrefInitialized) {
|
||||
rv = InitPrefs();
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPrefInitialized);
|
||||
mIsJavaScriptEnabled =
|
||||
Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
|
||||
|
||||
sStrictFileOriginPolicy =
|
||||
Preferences::GetBool(sFileOriginPolicyPrefName, false);
|
||||
|
||||
//
|
||||
// Rebuild the set of principals for which we allow file:// URI loads. This
|
||||
// implements a small subset of an old pref-based CAPS people that people
|
||||
// have come to depend on. See bug 995943.
|
||||
//
|
||||
|
||||
mFileURIWhitelist.Clear();
|
||||
auto policies = mozilla::Preferences::GetCString("capability.policy.policynames");
|
||||
for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
|
||||
base < policies.Length();
|
||||
base = SkipPast<IsWhitespaceOrComma>(policies, bound))
|
||||
{
|
||||
// Grab the current policy name.
|
||||
bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
|
||||
auto policyName = Substring(policies, base, bound - base);
|
||||
|
||||
// Figure out if this policy allows loading file:// URIs. If not, we can skip it.
|
||||
nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") +
|
||||
policyName +
|
||||
NS_LITERAL_CSTRING(".checkloaduri.enabled");
|
||||
if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Grab the list of domains associated with this policy.
|
||||
nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
|
||||
policyName +
|
||||
NS_LITERAL_CSTRING(".sites");
|
||||
auto siteList = Preferences::GetCString(domainPrefName.get());
|
||||
AddSitesToFileURIWhitelist(siteList);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScriptSecurityManager::AddSitesToFileURIWhitelist(const nsCString& aSiteList)
|
||||
{
|
||||
for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
|
||||
base < aSiteList.Length();
|
||||
base = SkipPast<IsWhitespace>(aSiteList, bound))
|
||||
{
|
||||
// Grab the current site.
|
||||
bound = SkipUntil<IsWhitespace>(aSiteList, base);
|
||||
auto site = Substring(aSiteList, base, bound - base);
|
||||
|
||||
// Convert it to a URI and add it to our list.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mFileURIWhitelist.AppendElement(uri);
|
||||
} else {
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
|
||||
if (console) {
|
||||
nsAutoString msg = NS_LITERAL_STRING("Unable to to add site to file:// URI whitelist: ") +
|
||||
NS_ConvertASCIItoUTF16(site);
|
||||
console->LogStringMessage(msg.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -2,6 +2,7 @@
|
||||
support-files =
|
||||
file_disableScript.html
|
||||
|
||||
[test_bug995943.xul]
|
||||
[test_disableScript.xul]
|
||||
[test_principal_jarprefix_origin_appid_appstatus.html]
|
||||
# jarPrefix test doesn't work on Windows, see bug 776296.
|
||||
|
98
caps/tests/mochitest/test_bug995943.xul
Normal file
98
caps/tests/mochitest/test_bug995943.xul
Normal file
@ -0,0 +1,98 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=995943
|
||||
-->
|
||||
<window title="Mozilla Bug 995943"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=995943"
|
||||
target="_blank">Mozilla Bug 995943</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
function debug(msg) { info(msg); }
|
||||
|
||||
/** Test for CAPS file:// URI prefs. **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var profileDir = "file://" + Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("ProfD", Ci.nsILocalFile).path;
|
||||
|
||||
function checkLoadFileURI(domain, shouldLoad) {
|
||||
debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad);
|
||||
return new Promise(function(resolve, reject) {
|
||||
$('ifr').addEventListener('load', function l1() {
|
||||
$('ifr').removeEventListener('load', l1);
|
||||
function l2() {
|
||||
$('ifr').removeEventListener('load', l2);
|
||||
ok(shouldLoad, "Successfully loaded file:// URI for domain: " + domain);
|
||||
resolve();
|
||||
}
|
||||
$('ifr').addEventListener('load', l2);
|
||||
try {
|
||||
window[0].wrappedJSObject.location = profileDir;
|
||||
} catch (e) {
|
||||
ok(!shouldLoad && /denied|insecure/.test(e),
|
||||
"Prevented loading of file:// URI for domain: " + domain + " - " + e);
|
||||
$('ifr').removeEventListener('load', l2);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
let targetURI = domain + '/tests/js/xpconnect/tests/mochitest/file_empty.html';
|
||||
debug("Navigating iframe to " + targetURI);
|
||||
$('ifr').contentWindow.location = targetURI;
|
||||
});
|
||||
}
|
||||
|
||||
function pushPrefs(prefs) {
|
||||
return new Promise(function(resolve) { SpecialPowers.pushPrefEnv({ set: prefs }, resolve); });
|
||||
}
|
||||
|
||||
function popPrefs() {
|
||||
return new Promise(function(resolve) { SpecialPowers.popPrefEnv(resolve); });
|
||||
}
|
||||
|
||||
function go() {
|
||||
checkLoadFileURI('http://example.com', false).then(
|
||||
pushPrefs.bind(null, [['capability.policy.policynames', ' somepolicy '],
|
||||
['capability.policy.somepolicy.checkloaduri.enabled', 'AlLAcCeSs'],
|
||||
['capability.policy.somepolicy.sites', 'http://example.com']]))
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.com', true))
|
||||
.then(popPrefs)
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.com', false))
|
||||
.then(
|
||||
pushPrefs.bind(null, [['capability.policy.policynames', ',somepolicy, someotherpolicy, '],
|
||||
['capability.policy.somepolicy.checkloaduri.enabled', 'allaccess'],
|
||||
['capability.policy.someotherpolicy.checkloaduri.enabled', 'nope'],
|
||||
['capability.policy.somepolicy.sites', ' http://example.org https://example.com'],
|
||||
['capability.policy.someotherpolicy.sites', 'http://example.net ']]))
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.org', true))
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.com', false))
|
||||
.then(checkLoadFileURI.bind(null, 'https://example.com', true))
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.net', false))
|
||||
.then(pushPrefs.bind(null, [['capability.policy.someotherpolicy.checkloaduri.enabled', 'allAccess']]))
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.net', true))
|
||||
.then(popPrefs)
|
||||
.then(popPrefs)
|
||||
.then(checkLoadFileURI.bind(null, 'http://example.net', false))
|
||||
.then(SimpleTest.finish.bind(SimpleTest));
|
||||
|
||||
}
|
||||
addLoadEvent(go);
|
||||
|
||||
]]>
|
||||
</script>
|
||||
<iframe id="ifr" type="content" />
|
||||
</window>
|
@ -21,11 +21,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=758258
|
||||
/** Test for Bug 758258 **/
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
if (navigator.platform.startsWith("Mac")) {
|
||||
SimpleTest.expectAssertions(2);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
/*
|
||||
|
28
configure.in
28
configure.in
@ -5790,16 +5790,22 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
|
||||
if test -n "$WINDOWSSDKDIR"; then
|
||||
MOZ_D3DCOMPILER_VISTA_DLL_PATH="$WINDOWSSDKDIR/Redist/D3D/$MOZ_D3D_CPU_SUFFIX/$MOZ_D3DCOMPILER_VISTA_DLL"
|
||||
if test -f "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
|
||||
MOZ_HAS_WINSDK_WITH_D3D=1
|
||||
AC_MSG_RESULT([Found MOZ_D3DCOMPILER_VISTA_DLL_PATH: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
|
||||
MOZ_HAS_WINSDK_WITH_D3D=1
|
||||
else
|
||||
AC_MSG_RESULT([MOZ_D3DCOMPILER_VISTA_DLL_PATH doesn't exist: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
|
||||
AC_MSG_ERROR([Windows SDK at "$WINDOWSSDKDIR" appears broken. Try updating to MozillaBuild 1.9 final or higher.])
|
||||
MOZ_D3DCOMPILER_VISTA_DLL_PATH=
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([WINDOWSSDKDIR is missing.])
|
||||
AC_MSG_RESULT([Windows SDK not found.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
|
||||
MOZ_D3DCOMPILER_VISTA_DLL=
|
||||
fi
|
||||
|
||||
######################################
|
||||
# Find _43 for use by XP.
|
||||
|
||||
@ -5816,7 +5822,7 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
|
||||
test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/dxguid.lib ; then
|
||||
AC_MSG_RESULT([Found DirectX SDK via registry, using $MOZ_DIRECTX_SDK_PATH])
|
||||
else
|
||||
AC_MSG_RESULT([Couldn't find the DirectX SDK.])
|
||||
AC_MSG_RESULT([DirectX SDK not found.])
|
||||
MOZ_DIRECTX_SDK_PATH=
|
||||
fi
|
||||
|
||||
@ -5835,21 +5841,21 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
|
||||
|
||||
if test -n "$MOZ_D3DX9_VERSION" ; then
|
||||
MOZ_D3DCOMPILER_XP_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *D3DCompiler_${MOZ_D3DX9_VERSION}_${MOZ_D3D_CPU_SUFFIX}.cab | head -n1`
|
||||
MOZ_D3DCOMPILER_XP_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
|
||||
|
||||
if test -n "$MOZ_D3DCOMPILER_XP_CAB"; then
|
||||
MOZ_D3DCOMPILER_XP_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
|
||||
else
|
||||
AC_MSG_RESULT([Couldn't find a CAB containing the D3D compiler DLL.])
|
||||
AC_MSG_ERROR([DirectX SDK at "$MOZ_DIRECTX_SDK_PATH" appears broken.])
|
||||
MOZ_DIRECTX_SDK_PATH=
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([Couldn't determine the D3DX9 version for the DirectX SDK.])
|
||||
fi
|
||||
|
||||
if test -z "$MOZ_D3DCOMPILER_XP_CAB"; then
|
||||
AC_MSG_RESULT([Couldn't find a CAB containing the D3D compiler DLL.])
|
||||
MOZ_DIRECTX_SDK_PATH=
|
||||
MOZ_D3DCOMPILER_XP_DLL=
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.])
|
||||
AC_MSG_RESULT([ Either ignore, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
|
||||
MOZ_DIRECTX_SDK_PATH=
|
||||
MOZ_D3DCOMPILER_XP_DLL=
|
||||
fi
|
||||
|
||||
######################################
|
||||
|
@ -49,6 +49,7 @@ class nsIScrollableFrame;
|
||||
class nsAttrValueOrString;
|
||||
class ContentUnbinder;
|
||||
class nsContentList;
|
||||
class nsDOMSettableTokenList;
|
||||
class nsDOMTokenList;
|
||||
struct nsRect;
|
||||
class nsFocusManager;
|
||||
@ -1164,6 +1165,10 @@ protected:
|
||||
*/
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
|
||||
nsDOMSettableTokenList* GetTokenList(nsIAtom* aAtom);
|
||||
void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult);
|
||||
nsresult SetTokenList(nsIAtom* aAtom, nsIVariant* aValue);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get this element's client area rect in app units.
|
||||
|
@ -41,6 +41,8 @@
|
||||
#include "nsDOMCSSAttrDeclaration.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "nsContentList.h"
|
||||
#include "nsVariant.h"
|
||||
#include "nsDOMSettableTokenList.h"
|
||||
#include "nsDOMTokenList.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "nsError.h"
|
||||
@ -2526,6 +2528,81 @@ Element::GetLinkTarget(nsAString& aTarget)
|
||||
aTarget.Truncate();
|
||||
}
|
||||
|
||||
static void
|
||||
nsDOMSettableTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty,
|
||||
void *aPropertyValue, void *aData)
|
||||
{
|
||||
nsDOMSettableTokenList* list =
|
||||
static_cast<nsDOMSettableTokenList*>(aPropertyValue);
|
||||
NS_RELEASE(list);
|
||||
}
|
||||
|
||||
static nsIAtom** sPropertiesToTraverseAndUnlink[] =
|
||||
{
|
||||
&nsGkAtoms::microdataProperties,
|
||||
&nsGkAtoms::itemtype,
|
||||
&nsGkAtoms::itemref,
|
||||
&nsGkAtoms::itemprop,
|
||||
&nsGkAtoms::sandbox,
|
||||
&nsGkAtoms::sizes,
|
||||
nullptr
|
||||
};
|
||||
|
||||
// static
|
||||
nsIAtom***
|
||||
nsGenericHTMLElement::PropertiesToTraverseAndUnlink()
|
||||
{
|
||||
return sPropertiesToTraverseAndUnlink;
|
||||
}
|
||||
|
||||
nsDOMSettableTokenList*
|
||||
Element::GetTokenList(nsIAtom* aAtom)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
nsIAtom*** props =
|
||||
nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
|
||||
bool found = false;
|
||||
for (uint32_t i = 0; props[i]; ++i) {
|
||||
if (*props[i] == aAtom) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(found, "Trying to use an unknown tokenlist!");
|
||||
#endif
|
||||
|
||||
nsDOMSettableTokenList* list = nullptr;
|
||||
if (HasProperties()) {
|
||||
list = static_cast<nsDOMSettableTokenList*>(GetProperty(aAtom));
|
||||
}
|
||||
if (!list) {
|
||||
list = new nsDOMSettableTokenList(this, aAtom);
|
||||
NS_ADDREF(list);
|
||||
SetProperty(aAtom, list, nsDOMSettableTokenListPropertyDestructor);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
Element::GetTokenList(nsIAtom* aAtom, nsIVariant** aResult)
|
||||
{
|
||||
nsISupports* itemType = GetTokenList(aAtom);
|
||||
nsCOMPtr<nsIWritableVariant> out = new nsVariant();
|
||||
out->SetAsInterface(NS_GET_IID(nsISupports), itemType);
|
||||
out.forget(aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Element::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue)
|
||||
{
|
||||
nsDOMSettableTokenList* itemType = GetTokenList(aAtom);
|
||||
nsAutoString string;
|
||||
aValue->GetAsAString(string);
|
||||
ErrorResult rv;
|
||||
itemType->SetValue(string, rv);
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
bool
|
||||
Element::MozMatchesSelector(const nsAString& aSelector,
|
||||
ErrorResult& aError)
|
||||
|
@ -16,6 +16,7 @@ EXPORTS += [
|
||||
'nsContentSink.h',
|
||||
'nsCrossSiteListenerProxy.h',
|
||||
'nsDOMAttributeMap.h',
|
||||
'nsElementFrameLoaderOwner.h',
|
||||
'nsFrameMessageManager.h',
|
||||
'nsGenericDOMDataNode.h',
|
||||
'nsGkAtomList.h',
|
||||
@ -121,6 +122,7 @@ UNIFIED_SOURCES += [
|
||||
'nsDOMSerializer.cpp',
|
||||
'nsDOMSettableTokenList.cpp',
|
||||
'nsDOMTokenList.cpp',
|
||||
'nsElementFrameLoaderOwner.cpp',
|
||||
'nsFormData.cpp',
|
||||
'nsFrameLoader.cpp',
|
||||
'nsFrameMessageManager.cpp',
|
||||
@ -130,7 +132,6 @@ UNIFIED_SOURCES += [
|
||||
'nsHostObjectProtocolHandler.cpp',
|
||||
'nsHostObjectURI.cpp',
|
||||
'nsHTMLContentSerializer.cpp',
|
||||
'nsImageLoadingContent.cpp',
|
||||
'nsINode.cpp',
|
||||
'nsInProcessTabChildGlobal.cpp',
|
||||
'nsLineBreaker.cpp',
|
||||
@ -169,6 +170,10 @@ UNIFIED_SOURCES += [
|
||||
'WebSocket.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'nsImageLoadingContent.cpp',
|
||||
]
|
||||
|
||||
# These files cannot be built in unified mode because they use FORCE_PR_LOG
|
||||
SOURCES += [
|
||||
'nsDocument.cpp',
|
||||
|
@ -315,6 +315,13 @@ struct FireChangeArgs {
|
||||
bool mHaveImageOverride;
|
||||
};
|
||||
|
||||
// XXX Workaround for bug 980560 to maintain the existing broken semantics
|
||||
template<>
|
||||
struct nsIStyleRule::COMTypeInfo<css::Rule, void> {
|
||||
static const nsIID kIID NS_HIDDEN;
|
||||
};
|
||||
const nsIID nsIStyleRule::COMTypeInfo<css::Rule, void>::kIID = NS_ISTYLE_RULE_IID;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
160
content/base/src/nsElementFrameLoaderOwner.cpp
Normal file
160
content/base/src/nsElementFrameLoaderOwner.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsElementFrameLoaderOwner.h"
|
||||
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
nsElementFrameLoaderOwner::~nsElementFrameLoaderOwner()
|
||||
{
|
||||
if (mFrameLoader) {
|
||||
mFrameLoader->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsElementFrameLoaderOwner::GetContentDocument(nsIDOMDocument** aContentDocument)
|
||||
{
|
||||
NS_PRECONDITION(aContentDocument, "Null out param");
|
||||
nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(GetContentDocument());
|
||||
document.forget(aContentDocument);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
nsElementFrameLoaderOwner::GetContentDocument()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetContentWindow();
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument *doc = win->GetDoc();
|
||||
|
||||
// Return null for cross-origin contentDocument.
|
||||
if (!nsContentUtils::GetSubjectPrincipal()->
|
||||
SubsumesConsideringDomain(doc->NodePrincipal())) {
|
||||
return nullptr;
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsElementFrameLoaderOwner::GetContentWindow(nsIDOMWindow** aContentWindow)
|
||||
{
|
||||
NS_PRECONDITION(aContentWindow, "Null out param");
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetContentWindow();
|
||||
window.forget(aContentWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsPIDOMWindow>
|
||||
nsElementFrameLoaderOwner::GetContentWindow()
|
||||
{
|
||||
EnsureFrameLoader();
|
||||
|
||||
if (!mFrameLoader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool depthTooGreat = false;
|
||||
mFrameLoader->GetDepthTooGreat(&depthTooGreat);
|
||||
if (depthTooGreat) {
|
||||
// Claim to have no contentWindow
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> doc_shell;
|
||||
mFrameLoader->GetDocShell(getter_AddRefs(doc_shell));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(doc_shell);
|
||||
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_ASSERTION(win->IsOuterWindow(),
|
||||
"Uh, this window should always be an outer window!");
|
||||
|
||||
return win.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsElementFrameLoaderOwner::EnsureFrameLoader()
|
||||
{
|
||||
Element* thisElement = ThisFrameElement();
|
||||
if (!thisElement->GetParent() ||
|
||||
!thisElement->IsInDoc() ||
|
||||
mFrameLoader ||
|
||||
mFrameLoaderCreationDisallowed) {
|
||||
// If frame loader is there, we just keep it around, cached
|
||||
return;
|
||||
}
|
||||
|
||||
// Strangely enough, this method doesn't actually ensure that the
|
||||
// frameloader exists. It's more of a best-effort kind of thing.
|
||||
mFrameLoader = nsFrameLoader::Create(thisElement, mNetworkCreated);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsElementFrameLoaderOwner::GetFrameLoader(nsIFrameLoader **aFrameLoader)
|
||||
{
|
||||
NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
|
||||
nsElementFrameLoaderOwner::GetFrameLoader()
|
||||
{
|
||||
nsRefPtr<nsFrameLoader> loader = mFrameLoader;
|
||||
return loader.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsElementFrameLoaderOwner::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
|
||||
{
|
||||
// We don't support this yet
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsElementFrameLoaderOwner::LoadSrc()
|
||||
{
|
||||
EnsureFrameLoader();
|
||||
|
||||
if (!mFrameLoader) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = mFrameLoader->LoadFrame();
|
||||
#ifdef DEBUG
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("failed to load URL");
|
||||
}
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsElementFrameLoaderOwner::SwapFrameLoaders(nsXULElement& aOtherOwner,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
77
content/base/src/nsElementFrameLoaderOwner.h
Normal file
77
content/base/src/nsElementFrameLoaderOwner.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsElementFrameLoaderOwner_h
|
||||
#define nsElementFrameLoaderOwner_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsIFrameLoader.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "mozilla/dom/FromParser.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
#include "nsFrameLoader.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Element;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsXULElement;
|
||||
|
||||
/**
|
||||
* A helper class for frame elements
|
||||
*/
|
||||
class nsElementFrameLoaderOwner : public nsIFrameLoaderOwner
|
||||
{
|
||||
public:
|
||||
nsElementFrameLoaderOwner(mozilla::dom::FromParser aFromParser)
|
||||
: mNetworkCreated(aFromParser == mozilla::dom::FROM_PARSER_NETWORK)
|
||||
, mBrowserFrameListenersRegistered(false)
|
||||
, mFrameLoaderCreationDisallowed(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~nsElementFrameLoaderOwner();
|
||||
|
||||
NS_DECL_NSIFRAMELOADEROWNER
|
||||
|
||||
// nsIContent
|
||||
void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aError);
|
||||
|
||||
protected:
|
||||
// This doesn't really ensure a frame loader in all cases, only when
|
||||
// it makes sense.
|
||||
void EnsureFrameLoader();
|
||||
nsresult LoadSrc();
|
||||
nsIDocument* GetContentDocument();
|
||||
nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
|
||||
already_AddRefed<nsPIDOMWindow> GetContentWindow();
|
||||
nsresult GetContentWindow(nsIDOMWindow** aContentWindow);
|
||||
|
||||
/**
|
||||
* Get element for this frame. Avoids diamond inheritance problem.
|
||||
* @return Element for this node
|
||||
*/
|
||||
virtual mozilla::dom::Element* ThisFrameElement() = 0;
|
||||
|
||||
nsRefPtr<nsFrameLoader> mFrameLoader;
|
||||
|
||||
/**
|
||||
* True when the element is created by the parser using the
|
||||
* NS_FROM_PARSER_NETWORK flag.
|
||||
* If the element is modified, it may lose the flag.
|
||||
*/
|
||||
bool mNetworkCreated;
|
||||
|
||||
bool mBrowserFrameListenersRegistered;
|
||||
bool mFrameLoaderCreationDisallowed;
|
||||
};
|
||||
|
||||
#endif // nsElementFrameLoaderOwner_h
|
@ -86,6 +86,7 @@
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/dom/HTMLIFrameElement.h"
|
||||
#include "mozilla/dom/SVGIFrameElement.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "JavaScriptParent.h"
|
||||
|
||||
@ -318,7 +319,8 @@ nsFrameLoader::LoadFrame()
|
||||
|
||||
nsAutoString src;
|
||||
|
||||
bool isSrcdoc = mOwnerContent->IsHTML(nsGkAtoms::iframe) &&
|
||||
bool isSrcdoc = (mOwnerContent->IsHTML(nsGkAtoms::iframe) ||
|
||||
mOwnerContent->IsSVG(nsGkAtoms::iframe)) &&
|
||||
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
|
||||
if (isSrcdoc) {
|
||||
src.AssignLiteral("about:srcdoc");
|
||||
@ -518,7 +520,8 @@ nsFrameLoader::ReallyStartLoadingInternal()
|
||||
nsCOMPtr<nsIURI> referrer;
|
||||
|
||||
nsAutoString srcdoc;
|
||||
bool isSrcdoc = mOwnerContent->IsHTML(nsGkAtoms::iframe) &&
|
||||
bool isSrcdoc = (mOwnerContent->IsHTML(nsGkAtoms::iframe) ||
|
||||
mOwnerContent->IsSVG(nsGkAtoms::iframe)) &&
|
||||
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc,
|
||||
srcdoc);
|
||||
|
||||
@ -1603,8 +1606,13 @@ nsFrameLoader::MaybeCreateDocShell()
|
||||
// Apply sandbox flags even if our owner is not an iframe, as this copies
|
||||
// flags from our owning content's owning document.
|
||||
uint32_t sandboxFlags = 0;
|
||||
HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
|
||||
if (iframe) {
|
||||
if (!mOwnerContent->IsSVG(nsGkAtoms::iframe)) {
|
||||
HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
|
||||
if (iframe) {
|
||||
sandboxFlags = iframe->GetSandboxFlags();
|
||||
}
|
||||
} else {
|
||||
SVGIFrameElement* iframe = static_cast<SVGIFrameElement*>(mOwnerContent);
|
||||
sandboxFlags = iframe->GetSandboxFlags();
|
||||
}
|
||||
ApplySandboxFlags(sandboxFlags);
|
||||
@ -1620,7 +1628,8 @@ nsFrameLoader::MaybeCreateDocShell()
|
||||
nsAutoString frameName;
|
||||
|
||||
int32_t namespaceID = mOwnerContent->GetNameSpaceID();
|
||||
if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) {
|
||||
if ((namespaceID == kNameSpaceID_XHTML || namespaceID == kNameSpaceID_SVG)
|
||||
&& !mOwnerContent->IsInHTMLDocument()) {
|
||||
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
|
||||
} else {
|
||||
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName);
|
||||
|
@ -795,6 +795,7 @@ GK_ATOM(onreadystatechange, "onreadystatechange")
|
||||
GK_ATOM(onreceived, "onreceived")
|
||||
GK_ATOM(onremoteheld, "onremoteheld")
|
||||
GK_ATOM(onremoteresumed, "onremoteresumed")
|
||||
GK_ATOM(onresourcetimingbufferfull, "onresourcetimingbufferfull")
|
||||
GK_ATOM(onretrieving, "onretrieving")
|
||||
GK_ATOM(onRequest, "onRequest")
|
||||
GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus")
|
||||
|
@ -174,7 +174,7 @@ int main(int argc, char *argv[])
|
||||
// us some comfort margin.
|
||||
int repeat = std::min(maxBufferSize, 64);
|
||||
for (int i = 0; i < repeat; i++) {
|
||||
size_t size = RandomInteger<size_t>(1, maxBufferSize);
|
||||
size_t size = RandomInteger<size_t>(0, maxBufferSize);
|
||||
MakeRandomVector(v, size);
|
||||
b.BufferData(v.Elements(), size);
|
||||
CheckValidate(b, 0, size);
|
||||
|
@ -124,21 +124,13 @@ UpdateUpperBound(uint32_t* out_upperBound, uint32_t newBound)
|
||||
* case where each element array buffer is only ever used with one type, this is also addressed
|
||||
* by having WebGLElementArrayCache lazily create trees for each type only upon first use.
|
||||
*
|
||||
* Another consequence of this constraint is that when invalidating the trees, we have to invalidate
|
||||
* Another consequence of this constraint is that when updating the trees, we have to update
|
||||
* all existing trees. So if trees for types uint8_t, uint16_t and uint32_t have ever been constructed for this buffer,
|
||||
* every subsequent invalidation will have to invalidate all trees even if one of the types is never
|
||||
* used again. This implies that it is important to minimize the cost of invalidation i.e.
|
||||
* do lazy updates upon use as opposed to immediately updating invalidated trees. This poses a problem:
|
||||
* it is nontrivial to keep track of the part of the tree that's invalidated. The current solution
|
||||
* can only keep track of an invalidated interval, from |mFirstInvalidatedLeaf| to |mLastInvalidatedLeaf|.
|
||||
* The problem is that if one does two small, far-apart partial buffer updates, the resulting invalidated
|
||||
* area is very large even though only a small part of the array really needed to be invalidated.
|
||||
* The real solution to this problem would be to use a smarter data structure to keep track of the
|
||||
* invalidated area, probably an interval tree. Meanwhile, we can probably live with the current situation
|
||||
* as the unfavorable case seems to be a small corner case: in order to run into performance issues,
|
||||
* the number of bufferSubData in between two consecutive draws must be small but greater than 1, and
|
||||
* the partial buffer updates must be small and far apart. Anything else than this corner case
|
||||
* should run fast in the current setting.
|
||||
* every subsequent update will have to update all trees even if one of the types is never
|
||||
* used again. That's inefficient, but content should not put indices of different types in the
|
||||
* same element array buffer anyways. Different index types can only be consumed in separate
|
||||
* drawElements calls, so nothing particular is to be achieved by lumping them in the same
|
||||
* buffer object.
|
||||
*/
|
||||
template<typename T>
|
||||
struct WebGLElementArrayCacheTree
|
||||
@ -154,23 +146,20 @@ private:
|
||||
WebGLElementArrayCache& mParent;
|
||||
FallibleTArray<T> mTreeData;
|
||||
size_t mNumLeaves;
|
||||
bool mInvalidated;
|
||||
size_t mFirstInvalidatedLeaf;
|
||||
size_t mLastInvalidatedLeaf;
|
||||
size_t mParentByteSize;
|
||||
|
||||
public:
|
||||
WebGLElementArrayCacheTree(WebGLElementArrayCache& p)
|
||||
: mParent(p)
|
||||
, mNumLeaves(0)
|
||||
, mInvalidated(false)
|
||||
, mFirstInvalidatedLeaf(0)
|
||||
, mLastInvalidatedLeaf(0)
|
||||
, mParentByteSize(0)
|
||||
{
|
||||
ResizeToParentSize();
|
||||
if (mParent.ByteSize()) {
|
||||
Update(0, mParent.ByteSize() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
T GlobalMaximum() const {
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
return mTreeData[1];
|
||||
}
|
||||
|
||||
@ -242,14 +231,13 @@ public:
|
||||
}
|
||||
|
||||
static size_t NextMultipleOfElementsPerLeaf(size_t numElements) {
|
||||
MOZ_ASSERT(numElements >= 1);
|
||||
return ((numElements - 1) | sElementsPerLeafMask) + 1;
|
||||
}
|
||||
|
||||
bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf,
|
||||
uint32_t* out_upperBound)
|
||||
{
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
|
||||
size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
|
||||
size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
|
||||
|
||||
@ -310,28 +298,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ResizeToParentSize()
|
||||
{
|
||||
size_t numberOfElements = mParent.ByteSize() / sizeof(T);
|
||||
size_t requiredNumLeaves = (numberOfElements + sElementsPerLeaf - 1) / sElementsPerLeaf;
|
||||
|
||||
size_t oldNumLeaves = mNumLeaves;
|
||||
mNumLeaves = NextPowerOfTwo(requiredNumLeaves);
|
||||
Invalidate(0, mParent.ByteSize() - 1);
|
||||
|
||||
// see class comment for why we the tree storage size is 2 * mNumLeaves
|
||||
if (!mTreeData.SetLength(2 * mNumLeaves)) {
|
||||
return false;
|
||||
}
|
||||
if (mNumLeaves != oldNumLeaves) {
|
||||
memset(mTreeData.Elements(), 0, mTreeData.Length() * sizeof(mTreeData[0]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Invalidate(size_t firstByte, size_t lastByte);
|
||||
|
||||
void Update();
|
||||
bool Update(size_t firstByte, size_t lastByte);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
@ -362,44 +329,53 @@ struct TreeForType<uint32_t>
|
||||
static WebGLElementArrayCacheTree<uint32_t>*& Run(WebGLElementArrayCache *b) { return b->mUint32Tree; }
|
||||
};
|
||||
|
||||
// When the buffer gets updated from firstByte to lastByte,
|
||||
// calling this method will notify the tree accordingly
|
||||
// Calling this method will 1) update the leaves in this interval
|
||||
// from the raw buffer data, and 2) propagate this update up the tree
|
||||
template<typename T>
|
||||
void WebGLElementArrayCacheTree<T>::Invalidate(size_t firstByte, size_t lastByte)
|
||||
bool WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
||||
{
|
||||
MOZ_ASSERT(firstByte <= lastByte);
|
||||
MOZ_ASSERT(lastByte < mParent.ByteSize());
|
||||
|
||||
// Step #0: if needed, resize our tree data storage.
|
||||
if (mParentByteSize != mParent.ByteSize())
|
||||
{
|
||||
size_t numberOfElements = mParent.ByteSize() / sizeof(T);
|
||||
if (numberOfElements == 0) {
|
||||
mParentByteSize = mParent.ByteSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t requiredNumLeaves = (numberOfElements + sElementsPerLeaf - 1) / sElementsPerLeaf;
|
||||
size_t oldNumLeaves = mNumLeaves;
|
||||
mNumLeaves = NextPowerOfTwo(requiredNumLeaves);
|
||||
if (mNumLeaves != oldNumLeaves) {
|
||||
// see class comment for why we the tree storage size is 2 * mNumLeaves
|
||||
if (!mTreeData.SetLength(2 * mNumLeaves)) {
|
||||
return false;
|
||||
}
|
||||
// when resizing, update the whole tree, not just the subset corresponding
|
||||
// to the part of the buffer being updated.
|
||||
memset(mTreeData.Elements(), 0, mTreeData.Length() * sizeof(T));
|
||||
firstByte = 0;
|
||||
lastByte = mParent.ByteSize() - 1;
|
||||
}
|
||||
|
||||
mParentByteSize = mParent.ByteSize();
|
||||
}
|
||||
|
||||
lastByte = std::min(lastByte, mNumLeaves * sElementsPerLeaf * sizeof(T) - 1);
|
||||
if (firstByte > lastByte) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t firstLeaf = LeafForByte(firstByte);
|
||||
size_t lastLeaf = LeafForByte(lastByte);
|
||||
|
||||
if (mInvalidated) {
|
||||
mFirstInvalidatedLeaf = std::min(firstLeaf, mFirstInvalidatedLeaf);
|
||||
mLastInvalidatedLeaf = std::max(lastLeaf, mLastInvalidatedLeaf);
|
||||
} else {
|
||||
mInvalidated = true;
|
||||
mFirstInvalidatedLeaf = firstLeaf;
|
||||
mLastInvalidatedLeaf = lastLeaf;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(firstLeaf <= lastLeaf && lastLeaf < mNumLeaves);
|
||||
|
||||
|
||||
// When tree has been partially invalidated, from mFirstInvalidatedLeaf to
|
||||
// mLastInvalidatedLeaf, calling this method will 1) update the leaves in this interval
|
||||
// from the raw buffer data, and 2) propagate this update up the tree
|
||||
template<typename T>
|
||||
void WebGLElementArrayCacheTree<T>::Update()
|
||||
{
|
||||
if (!mInvalidated) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mLastInvalidatedLeaf < mNumLeaves);
|
||||
|
||||
size_t firstTreeIndex = TreeIndexForLeaf(mFirstInvalidatedLeaf);
|
||||
size_t lastTreeIndex = TreeIndexForLeaf(mLastInvalidatedLeaf);
|
||||
size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
|
||||
size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
|
||||
|
||||
// Step #1: initialize the tree leaves from plain buffer data.
|
||||
// That is, each tree leaf must be set to the max of the |sElementsPerLeaf| corresponding
|
||||
@ -409,8 +385,8 @@ void WebGLElementArrayCacheTree<T>::Update()
|
||||
// treeIndex is the index of the tree leaf we're writing, i.e. the destination index
|
||||
size_t treeIndex = firstTreeIndex;
|
||||
// srcIndex is the index in the source buffer
|
||||
size_t srcIndex = mFirstInvalidatedLeaf * sElementsPerLeaf;
|
||||
size_t numberOfElements = mParent.ByteSize() / sizeof(T);
|
||||
size_t srcIndex = firstLeaf * sElementsPerLeaf;
|
||||
size_t numberOfElements = mParentByteSize / sizeof(T);
|
||||
while (treeIndex <= lastTreeIndex) {
|
||||
T m = 0;
|
||||
size_t a = srcIndex;
|
||||
@ -426,12 +402,11 @@ void WebGLElementArrayCacheTree<T>::Update()
|
||||
// Step #2: propagate the values up the tree. This is simply a matter of walking up
|
||||
// the tree and setting each node to the max of its two children.
|
||||
while (firstTreeIndex > 1) {
|
||||
|
||||
// move up 1 level
|
||||
firstTreeIndex = ParentNode(firstTreeIndex);
|
||||
lastTreeIndex = ParentNode(lastTreeIndex);
|
||||
|
||||
// fast-exit case where only one node is invalidated at the current level
|
||||
// fast-exit case where only one node is updated at the current level
|
||||
if (firstTreeIndex == lastTreeIndex) {
|
||||
mTreeData[firstTreeIndex] = std::max(mTreeData[LeftChildNode(firstTreeIndex)], mTreeData[RightChildNode(firstTreeIndex)]);
|
||||
continue;
|
||||
@ -468,7 +443,7 @@ void WebGLElementArrayCacheTree<T>::Update()
|
||||
}
|
||||
}
|
||||
|
||||
mInvalidated = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
WebGLElementArrayCache::~WebGLElementArrayCache() {
|
||||
@ -479,40 +454,43 @@ WebGLElementArrayCache::~WebGLElementArrayCache() {
|
||||
}
|
||||
|
||||
bool WebGLElementArrayCache::BufferData(const void* ptr, size_t byteSize) {
|
||||
mByteSize = byteSize;
|
||||
if (mUint8Tree)
|
||||
if (!mUint8Tree->ResizeToParentSize())
|
||||
if (byteSize == 0) {
|
||||
mByteSize = 0;
|
||||
free(mUntypedData);
|
||||
mUntypedData = nullptr;
|
||||
return true;
|
||||
}
|
||||
if (byteSize != mByteSize) {
|
||||
void* newUntypedData = realloc(mUntypedData, byteSize);
|
||||
if (!newUntypedData)
|
||||
return false;
|
||||
if (mUint16Tree)
|
||||
if (!mUint16Tree->ResizeToParentSize())
|
||||
return false;
|
||||
if (mUint32Tree)
|
||||
if (!mUint32Tree->ResizeToParentSize())
|
||||
return false;
|
||||
mUntypedData = realloc(mUntypedData, byteSize);
|
||||
if (!mUntypedData)
|
||||
return false;
|
||||
mByteSize = byteSize;
|
||||
mUntypedData = newUntypedData;
|
||||
}
|
||||
|
||||
BufferSubData(0, ptr, byteSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteSize) {
|
||||
if (!updateByteSize) return;
|
||||
bool WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteSize) {
|
||||
if (!updateByteSize) return true;
|
||||
if (ptr)
|
||||
memcpy(static_cast<uint8_t*>(mUntypedData) + pos, ptr, updateByteSize);
|
||||
else
|
||||
memset(static_cast<uint8_t*>(mUntypedData) + pos, 0, updateByteSize);
|
||||
InvalidateTrees(pos, pos + updateByteSize - 1);
|
||||
return UpdateTrees(pos, pos + updateByteSize - 1);
|
||||
}
|
||||
|
||||
void WebGLElementArrayCache::InvalidateTrees(size_t firstByte, size_t lastByte)
|
||||
bool WebGLElementArrayCache::UpdateTrees(size_t firstByte, size_t lastByte)
|
||||
{
|
||||
bool result = true;
|
||||
if (mUint8Tree)
|
||||
mUint8Tree->Invalidate(firstByte, lastByte);
|
||||
result &= mUint8Tree->Update(firstByte, lastByte);
|
||||
if (mUint16Tree)
|
||||
mUint16Tree->Invalidate(firstByte, lastByte);
|
||||
result &= mUint16Tree->Update(firstByte, lastByte);
|
||||
if (mUint32Tree)
|
||||
mUint32Tree->Invalidate(firstByte, lastByte);
|
||||
result &= mUint32Tree->Update(firstByte, lastByte);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -545,8 +523,6 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
||||
|
||||
size_t lastElement = firstElement + countElements - 1;
|
||||
|
||||
tree->Update();
|
||||
|
||||
// fast exit path when the global maximum for the whole element array buffer
|
||||
// falls in the allowed range
|
||||
T globalMax = tree->GlobalMaximum();
|
||||
|
@ -31,7 +31,7 @@ class WebGLElementArrayCache {
|
||||
|
||||
public:
|
||||
bool BufferData(const void* ptr, size_t byteSize);
|
||||
void BufferSubData(size_t pos, const void* ptr, size_t updateByteSize);
|
||||
bool BufferSubData(size_t pos, const void* ptr, size_t updateByteSize);
|
||||
|
||||
bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count,
|
||||
uint32_t* out_upperBound = nullptr);
|
||||
@ -66,7 +66,7 @@ private:
|
||||
template<typename T>
|
||||
T* Elements() { return static_cast<T*>(mUntypedData); }
|
||||
|
||||
void InvalidateTrees(size_t firstByte, size_t lastByte);
|
||||
bool UpdateTrees(size_t firstByte, size_t lastByte);
|
||||
|
||||
template<typename T>
|
||||
friend struct WebGLElementArrayCacheTree;
|
||||
|
@ -3138,81 +3138,6 @@ nsGenericHTMLElement::SetItemValueText(const nsAString& text)
|
||||
SetTextContentInternal(text, rv);
|
||||
}
|
||||
|
||||
static void
|
||||
nsDOMSettableTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty,
|
||||
void *aPropertyValue, void *aData)
|
||||
{
|
||||
nsDOMSettableTokenList* list =
|
||||
static_cast<nsDOMSettableTokenList*>(aPropertyValue);
|
||||
NS_RELEASE(list);
|
||||
}
|
||||
|
||||
static nsIAtom** sPropertiesToTraverseAndUnlink[] =
|
||||
{
|
||||
&nsGkAtoms::microdataProperties,
|
||||
&nsGkAtoms::itemtype,
|
||||
&nsGkAtoms::itemref,
|
||||
&nsGkAtoms::itemprop,
|
||||
&nsGkAtoms::sandbox,
|
||||
&nsGkAtoms::sizes,
|
||||
nullptr
|
||||
};
|
||||
|
||||
// static
|
||||
nsIAtom***
|
||||
nsGenericHTMLElement::PropertiesToTraverseAndUnlink()
|
||||
{
|
||||
return sPropertiesToTraverseAndUnlink;
|
||||
}
|
||||
|
||||
nsDOMSettableTokenList*
|
||||
nsGenericHTMLElement::GetTokenList(nsIAtom* aAtom)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
nsIAtom*** props =
|
||||
nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
|
||||
bool found = false;
|
||||
for (uint32_t i = 0; props[i]; ++i) {
|
||||
if (*props[i] == aAtom) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(found, "Trying to use an unknown tokenlist!");
|
||||
#endif
|
||||
|
||||
nsDOMSettableTokenList* list = nullptr;
|
||||
if (HasProperties()) {
|
||||
list = static_cast<nsDOMSettableTokenList*>(GetProperty(aAtom));
|
||||
}
|
||||
if (!list) {
|
||||
list = new nsDOMSettableTokenList(this, aAtom);
|
||||
NS_ADDREF(list);
|
||||
SetProperty(aAtom, list, nsDOMSettableTokenListPropertyDestructor);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLElement::GetTokenList(nsIAtom* aAtom, nsIVariant** aResult)
|
||||
{
|
||||
nsISupports* itemType = GetTokenList(aAtom);
|
||||
nsCOMPtr<nsIWritableVariant> out = new nsVariant();
|
||||
out->SetAsInterface(NS_GET_IID(nsISupports), itemType);
|
||||
out.forget(aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLElement::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue)
|
||||
{
|
||||
nsDOMSettableTokenList* itemType = GetTokenList(aAtom);
|
||||
nsAutoString string;
|
||||
aValue->GetAsAString(string);
|
||||
ErrorResult rv;
|
||||
itemType->SetValue(string, rv);
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
static void
|
||||
HTMLPropertiesCollectionDestructor(void *aObject, nsIAtom *aProperty,
|
||||
void *aPropertyValue, void *aData)
|
||||
|
@ -303,9 +303,6 @@ protected:
|
||||
// when an element has @itemprop but no @itemscope.
|
||||
virtual void GetItemValueText(nsAString& text);
|
||||
virtual void SetItemValueText(const nsAString& text);
|
||||
nsDOMSettableTokenList* GetTokenList(nsIAtom* aAtom);
|
||||
void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult);
|
||||
nsresult SetTokenList(nsIAtom* aAtom, nsIVariant* aValue);
|
||||
public:
|
||||
virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager();
|
||||
virtual bool UndoScope() MOZ_OVERRIDE;
|
||||
|
@ -39,11 +39,10 @@ NS_IMPL_RELEASE_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsGenericHTMLFrameElement)
|
||||
NS_INTERFACE_TABLE_INHERITED(nsGenericHTMLFrameElement,
|
||||
nsIFrameLoaderOwner,
|
||||
nsIDOMMozBrowserFrame,
|
||||
nsIMozBrowserFrame)
|
||||
nsIMozBrowserFrame,
|
||||
nsIFrameLoaderOwner)
|
||||
NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
|
||||
|
||||
NS_IMPL_BOOL_ATTR(nsGenericHTMLFrameElement, Mozbrowser, mozbrowser)
|
||||
|
||||
int32_t
|
||||
@ -52,92 +51,6 @@ nsGenericHTMLFrameElement::TabIndexDefault()
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
|
||||
{
|
||||
if (mFrameLoader) {
|
||||
mFrameLoader->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
|
||||
{
|
||||
NS_PRECONDITION(aContentDocument, "Null out param");
|
||||
nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(GetContentDocument());
|
||||
document.forget(aContentDocument);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
nsGenericHTMLFrameElement::GetContentDocument()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetContentWindow();
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument *doc = win->GetDoc();
|
||||
|
||||
// Return null for cross-origin contentDocument.
|
||||
if (!nsContentUtils::GetSubjectPrincipal()->SubsumesConsideringDomain(doc->NodePrincipal())) {
|
||||
return nullptr;
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLFrameElement::GetContentWindow(nsIDOMWindow** aContentWindow)
|
||||
{
|
||||
NS_PRECONDITION(aContentWindow, "Null out param");
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetContentWindow();
|
||||
window.forget(aContentWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsPIDOMWindow>
|
||||
nsGenericHTMLFrameElement::GetContentWindow()
|
||||
{
|
||||
EnsureFrameLoader();
|
||||
|
||||
if (!mFrameLoader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool depthTooGreat = false;
|
||||
mFrameLoader->GetDepthTooGreat(&depthTooGreat);
|
||||
if (depthTooGreat) {
|
||||
// Claim to have no contentWindow
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> doc_shell;
|
||||
mFrameLoader->GetDocShell(getter_AddRefs(doc_shell));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(doc_shell);
|
||||
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_ASSERTION(win->IsOuterWindow(),
|
||||
"Uh, this window should always be an outer window!");
|
||||
|
||||
return win.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLFrameElement::EnsureFrameLoader()
|
||||
{
|
||||
if (!GetParent() || !IsInDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
|
||||
// If frame loader is there, we just keep it around, cached
|
||||
return;
|
||||
}
|
||||
|
||||
// Strangely enough, this method doesn't actually ensure that the
|
||||
// frameloader exists. It's more of a best-effort kind of thing.
|
||||
mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLFrameElement::CreateRemoteFrameLoader(nsITabParent* aTabParent)
|
||||
{
|
||||
@ -148,46 +61,6 @@ nsGenericHTMLFrameElement::CreateRemoteFrameLoader(nsITabParent* aTabParent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGenericHTMLFrameElement::GetFrameLoader(nsIFrameLoader **aFrameLoader)
|
||||
{
|
||||
NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
|
||||
nsGenericHTMLFrameElement::GetFrameLoader()
|
||||
{
|
||||
nsRefPtr<nsFrameLoader> loader = mFrameLoader;
|
||||
return loader.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
|
||||
{
|
||||
// We don't support this yet
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLFrameElement::LoadSrc()
|
||||
{
|
||||
EnsureFrameLoader();
|
||||
|
||||
if (!mFrameLoader) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = mFrameLoader->LoadFrame();
|
||||
#ifdef DEBUG
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("failed to load URL");
|
||||
}
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLFrameElement::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aParent,
|
||||
@ -511,11 +384,3 @@ nsGenericHTMLFrameElement::AllowCreateFrameLoader()
|
||||
mFrameLoaderCreationDisallowed = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLFrameElement::SwapFrameLoaders(nsXULElement& aOtherOwner,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define nsGenericHTMLFrameElement_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsElementFrameLoaderOwner.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsIFrameLoader.h"
|
||||
#include "nsIMozBrowserFrame.h"
|
||||
@ -23,24 +24,19 @@ class nsXULElement;
|
||||
* A helper class for frame elements
|
||||
*/
|
||||
class nsGenericHTMLFrameElement : public nsGenericHTMLElement,
|
||||
public nsIFrameLoaderOwner,
|
||||
public nsElementFrameLoaderOwner,
|
||||
public nsIMozBrowserFrame
|
||||
{
|
||||
public:
|
||||
nsGenericHTMLFrameElement(already_AddRefed<nsINodeInfo>& aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser)
|
||||
: nsGenericHTMLElement(aNodeInfo)
|
||||
, mNetworkCreated(aFromParser == mozilla::dom::FROM_PARSER_NETWORK)
|
||||
, mBrowserFrameListenersRegistered(false)
|
||||
, mFrameLoaderCreationDisallowed(false)
|
||||
, nsElementFrameLoaderOwner(aFromParser)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~nsGenericHTMLFrameElement();
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIFRAMELOADEROWNER
|
||||
NS_DECL_NSIDOMMOZBROWSERFRAME
|
||||
NS_DECL_NSIMOZBROWSERFRAME
|
||||
|
||||
@ -73,8 +69,6 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsGenericHTMLFrameElement,
|
||||
nsGenericHTMLElement)
|
||||
|
||||
void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aError);
|
||||
|
||||
static bool BrowserFramesEnabled();
|
||||
|
||||
/**
|
||||
@ -88,24 +82,10 @@ public:
|
||||
static int32_t MapScrollingAttribute(const nsAttrValue* aValue);
|
||||
|
||||
protected:
|
||||
// This doesn't really ensure a frame loade in all cases, only when
|
||||
// it makes sense.
|
||||
void EnsureFrameLoader();
|
||||
nsresult LoadSrc();
|
||||
nsIDocument* GetContentDocument();
|
||||
nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
|
||||
already_AddRefed<nsPIDOMWindow> GetContentWindow();
|
||||
nsresult GetContentWindow(nsIDOMWindow** aContentWindow);
|
||||
|
||||
nsRefPtr<nsFrameLoader> mFrameLoader;
|
||||
|
||||
// True when the element is created by the parser
|
||||
// using NS_FROM_PARSER_NETWORK flag.
|
||||
// If the element is modified, it may lose the flag.
|
||||
bool mNetworkCreated;
|
||||
|
||||
bool mBrowserFrameListenersRegistered;
|
||||
bool mFrameLoaderCreationDisallowed;
|
||||
virtual mozilla::dom::Element* ThisFrameElement() MOZ_OVERRIDE
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // nsGenericHTMLFrameElement_h
|
||||
|
@ -245,14 +245,9 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
|
||||
NS_ASSERTION(!mPendingWakeDecoder.get(),
|
||||
"WakeDecoder should have been revoked already");
|
||||
|
||||
if (mDecodeTaskQueue) {
|
||||
mDecodeTaskQueue->Shutdown();
|
||||
mDecodeTaskQueue = nullptr;
|
||||
}
|
||||
|
||||
// No need to cancel the timer here for we've done that in
|
||||
// TimeoutExpired() triggered by Shutdown()
|
||||
mTimer = nullptr;
|
||||
MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN");
|
||||
// No need to cancel the timer here for we've done that in SHUTDOWN.
|
||||
MOZ_ASSERT(!mTimer, "Should be released in SHUTDOWN");
|
||||
mReader = nullptr;
|
||||
|
||||
#ifdef XP_WIN
|
||||
@ -1584,13 +1579,13 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
|
||||
return;
|
||||
}
|
||||
mIsReaderIdle = needIdle;
|
||||
nsRefPtr<nsIRunnable> event;
|
||||
RefPtr<nsIRunnable> event;
|
||||
if (mIsReaderIdle) {
|
||||
event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderIdle);
|
||||
} else {
|
||||
event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderActive);
|
||||
}
|
||||
if (NS_FAILED(mDecodeTaskQueue->Dispatch(event)) &&
|
||||
if (NS_FAILED(mDecodeTaskQueue->Dispatch(event.forget())) &&
|
||||
mState != DECODER_STATE_SHUTDOWN) {
|
||||
NS_WARNING("Failed to dispatch event to set decoder idle state");
|
||||
}
|
||||
@ -2149,6 +2144,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
// Wait for the thread decoding to exit.
|
||||
mDecodeTaskQueue->Shutdown();
|
||||
mDecodeTaskQueue = nullptr;
|
||||
mReader->ReleaseMediaResources();
|
||||
}
|
||||
// Now that those threads are stopped, there's no possibility of
|
||||
@ -2172,6 +2168,9 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
// state machine.
|
||||
GetStateMachineThread()->Dispatch(
|
||||
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
|
||||
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ MediaTaskQueue::~MediaTaskQueue()
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaTaskQueue::Dispatch(nsIRunnable* aRunnable)
|
||||
MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
|
||||
{
|
||||
MonitorAutoLock mon(mQueueMonitor);
|
||||
if (mIsShutdown) {
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
|
||||
MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool);
|
||||
|
||||
nsresult Dispatch(nsIRunnable* aRunnable);
|
||||
nsresult Dispatch(TemporaryRef<nsIRunnable> aRunnable);
|
||||
|
||||
// Removes all pending tasks from the task queue, and blocks until
|
||||
// the currently running task (if any) finishes.
|
||||
|
@ -40,7 +40,7 @@ namespace mozilla {
|
||||
// Uncomment to enable verbose per-sample logging.
|
||||
//#define LOG_SAMPLE_DECODE 1
|
||||
|
||||
class MP4Stream : public mp4_demuxer::Stream {
|
||||
class MP4Stream : public Stream {
|
||||
public:
|
||||
|
||||
MP4Stream(MediaResource* aResource)
|
||||
@ -265,7 +265,7 @@ MP4Reader::SampleQueue(TrackType aTrack)
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
MP4Reader::Decoder(mp4_demuxer::TrackType aTrack)
|
||||
MP4Reader::Decoder(TrackType aTrack)
|
||||
{
|
||||
return GetDecoderData(aTrack).mDecoder;
|
||||
}
|
||||
@ -385,7 +385,7 @@ TrackTypeToStr(TrackType aTrack)
|
||||
#endif
|
||||
|
||||
void
|
||||
MP4Reader::Output(mp4_demuxer::TrackType aTrack, MediaData* aSample)
|
||||
MP4Reader::Output(TrackType aTrack, MediaData* aSample)
|
||||
{
|
||||
#ifdef LOG_SAMPLE_DECODE
|
||||
LOG("Decoded %s sample time=%lld dur=%lld",
|
||||
@ -420,7 +420,7 @@ MP4Reader::Output(mp4_demuxer::TrackType aTrack, MediaData* aSample)
|
||||
}
|
||||
|
||||
void
|
||||
MP4Reader::InputExhausted(mp4_demuxer::TrackType aTrack)
|
||||
MP4Reader::InputExhausted(TrackType aTrack)
|
||||
{
|
||||
DecoderData& data = GetDecoderData(aTrack);
|
||||
MonitorAutoLock mon(data.mMonitor);
|
||||
@ -429,7 +429,7 @@ MP4Reader::InputExhausted(mp4_demuxer::TrackType aTrack)
|
||||
}
|
||||
|
||||
void
|
||||
MP4Reader::Error(mp4_demuxer::TrackType aTrack)
|
||||
MP4Reader::Error(TrackType aTrack)
|
||||
{
|
||||
DecoderData& data = GetDecoderData(aTrack);
|
||||
MonitorAutoLock mon(data.mMonitor);
|
||||
@ -445,7 +445,7 @@ MP4Reader::DecodeAudioData()
|
||||
}
|
||||
|
||||
void
|
||||
MP4Reader::Flush(mp4_demuxer::TrackType aTrack)
|
||||
MP4Reader::Flush(TrackType aTrack)
|
||||
{
|
||||
DecoderData& data = GetDecoderData(aTrack);
|
||||
if (!data.mDecoder) {
|
||||
|
@ -23,10 +23,6 @@ using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
#define ENCODER_CONFIG_BITRATE 2000000 // bps
|
||||
// How many seconds between I-frames.
|
||||
#define ENCODER_CONFIG_I_FRAME_INTERVAL 1
|
||||
// Wait up to 5ms for input buffers.
|
||||
#define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
|
||||
// AMR NB kbps
|
||||
#define AMRNB_BITRATE 12200
|
||||
@ -137,27 +133,20 @@ IsRunningOnEmulator()
|
||||
return strncmp(qemu, "1", 1) == 0;
|
||||
}
|
||||
|
||||
#define ENCODER_CONFIG_BITRATE 2000000 // bps
|
||||
// How many seconds between I-frames.
|
||||
#define ENCODER_CONFIG_I_FRAME_INTERVAL 1
|
||||
// Wait up to 5ms for input buffers.
|
||||
|
||||
nsresult
|
||||
OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate,
|
||||
BlobFormat aBlobFormat)
|
||||
{
|
||||
MOZ_ASSERT(!mStarted, "Configure() was called already.");
|
||||
|
||||
NS_ENSURE_TRUE(aWidth > 0 && aHeight > 0 && aFrameRate > 0,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
OMX_VIDEO_AVCLEVELTYPE level = OMX_VIDEO_AVCLevel3;
|
||||
OMX_VIDEO_CONTROLRATETYPE bitrateMode = OMX_Video_ControlRateConstant;
|
||||
// Limitation of soft AVC/H.264 encoder running on emulator in stagefright.
|
||||
static bool emu = IsRunningOnEmulator();
|
||||
if (emu) {
|
||||
if (aWidth > 352 || aHeight > 288) {
|
||||
CODEC_ERROR("SoftAVCEncoder doesn't support resolution larger than CIF");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
level = OMX_VIDEO_AVCLevel2;
|
||||
bitrateMode = OMX_Video_ControlRateVariable;
|
||||
}
|
||||
|
||||
// Set up configuration parameters for AVC/H.264 encoder.
|
||||
sp<AMessage> format = new AMessage;
|
||||
@ -180,12 +169,42 @@ OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate,
|
||||
format->setInt32("slice-height", aHeight);
|
||||
format->setInt32("frame-rate", aFrameRate);
|
||||
|
||||
status_t result = mCodec->configure(format, nullptr, nullptr,
|
||||
return ConfigureDirect(format, aBlobFormat);
|
||||
}
|
||||
|
||||
nsresult
|
||||
OMXVideoEncoder::ConfigureDirect(sp<AMessage>& aFormat,
|
||||
BlobFormat aBlobFormat)
|
||||
{
|
||||
MOZ_ASSERT(!mStarted, "Configure() was called already.");
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int frameRate = 0;
|
||||
aFormat->findInt32("width", &width);
|
||||
aFormat->findInt32("height", &height);
|
||||
aFormat->findInt32("frame-rate", &frameRate);
|
||||
NS_ENSURE_TRUE(width > 0 && height > 0 && frameRate > 0,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
// Limitation of soft AVC/H.264 encoder running on emulator in stagefright.
|
||||
static bool emu = IsRunningOnEmulator();
|
||||
if (emu) {
|
||||
if (width > 352 || height > 288) {
|
||||
CODEC_ERROR("SoftAVCEncoder doesn't support resolution larger than CIF");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
aFormat->setInt32("level", OMX_VIDEO_AVCLevel2);
|
||||
aFormat->setInt32("bitrate-mode", OMX_Video_ControlRateVariable);
|
||||
}
|
||||
|
||||
|
||||
status_t result = mCodec->configure(aFormat, nullptr, nullptr,
|
||||
MediaCodec::CONFIGURE_FLAG_ENCODE);
|
||||
NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
|
||||
|
||||
mWidth = aWidth;
|
||||
mHeight = aHeight;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mBlobFormat = aBlobFormat;
|
||||
|
||||
result = Start();
|
||||
|
@ -254,9 +254,13 @@ public:
|
||||
* before calling Encode() and GetNextEncodedFrame().
|
||||
* aBlobFormat specifies output blob format provided by encoder. It can be
|
||||
* AVC_MP4 or AVC_NAL.
|
||||
* Configure sets up most format value to values appropriate for camera use.
|
||||
* ConfigureDirect lets the caller determine all the defaults.
|
||||
*/
|
||||
nsresult Configure(int aWidth, int aHeight, int aFrameRate,
|
||||
BlobFormat aBlobFormat = BlobFormat::AVC_MP4);
|
||||
nsresult ConfigureDirect(sp<AMessage>& aFormat,
|
||||
BlobFormat aBlobFormat = BlobFormat::AVC_MP4);
|
||||
|
||||
/**
|
||||
* Encode a aWidth pixels wide and aHeight pixels tall video frame of
|
||||
|
@ -144,9 +144,7 @@ bool MediaPluginReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
if (mLastVideoFrame) {
|
||||
int64_t durationUs;
|
||||
mPlugin->GetDuration(mPlugin, &durationUs);
|
||||
if (durationUs < mLastVideoFrame->mTime) {
|
||||
durationUs = 0;
|
||||
}
|
||||
durationUs = std::max<int64_t>(durationUs - mLastVideoFrame->mTime, 0);
|
||||
mVideoQueue.Push(VideoData::ShallowCopyUpdateDuration(mLastVideoFrame,
|
||||
durationUs));
|
||||
mLastVideoFrame = nullptr;
|
||||
|
344
content/svg/content/src/SVGIFrameElement.cpp
Normal file
344
content/svg/content/src/SVGIFrameElement.cpp
Normal file
@ -0,0 +1,344 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SVGIFrameElement.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "mozilla/dom/SVGDocumentBinding.h"
|
||||
#include "mozilla/dom/SVGIFrameElementBinding.h"
|
||||
#include "mozilla/dom/SVGMatrix.h"
|
||||
#include "mozilla/dom/SVGSVGElement.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsStyleConsts.h"
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(IFrame)
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
JSObject*
|
||||
SVGIFrameElement::WrapNode(JSContext *aCx)
|
||||
{
|
||||
return SVGIFrameElementBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
//--------------------- IFrame ------------------------
|
||||
|
||||
nsSVGElement::LengthInfo SVGIFrameElement::sLengthInfo[4] =
|
||||
{
|
||||
{ &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
|
||||
{ &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
|
||||
{ &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
|
||||
{ &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISupports methods
|
||||
NS_IMPL_ISUPPORTS_INHERITED(SVGIFrameElement, SVGIFrameElementBase,
|
||||
nsIFrameLoaderOwner,
|
||||
nsIDOMNode, nsIDOMElement,
|
||||
nsIDOMSVGElement)
|
||||
//----------------------------------------------------------------------
|
||||
// Implementation
|
||||
|
||||
SVGIFrameElement::SVGIFrameElement(already_AddRefed<nsINodeInfo>& aNodeInfo,
|
||||
FromParser aFromParser)
|
||||
: SVGIFrameElementBase(aNodeInfo)
|
||||
, nsElementFrameLoaderOwner(aFromParser)
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
/* virtual */ gfxMatrix
|
||||
SVGIFrameElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
|
||||
TransformTypes aWhich) const
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(),
|
||||
"Skipping eUserSpaceToParent transforms makes no sense");
|
||||
|
||||
// 'transform' attribute:
|
||||
gfxMatrix fromUserSpace =
|
||||
SVGGraphicsElement::PrependLocalTransformsTo(aMatrix, aWhich);
|
||||
if (aWhich == eUserSpaceToParent) {
|
||||
return fromUserSpace;
|
||||
}
|
||||
// our 'x' and 'y' attributes:
|
||||
float x, y;
|
||||
const_cast<SVGIFrameElement*>(this)->
|
||||
GetAnimatedLengthValues(&x, &y, nullptr);
|
||||
gfxMatrix toUserSpace = gfxMatrix().Translate(gfxPoint(x, y));
|
||||
if (aWhich == eChildToUserSpace) {
|
||||
return toUserSpace;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(aWhich == eAllTransforms, "Unknown TransformTypes");
|
||||
return toUserSpace * fromUserSpace;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMNode methods
|
||||
|
||||
nsresult
|
||||
SVGIFrameElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
|
||||
{
|
||||
*aResult = nullptr;
|
||||
already_AddRefed<nsINodeInfo> ni = nsCOMPtr<nsINodeInfo>(aNodeInfo).forget();
|
||||
SVGIFrameElement *it = new SVGIFrameElement(ni, NOT_FROM_PARSER);
|
||||
|
||||
nsCOMPtr<nsINode> kungFuDeathGrip = it;
|
||||
nsresult rv1 = it->Init();
|
||||
nsresult rv2 = const_cast<SVGIFrameElement*>(this)->CopyInnerTo(it);
|
||||
if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
|
||||
kungFuDeathGrip.swap(*aResult);
|
||||
}
|
||||
|
||||
return NS_FAILED(rv1) ? rv1 : rv2;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
nsSVGElement::LengthAttributesInfo
|
||||
SVGIFrameElement::GetLengthInfo()
|
||||
{
|
||||
return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
|
||||
ArrayLength(sLengthInfo));
|
||||
}
|
||||
|
||||
SVGAnimatedPreserveAspectRatio *
|
||||
SVGIFrameElement::GetPreserveAspectRatio()
|
||||
{
|
||||
return &mPreserveAspectRatio;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMSVGIFrameElement methods:
|
||||
|
||||
already_AddRefed<SVGAnimatedLength>
|
||||
SVGIFrameElement::X()
|
||||
{
|
||||
return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
|
||||
}
|
||||
|
||||
already_AddRefed<SVGAnimatedLength>
|
||||
SVGIFrameElement::Y()
|
||||
{
|
||||
return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
|
||||
}
|
||||
|
||||
already_AddRefed<SVGAnimatedLength>
|
||||
SVGIFrameElement::Width()
|
||||
{
|
||||
return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
|
||||
}
|
||||
|
||||
already_AddRefed<SVGAnimatedLength>
|
||||
SVGIFrameElement::Height()
|
||||
{
|
||||
return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
|
||||
SVGIFrameElement::PreserveAspectRatio()
|
||||
{
|
||||
nsRefPtr<DOMSVGAnimatedPreserveAspectRatio> ratio;
|
||||
mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(getter_AddRefs(ratio),
|
||||
this);
|
||||
return ratio.forget();
|
||||
}
|
||||
|
||||
void
|
||||
SVGIFrameElement::GetName(DOMString& name)
|
||||
{
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
|
||||
}
|
||||
|
||||
void
|
||||
SVGIFrameElement::GetSrc(DOMString& src)
|
||||
{
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
|
||||
}
|
||||
|
||||
void
|
||||
SVGIFrameElement::GetSrcdoc(DOMString& srcdoc)
|
||||
{
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, srcdoc);
|
||||
}
|
||||
|
||||
already_AddRefed<nsDOMSettableTokenList>
|
||||
SVGIFrameElement::Sandbox()
|
||||
{
|
||||
return GetTokenList(nsGkAtoms::sandbox);
|
||||
}
|
||||
|
||||
bool
|
||||
SVGIFrameElement::ParseAttribute(int32_t aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
if (aAttribute == nsGkAtoms::sandbox) {
|
||||
aResult.ParseAtomArray(aValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return SVGIFrameElementBase::ParseAttribute(aNamespaceID, aAttribute,
|
||||
aValue, aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGIFrameElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
nsIAtom* aPrefix, const nsAString& aValue,
|
||||
bool aNotify)
|
||||
{
|
||||
nsresult rv = nsSVGElement::SetAttr(aNameSpaceID, aName, aPrefix,
|
||||
aValue, aNotify);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
if (aName == nsGkAtoms::src &&
|
||||
!HasAttr(kNameSpaceID_None,nsGkAtoms::srcdoc)) {
|
||||
// Don't propagate error here. The attribute was successfully set, that's
|
||||
// what we should reflect.
|
||||
LoadSrc();
|
||||
}
|
||||
if (aName == nsGkAtoms::srcdoc) {
|
||||
// Don't propagate error here. The attribute was successfully set, that's
|
||||
// what we should reflect.
|
||||
LoadSrc();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
bool aNotify)
|
||||
{
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
if (aName == nsGkAtoms::sandbox && mFrameLoader) {
|
||||
// If we have an nsFrameLoader, apply the new sandbox flags.
|
||||
// Since this is called after the setter, the sandbox flags have
|
||||
// alreay been updated.
|
||||
mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
|
||||
}
|
||||
}
|
||||
return nsSVGElement::AfterSetAttr(aNameSpaceID, aName, aValue, aNotify);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGIFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
bool aNotify)
|
||||
{
|
||||
// Invoke on the superclass.
|
||||
nsresult rv = nsSVGElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
if (aAttribute == nsGkAtoms::srcdoc) {
|
||||
// Fall back to the src attribute, if any
|
||||
LoadSrc();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SVGIFrameElement::GetSandboxFlags()
|
||||
{
|
||||
const nsAttrValue* sandboxAttr = GetParsedAttr(nsGkAtoms::sandbox);
|
||||
return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGIFrameElement::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
nsresult rv = nsSVGElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument) {
|
||||
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
|
||||
"Missing a script blocker!");
|
||||
|
||||
PROFILER_LABEL("SVGIFrameElement", "BindToTree");
|
||||
|
||||
// We're in a document now. Kick off the frame load.
|
||||
LoadSrc();
|
||||
|
||||
if (HasAttr(kNameSpaceID_None, nsGkAtoms::sandbox)) {
|
||||
if (mFrameLoader) {
|
||||
mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're now in document and scripts may move us, so clear
|
||||
// the mNetworkCreated flag.
|
||||
mNetworkCreated = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
SVGIFrameElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
if (mFrameLoader) {
|
||||
// This iframe is being taken out of the document, destroy the
|
||||
// iframe's frame loader (doing that will tear down the window in
|
||||
// this iframe).
|
||||
// XXXbz we really want to only partially destroy the frame
|
||||
// loader... we don't want to tear down the docshell. Food for
|
||||
// later bug.
|
||||
mFrameLoader->Destroy();
|
||||
mFrameLoader = nullptr;
|
||||
}
|
||||
|
||||
nsSVGElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
||||
void
|
||||
SVGIFrameElement::DestroyContent()
|
||||
{
|
||||
if (mFrameLoader) {
|
||||
mFrameLoader->Destroy();
|
||||
mFrameLoader = nullptr;
|
||||
}
|
||||
|
||||
nsSVGElement::DestroyContent();
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGIFrameElement::CopyInnerTo(Element* aDest)
|
||||
{
|
||||
nsresult rv = nsSVGElement::CopyInnerTo(aDest);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIDocument* doc = aDest->OwnerDoc();
|
||||
if (doc->IsStaticDocument() && mFrameLoader) {
|
||||
SVGIFrameElement* dest =
|
||||
static_cast<SVGIFrameElement*>(aDest);
|
||||
nsFrameLoader* fl = nsFrameLoader::Create(dest, false);
|
||||
NS_ENSURE_STATE(fl);
|
||||
dest->mFrameLoader = fl;
|
||||
static_cast<nsFrameLoader*>(mFrameLoader.get())->CreateStaticClone(fl);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
110
content/svg/content/src/SVGIFrameElement.h
Normal file
110
content/svg/content/src/SVGIFrameElement.h
Normal file
@ -0,0 +1,110 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/DOMString.h"
|
||||
#include "mozilla/dom/FromParser.h"
|
||||
#include "mozilla/dom/SVGGraphicsElement.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMSettableTokenList.h"
|
||||
#include "nsFrameLoader.h"
|
||||
#include "nsElementFrameLoaderOwner.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIFrameLoader.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsSVGLength2.h"
|
||||
#include "SVGAnimatedPreserveAspectRatio.h"
|
||||
|
||||
nsresult NS_NewSVGIFrameElement(nsIContent **aResult,
|
||||
already_AddRefed<nsINodeInfo>&& aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser);
|
||||
|
||||
typedef mozilla::dom::SVGGraphicsElement SVGIFrameElementBase;
|
||||
|
||||
class nsSVGIFrameFrame;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class DOMSVGAnimatedPreserveAspectRatio;
|
||||
|
||||
class SVGIFrameElement MOZ_FINAL : public SVGIFrameElementBase,
|
||||
public nsElementFrameLoaderOwner
|
||||
{
|
||||
friend class ::nsSVGIFrameFrame;
|
||||
friend nsresult (::NS_NewSVGIFrameElement(nsIContent **aResult,
|
||||
already_AddRefed<nsINodeInfo>&& aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser));
|
||||
|
||||
SVGIFrameElement(already_AddRefed<nsINodeInfo>& aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser);
|
||||
virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
|
||||
|
||||
public:
|
||||
// interface
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsSVGElement specializations:
|
||||
virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
|
||||
TransformTypes aWhich = eAllTransforms) const MOZ_OVERRIDE;
|
||||
|
||||
// nsIContent
|
||||
virtual bool ParseAttribute(int32_t aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult) MOZ_OVERRIDE;
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers) MOZ_OVERRIDE;
|
||||
virtual void UnbindFromTree(bool aDeep = true,
|
||||
bool aNullParent = true) MOZ_OVERRIDE;
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
nsIAtom* aPrefix, const nsAString& aValue,
|
||||
bool aNotify) MOZ_OVERRIDE;
|
||||
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
bool aNotify) MOZ_OVERRIDE;
|
||||
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
bool aNotify) MOZ_OVERRIDE;
|
||||
|
||||
virtual void DestroyContent() MOZ_OVERRIDE;
|
||||
nsresult CopyInnerTo(mozilla::dom::Element* aDest);
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
|
||||
|
||||
// WebIDL
|
||||
already_AddRefed<SVGAnimatedLength> X();
|
||||
already_AddRefed<SVGAnimatedLength> Y();
|
||||
already_AddRefed<SVGAnimatedLength> Width();
|
||||
already_AddRefed<SVGAnimatedLength> Height();
|
||||
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
|
||||
void GetName(DOMString& name);
|
||||
void GetSrc(DOMString& src);
|
||||
void GetSrcdoc(DOMString& srcdoc);
|
||||
already_AddRefed<nsDOMSettableTokenList> Sandbox();
|
||||
using nsElementFrameLoaderOwner::GetContentDocument;
|
||||
using nsElementFrameLoaderOwner::GetContentWindow;
|
||||
|
||||
// Parses a sandbox attribute and converts it to the set of flags used internally.
|
||||
// Returns 0 if there isn't the attribute.
|
||||
uint32_t GetSandboxFlags();
|
||||
|
||||
private:
|
||||
virtual LengthAttributesInfo GetLengthInfo();
|
||||
virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
|
||||
virtual mozilla::dom::Element* ThisFrameElement() MOZ_OVERRIDE
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT };
|
||||
nsSVGLength2 mLengthAttributes[4];
|
||||
static LengthInfo sLengthInfo[4];
|
||||
|
||||
SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -65,6 +65,7 @@ SVG_TAG(feTurbulence, FETurbulence)
|
||||
SVG_TAG(filter, Filter)
|
||||
SVG_TAG(foreignObject, ForeignObject)
|
||||
SVG_TAG(g, G)
|
||||
SVG_FROM_PARSER_TAG(iframe, IFrame)
|
||||
SVG_TAG(image, Image)
|
||||
SVG_TAG(line, Line)
|
||||
SVG_TAG(linearGradient, LinearGradient)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user