merge fx-team to mozilla-central on a CLOSED TREE
@ -134,12 +134,12 @@ function exceptions(key, value) {
|
||||
// workers for windows in this tab
|
||||
var keepAlive = new Map();
|
||||
|
||||
process.port.on('sdk/worker/create', (process, options) => {
|
||||
options.window = getByInnerId(options.windowId);
|
||||
if (!options.window)
|
||||
return;
|
||||
|
||||
process.port.on('sdk/worker/create', (process, options, cpows) => {
|
||||
options.window = cpows.window;
|
||||
let worker = new WorkerChild(options);
|
||||
|
||||
let frame = frames.getFrameForWindow(options.window.top);
|
||||
frame.port.emit('sdk/worker/connect', options.id, options.window.location.href);
|
||||
});
|
||||
|
||||
when(reason => {
|
||||
|
@ -116,29 +116,21 @@ const Worker = Class({
|
||||
exports.Worker = Worker;
|
||||
|
||||
attach.define(Worker, function(worker, window) {
|
||||
// This method of attaching should be deprecated
|
||||
|
||||
if (Cu.isCrossProcessWrapper(window))
|
||||
throw new Error("Attaching worker to a window from another " +
|
||||
"process directly is not supported.");
|
||||
|
||||
let model = modelFor(worker);
|
||||
if (model.attached)
|
||||
detach(worker);
|
||||
|
||||
model.window = window;
|
||||
let frame = null;
|
||||
let tab = getTabForContentWindowNoShim(window);
|
||||
if (tab)
|
||||
frame = frames.getFrameForBrowser(getBrowserForTab(tab));
|
||||
|
||||
let childOptions = makeChildOptions(model.options);
|
||||
childOptions.windowId = getInnerId(window);
|
||||
processes.port.emitCPOW('sdk/worker/create', [childOptions], { window });
|
||||
|
||||
processes.port.emit('sdk/worker/create', childOptions);
|
||||
|
||||
connect(worker, frame, { id: childOptions.id, url: String(window.location) });
|
||||
})
|
||||
let listener = (frame, id, url) => {
|
||||
if (id != childOptions.id)
|
||||
return;
|
||||
frames.port.off('sdk/worker/connect', listener);
|
||||
connect(worker, frame, { id, url });
|
||||
};
|
||||
frames.port.on('sdk/worker/connect', listener);
|
||||
});
|
||||
|
||||
connect.define(Worker, function(worker, frame, { id, url }) {
|
||||
let model = modelFor(worker);
|
||||
@ -159,7 +151,7 @@ connect.define(Worker, function(worker, frame, { id, url }) {
|
||||
|
||||
model.earlyEvents.forEach(args => worker.send(...args));
|
||||
model.earlyEvents = [];
|
||||
emit(worker, 'attach', model.window);
|
||||
emit(worker, 'attach');
|
||||
});
|
||||
|
||||
// unload and release the child worker, release window reference
|
||||
@ -171,7 +163,6 @@ detach.define(Worker, function(worker) {
|
||||
processes.port.off('sdk/worker/event', worker.receive);
|
||||
model.attached = false;
|
||||
model.destroyed = true;
|
||||
model.window = null;
|
||||
emit(worker, 'detach');
|
||||
});
|
||||
|
||||
|
@ -7,7 +7,7 @@ const { isChildLoader } = require('./core');
|
||||
if (!isChildLoader)
|
||||
throw new Error("Cannot load sdk/remote/child in a main process loader.");
|
||||
|
||||
const { Ci, Cc } = require('chrome');
|
||||
const { Ci, Cc, Cu } = require('chrome');
|
||||
const runtime = require('../system/runtime');
|
||||
const { Class } = require('../core/heritage');
|
||||
const { Namespace } = require('../core/namespace');
|
||||
@ -39,21 +39,37 @@ const process = {
|
||||
};
|
||||
exports.process = process;
|
||||
|
||||
process.port.emit = (...args) => {
|
||||
mm.sendAsyncMessage('sdk/remote/process/message', {
|
||||
loaderID,
|
||||
args
|
||||
});
|
||||
function definePort(obj, name) {
|
||||
obj.port.emit = (event, ...args) => {
|
||||
let manager = ns(obj).messageManager;
|
||||
if (!manager)
|
||||
return;
|
||||
|
||||
manager.sendAsyncMessage(name, { loaderID, event, args });
|
||||
};
|
||||
}
|
||||
|
||||
function processMessageReceived({ data }) {
|
||||
function messageReceived({ data, objects }) {
|
||||
// Ignore messages from other loaders
|
||||
if (data.loaderID != loaderID)
|
||||
return;
|
||||
let [event, ...args] = data.args;
|
||||
emit(process.port, event, process, ...args);
|
||||
|
||||
let keys = Object.keys(objects);
|
||||
if (keys.length) {
|
||||
// If any objects are CPOWs then ignore this message. We don't want child
|
||||
// processes interracting with CPOWs
|
||||
if (!keys.every(name => !Cu.isCrossProcessWrapper(objects[name])))
|
||||
return;
|
||||
|
||||
data.args.push(objects);
|
||||
}
|
||||
|
||||
emit(this.port, data.event, this, ...data.args);
|
||||
}
|
||||
|
||||
ns(process).messageManager = mm;
|
||||
definePort(process, 'sdk/remote/process/message');
|
||||
let processMessageReceived = messageReceived.bind(process);
|
||||
mm.addMessageListener('sdk/remote/process/message', processMessageReceived);
|
||||
|
||||
when(() => {
|
||||
@ -111,13 +127,6 @@ function makeFrameEventListener(frame, callback) {
|
||||
var FRAME_ID = 0;
|
||||
var tabMap = new Map();
|
||||
|
||||
function frameMessageReceived({ data }) {
|
||||
if (data.loaderID != loaderID)
|
||||
return;
|
||||
let [event, ...args] = data.args;
|
||||
emit(this.port, event, this, ...args);
|
||||
}
|
||||
|
||||
const Frame = Class({
|
||||
implements: [ Disposable ],
|
||||
extends: EventTarget,
|
||||
@ -131,16 +140,11 @@ const Frame = Class({
|
||||
|
||||
tabMap.set(contentFrame.docShell, this);
|
||||
|
||||
ns(this).messageReceived = frameMessageReceived.bind(this);
|
||||
ns(this).messageReceived = messageReceived.bind(this);
|
||||
ns(this).messageManager.addMessageListener('sdk/remote/frame/message', ns(this).messageReceived);
|
||||
|
||||
this.port = new EventTarget();
|
||||
this.port.emit = (...args) => {
|
||||
ns(this).messageManager.sendAsyncMessage('sdk/remote/frame/message', {
|
||||
loaderID,
|
||||
args
|
||||
});
|
||||
};
|
||||
definePort(this, 'sdk/remote/frame/message');
|
||||
|
||||
ns(this).messageManager.sendAsyncMessage('sdk/remote/frame/attach', {
|
||||
loaderID,
|
||||
|
@ -73,11 +73,27 @@ const ns = Namespace();
|
||||
|
||||
var processMap = new Map();
|
||||
|
||||
function processMessageReceived({ target, data }) {
|
||||
function definePort(obj, name) {
|
||||
obj.port.emitCPOW = (event, args, cpows = {}) => {
|
||||
let manager = ns(obj).messageManager;
|
||||
if (!manager)
|
||||
return;
|
||||
|
||||
let method = manager instanceof Ci.nsIMessageBroadcaster ?
|
||||
"broadcastAsyncMessage" : "sendAsyncMessage";
|
||||
|
||||
manager[method](name, { loaderID, event, args }, cpows);
|
||||
};
|
||||
|
||||
obj.port.emit = (event, ...args) => obj.port.emitCPOW(event, args);
|
||||
}
|
||||
|
||||
function messageReceived({ target, data }) {
|
||||
// Ignore messages from other loaders
|
||||
if (data.loaderID != loaderID)
|
||||
return;
|
||||
let [event, ...args] = data.args;
|
||||
emit(this.port, event, this, ...args);
|
||||
|
||||
emit(this.port, data.event, this, ...data.args);
|
||||
}
|
||||
|
||||
// Process represents a gecko process that can load webpages. Each process
|
||||
@ -90,18 +106,13 @@ const Process = Class({
|
||||
ns(this).id = id;
|
||||
ns(this).isRemote = isRemote;
|
||||
ns(this).messageManager = messageManager;
|
||||
ns(this).messageReceived = processMessageReceived.bind(this);
|
||||
ns(this).messageReceived = messageReceived.bind(this);
|
||||
this.destroy = this.destroy.bind(this);
|
||||
ns(this).messageManager.addMessageListener('sdk/remote/process/message', ns(this).messageReceived);
|
||||
ns(this).messageManager.addMessageListener('child-process-shutdown', this.destroy);
|
||||
|
||||
this.port = new EventTarget();
|
||||
this.port.emit = (...args) => {
|
||||
ns(this).messageManager.sendAsyncMessage('sdk/remote/process/message', {
|
||||
loaderID,
|
||||
args
|
||||
});
|
||||
};
|
||||
definePort(this, 'sdk/remote/process/message');
|
||||
|
||||
// Load any remote modules
|
||||
for (let module of remoteModules.values())
|
||||
@ -132,14 +143,10 @@ const Processes = Class({
|
||||
extends: EventTarget,
|
||||
initialize: function() {
|
||||
EventParent.prototype.initialize.call(this);
|
||||
ns(this).messageManager = ppmm;
|
||||
|
||||
this.port = new EventTarget();
|
||||
this.port.emit = (...args) => {
|
||||
ppmm.broadcastAsyncMessage('sdk/remote/process/message', {
|
||||
loaderID,
|
||||
args
|
||||
});
|
||||
};
|
||||
definePort(this, 'sdk/remote/process/message');
|
||||
},
|
||||
|
||||
getById: function(id) {
|
||||
@ -150,13 +157,6 @@ var processes = exports.processes = new Processes();
|
||||
|
||||
var frameMap = new Map();
|
||||
|
||||
function frameMessageReceived({ target, data }) {
|
||||
if (data.loaderID != loaderID)
|
||||
return;
|
||||
let [event, ...args] = data.args;
|
||||
emit(this.port, event, this, ...args);
|
||||
}
|
||||
|
||||
function setFrameProcess(frame, process) {
|
||||
ns(frame).process = process;
|
||||
frames.attachItem(frame);
|
||||
@ -174,19 +174,11 @@ const Frame = Class({
|
||||
let frameLoader = node.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||
ns(this).messageManager = frameLoader.messageManager;
|
||||
|
||||
ns(this).messageReceived = frameMessageReceived.bind(this);
|
||||
ns(this).messageReceived = messageReceived.bind(this);
|
||||
ns(this).messageManager.addMessageListener('sdk/remote/frame/message', ns(this).messageReceived);
|
||||
|
||||
this.port = new EventTarget();
|
||||
this.port.emit = (...args) => {
|
||||
let manager = ns(this).messageManager;
|
||||
if (!manager)
|
||||
return;
|
||||
manager.sendAsyncMessage('sdk/remote/frame/message', {
|
||||
loaderID,
|
||||
args
|
||||
});
|
||||
};
|
||||
definePort(this, 'sdk/remote/frame/message');
|
||||
|
||||
frameMap.set(ns(this).messageManager, this);
|
||||
},
|
||||
@ -230,14 +222,10 @@ const FrameList = Class({
|
||||
extends: EventTarget,
|
||||
initialize: function() {
|
||||
EventParent.prototype.initialize.call(this);
|
||||
ns(this).messageManager = gmm;
|
||||
|
||||
this.port = new EventTarget();
|
||||
this.port.emit = (...args) => {
|
||||
gmm.broadcastAsyncMessage('sdk/remote/frame/message', {
|
||||
loaderID,
|
||||
args
|
||||
});
|
||||
};
|
||||
definePort(this, 'sdk/remote/frame/message');
|
||||
},
|
||||
|
||||
// Returns the frame for a browser element
|
||||
|
@ -7,6 +7,7 @@
|
||||
const LOCAL_URI = "about:robots";
|
||||
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
|
||||
|
||||
const { Cu } = require('chrome');
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
@ -46,6 +47,7 @@ exports["test process restart"] = function*(assert) {
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
let tab = tabs[0];
|
||||
let browser = getBrowserForTab(tab);
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes, frames } = yield waitForProcesses(loader);
|
||||
@ -59,6 +61,7 @@ exports["test process restart"] = function*(assert) {
|
||||
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
|
||||
let frameAttach = promiseTabFrameAttach(frames);
|
||||
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
|
||||
let browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, LOCAL_URI);
|
||||
// The load should kill the remote frame
|
||||
yield frameDetach;
|
||||
@ -67,10 +70,12 @@ exports["test process restart"] = function*(assert) {
|
||||
assert.equal(newFrame.process, localProcess, "New frame should be in the local process");
|
||||
// And kill the process
|
||||
yield processDetach;
|
||||
yield browserLoad;
|
||||
|
||||
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
|
||||
let processAttach = promiseEvent(processes, 'attach');
|
||||
frameAttach = promiseTabFrameAttach(frames);
|
||||
browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, REMOTE_URI);
|
||||
// The load should kill the remote frame
|
||||
yield frameDetach;
|
||||
@ -80,8 +85,11 @@ exports["test process restart"] = function*(assert) {
|
||||
// And create a new frame in the remote process
|
||||
[newFrame] = yield frameAttach;
|
||||
assert.equal(newFrame.process, remoteProcess, "New frame should be in the remote process");
|
||||
yield browserLoad;
|
||||
|
||||
browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, "about:blank");
|
||||
yield browserLoad;
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
@ -536,6 +544,33 @@ exports["test cannot load in wrong loader"] = function*(assert) {
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports["test send cpow"] = function*(assert) {
|
||||
if (!isE10S) {
|
||||
assert.pass("Skipping test in non-e10s mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
let tab = tabs[0];
|
||||
let browser = getBrowserForTab(tab);
|
||||
|
||||
assert.ok(Cu.isCrossProcessWrapper(browser.contentWindow),
|
||||
"Should have a CPOW for the browser content window");
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes } = yield waitForProcesses(loader);
|
||||
|
||||
processes.port.emitCPOW('sdk/test/cpow', ['foobar'], { window: browser.contentWindow });
|
||||
let [process, arg, id] = yield promiseEvent(processes.port, 'sdk/test/cpow');
|
||||
|
||||
assert.ok(process.isRemote, "Response should come from the remote process");
|
||||
assert.equal(arg, "foobar", "Argument should have passed through");
|
||||
assert.equal(id, browser.outerWindowID, "Should have got the ID from the child");
|
||||
};
|
||||
|
||||
after(exports, function*(name, assert) {
|
||||
yield cleanUI();
|
||||
});
|
||||
|
@ -9,6 +9,7 @@ const { processID } = require('sdk/system/runtime');
|
||||
const system = require('sdk/system/events');
|
||||
const { Cu } = require('chrome');
|
||||
const { isChildLoader } = require('sdk/remote/core');
|
||||
const { getOuterId } = require('sdk/window/utils');
|
||||
|
||||
function log(str) {
|
||||
console.log("remote[" + loaderID + "][" + processID + "]: " + str);
|
||||
@ -122,3 +123,7 @@ frames.port.on('sdk/test/registerframeevent', (frame) => {
|
||||
frames.port.on('sdk/test/unregisterframeevent', (frame) => {
|
||||
frame.removeEventListener("Test:Event", listener, true);
|
||||
});
|
||||
|
||||
process.port.on('sdk/test/cpow', (process, arg, cpows) => {
|
||||
process.port.emit('sdk/test/cpow', arg, getOuterId(cpows.window));
|
||||
});
|
||||
|
@ -7,6 +7,7 @@
|
||||
const LOCAL_URI = "about:robots";
|
||||
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
|
||||
|
||||
const { Cu } = require('chrome');
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
@ -46,6 +47,7 @@ exports["test process restart"] = function*(assert) {
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
let tab = tabs[0];
|
||||
let browser = getBrowserForTab(tab);
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes, frames } = yield waitForProcesses(loader);
|
||||
@ -59,6 +61,7 @@ exports["test process restart"] = function*(assert) {
|
||||
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
|
||||
let frameAttach = promiseTabFrameAttach(frames);
|
||||
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
|
||||
let browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, LOCAL_URI);
|
||||
// The load should kill the remote frame
|
||||
yield frameDetach;
|
||||
@ -67,10 +70,12 @@ exports["test process restart"] = function*(assert) {
|
||||
assert.equal(newFrame.process, localProcess, "New frame should be in the local process");
|
||||
// And kill the process
|
||||
yield processDetach;
|
||||
yield browserLoad;
|
||||
|
||||
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
|
||||
let processAttach = promiseEvent(processes, 'attach');
|
||||
frameAttach = promiseTabFrameAttach(frames);
|
||||
browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, REMOTE_URI);
|
||||
// The load should kill the remote frame
|
||||
yield frameDetach;
|
||||
@ -80,8 +85,11 @@ exports["test process restart"] = function*(assert) {
|
||||
// And create a new frame in the remote process
|
||||
[newFrame] = yield frameAttach;
|
||||
assert.equal(newFrame.process, remoteProcess, "New frame should be in the remote process");
|
||||
yield browserLoad;
|
||||
|
||||
browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, "about:blank");
|
||||
yield browserLoad;
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
@ -536,6 +544,33 @@ exports["test cannot load in wrong loader"] = function*(assert) {
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports["test send cpow"] = function*(assert) {
|
||||
if (!isE10S) {
|
||||
assert.pass("Skipping test in non-e10s mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
let tab = tabs[0];
|
||||
let browser = getBrowserForTab(tab);
|
||||
|
||||
assert.ok(Cu.isCrossProcessWrapper(browser.contentWindow),
|
||||
"Should have a CPOW for the browser content window");
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes } = yield waitForProcesses(loader);
|
||||
|
||||
processes.port.emitCPOW('sdk/test/cpow', ['foobar'], { window: browser.contentWindow });
|
||||
let [process, arg, id] = yield promiseEvent(processes.port, 'sdk/test/cpow');
|
||||
|
||||
assert.ok(process.isRemote, "Response should come from the remote process");
|
||||
assert.equal(arg, "foobar", "Argument should have passed through");
|
||||
assert.equal(id, browser.outerWindowID, "Should have got the ID from the child");
|
||||
};
|
||||
|
||||
after(exports, function*(name, assert) {
|
||||
yield cleanUI();
|
||||
});
|
||||
|
@ -9,6 +9,7 @@ const { processID } = require('sdk/system/runtime');
|
||||
const system = require('sdk/system/events');
|
||||
const { Cu } = require('chrome');
|
||||
const { isChildLoader } = require('sdk/remote/core');
|
||||
const { getOuterId } = require('sdk/window/utils');
|
||||
|
||||
function log(str) {
|
||||
console.log("remote[" + loaderID + "][" + processID + "]: " + str);
|
||||
@ -122,3 +123,7 @@ frames.port.on('sdk/test/registerframeevent', (frame) => {
|
||||
frames.port.on('sdk/test/unregisterframeevent', (frame) => {
|
||||
frame.removeEventListener("Test:Event", listener, true);
|
||||
});
|
||||
|
||||
process.port.on('sdk/test/cpow', (process, arg, cpows) => {
|
||||
process.port.emit('sdk/test/cpow', arg, getOuterId(cpows.window));
|
||||
});
|
||||
|
@ -118,14 +118,15 @@ exports["test:sample"] = WorkerTest(
|
||||
assert.equal(worker.url, window.location.href,
|
||||
"worker.url still works");
|
||||
done();
|
||||
},
|
||||
onAttach: function() {
|
||||
assert.equal(worker.url, window.location.href,
|
||||
"worker.url works");
|
||||
assert.equal(worker.contentURL, window.location.href,
|
||||
"worker.contentURL works");
|
||||
worker.postMessage("hi!");
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(worker.url, window.location.href,
|
||||
"worker.url works");
|
||||
assert.equal(worker.contentURL, window.location.href,
|
||||
"worker.contentURL works");
|
||||
worker.postMessage("hi!");
|
||||
}
|
||||
);
|
||||
|
||||
@ -841,10 +842,9 @@ exports["test:worker events"] = WorkerTest(
|
||||
contentScript: 'new ' + function WorkerScope() {
|
||||
self.postMessage('start');
|
||||
},
|
||||
onAttach: win => {
|
||||
onAttach: () => {
|
||||
events.push('attach');
|
||||
assert.pass('attach event called when attached');
|
||||
assert.equal(window, win, 'attach event passes in attached window');
|
||||
},
|
||||
onError: err => {
|
||||
assert.equal(err.message, 'Custom',
|
||||
@ -876,13 +876,16 @@ exports["test:onDetach in contentScript on destroy"] = WorkerTest(
|
||||
window.location.hash += '!' + reason;
|
||||
})
|
||||
},
|
||||
|
||||
onAttach: function() {
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!',
|
||||
"location.href is as expected");
|
||||
done();
|
||||
})
|
||||
worker.destroy();
|
||||
}
|
||||
});
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!',
|
||||
"location.href is as expected");
|
||||
done();
|
||||
})
|
||||
worker.destroy();
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -2,96 +2,257 @@
|
||||
* 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 parseQueryString() {
|
||||
let URL = document.documentURI;
|
||||
let queryString = URL.replace(/^about:tabcrashed?e=tabcrashed/, "");
|
||||
var AboutTabCrashed = {
|
||||
/**
|
||||
* This can be set to true once this page receives a message from the
|
||||
* parent saying whether or not a crash report is available.
|
||||
*/
|
||||
hasReport: false,
|
||||
|
||||
let titleMatch = queryString.match(/d=([^&]*)/);
|
||||
let URLMatch = queryString.match(/u=([^&]*)/);
|
||||
return {
|
||||
title: titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "",
|
||||
URL: URLMatch && URLMatch[1] ? decodeURIComponent(URLMatch[1]) : "",
|
||||
};
|
||||
}
|
||||
/**
|
||||
* The messages that we might receive from the parent.
|
||||
*/
|
||||
MESSAGES: [
|
||||
"SetCrashReportAvailable",
|
||||
"CrashReportSent",
|
||||
"UpdateCount",
|
||||
],
|
||||
|
||||
function displayUI() {
|
||||
if (!hasReport()) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Items for which we will listen for click events.
|
||||
*/
|
||||
CLICK_TARGETS: [
|
||||
"closeTab",
|
||||
"restoreTab",
|
||||
"restoreAll",
|
||||
"sendReport",
|
||||
],
|
||||
|
||||
let sendCrashReport = document.getElementById("sendReport").checked;
|
||||
let container = document.getElementById("crash-reporter-container");
|
||||
container.hidden = !sendCrashReport;
|
||||
}
|
||||
/**
|
||||
* Returns information about this crashed tab.
|
||||
*
|
||||
* @return (Object) An object with the following properties:
|
||||
* title (String):
|
||||
* The title of the page that crashed.
|
||||
* URL (String):
|
||||
* The URL of the page that crashed.
|
||||
*/
|
||||
get pageData() {
|
||||
delete this.pageData;
|
||||
|
||||
function hasReport() {
|
||||
return document.documentElement.classList.contains("crashDumpAvailable");
|
||||
}
|
||||
let URL = document.documentURI;
|
||||
let queryString = URL.replace(/^about:tabcrashed?e=tabcrashed/, "");
|
||||
|
||||
function sendEvent(message) {
|
||||
let comments = "";
|
||||
let email = "";
|
||||
let URL = "";
|
||||
let sendCrashReport = false;
|
||||
let emailMe = false;
|
||||
let includeURL = false;
|
||||
let titleMatch = queryString.match(/d=([^&]*)/);
|
||||
let URLMatch = queryString.match(/u=([^&]*)/);
|
||||
|
||||
if (hasReport()) {
|
||||
sendCrashReport = document.getElementById("sendReport").checked;
|
||||
if (sendCrashReport) {
|
||||
comments = document.getElementById("comments").value.trim();
|
||||
return this.pageData = {
|
||||
title: titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "",
|
||||
URL: URLMatch && URLMatch[1] ? decodeURIComponent(URLMatch[1]) : "",
|
||||
};
|
||||
},
|
||||
|
||||
includeURL = document.getElementById("includeURL").checked;
|
||||
if (includeURL) {
|
||||
URL = parseQueryString().URL.trim();
|
||||
init() {
|
||||
this.MESSAGES.forEach((msg) => addMessageListener(msg, this.receiveMessage.bind(this)));
|
||||
addEventListener("DOMContentLoaded", this);
|
||||
|
||||
document.title = this.pageData.title;
|
||||
},
|
||||
|
||||
receiveMessage(message) {
|
||||
switch(message.name) {
|
||||
case "UpdateCount": {
|
||||
this.showRestoreAll(message.data.count > 1);
|
||||
break;
|
||||
}
|
||||
|
||||
emailMe = document.getElementById("emailMe").checked;
|
||||
if (emailMe) {
|
||||
email = document.getElementById("email").value.trim();
|
||||
case "SetCrashReportAvailable": {
|
||||
this.onSetCrashReportAvailable(message);
|
||||
break;
|
||||
}
|
||||
case "CrashReportSent": {
|
||||
this.onCrashReportSent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
let event = new CustomEvent("AboutTabCrashedMessage", {
|
||||
bubbles: true,
|
||||
detail: {
|
||||
message,
|
||||
sendCrashReport,
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "DOMContentLoaded": {
|
||||
this.onDOMContentLoaded();
|
||||
break;
|
||||
}
|
||||
case "click": {
|
||||
this.onClick(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onDOMContentLoaded() {
|
||||
this.CLICK_TARGETS.forEach((targetID) => {
|
||||
let el = document.getElementById(targetID);
|
||||
el.addEventListener("click", this);
|
||||
});
|
||||
|
||||
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
|
||||
let event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
|
||||
document.dispatchEvent(event);
|
||||
|
||||
sendAsyncMessage("Load");
|
||||
},
|
||||
|
||||
onClick(event) {
|
||||
switch(event.target.id) {
|
||||
case "closeTab": {
|
||||
this.sendMessage("closeTab");
|
||||
break;
|
||||
}
|
||||
|
||||
case "restoreTab": {
|
||||
this.sendMessage("restoreTab");
|
||||
break;
|
||||
}
|
||||
|
||||
case "restoreAll": {
|
||||
this.sendMessage("restoreAll");
|
||||
break;
|
||||
}
|
||||
|
||||
case "sendReport": {
|
||||
this.showCrashReportUI(event.target.checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* After this page tells the parent that it has loaded, the parent
|
||||
* will respond with whether or not a crash report is available. This
|
||||
* method handles that message.
|
||||
*
|
||||
* @param message
|
||||
* The message from the parent, which should contain a data
|
||||
* Object property with the following properties:
|
||||
*
|
||||
* hasReport (bool):
|
||||
* Whether or not there is a crash report
|
||||
*
|
||||
* sendReport (bool):
|
||||
* Whether or not the the user prefers to send the report
|
||||
* by default
|
||||
*
|
||||
* includeURL (bool):
|
||||
* Whether or not the user prefers to send the URL of
|
||||
* the tab that crashed.
|
||||
*
|
||||
* emailMe (bool):
|
||||
* Whether or not to send the email address of the user
|
||||
* in the report.
|
||||
*
|
||||
* email (String):
|
||||
* The email address of the user (empty if emailMe is false)
|
||||
*
|
||||
*/
|
||||
onSetCrashReportAvailable(message) {
|
||||
if (message.data.hasReport) {
|
||||
this.hasReport = true;
|
||||
document.documentElement.classList.add("crashDumpAvailable");
|
||||
|
||||
let data = message.data;
|
||||
document.getElementById("sendReport").checked = data.sendReport;
|
||||
document.getElementById("includeURL").checked = data.includeURL;
|
||||
document.getElementById("emailMe").checked = data.emailMe;
|
||||
if (data.emailMe) {
|
||||
document.getElementById("email").value = data.email;
|
||||
}
|
||||
}
|
||||
|
||||
let event = new CustomEvent("AboutTabCrashedReady", {bubbles:true});
|
||||
document.dispatchEvent(event);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for when the parent reports that the crash report associated
|
||||
* with this about:tabcrashed page has been sent.
|
||||
*/
|
||||
onCrashReportSent() {
|
||||
document.documentElement.classList.remove("crashDumpAvailable");
|
||||
document.documentElement.classList.add("crashDumpSubmitted");
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the display of the crash report form.
|
||||
*
|
||||
* @param shouldShow (bool)
|
||||
* True if the crash report form should be shown
|
||||
*/
|
||||
showCrashReportUI(shouldShow) {
|
||||
let container = document.getElementById("crash-reporter-container");
|
||||
container.hidden = !shouldShow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the display of the "Restore All" button.
|
||||
*
|
||||
* @param shouldShow (bool)
|
||||
* True if the "Restore All" button should be shown
|
||||
*/
|
||||
showRestoreAll(shouldShow) {
|
||||
let restoreAll = document.getElementById("restoreAll");
|
||||
let restoreTab = document.getElementById("restoreTab");
|
||||
if (shouldShow) {
|
||||
restoreAll.removeAttribute("hidden");
|
||||
restoreTab.classList.remove("primary");
|
||||
} else {
|
||||
restoreAll.setAttribute("hidden", true);
|
||||
restoreTab.classList.add("primary");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message to the parent in response to the user choosing
|
||||
* one of the actions available on the page. This might also send up
|
||||
* crash report information if the user has chosen to submit a crash
|
||||
* report.
|
||||
*
|
||||
* @param messageName (String)
|
||||
* The message to send to the parent
|
||||
*/
|
||||
sendMessage(messageName) {
|
||||
let comments = "";
|
||||
let email = "";
|
||||
let URL = "";
|
||||
let sendReport = false;
|
||||
let emailMe = false;
|
||||
let includeURL = false;
|
||||
|
||||
if (this.hasReport) {
|
||||
sendReport = document.getElementById("sendReport").checked;
|
||||
if (sendReport) {
|
||||
comments = document.getElementById("comments").value.trim();
|
||||
|
||||
includeURL = document.getElementById("includeURL").checked;
|
||||
if (includeURL) {
|
||||
URL = this.pageData.URL.trim();
|
||||
}
|
||||
|
||||
emailMe = document.getElementById("emailMe").checked;
|
||||
if (emailMe) {
|
||||
email = document.getElementById("email").value.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendAsyncMessage(messageName, {
|
||||
sendReport,
|
||||
comments,
|
||||
email,
|
||||
emailMe,
|
||||
includeURL,
|
||||
URL,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function closeTab() {
|
||||
sendEvent("closeTab");
|
||||
}
|
||||
|
||||
function restoreTab() {
|
||||
sendEvent("restoreTab");
|
||||
}
|
||||
|
||||
function restoreAll() {
|
||||
sendEvent("restoreAll");
|
||||
}
|
||||
|
||||
document.title = parseQueryString().title;
|
||||
|
||||
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
|
||||
var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
|
||||
document.dispatchEvent(event);
|
||||
|
||||
addEventListener("DOMContentLoaded", function() {
|
||||
let sendReport = document.getElementById("sendReport");
|
||||
sendReport.addEventListener("click", function() {
|
||||
displayUI();
|
||||
});
|
||||
|
||||
displayUI();
|
||||
});
|
||||
AboutTabCrashed.init();
|
@ -58,11 +58,11 @@
|
||||
<p id="reportSent">&tabCrashed.reportSent;</p>
|
||||
|
||||
<div class="button-container">
|
||||
<button id="closeTab" onclick="closeTab()">
|
||||
<button id="closeTab">
|
||||
&tabCrashed.closeTab;</button>
|
||||
<button id="restoreTab" onclick="restoreTab()">
|
||||
<button id="restoreTab">
|
||||
&tabCrashed.restoreTab;</button>
|
||||
<button id="restoreAll" onclick="restoreAll()" autofocus="true" class="primary">
|
||||
<button id="restoreAll" autofocus="true" class="primary">
|
||||
&tabCrashed.restoreAll;</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -230,11 +230,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "gWebRTCUI",
|
||||
"resource:///modules/webrtcUI.jsm", "webrtcUI");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
|
||||
"resource:///modules/ContentCrashHandlers.jsm");
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
|
||||
"resource:///modules/ContentCrashReporters.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
|
||||
"resource:///modules/ContentCrashReporters.jsm");
|
||||
"resource:///modules/ContentCrashHandlers.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
|
||||
@ -916,6 +916,7 @@ function _loadURIWithFlags(browser, uri, params) {
|
||||
// We might lose history that way but at least the browser loaded a page.
|
||||
// This might be necessary if SessionStore wasn't initialized yet i.e.
|
||||
// when the homepage is a non-remote page.
|
||||
Cu.reportError(e);
|
||||
gBrowser.updateBrowserRemotenessByURL(browser, uri);
|
||||
browser.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
|
||||
postData, null, null);
|
||||
@ -1144,56 +1145,15 @@ var gBrowserInit = {
|
||||
});
|
||||
|
||||
gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
|
||||
let browser = gBrowser.getBrowserForDocument(event.target);
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
TabCrashReporter.onAboutTabCrashedLoad(browser, {
|
||||
crashedTabCount: SessionStore.crashedTabCount,
|
||||
});
|
||||
#endif
|
||||
|
||||
// Reset the zoom for the tabcrashed page.
|
||||
ZoomManager.setZoomForBrowser(browser, 1);
|
||||
}, false, true);
|
||||
|
||||
gBrowser.addEventListener("AboutTabCrashedMessage", function(event) {
|
||||
let ownerDoc = event.originalTarget;
|
||||
|
||||
if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let isTopFrame = (ownerDoc.defaultView.parent === ownerDoc.defaultView);
|
||||
if (!isTopFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
let browser = gBrowser.getBrowserForDocument(ownerDoc);
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (event.detail.sendCrashReport) {
|
||||
TabCrashReporter.submitCrashReport(browser, {
|
||||
comments: event.detail.comments,
|
||||
email: event.detail.email,
|
||||
emailMe: event.detail.emailMe,
|
||||
includeURL: event.detail.includeURL,
|
||||
URL: event.detail.URL,
|
||||
});
|
||||
} else {
|
||||
TabCrashReporter.dontSubmitCrashReport();
|
||||
}
|
||||
#endif
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
switch (event.detail.message) {
|
||||
case "closeTab":
|
||||
gBrowser.removeTab(tab, { animate: true });
|
||||
break;
|
||||
case "restoreTab":
|
||||
SessionStore.reviveCrashedTab(tab);
|
||||
break;
|
||||
case "restoreAll":
|
||||
SessionStore.reviveAllCrashedTabs();
|
||||
break;
|
||||
}
|
||||
let browser = gBrowser.getBrowserForDocument(event.target);
|
||||
// Reset the zoom for the tabcrashed page.
|
||||
ZoomManager.setZoomForBrowser(browser, 1);
|
||||
}, false, true);
|
||||
|
||||
gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
|
||||
|
@ -4427,13 +4427,19 @@
|
||||
let uri = browser.currentURI;
|
||||
let icon = browser.mIconURL;
|
||||
|
||||
this.updateBrowserRemotenessByURL(browser, "about:tabcrashed");
|
||||
|
||||
browser.setAttribute("crashedPageTitle", title);
|
||||
browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
|
||||
browser.removeAttribute("crashedPageTitle");
|
||||
let tab = this.getTabForBrowser(browser);
|
||||
tab.setAttribute("crashed", true);
|
||||
|
||||
if (this.selectedBrowser == browser) {
|
||||
this.updateBrowserRemotenessByURL(browser, "about:tabcrashed");
|
||||
browser.setAttribute("crashedPageTitle", title);
|
||||
browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
|
||||
browser.removeAttribute("crashedPageTitle");
|
||||
tab.setAttribute("crashed", true);
|
||||
} else {
|
||||
this.updateBrowserRemoteness(browser, false);
|
||||
SessionStore.reviveCrashedTab(tab);
|
||||
}
|
||||
|
||||
tab.removeAttribute("soundplaying");
|
||||
this.setIcon(tab, icon);
|
||||
]]>
|
||||
|
@ -70,7 +70,7 @@ function crashTabTestHelper(fieldValues, expectedExtra) {
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, function*(browser) {
|
||||
let prefs = TabCrashReporter.prefs;
|
||||
let prefs = TabCrashHandler.prefs;
|
||||
let originalSendReport = prefs.getBoolPref("sendReport");
|
||||
let originalEmailMe = prefs.getBoolPref("emailMe");
|
||||
let originalIncludeURL = prefs.getBoolPref("includeURL");
|
||||
|
@ -37,7 +37,7 @@ add_task(function* test_clear_email() {
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, function*(browser) {
|
||||
let prefs = TabCrashReporter.prefs;
|
||||
let prefs = TabCrashHandler.prefs;
|
||||
let originalSendReport = prefs.getBoolPref("sendReport");
|
||||
let originalEmailMe = prefs.getBoolPref("emailMe");
|
||||
let originalIncludeURL = prefs.getBoolPref("includeURL");
|
||||
|
@ -8,8 +8,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
|
||||
"resource:///modules/ContentCrashReporters.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
|
||||
"resource:///modules/ContentCrashHandlers.jsm");
|
||||
|
||||
/**
|
||||
* Wait for a <notification> to be closed then call the specified callback.
|
||||
|
@ -142,11 +142,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
|
||||
"resource:///modules/ContentCrashHandlers.jsm");
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
|
||||
"resource:///modules/ContentCrashReporters.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
|
||||
"resource:///modules/ContentCrashReporters.jsm");
|
||||
"resource:///modules/ContentCrashHandlers.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
|
||||
@ -890,8 +890,8 @@ BrowserGlue.prototype = {
|
||||
});
|
||||
#endif
|
||||
|
||||
TabCrashHandler.init();
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
TabCrashReporter.init();
|
||||
PluginCrashReporter.init();
|
||||
#endif
|
||||
|
||||
|
@ -8,6 +8,7 @@ this.EXPORTED_SYMBOLS = ["ContentRestore"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
@ -182,8 +183,19 @@ ContentRestoreInternal.prototype = {
|
||||
let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory;
|
||||
|
||||
// Listen for the tab to finish loading.
|
||||
this.restoreTabContentStarted(finishCallback);
|
||||
// Wait for the tab load to complete or fail
|
||||
this.restoreTabContentStarted((status) => {
|
||||
// If loadArgument is not null then we're attempting to load a new url
|
||||
// that required us to switch process. If that load is cancelled (for
|
||||
// example by a content handler) we want to restore the current history
|
||||
// entry.
|
||||
if (loadArguments && (status == Cr.NS_BINDING_ABORTED)) {
|
||||
this._tabData = tabData;
|
||||
this.restoreTabContent(null, finishCallback);
|
||||
} else {
|
||||
finishCallback();
|
||||
}
|
||||
});
|
||||
|
||||
// Reset the current URI to about:blank. We changed it above for
|
||||
// switch-to-tab, but now it must go back to the correct value before the
|
||||
@ -248,22 +260,26 @@ ContentRestoreInternal.prototype = {
|
||||
*/
|
||||
restoreTabContentStarted(finishCallback) {
|
||||
// The reload listener is no longer needed.
|
||||
this._historyListener.uninstall();
|
||||
this._historyListener = null;
|
||||
if (this._historyListener) {
|
||||
this._historyListener.uninstall();
|
||||
this._historyListener = null;
|
||||
}
|
||||
|
||||
// Remove the old progress listener.
|
||||
this._progressListener.uninstall();
|
||||
if (this._progressListener) {
|
||||
this._progressListener.uninstall();
|
||||
}
|
||||
|
||||
// We're about to start a load. This listener will be called when the load
|
||||
// has finished getting everything from the network.
|
||||
this._progressListener = new ProgressListener(this.docShell, {
|
||||
onStopRequest: () => {
|
||||
onStopRequest: (status) => {
|
||||
// Call resetRestore() to reset the state back to normal. The data
|
||||
// needed for restoreDocument() (which hasn't happened yet) will
|
||||
// remain in _restoringDocument.
|
||||
this.resetRestore();
|
||||
|
||||
finishCallback();
|
||||
finishCallback(status);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -415,7 +431,7 @@ ProgressListener.prototype = {
|
||||
}
|
||||
|
||||
if (stateFlags & STATE_STOP && this.callbacks.onStopRequest) {
|
||||
this.callbacks.onStopRequest();
|
||||
this.callbacks.onStopRequest(status);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -16,6 +16,7 @@ const FORMAT_VERSION = 1;
|
||||
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
const TAB_STATE_RESTORING = 2;
|
||||
const TAB_STATE_WILL_RESTORE = 3;
|
||||
|
||||
const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
|
||||
const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
|
||||
@ -201,10 +202,6 @@ this.SessionStore = {
|
||||
return SessionStoreInternal.canRestoreLastSession;
|
||||
},
|
||||
|
||||
get crashedTabCount() {
|
||||
return SessionStoreInternal._crashedBrowsersCount;
|
||||
},
|
||||
|
||||
set canRestoreLastSession(val) {
|
||||
SessionStoreInternal.canRestoreLastSession = val;
|
||||
},
|
||||
@ -388,9 +385,6 @@ var SessionStoreInternal = {
|
||||
// they get restored).
|
||||
_crashedBrowsers: new WeakSet(),
|
||||
|
||||
// The number of crashed browsers.
|
||||
_crashedBrowsersCount: 0,
|
||||
|
||||
// A map (xul:browser -> nsIFrameLoader) that maps a browser to the last
|
||||
// associated frameLoader we heard about.
|
||||
_lastKnownFrameLoader: new WeakMap(),
|
||||
@ -405,6 +399,11 @@ var SessionStoreInternal = {
|
||||
// that is being stored in _closedWindows for that tab.
|
||||
_closedWindowTabs: new WeakMap(),
|
||||
|
||||
// A map (xul:browser -> object) that maps a browser that is switching
|
||||
// remoteness via navigateAndRestore, to the loadArguments that were
|
||||
// most recently passed when calling navigateAndRestore.
|
||||
_remotenessChangingBrowsers: new WeakMap(),
|
||||
|
||||
// whether a setBrowserState call is in progress
|
||||
_browserSetState: false,
|
||||
|
||||
@ -828,6 +827,8 @@ var SessionStoreInternal = {
|
||||
this._sendTabRestoredNotification(tab);
|
||||
break;
|
||||
case "SessionStore:crashedTabRevived":
|
||||
// The browser was revived by navigating to a different page
|
||||
// manually, so we remove it from the ignored browser set.
|
||||
this._crashedBrowsers.delete(browser.permanentKey);
|
||||
break;
|
||||
case "SessionStore:error":
|
||||
@ -1575,10 +1576,6 @@ var SessionStoreInternal = {
|
||||
if (!aNoNotification) {
|
||||
this.saveStateDelayed(aWindow);
|
||||
}
|
||||
|
||||
if (this._crashedBrowsers.has(browser.permanentKey)) {
|
||||
this._crashedBrowsersCount++;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1608,10 +1605,6 @@ var SessionStoreInternal = {
|
||||
if (!aNoNotification) {
|
||||
this.saveStateDelayed(aWindow);
|
||||
}
|
||||
|
||||
if (this._crashedBrowsers.has(browser.permanentKey)) {
|
||||
this._crashedBrowsersCount--;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1791,17 +1784,14 @@ var SessionStoreInternal = {
|
||||
* The <xul:browser> that is now in the crashed state.
|
||||
*/
|
||||
onBrowserCrashed: function(aWindow, aBrowser) {
|
||||
NS_ASSERT(aBrowser.isRemoteBrowser,
|
||||
"Only remote browsers should be able to crash");
|
||||
this._crashedBrowsers.add(aBrowser.permanentKey);
|
||||
this._crashedBrowsersCount++;
|
||||
// If we never got around to restoring this tab, clear its state so
|
||||
// that we don't try restoring if the user switches to it before
|
||||
// reviving the crashed browser. This is throwing away the information
|
||||
// that the tab was in a pending state when the browser crashed, which
|
||||
// is an explicit choice. For now, when restoring all crashed tabs, based
|
||||
// on a user preference we'll either restore all of them at once, or only
|
||||
// restore the selected tab and lazily restore the rest. We'll make no
|
||||
// efforts at this time to be smart and restore all of the tabs that had
|
||||
// been in a restored state at the time of the crash.
|
||||
|
||||
// If we hadn't yet restored, or were still in the midst of
|
||||
// restoring this browser at the time of the crash, we need
|
||||
// to reset its state so that we can try to restore it again
|
||||
// when the user revives the tab from the crash.
|
||||
if (aBrowser.__SS_restoreState) {
|
||||
let tab = aWindow.gBrowser.getTabForBrowser(aBrowser);
|
||||
this._resetLocalTabRestoringState(tab);
|
||||
@ -2346,10 +2336,17 @@ var SessionStoreInternal = {
|
||||
"Somehow a crashed browser is still remote.")
|
||||
}
|
||||
|
||||
let data = TabState.collect(aTab);
|
||||
this.restoreTab(aTab, data);
|
||||
// We put the browser at about:blank in case the user is
|
||||
// restoring tabs on demand. This way, the user won't see
|
||||
// a flash of the about:tabcrashed page after selecting
|
||||
// the revived tab.
|
||||
aTab.removeAttribute("crashed");
|
||||
browser.loadURI("about:blank", null, null);
|
||||
|
||||
this._crashedBrowsersCount--;
|
||||
let data = TabState.collect(aTab);
|
||||
this.restoreTab(aTab, data, {
|
||||
forceOnDemand: true,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2363,8 +2360,6 @@ var SessionStoreInternal = {
|
||||
this.reviveCrashedTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
this._crashedBrowsersCount = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2373,12 +2368,32 @@ var SessionStoreInternal = {
|
||||
* or restoring the exact same state again and passing the new URL to load
|
||||
* in |loadArguments|. Use this method to seamlessly switch between pages
|
||||
* loaded in the parent and pages loaded in the child process.
|
||||
*
|
||||
* This method might be called multiple times before it has finished
|
||||
* flushing the browser tab. If that occurs, the loadArguments from
|
||||
* the most recent call to navigateAndRestore will be used once the
|
||||
* flush has finished.
|
||||
*/
|
||||
navigateAndRestore(tab, loadArguments, historyIndex) {
|
||||
let window = tab.ownerDocument.defaultView;
|
||||
NS_ASSERT(window.__SSi, "tab's window must be tracked");
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
// Were we already waiting for a flush from a previous call to
|
||||
// navigateAndRestore on this tab?
|
||||
let alreadyRestoring =
|
||||
this._remotenessChangingBrowsers.has(browser.permanentKey);
|
||||
|
||||
// Stash the most recent loadArguments in this WeakMap so that
|
||||
// we know to use it when the TabStateFlusher.flush resolves.
|
||||
this._remotenessChangingBrowsers.set(browser.permanentKey, loadArguments);
|
||||
|
||||
if (alreadyRestoring) {
|
||||
// This tab was already being restored to run in the
|
||||
// correct process. We're done here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set tab title to "Connecting..." and start the throbber to pretend we're
|
||||
// doing something while actually waiting for data from the frame script.
|
||||
window.gBrowser.setTabTitleLoading(tab);
|
||||
@ -2386,6 +2401,13 @@ var SessionStoreInternal = {
|
||||
|
||||
// Flush to get the latest tab state.
|
||||
TabStateFlusher.flush(browser).then(() => {
|
||||
// loadArguments might have been overwritten by multiple calls
|
||||
// to navigateAndRestore while we waited for the tab to flush,
|
||||
// so we use the most recently stored one.
|
||||
let recentLoadArguments =
|
||||
this._remotenessChangingBrowsers.get(browser.permanentKey);
|
||||
this._remotenessChangingBrowsers.delete(browser.permanentKey);
|
||||
|
||||
// The tab might have been closed/gone in the meantime.
|
||||
if (tab.closing || !tab.linkedBrowser) {
|
||||
return;
|
||||
@ -2406,7 +2428,7 @@ var SessionStoreInternal = {
|
||||
tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
|
||||
} else {
|
||||
tabState.userTypedValue = null;
|
||||
options.loadArguments = loadArguments;
|
||||
options.loadArguments = recentLoadArguments;
|
||||
}
|
||||
|
||||
// Need to reset restoring tabs.
|
||||
@ -2417,6 +2439,8 @@ var SessionStoreInternal = {
|
||||
// Restore the state into the tab.
|
||||
this.restoreTab(tab, tabState, options);
|
||||
});
|
||||
|
||||
tab.linkedBrowser.__SS_restoreState = TAB_STATE_WILL_RESTORE;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2805,7 +2829,10 @@ var SessionStoreInternal = {
|
||||
for (var t = 0; t < newTabCount; t++) {
|
||||
tabs.push(t < openTabCount ?
|
||||
tabbrowser.tabs[t] :
|
||||
tabbrowser.addTab("about:blank", {skipAnimation: true}));
|
||||
tabbrowser.addTab("about:blank", {
|
||||
skipAnimation: true,
|
||||
forceNotRemote: true,
|
||||
}));
|
||||
|
||||
if (winData.tabs[t].pinned)
|
||||
tabbrowser.pinTab(tabs[t]);
|
||||
@ -3046,6 +3073,7 @@ var SessionStoreInternal = {
|
||||
let browser = tab.linkedBrowser;
|
||||
let window = tab.ownerDocument.defaultView;
|
||||
let tabbrowser = window.gBrowser;
|
||||
let forceOnDemand = options.forceOnDemand;
|
||||
|
||||
// Increase the busy state counter before modifying the tab.
|
||||
this._setWindowStateBusy(window);
|
||||
@ -3113,21 +3141,6 @@ var SessionStoreInternal = {
|
||||
// Save the index in case we updated it above.
|
||||
tabData.index = activeIndex + 1;
|
||||
|
||||
// In electrolysis, we may need to change the browser's remote
|
||||
// attribute so that it runs in a content process.
|
||||
let activePageData = tabData.entries[activeIndex] || null;
|
||||
let uri = activePageData ? activePageData.url || null : null;
|
||||
if (loadArguments) {
|
||||
uri = loadArguments.uri;
|
||||
}
|
||||
tabbrowser.updateBrowserRemotenessByURL(browser, uri);
|
||||
|
||||
// If the restored browser wants to show view source content, start up a
|
||||
// view source browser that will load the required frame script.
|
||||
if (uri && ViewSourceBrowser.isViewSource(uri)) {
|
||||
new ViewSourceBrowser(browser);
|
||||
}
|
||||
|
||||
// Start a new epoch to discard all frame script messages relating to a
|
||||
// previous epoch. All async messages that are still on their way to chrome
|
||||
// will be ignored and don't override any tab data set when restoring.
|
||||
@ -3139,6 +3152,10 @@ var SessionStoreInternal = {
|
||||
browser.setAttribute("pending", "true");
|
||||
tab.setAttribute("pending", "true");
|
||||
|
||||
// If we're restoring this tab, it certainly shouldn't be in
|
||||
// the ignored set anymore.
|
||||
this._crashedBrowsers.delete(browser.permanentKey);
|
||||
|
||||
// Update the persistent tab state cache with |tabData| information.
|
||||
TabStateCache.update(browser, {
|
||||
history: {entries: tabData.entries, index: tabData.index},
|
||||
@ -3168,7 +3185,7 @@ var SessionStoreInternal = {
|
||||
// it ensures each window will have its selected tab loaded.
|
||||
if (restoreImmediately || tabbrowser.selectedBrowser == browser || loadArguments) {
|
||||
this.restoreTabContent(tab, loadArguments);
|
||||
} else {
|
||||
} else if (!forceOnDemand) {
|
||||
TabRestoreQueue.add(tab);
|
||||
this.restoreNextTab();
|
||||
}
|
||||
@ -3186,9 +3203,45 @@ var SessionStoreInternal = {
|
||||
* optional load arguments used for loadURI()
|
||||
*/
|
||||
restoreTabContent: function (aTab, aLoadArguments = null) {
|
||||
let browser = aTab.linkedBrowser;
|
||||
let window = aTab.ownerDocument.defaultView;
|
||||
let tabbrowser = window.gBrowser;
|
||||
let tabData = TabState.clone(aTab);
|
||||
let activeIndex = tabData.index - 1;
|
||||
let activePageData = tabData.entries[activeIndex] || null;
|
||||
let uri = activePageData ? activePageData.url || null : null;
|
||||
if (aLoadArguments) {
|
||||
uri = aLoadArguments.uri;
|
||||
}
|
||||
|
||||
// We have to mark this tab as restoring first, otherwise
|
||||
// the "pending" attribute will be applied to the linked
|
||||
// browser, which removes it from the display list. We cannot
|
||||
// flip the remoteness of any browser that is not being displayed.
|
||||
this.markTabAsRestoring(aTab);
|
||||
|
||||
let browser = aTab.linkedBrowser;
|
||||
if (tabbrowser.updateBrowserRemotenessByURL(browser, uri)) {
|
||||
// We updated the remoteness, so we need to send the history down again.
|
||||
//
|
||||
// Start a new epoch to discard all frame script messages relating to a
|
||||
// previous epoch. All async messages that are still on their way to chrome
|
||||
// will be ignored and don't override any tab data set when restoring.
|
||||
let epoch = this.startNextEpoch(browser);
|
||||
|
||||
browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory", {
|
||||
tabData: tabData,
|
||||
epoch: epoch,
|
||||
loadArguments: aLoadArguments
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// If the restored browser wants to show view source content, start up a
|
||||
// view source browser that will load the required frame script.
|
||||
if (uri && ViewSourceBrowser.isViewSource(uri)) {
|
||||
new ViewSourceBrowser(browser);
|
||||
}
|
||||
|
||||
browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
|
||||
{loadArguments: aLoadArguments});
|
||||
},
|
||||
@ -4123,8 +4176,6 @@ var TabRestoreQueue = {
|
||||
if (index > -1) {
|
||||
hidden.splice(index, 1);
|
||||
visible.push(tab);
|
||||
} else {
|
||||
throw new Error("restore queue: hidden tab not found");
|
||||
}
|
||||
},
|
||||
|
||||
@ -4136,8 +4187,6 @@ var TabRestoreQueue = {
|
||||
if (index > -1) {
|
||||
visible.splice(index, 1);
|
||||
hidden.push(tab);
|
||||
} else {
|
||||
throw new Error("restore queue: visible tab not found");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -78,6 +78,10 @@ run-if = e10s
|
||||
[browser_cookies.js]
|
||||
[browser_crashedTabs.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_unrestored_crashedTabs.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_revive_crashed_bg_tabs.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_dying_cache.js]
|
||||
[browser_dynamic_frames.js]
|
||||
[browser_form_restore_events.js]
|
||||
@ -211,3 +215,5 @@ skip-if = os == "mac"
|
||||
|
||||
[browser_911547.js]
|
||||
[browser_send_async_message_oom.js]
|
||||
[browser_multiple_navigateAndRestore.js]
|
||||
run-if = e10s
|
||||
|
@ -42,6 +42,7 @@ add_task(function* test_flush() {
|
||||
add_task(function* test_crash() {
|
||||
// Create new tab.
|
||||
let tab = gBrowser.addTab(URL);
|
||||
gBrowser.selectedTab = tab;
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
|
@ -101,6 +101,23 @@ function promiseHistoryLength(browser, length) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves when a browser has fired the
|
||||
* AboutTabCrashedReady event.
|
||||
*
|
||||
* @param browser
|
||||
* The remote <xul:browser> that will fire the event.
|
||||
* @return Promise
|
||||
*/
|
||||
function promiseTabCrashedReady(browser) {
|
||||
return new Promise((resolve) => {
|
||||
browser.addEventListener("AboutTabCrashedReady", function ready(e) {
|
||||
browser.removeEventListener("AboutTabCrashedReady", ready, false, true);
|
||||
resolve();
|
||||
}, false, true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that if a tab crashes, that information about the tab crashed
|
||||
* page does not get added to the tab history.
|
||||
@ -236,13 +253,16 @@ add_task(function test_revive_tab_from_session_store() {
|
||||
|
||||
// Crash the tab
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
|
||||
// Background tabs should not be crashed, but should be in the "to be restored"
|
||||
// state.
|
||||
ok(!newTab2.hasAttribute("crashed"), "Second tab should not be crashed.");
|
||||
ok(newTab2.hasAttribute("pending"), "Second tab should be pending.");
|
||||
|
||||
// Use SessionStore to revive the tab
|
||||
// Use SessionStore to revive the first tab
|
||||
clickButton(browser, "restoreTab");
|
||||
yield promiseTabRestored(newTab);
|
||||
ok(!newTab.hasAttribute("crashed"), "Tab shouldn't be marked as crashed anymore.");
|
||||
is(newTab2.getAttribute("crashed"), "true", "Second tab should still be crashed though.");
|
||||
ok(newTab2.hasAttribute("pending"), "Second tab should still be pending.");
|
||||
|
||||
// We can't just check browser.currentURI.spec, because from
|
||||
// the outside, a crashed tab has the same URI as the page
|
||||
@ -258,8 +278,8 @@ add_task(function test_revive_tab_from_session_store() {
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that we can revive a crashed tab back to the page that
|
||||
* it was on when it crashed.
|
||||
* Checks that we can revive multiple crashed tabs back to the pages
|
||||
* that they were on when they crashed.
|
||||
*/
|
||||
add_task(function test_revive_all_tabs_from_session_store() {
|
||||
let newTab = gBrowser.addTab();
|
||||
@ -271,7 +291,12 @@ add_task(function test_revive_all_tabs_from_session_store() {
|
||||
browser.loadURI(PAGE_1);
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
let newTab2 = gBrowser.addTab(PAGE_1);
|
||||
// In order to see a second about:tabcrashed page, we'll need
|
||||
// a second window, since only selected tabs will show
|
||||
// about:tabcrashed.
|
||||
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let newTab2 = win2.gBrowser.addTab(PAGE_1);
|
||||
win2.gBrowser.selectedTab = newTab2;
|
||||
let browser2 = newTab2.linkedBrowser;
|
||||
ok(browser2.isRemoteBrowser, "Should be a remote browser");
|
||||
yield promiseBrowserLoaded(browser2);
|
||||
@ -287,7 +312,9 @@ add_task(function test_revive_all_tabs_from_session_store() {
|
||||
|
||||
// Crash the tab
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
|
||||
// Both tabs should now be crashed.
|
||||
is(newTab.getAttribute("crashed"), "true", "First tab should be crashed");
|
||||
is(newTab2.getAttribute("crashed"), "true", "Second window tab should be crashed");
|
||||
|
||||
// Use SessionStore to revive all the tabs
|
||||
clickButton(browser, "restoreAll");
|
||||
@ -295,10 +322,6 @@ add_task(function test_revive_all_tabs_from_session_store() {
|
||||
ok(!newTab.hasAttribute("crashed"), "Tab shouldn't be marked as crashed anymore.");
|
||||
ok(!newTab.hasAttribute("pending"), "Tab shouldn't be pending.");
|
||||
ok(!newTab2.hasAttribute("crashed"), "Second tab shouldn't be marked as crashed anymore.");
|
||||
ok(newTab2.hasAttribute("pending"), "Second tab should be pending.");
|
||||
|
||||
gBrowser.selectedTab = newTab2;
|
||||
yield promiseTabRestored(newTab2);
|
||||
ok(!newTab2.hasAttribute("pending"), "Second tab shouldn't be pending.");
|
||||
|
||||
// We can't just check browser.currentURI.spec, because from
|
||||
@ -311,8 +334,8 @@ add_task(function test_revive_all_tabs_from_session_store() {
|
||||
// We should also have two entries in the browser history.
|
||||
yield promiseHistoryLength(browser, 2);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win2);
|
||||
gBrowser.removeTab(newTab);
|
||||
gBrowser.removeTab(newTab2);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -342,9 +365,10 @@ add_task(function test_close_tab_after_crash() {
|
||||
is(gBrowser.tabs.length, 1, "Should have closed the tab");
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Checks that "restore all" button is only shown if more than one tab
|
||||
* has crashed.
|
||||
* is showing about:tabcrashed
|
||||
*/
|
||||
add_task(function* test_hide_restore_all_button() {
|
||||
let newTab = gBrowser.addTab();
|
||||
@ -374,8 +398,21 @@ add_task(function* test_hide_restore_all_button() {
|
||||
browser.loadURI(PAGE_2);
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Crash the tab
|
||||
// Load up a second window so we can get another tab to show
|
||||
// about:tabcrashed
|
||||
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let newTab3 = win2.gBrowser.addTab(PAGE_2);
|
||||
win2.gBrowser.selectedTab = newTab3;
|
||||
let otherWinBrowser = newTab3.linkedBrowser;
|
||||
yield promiseBrowserLoaded(otherWinBrowser);
|
||||
// We'll need to make sure the second tab's browser has finished
|
||||
// sending its AboutTabCrashedReady event before we know for
|
||||
// sure whether or not we're showing the right Restore buttons.
|
||||
let otherBrowserReady = promiseTabCrashedReady(otherWinBrowser);
|
||||
|
||||
// Crash the first tab.
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
yield otherBrowserReady;
|
||||
|
||||
doc = browser.contentDocument;
|
||||
restoreAllButton = doc.getElementById("restoreAll");
|
||||
@ -384,6 +421,7 @@ add_task(function* test_hide_restore_all_button() {
|
||||
ok(!restoreAllButton.hasAttribute("hidden"), "Restore All button should not be hidden");
|
||||
ok(!(restoreOneButton.classList.contains("primary")), "Restore Tab button should not have the primary class");
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win2);
|
||||
gBrowser.removeTab(newTab);
|
||||
gBrowser.removeTab(newTab2);
|
||||
});
|
||||
|
@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
|
||||
const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
|
||||
const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
|
||||
|
||||
add_task(function*() {
|
||||
// Load an empty, non-remote tab at about:blank...
|
||||
let tab = gBrowser.addTab("about:blank", {
|
||||
forceNotRemote: true,
|
||||
});
|
||||
gBrowser.selectedTab = tab;
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
ok(!browser.isRemoteBrowser, "Ensure browser is not remote");
|
||||
// Load a remote page, and then another remote page immediately
|
||||
// after.
|
||||
browser.loadURI(PAGE_1);
|
||||
browser.stop();
|
||||
browser.loadURI(PAGE_2);
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
ok(browser.isRemoteBrowser, "Should have switched remoteness");
|
||||
yield TabStateFlusher.flush(browser);
|
||||
let state = JSON.parse(ss.getTabState(tab));
|
||||
let entries = state.entries;
|
||||
is(entries.length, 1, "There should only be one entry");
|
||||
is(entries[0].url, PAGE_2, "Should have PAGE_2 as the sole history entry");
|
||||
is(browser.currentURI.spec, PAGE_2, "Should have PAGE_2 as the browser currentURI");
|
||||
|
||||
yield ContentTask.spawn(browser, PAGE_2, function*(PAGE_2) {
|
||||
docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
is(docShell.currentURI.spec, PAGE_2,
|
||||
"Content should have PAGE_2 as the browser currentURI");
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
@ -34,9 +34,8 @@ var testSwitchToTab = Task.async(function* (url, options) {
|
||||
|
||||
// Switch-to-tab with a similar URI.
|
||||
switchToTabHavingURI(url, false, options);
|
||||
ok(!tab.hasAttribute("pending"), "tab is no longer pending");
|
||||
|
||||
// Wait until the tab is restored.
|
||||
// Tab should now restore
|
||||
yield promiseTabRestored(tab);
|
||||
is(browser.currentURI.spec, url, "correct URL loaded");
|
||||
|
||||
|
@ -0,0 +1,55 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that even if the user has set their tabs to restore
|
||||
* immediately on session start, that background tabs after a
|
||||
* content process crash restore on demand.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
|
||||
const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
|
||||
|
||||
add_task(function* setup() {
|
||||
yield pushPrefs(["browser.tabs.animate", false],
|
||||
["browser.sessionstore.restore_on_demand", false]);
|
||||
});
|
||||
|
||||
add_task(function* test_revive_bg_tabs_on_demand() {
|
||||
let newTab1 = gBrowser.addTab(PAGE_1);
|
||||
let browser1 = newTab1.linkedBrowser;
|
||||
gBrowser.selectedTab = newTab1;
|
||||
|
||||
let newTab2 = gBrowser.addTab(PAGE_2);
|
||||
let browser2 = newTab2.linkedBrowser;
|
||||
|
||||
yield BrowserTestUtils.browserLoaded(browser1);
|
||||
yield BrowserTestUtils.browserLoaded(browser2);
|
||||
|
||||
yield TabStateFlusher.flush(browser2);
|
||||
|
||||
// Now crash the selected tab
|
||||
let windowReady = BrowserTestUtils.waitForEvent(window, "SSWindowStateReady");
|
||||
yield BrowserTestUtils.crashBrowser(browser1);
|
||||
|
||||
ok(newTab1.hasAttribute("crashed"), "Selected tab should be crashed");
|
||||
ok(!newTab2.hasAttribute("crashed"), "Background tab should not be crashed");
|
||||
|
||||
// Wait until we've had a chance to restore all tabs immediately
|
||||
yield windowReady;
|
||||
|
||||
// But we should not have restored the background tab
|
||||
ok(newTab2.hasAttribute("pending"), "Background tab should be pending");
|
||||
|
||||
// Now select newTab2 to make sure it restores.
|
||||
let newTab2Restored = promiseTabRestored(newTab2);
|
||||
gBrowser.selectedTab = newTab2;
|
||||
yield newTab2Restored;
|
||||
|
||||
ok(browser2.isRemoteBrowser, "Restored browser should be remote");
|
||||
|
||||
yield BrowserTestUtils.removeTab(newTab1);
|
||||
yield BrowserTestUtils.removeTab(newTab2);
|
||||
});
|
@ -45,3 +45,53 @@ add_task(function* () {
|
||||
// Cleanup.
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
// Add a new non-remote tab.
|
||||
let tab = gBrowser.addTab("about:robots");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
ok(!browser.isRemoteBrowser, "browser is not remote");
|
||||
|
||||
// Wait for the tab to change to remote before adding the progress listener
|
||||
tab.addEventListener("TabRemotenessChange", function listener() {
|
||||
tab.removeEventListener("TabRemotenessChange", listener);
|
||||
|
||||
ContentTask.spawn(browser, URL, function*(url) {
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
|
||||
wp.addProgressListener({
|
||||
onStateChange: function(progress, request, stateFlags, status) {
|
||||
if (!(request instanceof Ci.nsIChannel))
|
||||
return;
|
||||
|
||||
if (request.URI.spec == url) {
|
||||
request.cancel(Cr.NS_BINDING_ABORTED);
|
||||
wp.removeProgressListener(this);
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference
|
||||
])
|
||||
}, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
});
|
||||
});
|
||||
|
||||
// Load a new remote URI and when we see the load start cancel it
|
||||
browser.loadURI(URL);
|
||||
yield promiseTabRestored(tab);
|
||||
|
||||
let count = yield countHistoryEntries(browser);
|
||||
is(count, 1, "Should only be the one history entry.");
|
||||
|
||||
is(browser.currentURI.spec, "about:robots", "Should be back to the original URI");
|
||||
ok(!browser.isRemoteBrowser, "Should have gone back to a remote browser");
|
||||
|
||||
// Cleanup.
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
@ -0,0 +1,69 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that if we have tabs that are still in the "click to
|
||||
* restore" state, that if their browsers crash, that we don't
|
||||
* show the crashed state for those tabs (since selecting them
|
||||
* should restore them anyway).
|
||||
*/
|
||||
|
||||
const PREF = "browser.sessionstore.restore_on_demand";
|
||||
const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
|
||||
|
||||
add_task(function* test() {
|
||||
yield pushPrefs([PREF, true]);
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, function*(browser) {
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
// We'll create a second "pending" tab. This is the one we'll
|
||||
// ensure doesn't go to about:tabcrashed. We start it non-remote
|
||||
// since this is how SessionStore creates all browsers before
|
||||
// they are restored.
|
||||
let unrestoredTab = gBrowser.addTab("about:blank", {
|
||||
skipAnimation: true,
|
||||
forceNotRemote: true,
|
||||
});
|
||||
|
||||
let state = {
|
||||
entries: [{url: PAGE}],
|
||||
};
|
||||
|
||||
ss.setTabState(unrestoredTab, JSON.stringify(state));
|
||||
|
||||
ok(!unrestoredTab.hasAttribute("crashed"), "tab is not crashed");
|
||||
ok(unrestoredTab.hasAttribute("pending"), "tab is pending");
|
||||
|
||||
// Now crash the selected browser.
|
||||
yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
ok(!unrestoredTab.hasAttribute("crashed"), "tab is still not crashed");
|
||||
ok(unrestoredTab.hasAttribute("pending"), "tab is still pending");
|
||||
|
||||
// Selecting the tab should now restore it.
|
||||
gBrowser.selectedTab = unrestoredTab;
|
||||
yield promiseTabRestored(unrestoredTab);
|
||||
|
||||
ok(!unrestoredTab.hasAttribute("crashed"), "tab is still not crashed");
|
||||
ok(!unrestoredTab.hasAttribute("pending"), "tab is no longer pending");
|
||||
|
||||
// The original tab should still be crashed
|
||||
let originalTab = gBrowser.getTabForBrowser(browser);
|
||||
ok(originalTab.hasAttribute("crashed"), "original tab is crashed");
|
||||
ok(!originalTab.isRemoteBrowser, "Should not be remote");
|
||||
|
||||
// We'd better be able to restore it still.
|
||||
gBrowser.selectedTab = originalTab;
|
||||
SessionStore.reviveCrashedTab(originalTab);
|
||||
yield promiseTabRestored(originalTab);
|
||||
|
||||
// Clean up.
|
||||
yield BrowserTestUtils.removeTab(unrestoredTab);
|
||||
});
|
||||
});
|
@ -562,3 +562,4 @@ function popPrefs() {
|
||||
SpecialPowers.popPrefEnv(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.2.109
|
||||
Current extension version is: 1.3.14
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -14,11 +12,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* jshint esnext:true */
|
||||
/* globals Components, Services, XPCOMUtils, PdfjsChromeUtils,
|
||||
PdfjsContentUtils, DEFAULT_PREFERENCES, PdfStreamConverter */
|
||||
|
||||
'use strict';
|
||||
|
||||
var EXPORTED_SYMBOLS = ['PdfJs'];
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2013 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -14,10 +12,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* jshint esnext:true */
|
||||
/* globals Components, Services, XPCOMUtils, DEFAULT_PREFERENCES */
|
||||
|
||||
'use strict';
|
||||
|
||||
var EXPORTED_SYMBOLS = ['PdfjsChromeUtils'];
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,8 +20,8 @@ if (typeof PDFJS === 'undefined') {
|
||||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '1.2.109';
|
||||
PDFJS.build = '875588d';
|
||||
PDFJS.version = '1.3.14';
|
||||
PDFJS.build = 'df46b64';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
@ -1963,6 +1961,8 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
||||
* calling of PDFPage.getViewport method.
|
||||
* @property {string} intent - Rendering intent, can be 'display' or 'print'
|
||||
* (default value is 'display').
|
||||
* @property {Array} transform - (optional) Additional transform, applied
|
||||
* just before viewport transform.
|
||||
* @property {Object} imageLayer - (optional) An object that has beginLayout,
|
||||
* endLayout and appendImage functions.
|
||||
* @property {function} continueCallback - (deprecated) A function that will be
|
||||
@ -3025,7 +3025,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() {
|
||||
this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
|
||||
this.objs, params.imageLayer);
|
||||
|
||||
this.gfx.beginDrawing(params.viewport, transparency);
|
||||
this.gfx.beginDrawing(params.transform, params.viewport, transparency);
|
||||
this.operatorListIdx = 0;
|
||||
this.graphicsReady = true;
|
||||
if (this.graphicsReadyCallback) {
|
||||
@ -3322,13 +3322,15 @@ function addContextCurrentTransform(ctx) {
|
||||
}
|
||||
|
||||
var CachedCanvases = (function CachedCanvasesClosure() {
|
||||
var cache = {};
|
||||
return {
|
||||
function CachedCanvases() {
|
||||
this.cache = Object.create(null);
|
||||
}
|
||||
CachedCanvases.prototype = {
|
||||
getCanvas: function CachedCanvases_getCanvas(id, width, height,
|
||||
trackTransform) {
|
||||
var canvasEntry;
|
||||
if (cache[id] !== undefined) {
|
||||
canvasEntry = cache[id];
|
||||
if (this.cache[id] !== undefined) {
|
||||
canvasEntry = this.cache[id];
|
||||
canvasEntry.canvas.width = width;
|
||||
canvasEntry.canvas.height = height;
|
||||
// reset canvas transform for emulated mozCurrentTransform, if needed
|
||||
@ -3339,21 +3341,22 @@ var CachedCanvases = (function CachedCanvasesClosure() {
|
||||
if (trackTransform) {
|
||||
addContextCurrentTransform(ctx);
|
||||
}
|
||||
cache[id] = canvasEntry = {canvas: canvas, context: ctx};
|
||||
this.cache[id] = canvasEntry = {canvas: canvas, context: ctx};
|
||||
}
|
||||
return canvasEntry;
|
||||
},
|
||||
clear: function () {
|
||||
for (var id in cache) {
|
||||
var canvasEntry = cache[id];
|
||||
for (var id in this.cache) {
|
||||
var canvasEntry = this.cache[id];
|
||||
// Zeroing the width and height causes Firefox to release graphics
|
||||
// resources immediately, which can greatly reduce memory consumption.
|
||||
canvasEntry.canvas.width = 0;
|
||||
canvasEntry.canvas.height = 0;
|
||||
delete cache[id];
|
||||
delete this.cache[id];
|
||||
}
|
||||
}
|
||||
};
|
||||
return CachedCanvases;
|
||||
})();
|
||||
|
||||
function compileType3Glyph(imgData) {
|
||||
@ -3591,6 +3594,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.smaskStack = [];
|
||||
this.smaskCounter = 0;
|
||||
this.tempSMask = null;
|
||||
this.cachedCanvases = new CachedCanvases();
|
||||
if (canvasCtx) {
|
||||
// NOTE: if mozCurrentTransform is polyfilled, then the current state of
|
||||
// the transformation must already be set in canvasCtx._transformMatrix.
|
||||
@ -3867,28 +3871,39 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
|
||||
CanvasGraphics.prototype = {
|
||||
|
||||
beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) {
|
||||
beginDrawing: function CanvasGraphics_beginDrawing(transform, viewport,
|
||||
transparency) {
|
||||
// For pdfs that use blend modes we have to clear the canvas else certain
|
||||
// blend modes can look wrong since we'd be blending with a white
|
||||
// backdrop. The problem with a transparent backdrop though is we then
|
||||
// don't get sub pixel anti aliasing on text, so we fill with white if
|
||||
// we can.
|
||||
// don't get sub pixel anti aliasing on text, creating temporary
|
||||
// transparent canvas when we have blend modes.
|
||||
var width = this.ctx.canvas.width;
|
||||
var height = this.ctx.canvas.height;
|
||||
if (transparency) {
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
} else {
|
||||
this.ctx.mozOpaque = true;
|
||||
this.ctx.save();
|
||||
this.ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
this.ctx.fillRect(0, 0, width, height);
|
||||
this.ctx.restore();
|
||||
}
|
||||
|
||||
var transform = viewport.transform;
|
||||
|
||||
this.ctx.save();
|
||||
this.ctx.transform.apply(this.ctx, transform);
|
||||
this.ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
this.ctx.fillRect(0, 0, width, height);
|
||||
this.ctx.restore();
|
||||
|
||||
if (transparency) {
|
||||
var transparentCanvas = this.cachedCanvases.getCanvas(
|
||||
'transparent', width, height, true);
|
||||
this.compositeCtx = this.ctx;
|
||||
this.transparentCanvas = transparentCanvas.canvas;
|
||||
this.ctx = transparentCanvas.context;
|
||||
this.ctx.save();
|
||||
// The transform can be applied before rendering, transferring it to
|
||||
// the new canvas.
|
||||
this.ctx.transform.apply(this.ctx,
|
||||
this.compositeCtx.mozCurrentTransform);
|
||||
}
|
||||
|
||||
this.ctx.save();
|
||||
if (transform) {
|
||||
this.ctx.transform.apply(this.ctx, transform);
|
||||
}
|
||||
this.ctx.transform.apply(this.ctx, viewport.transform);
|
||||
|
||||
this.baseTransform = this.ctx.mozCurrentTransform.slice();
|
||||
|
||||
@ -3970,7 +3985,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
|
||||
endDrawing: function CanvasGraphics_endDrawing() {
|
||||
this.ctx.restore();
|
||||
CachedCanvases.clear();
|
||||
|
||||
if (this.transparentCanvas) {
|
||||
this.ctx = this.compositeCtx;
|
||||
this.ctx.drawImage(this.transparentCanvas, 0, 0);
|
||||
this.transparentCanvas = null;
|
||||
}
|
||||
|
||||
this.cachedCanvases.clear();
|
||||
WebGLUtils.clear();
|
||||
|
||||
if (this.imageLayer) {
|
||||
@ -4084,7 +4106,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
var drawnWidth = activeSMask.canvas.width;
|
||||
var drawnHeight = activeSMask.canvas.height;
|
||||
var cacheId = 'smaskGroupAt' + this.groupLevel;
|
||||
var scratchCanvas = CachedCanvases.getCanvas(
|
||||
var scratchCanvas = this.cachedCanvases.getCanvas(
|
||||
cacheId, drawnWidth, drawnHeight, true);
|
||||
|
||||
var currentCtx = this.ctx;
|
||||
@ -4868,7 +4890,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// Using two cache entries is case if masks are used one after another.
|
||||
cacheId += '_smask_' + ((this.smaskCounter++) % 2);
|
||||
}
|
||||
var scratchCanvas = CachedCanvases.getCanvas(
|
||||
var scratchCanvas = this.cachedCanvases.getCanvas(
|
||||
cacheId, drawnWidth, drawnHeight, true);
|
||||
var groupCtx = scratchCanvas.context;
|
||||
|
||||
@ -5009,7 +5031,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
return;
|
||||
}
|
||||
|
||||
var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
|
||||
var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
|
||||
width, height);
|
||||
var maskCtx = maskCanvas.context;
|
||||
maskCtx.save();
|
||||
|
||||
@ -5034,7 +5057,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
var fillColor = this.current.fillColor;
|
||||
var isPatternFill = this.current.patternFill;
|
||||
|
||||
var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
|
||||
var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
|
||||
width, height);
|
||||
var maskCtx = maskCanvas.context;
|
||||
maskCtx.save();
|
||||
|
||||
@ -5069,7 +5093,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
var image = images[i];
|
||||
var width = image.width, height = image.height;
|
||||
|
||||
var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
|
||||
var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
|
||||
width, height);
|
||||
var maskCtx = maskCanvas.context;
|
||||
maskCtx.save();
|
||||
|
||||
@ -5142,7 +5167,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
if (imgData instanceof HTMLElement || !imgData.data) {
|
||||
imgToPaint = imgData;
|
||||
} else {
|
||||
tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height);
|
||||
tmpCanvas = this.cachedCanvases.getCanvas('inlineImage',
|
||||
width, height);
|
||||
var tmpCtx = tmpCanvas.context;
|
||||
putBinaryImageData(tmpCtx, imgData);
|
||||
imgToPaint = tmpCanvas.canvas;
|
||||
@ -5164,7 +5190,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
newHeight = Math.ceil(paintHeight / 2);
|
||||
heightScale /= paintHeight / newHeight;
|
||||
}
|
||||
tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
|
||||
tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId,
|
||||
newWidth, newHeight);
|
||||
tmpCtx = tmpCanvas.context;
|
||||
tmpCtx.clearRect(0, 0, newWidth, newHeight);
|
||||
tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
|
||||
@ -5196,7 +5223,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
var w = imgData.width;
|
||||
var h = imgData.height;
|
||||
|
||||
var tmpCanvas = CachedCanvases.getCanvas('inlineImage', w, h);
|
||||
var tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', w, h);
|
||||
var tmpCtx = tmpCanvas.context;
|
||||
putBinaryImageData(tmpCtx, imgData);
|
||||
|
||||
@ -5854,7 +5881,7 @@ var createMeshCanvas = (function createMeshCanvasClosure() {
|
||||
}
|
||||
|
||||
function createMeshCanvas(bounds, combinesScale, coords, colors, figures,
|
||||
backgroundColor) {
|
||||
backgroundColor, cachedCanvases) {
|
||||
// we will increase scale on some weird factor to let antialiasing take
|
||||
// care of "rough" edges
|
||||
var EXPECTED_SCALE = 1.1;
|
||||
@ -5888,11 +5915,11 @@ var createMeshCanvas = (function createMeshCanvasClosure() {
|
||||
figures, context);
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=972126
|
||||
tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false);
|
||||
tmpCanvas = cachedCanvases.getCanvas('mesh', width, height, false);
|
||||
tmpCanvas.context.drawImage(canvas, 0, 0);
|
||||
canvas = tmpCanvas.canvas;
|
||||
} else {
|
||||
tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false);
|
||||
tmpCanvas = cachedCanvases.getCanvas('mesh', width, height, false);
|
||||
var tmpCtx = tmpCanvas.context;
|
||||
|
||||
var data = tmpCtx.createImageData(width, height);
|
||||
@ -5948,7 +5975,8 @@ ShadingIRs.Mesh = {
|
||||
// Rasterizing on the main thread since sending/queue large canvases
|
||||
// might cause OOM.
|
||||
var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords,
|
||||
colors, figures, shadingFill ? null : background);
|
||||
colors, figures, shadingFill ? null : background,
|
||||
owner.cachedCanvases);
|
||||
|
||||
if (!shadingFill) {
|
||||
ctx.setTransform.apply(ctx, owner.baseTransform);
|
||||
@ -6051,7 +6079,8 @@ var TilingPattern = (function TilingPatternClosure() {
|
||||
height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
|
||||
MAX_PATTERN_SIZE);
|
||||
|
||||
var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true);
|
||||
var tmpCanvas = owner.cachedCanvases.getCanvas('pattern',
|
||||
width, height, true);
|
||||
var tmpCtx = tmpCanvas.context;
|
||||
var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs);
|
||||
graphics.groupLevel = owner.groupLevel;
|
||||
@ -6254,6 +6283,63 @@ var FontFaceObject = (function FontFaceObjectClosure() {
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* Optimised CSS custom property getter/setter.
|
||||
* @class
|
||||
*/
|
||||
var CustomStyle = (function CustomStyleClosure() {
|
||||
|
||||
// As noted on: http://www.zachstronaut.com/posts/2009/02/17/
|
||||
// animate-css-transforms-firefox-webkit.html
|
||||
// in some versions of IE9 it is critical that ms appear in this list
|
||||
// before Moz
|
||||
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
||||
var _cache = {};
|
||||
|
||||
function CustomStyle() {}
|
||||
|
||||
CustomStyle.getProp = function get(propName, element) {
|
||||
// check cache only when no element is given
|
||||
if (arguments.length === 1 && typeof _cache[propName] === 'string') {
|
||||
return _cache[propName];
|
||||
}
|
||||
|
||||
element = element || document.documentElement;
|
||||
var style = element.style, prefixed, uPropName;
|
||||
|
||||
// test standard property first
|
||||
if (typeof style[propName] === 'string') {
|
||||
return (_cache[propName] = propName);
|
||||
}
|
||||
|
||||
// capitalize
|
||||
uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
|
||||
|
||||
// test vendor specific properties
|
||||
for (var i = 0, l = prefixes.length; i < l; i++) {
|
||||
prefixed = prefixes[i] + uPropName;
|
||||
if (typeof style[prefixed] === 'string') {
|
||||
return (_cache[propName] = prefixed);
|
||||
}
|
||||
}
|
||||
|
||||
//if all fails then set to undefined
|
||||
return (_cache[propName] = 'undefined');
|
||||
};
|
||||
|
||||
CustomStyle.setProp = function set(propName, element, str) {
|
||||
var prop = this.getProp(propName);
|
||||
if (prop !== 'undefined') {
|
||||
element.style[prop] = str;
|
||||
}
|
||||
};
|
||||
|
||||
return CustomStyle;
|
||||
})();
|
||||
|
||||
PDFJS.CustomStyle = CustomStyle;
|
||||
|
||||
|
||||
var ANNOT_MIN_SIZE = 10; // px
|
||||
|
||||
var AnnotationUtils = (function AnnotationUtilsClosure() {
|
||||
@ -6526,6 +6612,225 @@ var AnnotationUtils = (function AnnotationUtilsClosure() {
|
||||
PDFJS.AnnotationUtils = AnnotationUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Text layer render parameters.
|
||||
*
|
||||
* @typedef {Object} TextLayerRenderParameters
|
||||
* @property {TextContent} textContent - Text content to render (the object is
|
||||
* returned by the page's getTextContent() method).
|
||||
* @property {HTMLElement} container - HTML element that will contain text runs.
|
||||
* @property {PDFJS.PageViewport} viewport - The target viewport to properly
|
||||
* layout the text runs.
|
||||
* @property {Array} textDivs - (optional) HTML elements that are correspond
|
||||
* the text items of the textContent input. This is output and shall be
|
||||
* initially be set to empty array.
|
||||
* @property {number} timeout - (optional) Delay in milliseconds before
|
||||
* rendering of the text runs occurs.
|
||||
*/
|
||||
var renderTextLayer = (function renderTextLayerClosure() {
|
||||
var MAX_TEXT_DIVS_TO_RENDER = 100000;
|
||||
|
||||
var NonWhitespaceRegexp = /\S/;
|
||||
|
||||
function isAllWhitespace(str) {
|
||||
return !NonWhitespaceRegexp.test(str);
|
||||
}
|
||||
|
||||
function appendText(textDivs, viewport, geom, styles) {
|
||||
var style = styles[geom.fontName];
|
||||
var textDiv = document.createElement('div');
|
||||
textDivs.push(textDiv);
|
||||
if (isAllWhitespace(geom.str)) {
|
||||
textDiv.dataset.isWhitespace = true;
|
||||
return;
|
||||
}
|
||||
var tx = PDFJS.Util.transform(viewport.transform, geom.transform);
|
||||
var angle = Math.atan2(tx[1], tx[0]);
|
||||
if (style.vertical) {
|
||||
angle += Math.PI / 2;
|
||||
}
|
||||
var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
|
||||
var fontAscent = fontHeight;
|
||||
if (style.ascent) {
|
||||
fontAscent = style.ascent * fontAscent;
|
||||
} else if (style.descent) {
|
||||
fontAscent = (1 + style.descent) * fontAscent;
|
||||
}
|
||||
|
||||
var left;
|
||||
var top;
|
||||
if (angle === 0) {
|
||||
left = tx[4];
|
||||
top = tx[5] - fontAscent;
|
||||
} else {
|
||||
left = tx[4] + (fontAscent * Math.sin(angle));
|
||||
top = tx[5] - (fontAscent * Math.cos(angle));
|
||||
}
|
||||
textDiv.style.left = left + 'px';
|
||||
textDiv.style.top = top + 'px';
|
||||
textDiv.style.fontSize = fontHeight + 'px';
|
||||
textDiv.style.fontFamily = style.fontFamily;
|
||||
|
||||
textDiv.textContent = geom.str;
|
||||
// |fontName| is only used by the Font Inspector. This test will succeed
|
||||
// when e.g. the Font Inspector is off but the Stepper is on, but it's
|
||||
// not worth the effort to do a more accurate test.
|
||||
if (PDFJS.pdfBug) {
|
||||
textDiv.dataset.fontName = geom.fontName;
|
||||
}
|
||||
// Storing into dataset will convert number into string.
|
||||
if (angle !== 0) {
|
||||
textDiv.dataset.angle = angle * (180 / Math.PI);
|
||||
}
|
||||
// We don't bother scaling single-char text divs, because it has very
|
||||
// little effect on text highlighting. This makes scrolling on docs with
|
||||
// lots of such divs a lot faster.
|
||||
if (geom.str.length > 1) {
|
||||
if (style.vertical) {
|
||||
textDiv.dataset.canvasWidth = geom.height * viewport.scale;
|
||||
} else {
|
||||
textDiv.dataset.canvasWidth = geom.width * viewport.scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function render(task) {
|
||||
if (task._canceled) {
|
||||
return;
|
||||
}
|
||||
var textLayerFrag = task._container;
|
||||
var textDivs = task._textDivs;
|
||||
var capability = task._capability;
|
||||
var textDivsLength = textDivs.length;
|
||||
|
||||
// No point in rendering many divs as it would make the browser
|
||||
// unusable even after the divs are rendered.
|
||||
if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
|
||||
capability.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.mozOpaque = true;
|
||||
var ctx = canvas.getContext('2d', {alpha: false});
|
||||
|
||||
var lastFontSize;
|
||||
var lastFontFamily;
|
||||
for (var i = 0; i < textDivsLength; i++) {
|
||||
var textDiv = textDivs[i];
|
||||
if (textDiv.dataset.isWhitespace !== undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var fontSize = textDiv.style.fontSize;
|
||||
var fontFamily = textDiv.style.fontFamily;
|
||||
|
||||
// Only build font string and set to context if different from last.
|
||||
if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) {
|
||||
ctx.font = fontSize + ' ' + fontFamily;
|
||||
lastFontSize = fontSize;
|
||||
lastFontFamily = fontFamily;
|
||||
}
|
||||
|
||||
var width = ctx.measureText(textDiv.textContent).width;
|
||||
if (width > 0) {
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
var transform;
|
||||
if (textDiv.dataset.canvasWidth !== undefined) {
|
||||
// Dataset values come of type string.
|
||||
var textScale = textDiv.dataset.canvasWidth / width;
|
||||
transform = 'scaleX(' + textScale + ')';
|
||||
} else {
|
||||
transform = '';
|
||||
}
|
||||
var rotation = textDiv.dataset.angle;
|
||||
if (rotation) {
|
||||
transform = 'rotate(' + rotation + 'deg) ' + transform;
|
||||
}
|
||||
if (transform) {
|
||||
PDFJS.CustomStyle.setProp('transform' , textDiv, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
capability.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Text layer rendering task.
|
||||
*
|
||||
* @param {TextContent} textContent
|
||||
* @param {HTMLElement} container
|
||||
* @param {PDFJS.PageViewport} viewport
|
||||
* @param {Array} textDivs
|
||||
* @private
|
||||
*/
|
||||
function TextLayerRenderTask(textContent, container, viewport, textDivs) {
|
||||
this._textContent = textContent;
|
||||
this._container = container;
|
||||
this._viewport = viewport;
|
||||
textDivs = textDivs || [];
|
||||
this._textDivs = textDivs;
|
||||
this._canceled = false;
|
||||
this._capability = createPromiseCapability();
|
||||
this._renderTimer = null;
|
||||
}
|
||||
TextLayerRenderTask.prototype = {
|
||||
get promise() {
|
||||
return this._capability.promise;
|
||||
},
|
||||
|
||||
cancel: function TextLayer_cancel() {
|
||||
this._canceled = true;
|
||||
if (this._renderTimer !== null) {
|
||||
clearTimeout(this._renderTimer);
|
||||
this._renderTimer = null;
|
||||
}
|
||||
this._capability.reject('canceled');
|
||||
},
|
||||
|
||||
_render: function TextLayer_render(timeout) {
|
||||
var textItems = this._textContent.items;
|
||||
var styles = this._textContent.styles;
|
||||
var textDivs = this._textDivs;
|
||||
var viewport = this._viewport;
|
||||
for (var i = 0, len = textItems.length; i < len; i++) {
|
||||
appendText(textDivs, viewport, textItems[i], styles);
|
||||
}
|
||||
|
||||
if (!timeout) { // Render right away
|
||||
render(this);
|
||||
} else { // Schedule
|
||||
var self = this;
|
||||
this._renderTimer = setTimeout(function() {
|
||||
render(self);
|
||||
self._renderTimer = null;
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Starts rendering of the text layer.
|
||||
*
|
||||
* @param {TextLayerRenderParameters} renderParameters
|
||||
* @returns {TextLayerRenderTask}
|
||||
*/
|
||||
function renderTextLayer(renderParameters) {
|
||||
var task = new TextLayerRenderTask(renderParameters.textContent,
|
||||
renderParameters.container,
|
||||
renderParameters.viewport,
|
||||
renderParameters.textDivs);
|
||||
task._render(renderParameters.timeout);
|
||||
return task;
|
||||
}
|
||||
|
||||
return renderTextLayer;
|
||||
})();
|
||||
|
||||
PDFJS.renderTextLayer = renderTextLayer;
|
||||
|
||||
|
||||
|
||||
|
||||
}).call((typeof window === 'undefined') ? this : window);
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,8 +20,8 @@ if (typeof PDFJS === 'undefined') {
|
||||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '1.2.109';
|
||||
PDFJS.build = '875588d';
|
||||
PDFJS.version = '1.3.14';
|
||||
PDFJS.build = 'df46b64';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
@ -9664,6 +9662,11 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
||||
if (matrix) {
|
||||
p0 = Util.applyTransform(p0, matrix);
|
||||
p1 = Util.applyTransform(p1, matrix);
|
||||
if (shadingType === ShadingType.RADIAL) {
|
||||
var scale = Util.singularValueDecompose2dScale(matrix);
|
||||
r0 *= scale[0];
|
||||
r1 *= scale[1];
|
||||
}
|
||||
}
|
||||
|
||||
return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1];
|
||||
@ -34555,6 +34558,7 @@ var ArithmeticDecoder = (function ArithmeticDecoderClosure() {
|
||||
})();
|
||||
|
||||
|
||||
|
||||
var JpegImage = (function jpegImage() {
|
||||
var dctZigZag = new Uint8Array([
|
||||
0,
|
||||
@ -39233,6 +39237,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() {
|
||||
})();
|
||||
|
||||
|
||||
|
||||
var MurmurHash3_64 = (function MurmurHash3_64Closure (seed) {
|
||||
// Workaround for missing math precison in JS.
|
||||
var MASK_HIGH = 0xffff0000;
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* globals FirefoxCom */
|
||||
|
||||
'use strict';
|
||||
|
@ -1574,7 +1574,25 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#viewer.textLayer-visible .textLayer > div,
|
||||
#viewer.textLayer-visible .textLayer {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
#viewer.textLayer-visible .canvasWrapper {
|
||||
background-color: rgb(128,255,128);
|
||||
}
|
||||
|
||||
#viewer.textLayer-visible .canvasWrapper canvas {
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
#viewer.textLayer-visible .textLayer > div {
|
||||
background-color: rgba(255, 255, 0, 0.1);
|
||||
color: black;
|
||||
border: solid 1px rgba(255, 0, 0, 0.5);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#viewer.textLayer-hover .textLayer > div:hover {
|
||||
background-color: white;
|
||||
color: black;
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -14,18 +12,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, ProgressBar,
|
||||
DownloadManager, getFileName, getPDFFileNameFromURL,
|
||||
PDFHistory, Preferences, SidebarView, ViewHistory, Stats,
|
||||
PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar,
|
||||
PasswordPrompt, PDFPresentationMode, PDFDocumentProperties, HandTool,
|
||||
Promise, PDFLinkService, PDFOutlineView, PDFAttachmentView,
|
||||
OverlayManager, PDFFindController, PDFFindBar, PDFViewer,
|
||||
PDFRenderingQueue, PresentationModeState, parseQueryString,
|
||||
RenderingStates, UNKNOWN_SCALE, DEFAULT_SCALE_VALUE,
|
||||
IGNORE_CURRENT_POSITION_ON_ZOOM: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||
var DEFAULT_SCALE_DELTA = 1.1;
|
||||
@ -52,57 +38,6 @@ var MAX_AUTO_SCALE = 1.25;
|
||||
var SCROLLBAR_PADDING = 40;
|
||||
var VERTICAL_PADDING = 5;
|
||||
|
||||
// optimised CSS custom property getter/setter
|
||||
var CustomStyle = (function CustomStyleClosure() {
|
||||
|
||||
// As noted on: http://www.zachstronaut.com/posts/2009/02/17/
|
||||
// animate-css-transforms-firefox-webkit.html
|
||||
// in some versions of IE9 it is critical that ms appear in this list
|
||||
// before Moz
|
||||
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
||||
var _cache = {};
|
||||
|
||||
function CustomStyle() {}
|
||||
|
||||
CustomStyle.getProp = function get(propName, element) {
|
||||
// check cache only when no element is given
|
||||
if (arguments.length === 1 && typeof _cache[propName] === 'string') {
|
||||
return _cache[propName];
|
||||
}
|
||||
|
||||
element = element || document.documentElement;
|
||||
var style = element.style, prefixed, uPropName;
|
||||
|
||||
// test standard property first
|
||||
if (typeof style[propName] === 'string') {
|
||||
return (_cache[propName] = propName);
|
||||
}
|
||||
|
||||
// capitalize
|
||||
uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
|
||||
|
||||
// test vendor specific properties
|
||||
for (var i = 0, l = prefixes.length; i < l; i++) {
|
||||
prefixed = prefixes[i] + uPropName;
|
||||
if (typeof style[prefixed] === 'string') {
|
||||
return (_cache[propName] = prefixed);
|
||||
}
|
||||
}
|
||||
|
||||
//if all fails then set to undefined
|
||||
return (_cache[propName] = 'undefined');
|
||||
};
|
||||
|
||||
CustomStyle.setProp = function set(propName, element, str) {
|
||||
var prop = this.getProp(propName);
|
||||
if (prop !== 'undefined') {
|
||||
element.style[prop] = str;
|
||||
}
|
||||
};
|
||||
|
||||
return CustomStyle;
|
||||
})();
|
||||
|
||||
var NullCharactersRegExp = /\x00/g;
|
||||
|
||||
function removeNullCharacters(str) {
|
||||
@ -2639,23 +2574,6 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() {
|
||||
})();
|
||||
|
||||
|
||||
/* Copyright 2013 Rob Wu <gwnRob@gmail.com>
|
||||
* https://github.com/Rob--W/grab-to-pan.js
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var GrabToPan = (function GrabToPanClosure() {
|
||||
/**
|
||||
@ -3518,6 +3436,8 @@ var TEXT_LAYER_RENDER_DELAY = 200; // ms
|
||||
* @implements {IRenderableView}
|
||||
*/
|
||||
var PDFPageView = (function PDFPageViewClosure() {
|
||||
var CustomStyle = PDFJS.CustomStyle;
|
||||
|
||||
/**
|
||||
* @constructs PDFPageView
|
||||
* @param {PDFPageViewOptions} options
|
||||
@ -3535,7 +3455,7 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
this.renderingId = 'page' + id;
|
||||
|
||||
this.rotation = 0;
|
||||
this.scale = scale || 1.0;
|
||||
this.scale = scale || DEFAULT_SCALE;
|
||||
this.viewport = defaultViewport;
|
||||
this.pdfPageRotate = defaultViewport.rotation;
|
||||
this.hasRestrictedScaling = false;
|
||||
@ -3646,8 +3566,7 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
|
||||
var isScalingRestricted = false;
|
||||
if (this.canvas && PDFJS.maxCanvasPixels > 0) {
|
||||
var ctx = this.canvas.getContext('2d');
|
||||
var outputScale = getOutputScale(ctx);
|
||||
var outputScale = this.outputScale;
|
||||
var pixelsInViewport = this.viewport.width * this.viewport.height;
|
||||
var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
|
||||
if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
|
||||
@ -3791,6 +3710,11 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = 'page' + this.id;
|
||||
// Keep the canvas hidden until the first draw callback, or until drawing
|
||||
// is complete when `!this.renderingQueue`, to prevent black flickering.
|
||||
canvas.setAttribute('hidden', 'hidden');
|
||||
var isCanvasHidden = true;
|
||||
|
||||
canvasWrapper.appendChild(canvas);
|
||||
if (this.annotationLayer && this.annotationLayer.div) {
|
||||
// annotationLayer needs to stay on top
|
||||
@ -3800,8 +3724,10 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
}
|
||||
this.canvas = canvas;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
canvas.mozOpaque = true;
|
||||
var ctx = canvas.getContext('2d', {alpha: false});
|
||||
var outputScale = getOutputScale(ctx);
|
||||
this.outputScale = outputScale;
|
||||
|
||||
if (PDFJS.useOnlyCssZoom) {
|
||||
var actualSizeViewport = viewport.clone({scale: CSS_UNITS});
|
||||
@ -3854,10 +3780,6 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
}
|
||||
this.textLayer = textLayer;
|
||||
|
||||
if (outputScale.scaled) {
|
||||
ctx.scale(outputScale.sx, outputScale.sy);
|
||||
}
|
||||
|
||||
var resolveRenderPromise, rejectRenderPromise;
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
resolveRenderPromise = resolve;
|
||||
@ -3882,6 +3804,11 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
|
||||
self.renderingState = RenderingStates.FINISHED;
|
||||
|
||||
if (isCanvasHidden) {
|
||||
self.canvas.removeAttribute('hidden');
|
||||
isCanvasHidden = false;
|
||||
}
|
||||
|
||||
if (self.loadingIconDiv) {
|
||||
div.removeChild(self.loadingIconDiv);
|
||||
delete self.loadingIconDiv;
|
||||
@ -3928,12 +3855,19 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (isCanvasHidden) {
|
||||
self.canvas.removeAttribute('hidden');
|
||||
isCanvasHidden = false;
|
||||
}
|
||||
cont();
|
||||
};
|
||||
}
|
||||
|
||||
var transform = !outputScale.scaled ? null :
|
||||
[outputScale.sx, 0, 0, outputScale.sy, 0, 0];
|
||||
var renderContext = {
|
||||
canvasContext: ctx,
|
||||
transform: transform,
|
||||
viewport: this.viewport,
|
||||
// intent: 'default', // === 'display'
|
||||
};
|
||||
@ -4037,14 +3971,6 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
})();
|
||||
|
||||
|
||||
var MAX_TEXT_DIVS_TO_RENDER = 100000;
|
||||
|
||||
var NonWhitespaceRegexp = /\S/;
|
||||
|
||||
function isAllWhitespace(str) {
|
||||
return !NonWhitespaceRegexp.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} TextLayerBuilderOptions
|
||||
* @property {HTMLDivElement} textLayerDiv - The text layer container.
|
||||
@ -4071,6 +3997,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
this.viewport = options.viewport;
|
||||
this.textDivs = [];
|
||||
this.findController = options.findController || null;
|
||||
this.textLayerRenderTask = null;
|
||||
this._bindMouse();
|
||||
}
|
||||
|
||||
@ -4089,64 +4016,6 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
this.textLayerDiv.dispatchEvent(event);
|
||||
},
|
||||
|
||||
renderLayer: function TextLayerBuilder_renderLayer() {
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
var textDivs = this.textDivs;
|
||||
var textDivsLength = textDivs.length;
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
// No point in rendering many divs as it would make the browser
|
||||
// unusable even after the divs are rendered.
|
||||
if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
|
||||
this._finishRendering();
|
||||
return;
|
||||
}
|
||||
|
||||
var lastFontSize;
|
||||
var lastFontFamily;
|
||||
for (var i = 0; i < textDivsLength; i++) {
|
||||
var textDiv = textDivs[i];
|
||||
if (textDiv.dataset.isWhitespace !== undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var fontSize = textDiv.style.fontSize;
|
||||
var fontFamily = textDiv.style.fontFamily;
|
||||
|
||||
// Only build font string and set to context if different from last.
|
||||
if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) {
|
||||
ctx.font = fontSize + ' ' + fontFamily;
|
||||
lastFontSize = fontSize;
|
||||
lastFontFamily = fontFamily;
|
||||
}
|
||||
|
||||
var width = ctx.measureText(textDiv.textContent).width;
|
||||
if (width > 0) {
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
var transform;
|
||||
if (textDiv.dataset.canvasWidth !== undefined) {
|
||||
// Dataset values come of type string.
|
||||
var textScale = textDiv.dataset.canvasWidth / width;
|
||||
transform = 'scaleX(' + textScale + ')';
|
||||
} else {
|
||||
transform = '';
|
||||
}
|
||||
var rotation = textDiv.dataset.angle;
|
||||
if (rotation) {
|
||||
transform = 'rotate(' + rotation + 'deg) ' + transform;
|
||||
}
|
||||
if (transform) {
|
||||
CustomStyle.setProp('transform' , textDiv, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.textLayerDiv.appendChild(textLayerFrag);
|
||||
this._finishRendering();
|
||||
this.updateMatches();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the text layer.
|
||||
* @param {number} timeout (optional) if specified, the rendering waits
|
||||
@ -4157,87 +4026,35 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.renderTimer) {
|
||||
clearTimeout(this.renderTimer);
|
||||
this.renderTimer = null;
|
||||
if (this.textLayerRenderTask) {
|
||||
this.textLayerRenderTask.cancel();
|
||||
this.textLayerRenderTask = null;
|
||||
}
|
||||
|
||||
if (!timeout) { // Render right away
|
||||
this.renderLayer();
|
||||
} else { // Schedule
|
||||
var self = this;
|
||||
this.renderTimer = setTimeout(function() {
|
||||
self.renderLayer();
|
||||
self.renderTimer = null;
|
||||
}, timeout);
|
||||
}
|
||||
},
|
||||
|
||||
appendText: function TextLayerBuilder_appendText(geom, styles) {
|
||||
var style = styles[geom.fontName];
|
||||
var textDiv = document.createElement('div');
|
||||
this.textDivs.push(textDiv);
|
||||
if (isAllWhitespace(geom.str)) {
|
||||
textDiv.dataset.isWhitespace = true;
|
||||
return;
|
||||
}
|
||||
var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
|
||||
var angle = Math.atan2(tx[1], tx[0]);
|
||||
if (style.vertical) {
|
||||
angle += Math.PI / 2;
|
||||
}
|
||||
var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
|
||||
var fontAscent = fontHeight;
|
||||
if (style.ascent) {
|
||||
fontAscent = style.ascent * fontAscent;
|
||||
} else if (style.descent) {
|
||||
fontAscent = (1 + style.descent) * fontAscent;
|
||||
}
|
||||
|
||||
var left;
|
||||
var top;
|
||||
if (angle === 0) {
|
||||
left = tx[4];
|
||||
top = tx[5] - fontAscent;
|
||||
} else {
|
||||
left = tx[4] + (fontAscent * Math.sin(angle));
|
||||
top = tx[5] - (fontAscent * Math.cos(angle));
|
||||
}
|
||||
textDiv.style.left = left + 'px';
|
||||
textDiv.style.top = top + 'px';
|
||||
textDiv.style.fontSize = fontHeight + 'px';
|
||||
textDiv.style.fontFamily = style.fontFamily;
|
||||
|
||||
textDiv.textContent = geom.str;
|
||||
// |fontName| is only used by the Font Inspector. This test will succeed
|
||||
// when e.g. the Font Inspector is off but the Stepper is on, but it's
|
||||
// not worth the effort to do a more accurate test.
|
||||
if (PDFJS.pdfBug) {
|
||||
textDiv.dataset.fontName = geom.fontName;
|
||||
}
|
||||
// Storing into dataset will convert number into string.
|
||||
if (angle !== 0) {
|
||||
textDiv.dataset.angle = angle * (180 / Math.PI);
|
||||
}
|
||||
// We don't bother scaling single-char text divs, because it has very
|
||||
// little effect on text highlighting. This makes scrolling on docs with
|
||||
// lots of such divs a lot faster.
|
||||
if (geom.str.length > 1) {
|
||||
if (style.vertical) {
|
||||
textDiv.dataset.canvasWidth = geom.height * this.viewport.scale;
|
||||
} else {
|
||||
textDiv.dataset.canvasWidth = geom.width * this.viewport.scale;
|
||||
}
|
||||
}
|
||||
this.textDivs = [];
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
this.textLayerRenderTask = PDFJS.renderTextLayer({
|
||||
textContent: this.textContent,
|
||||
container: textLayerFrag,
|
||||
viewport: this.viewport,
|
||||
textDivs: this.textDivs,
|
||||
timeout: timeout
|
||||
});
|
||||
this.textLayerRenderTask.promise.then(function () {
|
||||
this.textLayerDiv.appendChild(textLayerFrag);
|
||||
this._finishRendering();
|
||||
this.updateMatches();
|
||||
}.bind(this), function (reason) {
|
||||
// canceled or failed to render text layer -- skipping errors
|
||||
});
|
||||
},
|
||||
|
||||
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
|
||||
this.textContent = textContent;
|
||||
|
||||
var textItems = textContent.items;
|
||||
for (var i = 0, len = textItems.length; i < len; i++) {
|
||||
this.appendText(textItems[i], textContent.styles);
|
||||
if (this.textLayerRenderTask) {
|
||||
this.textLayerRenderTask.cancel();
|
||||
this.textLayerRenderTask = null;
|
||||
}
|
||||
this.textContent = textContent;
|
||||
this.divContentDone = true;
|
||||
},
|
||||
|
||||
@ -4478,6 +4295,8 @@ DefaultTextLayerFactory.prototype = {
|
||||
* @class
|
||||
*/
|
||||
var AnnotationsLayerBuilder = (function AnnotationsLayerBuilderClosure() {
|
||||
var CustomStyle = PDFJS.CustomStyle;
|
||||
|
||||
/**
|
||||
* @param {AnnotationsLayerBuilderOptions} options
|
||||
* @constructs AnnotationsLayerBuilder
|
||||
@ -5442,7 +5261,8 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
|
||||
|
||||
// Since this is a temporary canvas, we need to fill the canvas with a white
|
||||
// background ourselves. |_getPageDrawContext| uses CSS rules for this.
|
||||
var ctx = tempCanvas.getContext('2d');
|
||||
tempCanvas.mozOpaque = true;
|
||||
var ctx = tempCanvas.getContext('2d', {alpha: false});
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
@ -5582,7 +5402,8 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
|
||||
var canvas = document.createElement('canvas');
|
||||
this.canvas = canvas;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
canvas.mozOpaque = true;
|
||||
var ctx = canvas.getContext('2d', {alpha: false});
|
||||
var outputScale = getOutputScale(ctx);
|
||||
|
||||
canvas.width = (this.canvasWidth * outputScale.sx) | 0;
|
||||
|
@ -8,15 +8,23 @@ var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "TabCrashReporter", "PluginCrashReporter" ];
|
||||
this.EXPORTED_SYMBOLS = [ "TabCrashHandler", "PluginCrashReporter" ];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
|
||||
"resource://gre/modules/CrashSubmit.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemotePages",
|
||||
"resource://gre/modules/RemotePageManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
this.TabCrashHandler = {
|
||||
_crashedTabCount: 0,
|
||||
|
||||
this.TabCrashReporter = {
|
||||
get prefs() {
|
||||
delete this.prefs;
|
||||
return this.prefs = Services.prefs.getBranch("browser.tabs.crashReporting.");
|
||||
@ -27,11 +35,23 @@ this.TabCrashReporter = {
|
||||
return;
|
||||
this.initialized = true;
|
||||
|
||||
Services.obs.addObserver(this, "ipc:content-shutdown", false);
|
||||
Services.obs.addObserver(this, "oop-frameloader-crashed", false);
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
Services.obs.addObserver(this, "ipc:content-shutdown", false);
|
||||
Services.obs.addObserver(this, "oop-frameloader-crashed", false);
|
||||
|
||||
this.childMap = new Map();
|
||||
this.browserMap = new WeakMap();
|
||||
this.childMap = new Map();
|
||||
this.browserMap = new WeakMap();
|
||||
}
|
||||
|
||||
this.pageListener = new RemotePages("about:tabcrashed");
|
||||
// LOAD_BACKGROUND pages don't fire load events, so the about:tabcrashed
|
||||
// content will fire up its own message when its initial scripts have
|
||||
// finished running.
|
||||
this.pageListener.addMessageListener("Load", this.receiveMessage.bind(this));
|
||||
this.pageListener.addMessageListener("RemotePage:Unload", this.receiveMessage.bind(this));
|
||||
this.pageListener.addMessageListener("closeTab", this.receiveMessage.bind(this));
|
||||
this.pageListener.addMessageListener("restoreTab", this.receiveMessage.bind(this));
|
||||
this.pageListener.addMessageListener("restoreAll", this.receiveMessage.bind(this));
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
@ -57,8 +77,45 @@ this.TabCrashReporter = {
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
let browser = message.target.browser;
|
||||
let gBrowser = browser.ownerDocument.defaultView.gBrowser;
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
|
||||
switch(message.name) {
|
||||
case "Load": {
|
||||
this.onAboutTabCrashedLoad(message);
|
||||
break;
|
||||
}
|
||||
|
||||
case "RemotePage:Unload": {
|
||||
this.onAboutTabCrashedUnload(message);
|
||||
break;
|
||||
}
|
||||
|
||||
case "closeTab": {
|
||||
this.maybeSendCrashReport(message);
|
||||
gBrowser.removeTab(tab, { animate: true });
|
||||
break;
|
||||
}
|
||||
|
||||
case "restoreTab": {
|
||||
this.maybeSendCrashReport(message);
|
||||
SessionStore.reviveCrashedTab(tab);
|
||||
break;
|
||||
}
|
||||
|
||||
case "restoreAll": {
|
||||
this.maybeSendCrashReport(message);
|
||||
SessionStore.reviveAllCrashedTabs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Submits a crash report from about:tabcrashed
|
||||
* Submits a crash report from about:tabcrashed, if the crash
|
||||
* reporter is enabled and a crash report can be found.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The <xul:browser> that the report was sent from.
|
||||
@ -82,26 +139,44 @@ this.TabCrashReporter = {
|
||||
* Note that it is expected that all properties are set,
|
||||
* even if they are empty.
|
||||
*/
|
||||
submitCrashReport: function (aBrowser, aFormData) {
|
||||
let childID = this.browserMap.get(aBrowser.permanentKey);
|
||||
maybeSendCrashReport(message) {
|
||||
if (!AppConstants.MOZ_CRASHREPORTER)
|
||||
return;
|
||||
|
||||
let browser = message.target.browser;
|
||||
|
||||
let childID = this.browserMap.get(browser.permanentKey);
|
||||
let dumpID = this.childMap.get(childID);
|
||||
if (!dumpID)
|
||||
return
|
||||
|
||||
if (!message.data.sendReport) {
|
||||
this.prefs.setBoolPref("sendReport", false);
|
||||
return;
|
||||
}
|
||||
|
||||
let {
|
||||
includeURL,
|
||||
comments,
|
||||
email,
|
||||
emailMe,
|
||||
URL,
|
||||
} = message.data;
|
||||
|
||||
CrashSubmit.submit(dumpID, {
|
||||
recordSubmission: true,
|
||||
extraExtraKeyVals: {
|
||||
Comments: aFormData.comments,
|
||||
Email: aFormData.email,
|
||||
URL: aFormData.URL,
|
||||
Comments: comments,
|
||||
Email: email,
|
||||
URL: URL,
|
||||
},
|
||||
}).then(null, Cu.reportError);
|
||||
|
||||
this.prefs.setBoolPref("sendReport", true);
|
||||
this.prefs.setBoolPref("includeURL", aFormData.includeURL);
|
||||
this.prefs.setBoolPref("emailMe", aFormData.emailMe);
|
||||
if (aFormData.emailMe) {
|
||||
this.prefs.setCharPref("email", aFormData.email);
|
||||
this.prefs.setBoolPref("includeURL", includeURL);
|
||||
this.prefs.setBoolPref("emailMe", emailMe);
|
||||
if (emailMe) {
|
||||
this.prefs.setCharPref("email", email);
|
||||
} else {
|
||||
this.prefs.setCharPref("email", "");
|
||||
}
|
||||
@ -110,10 +185,6 @@ this.TabCrashReporter = {
|
||||
this.removeSubmitCheckboxesForSameCrash(childID);
|
||||
},
|
||||
|
||||
dontSubmitCrashReport: function() {
|
||||
this.prefs.setBoolPref("sendReport", false);
|
||||
},
|
||||
|
||||
removeSubmitCheckboxesForSameCrash: function(childID) {
|
||||
let enumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
while (enumerator.hasMoreElements()) {
|
||||
@ -131,49 +202,81 @@ this.TabCrashReporter = {
|
||||
|
||||
if (this.browserMap.get(browser.permanentKey) == childID) {
|
||||
this.browserMap.delete(browser.permanentKey);
|
||||
browser.contentDocument.documentElement.classList.remove("crashDumpAvailable");
|
||||
browser.contentDocument.documentElement.classList.add("crashDumpSubmitted");
|
||||
let ports = this.pageListener.portsForBrowser(browser);
|
||||
if (ports.length) {
|
||||
// For about:tabcrashed, we don't expect subframes. We can
|
||||
// assume sending to the first port is sufficient.
|
||||
ports[0].sendAsyncMessage("CrashReportSent");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onAboutTabCrashedLoad: function (aBrowser, aParams) {
|
||||
// If there was only one tab open that crashed, do not show the "restore all tabs" button
|
||||
if (aParams.crashedTabCount == 1) {
|
||||
this.hideRestoreAllButton(aBrowser);
|
||||
onAboutTabCrashedLoad: function (message) {
|
||||
this._crashedTabCount++;
|
||||
|
||||
// Broadcast to all about:tabcrashed pages a count of
|
||||
// how many about:tabcrashed pages exist, so that they
|
||||
// can decide whether or not to display the "Restore All
|
||||
// Crashed Tabs" button.
|
||||
this.pageListener.sendAsyncMessage("UpdateCount", {
|
||||
count: this._crashedTabCount,
|
||||
});
|
||||
|
||||
let browser = message.target.browser;
|
||||
|
||||
let dumpID = this.getDumpID(browser);
|
||||
if (!dumpID) {
|
||||
message.target.sendAsyncMessge("SetCrashReportAvailable", {
|
||||
hasReport: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.childMap)
|
||||
return;
|
||||
|
||||
let dumpID = this.childMap.get(this.browserMap.get(aBrowser.permanentKey));
|
||||
if (!dumpID)
|
||||
return;
|
||||
|
||||
let doc = aBrowser.contentDocument;
|
||||
|
||||
doc.documentElement.classList.add("crashDumpAvailable");
|
||||
|
||||
let sendReport = this.prefs.getBoolPref("sendReport");
|
||||
doc.getElementById("sendReport").checked = sendReport;
|
||||
|
||||
let includeURL = this.prefs.getBoolPref("includeURL");
|
||||
doc.getElementById("includeURL").checked = includeURL;
|
||||
|
||||
let emailMe = this.prefs.getBoolPref("emailMe");
|
||||
doc.getElementById("emailMe").checked = emailMe;
|
||||
|
||||
let data = { hasReport: true, sendReport, includeURL, emailMe };
|
||||
if (emailMe) {
|
||||
let email = this.prefs.getCharPref("email", "");
|
||||
doc.getElementById("email").value = email;
|
||||
data.email = this.prefs.getCharPref("email", "");
|
||||
}
|
||||
|
||||
message.target.sendAsyncMessage("SetCrashReportAvailable", data);
|
||||
},
|
||||
|
||||
hideRestoreAllButton: function (aBrowser) {
|
||||
aBrowser.contentDocument.getElementById("restoreAll").setAttribute("hidden", true);
|
||||
aBrowser.contentDocument.getElementById("restoreTab").setAttribute("class", "primary");
|
||||
}
|
||||
onAboutTabCrashedUnload: function() {
|
||||
if (!this._crashedTabCount) {
|
||||
Cu.reportError("Can not decrement crashed tab count to below 0");
|
||||
return;
|
||||
}
|
||||
this._crashedTabCount--;
|
||||
|
||||
// Broadcast to all about:tabcrashed pages a count of
|
||||
// how many about:tabcrashed pages exist, so that they
|
||||
// can decide whether or not to display the "Restore All
|
||||
// Crashed Tabs" button.
|
||||
this.pageListener.sendAsyncMessage("UpdateCount", {
|
||||
count: this._crashedTabCount,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* For some <xul:browser>, return a crash report dump ID for that browser
|
||||
* if we have been informed of one. Otherwise, return null.
|
||||
*
|
||||
* @param browser (<xul:browser)
|
||||
* The browser to try to get the dump ID for
|
||||
* @returns dumpID (String)
|
||||
*/
|
||||
getDumpID(browser) {
|
||||
if (!this.childMap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.childMap.get(this.browserMap.get(browser.permanentKey));
|
||||
},
|
||||
}
|
||||
|
||||
this.PluginCrashReporter = {
|
@ -17,7 +17,7 @@ EXTRA_JS_MODULES += [
|
||||
'CastingApps.jsm',
|
||||
'Chat.jsm',
|
||||
'ContentClick.jsm',
|
||||
'ContentCrashReporters.jsm',
|
||||
'ContentCrashHandlers.jsm',
|
||||
'ContentLinkHandler.jsm',
|
||||
'ContentObservers.jsm',
|
||||
'ContentSearch.jsm',
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
|
||||
const { assert, safeErrorString } = require("devtools/shared/DevToolsUtils");
|
||||
const Tree = createFactory(require("./tree"));
|
||||
const Tree = createFactory(require("devtools/client/shared/components/tree"));
|
||||
const TreeItem = createFactory(require("./tree-item"));
|
||||
const { getStatusTextFull, L10N } = require("../utils");
|
||||
const { snapshotState: states, diffingState } = require("../constants");
|
||||
|
@ -10,7 +10,4 @@ DevToolsModules(
|
||||
'snapshot-list-item.js',
|
||||
'toolbar.js',
|
||||
'tree-item.js',
|
||||
'tree.js',
|
||||
)
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
|
||||
|
@ -231,7 +231,7 @@ pref("devtools.webconsole.filter.info", true);
|
||||
pref("devtools.webconsole.filter.log", true);
|
||||
pref("devtools.webconsole.filter.secerror", true);
|
||||
pref("devtools.webconsole.filter.secwarn", true);
|
||||
pref("devtools.webconsole.filter.serviceworkers", false);
|
||||
pref("devtools.webconsole.filter.serviceworkers", true);
|
||||
pref("devtools.webconsole.filter.sharedworkers", false);
|
||||
pref("devtools.webconsole.filter.windowlessworkers", false);
|
||||
pref("devtools.webconsole.filter.servererror", false);
|
||||
|
@ -1,9 +1,10 @@
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
|
||||
const devtools = Cu.import("resource://devtools/shared/Loader.jsm", {}).devtools;
|
||||
const { devtools, DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const { joinURI } = devtools.require("devtools/shared/path");
|
||||
const VENDOR_CONTENT_URL = "resource://devtools/client/shared/vendor";
|
||||
const COMPONENTS_URL = "resource://devtools/client/shared/components";
|
||||
|
||||
/*
|
||||
* Create a loader to be used in a browser environment. This evaluates
|
||||
@ -18,8 +19,8 @@ const VENDOR_CONTENT_URL = "resource://devtools/client/shared/vendor";
|
||||
* outside of that path will still be loaded from the devtools loader,
|
||||
* so all system modules are still shared and cached across instances.
|
||||
* An exception to this is anything under
|
||||
* `devtools/client/shared/content`, which is where shared libraries
|
||||
* live that should be evaluated in a browser environment.
|
||||
* `devtools/client/shared/{vendor/components}`, which is where shared libraries
|
||||
* and React components live that should be evaluated in a browser environment.
|
||||
*
|
||||
* @param string baseURI
|
||||
* Base path to load modules from.
|
||||
@ -32,6 +33,7 @@ const VENDOR_CONTENT_URL = "resource://devtools/client/shared/vendor";
|
||||
*/
|
||||
function BrowserLoader(baseURI, window) {
|
||||
const loaderOptions = devtools.require("@loader/options");
|
||||
|
||||
const opts = {
|
||||
id: "browser-loader",
|
||||
sharedGlobal: true,
|
||||
@ -42,6 +44,7 @@ function BrowserLoader(baseURI, window) {
|
||||
let uri = require.resolve(id);
|
||||
|
||||
if (!uri.startsWith(baseURI) &&
|
||||
!uri.startsWith(COMPONENTS_URL) &&
|
||||
!uri.startsWith(VENDOR_CONTENT_URL)) {
|
||||
return devtools.require(uri);
|
||||
}
|
||||
@ -49,9 +52,6 @@ function BrowserLoader(baseURI, window) {
|
||||
}
|
||||
};
|
||||
|
||||
// The main.js file does not have to actually exist. It just
|
||||
// represents the base environment, so requires will be relative to
|
||||
// that path when used outside of modules.
|
||||
const mainModule = loaders.Module(baseURI, joinURI(baseURI, "main.js"));
|
||||
const mainLoader = loaders.Loader(opts);
|
||||
|
||||
|
11
devtools/client/shared/components/moz.build
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'tree.js',
|
||||
)
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
|
@ -28,9 +28,9 @@ var { TargetFactory } = require("devtools/client/framework/target");
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
|
||||
DevToolsUtils.testing = true;
|
||||
var { require: bRequire } = BrowserLoader("resource://devtools/client/memory/", this);
|
||||
var { require: browserRequire } = BrowserLoader("resource://devtools/client/shared/", this);
|
||||
|
||||
var EXAMPLE_URL = "http://example.com/browser/browser/devtools/memory/test/";
|
||||
var EXAMPLE_URL = "http://example.com/browser/browser/devtools/shared/test/";
|
||||
|
||||
// Encoding of the following tree/forest:
|
||||
//
|
@ -16,9 +16,9 @@ depth.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
let React = bRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
let React = browserRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
|
||||
ok(React, "Should get React");
|
||||
ok(Tree, "Should get Tree");
|
@ -15,9 +15,9 @@ Test that collapsed subtrees aren't rendered.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
let React = bRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
let React = browserRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
@ -15,9 +15,9 @@ Test Tree's autoExpandDepth.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
let React = bRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
let React = browserRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
@ -15,9 +15,9 @@ Test that we only render visible tree items.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = browserRequire("devtools/client/shared/vendor/react");
|
||||
const Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
@ -16,10 +16,10 @@ Test focusing with the Tree component.
|
||||
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = browserRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = React.addons.TestUtils;
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
@ -15,10 +15,10 @@ Test keyboard navigation with the Tree component.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = browserRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = React.addons.TestUtils;
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
@ -16,9 +16,9 @@ Test that arrows get the open attribute when their item's children are expanded.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = browserRequire("devtools/client/shared/vendor/react");
|
||||
const Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setProps(tree, {
|
@ -17,10 +17,10 @@ other inputs.
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const React = browserRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = React.addons.TestUtils;
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const Tree = React.createFactory(browserRequire("devtools/client/shared/components/tree"));
|
||||
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
const input = document.createElement("input");
|
@ -8,6 +8,7 @@ BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||
|
||||
DIRS += [
|
||||
'components',
|
||||
'redux',
|
||||
'vendor',
|
||||
'widgets',
|
||||
|
@ -1,11 +1,11 @@
|
||||
skip-if(B2G||Mulet) fails-if(Android) == resize.html resize-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) == resize.html resize-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
# an offset seems to apply to the native resizer on windows so skip this test for now
|
||||
skip-if(B2G||Mulet) fails-if(Android) skip-if(winWidget) fuzzy-if(cocoaWidget,1,33) == resize-background.html resize-background-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fails-if(Android) != ltr.html rtl.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fails-if(Android) != ltr-scrollbar.html rtl-scrollbar.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fails-if(Android) != in-ltr-doc-scrollbar.html in-rtl-doc-scrollbar.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fails-if(Android) != ltr.html no-resize.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fails-if(Android) fails-if(xulRuntime.widgetToolkit=="gtk2") != rtl.html no-resize.html # bug 834724 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) skip-if(winWidget) fuzzy-if(cocoaWidget,1,33) == resize-background.html resize-background-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) != ltr.html rtl.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) != ltr-scrollbar.html rtl-scrollbar.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) != in-ltr-doc-scrollbar.html in-rtl-doc-scrollbar.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) != ltr.html no-resize.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) fails-if(xulRuntime.widgetToolkit=="gtk2") != rtl.html no-resize.html # bug 834724 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== rtl.html rtl-dynamic-attr.html
|
||||
== rtl.html rtl-dynamic-style.html
|
||||
== rtl.html in-dynamic-rtl-doc.html
|
||||
|
@ -1,10 +1,11 @@
|
||||
# access-key tests are no use on OS X because access keys are not indicated visually
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)) != accesskey-1.xul accesskey-1-notref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)) == accesskey-2.xul accesskey-2-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
# no real XUL theme on Android so we just skip
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)||Android) != accesskey-1.xul accesskey-1-notref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)||Android) == accesskey-2.xul accesskey-2-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
# accesskey-3 fails because of defects in XUL bidi support
|
||||
fails-if(!cocoaWidget) skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)) == accesskey-3.xul accesskey-3-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)) != accesskey-3.xul accesskey-3-notref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)) == accesskey-4.xul accesskey-4-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)) != accesskey-4.xul accesskey-4-notref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if((B2G&&browserIsRemote)||Mulet) == align-baseline-1.xul align-baseline-1-ref.xul # test for bug 494901 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android) skip-if(B2G||Mulet) == setsize.xul setsize-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(!cocoaWidget) skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)||Android) == accesskey-3.xul accesskey-3-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)||Android) != accesskey-3.xul accesskey-3-notref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)||Android) == accesskey-4.xul accesskey-4-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(cocoaWidget||((B2G&&browserIsRemote)||Mulet)||Android) != accesskey-4.xul accesskey-4-notref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if((B2G&&browserIsRemote)||Mulet||Android) == align-baseline-1.xul align-baseline-1-ref.xul # test for bug 494901 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet||Android) == setsize.xul setsize-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
@ -240,7 +240,7 @@ include marquee/reftest.list
|
||||
|
||||
# native-theme/
|
||||
# skipping for B2G since something around radio-nonnative.html makes the whole suite hang
|
||||
skip-if(B2G||Mulet) include native-theme/reftest.list # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Android||Mulet) include native-theme/reftest.list # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
||||
# netwerk/
|
||||
include ../../netwerk/test/reftest/reftest.list
|
||||
@ -355,17 +355,17 @@ include ../../widget/reftests/reftest.list
|
||||
# xml-stylesheet/
|
||||
include ../../dom/tests/reftest/xml-stylesheet/reftest.list
|
||||
|
||||
# xul-document-load/
|
||||
include xul-document-load/reftest.list
|
||||
# xul-document-load/ (no XUL theme on Android)
|
||||
skip-if(Android) include xul-document-load/reftest.list
|
||||
|
||||
# xul/
|
||||
include xul/reftest.list
|
||||
# xul/ (no XUL theme on Android)
|
||||
skip-if(Android) include xul/reftest.list
|
||||
|
||||
# xul
|
||||
include ../xul/reftest/reftest.list
|
||||
# xul (no XUL theme on Android)
|
||||
skip-if(Android) include ../xul/reftest/reftest.list
|
||||
|
||||
# xul grid
|
||||
include ../xul/grid/reftests/reftest.list
|
||||
# xul grid (no XUL theme on Android)
|
||||
skip-if(Android) include ../xul/grid/reftests/reftest.list
|
||||
|
||||
# writing-mode
|
||||
include writing-mode/reftest.list
|
||||
|
@ -8,4 +8,4 @@ skip-if = toolkit == 'android' #bug 798806
|
||||
[test_bug563416.html]
|
||||
[test_resizer_incontent.xul]
|
||||
[test_splitter.xul]
|
||||
skip-if = android_version == '18' # bug 1147982
|
||||
skip-if = toolkit == 'android' # no XUL theme
|
||||
|
@ -6,6 +6,8 @@
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
@ -629,9 +631,8 @@ public abstract class GeckoApp
|
||||
setLocale(message.getString("locale"));
|
||||
|
||||
} else if ("Permissions:Data".equals(event)) {
|
||||
String host = message.getString("host");
|
||||
final NativeJSObject[] permissions = message.getObjectArray("permissions");
|
||||
showSiteSettingsDialog(host, permissions);
|
||||
showSiteSettingsDialog(permissions);
|
||||
|
||||
} else if ("PrivateBrowsing:Data".equals(event)) {
|
||||
mPrivateBrowsingSession = message.optString("session", null);
|
||||
@ -759,60 +760,49 @@ public abstract class GeckoApp
|
||||
* Array of JSON objects to represent site permissions.
|
||||
* Example: { type: "offline-app", setting: "Store Offline Data", value: "Allow" }
|
||||
*/
|
||||
private void showSiteSettingsDialog(final String host, final NativeJSObject[] permissions) {
|
||||
private void showSiteSettingsDialog(final NativeJSObject[] permissions) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.site_settings_title);
|
||||
|
||||
View customTitleView = getLayoutInflater().inflate(R.layout.site_setting_title, null);
|
||||
((TextView) customTitleView.findViewById(R.id.title)).setText(R.string.site_settings_title);
|
||||
((TextView) customTitleView.findViewById(R.id.host)).setText(host);
|
||||
builder.setCustomTitle(customTitleView);
|
||||
|
||||
// If there are no permissions to clear, show the user a message about that.
|
||||
// In the future, we want to disable the menu item if there are no permissions to clear.
|
||||
if (permissions.length == 0) {
|
||||
builder.setMessage(R.string.site_settings_no_settings);
|
||||
} else {
|
||||
|
||||
final ArrayList<HashMap<String, String>> itemList =
|
||||
new ArrayList<HashMap<String, String>>();
|
||||
for (final NativeJSObject permObj : permissions) {
|
||||
final HashMap<String, String> map = new HashMap<String, String>();
|
||||
map.put("setting", permObj.getString("setting"));
|
||||
map.put("value", permObj.getString("value"));
|
||||
itemList.add(map);
|
||||
}
|
||||
|
||||
// setMultiChoiceItems doesn't support using an adapter, so we're creating a hack with
|
||||
// setSingleChoiceItems and changing the choiceMode below when we create the dialog
|
||||
builder.setSingleChoiceItems(new SimpleAdapter(
|
||||
GeckoApp.this,
|
||||
itemList,
|
||||
R.layout.site_setting_item,
|
||||
new String[] { "setting", "value" },
|
||||
new int[] { R.id.setting, R.id.value }
|
||||
), -1, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) { }
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.site_settings_clear, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
ListView listView = ((AlertDialog) dialog).getListView();
|
||||
SparseBooleanArray checkedItemPositions = listView.getCheckedItemPositions();
|
||||
|
||||
// An array of the indices of the permissions we want to clear
|
||||
JSONArray permissionsToClear = new JSONArray();
|
||||
for (int i = 0; i < checkedItemPositions.size(); i++)
|
||||
if (checkedItemPositions.get(i))
|
||||
permissionsToClear.put(i);
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
|
||||
"Permissions:Clear", permissionsToClear.toString()));
|
||||
}
|
||||
});
|
||||
final ArrayList<HashMap<String, String>> itemList =
|
||||
new ArrayList<HashMap<String, String>>();
|
||||
for (final NativeJSObject permObj : permissions) {
|
||||
final HashMap<String, String> map = new HashMap<String, String>();
|
||||
map.put("setting", permObj.getString("setting"));
|
||||
map.put("value", permObj.getString("value"));
|
||||
itemList.add(map);
|
||||
}
|
||||
|
||||
// setMultiChoiceItems doesn't support using an adapter, so we're creating a hack with
|
||||
// setSingleChoiceItems and changing the choiceMode below when we create the dialog
|
||||
builder.setSingleChoiceItems(new SimpleAdapter(
|
||||
GeckoApp.this,
|
||||
itemList,
|
||||
R.layout.site_setting_item,
|
||||
new String[] { "setting", "value" },
|
||||
new int[] { R.id.setting, R.id.value }
|
||||
), -1, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) { }
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.site_settings_clear, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
ListView listView = ((AlertDialog) dialog).getListView();
|
||||
SparseBooleanArray checkedItemPositions = listView.getCheckedItemPositions();
|
||||
|
||||
// An array of the indices of the permissions we want to clear
|
||||
JSONArray permissionsToClear = new JSONArray();
|
||||
for (int i = 0; i < checkedItemPositions.size(); i++)
|
||||
if (checkedItemPositions.get(i))
|
||||
permissionsToClear.put(i);
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
|
||||
"Permissions:Clear", permissionsToClear.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(R.string.site_settings_cancel, new DialogInterface.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
@ -823,16 +813,39 @@ public abstract class GeckoApp
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Dialog dialog = builder.create();
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
ListView listView = ((AlertDialog) dialog).getListView();
|
||||
final ListView listView = dialog.getListView();
|
||||
if (listView != null) {
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
int listSize = listView.getAdapter().getCount();
|
||||
for (int i = 0; i < listSize; i++)
|
||||
listView.setItemChecked(i, true);
|
||||
}
|
||||
|
||||
final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||
clearButton.setEnabled(false);
|
||||
|
||||
dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
if (Versions.feature11Plus) {
|
||||
if (listView.getCheckedItemCount() == 0) {
|
||||
clearButton.setEnabled(false);
|
||||
} else {
|
||||
clearButton.setEnabled(true);
|
||||
}
|
||||
} else {
|
||||
final SparseBooleanArray items = listView.getCheckedItemPositions();
|
||||
for (int j = 0; j < items.size(); j++) {
|
||||
if (items.valueAt(j) == true) {
|
||||
clearButton.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clearButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -456,7 +456,6 @@ size. -->
|
||||
<!ENTITY site_settings_title3 "Site Settings">
|
||||
<!ENTITY site_settings_cancel "Cancel">
|
||||
<!ENTITY site_settings_clear "Clear">
|
||||
<!ENTITY site_settings_no_settings "There are no settings to clear.">
|
||||
|
||||
<!-- Localization note (reading_list_added3) : Used in a toast, please keep as short
|
||||
as possible. -->
|
||||
|
@ -1,31 +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/. -->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
style="@style/GeckoDialogTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="6dip"
|
||||
android:paddingBottom="0dip"
|
||||
android:paddingLeft="10dip"
|
||||
android:paddingRight="10dip"/>
|
||||
|
||||
<TextView android:id="@+id/host"
|
||||
style="@style/GeckoDialogTitle.SubTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="2dip"
|
||||
android:paddingBottom="6dip"
|
||||
android:paddingLeft="10dip"
|
||||
android:paddingRight="10dip"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</LinearLayout>
|
@ -355,7 +355,6 @@
|
||||
<string name="site_settings_title">&site_settings_title3;</string>
|
||||
<string name="site_settings_cancel">&site_settings_cancel;</string>
|
||||
<string name="site_settings_clear">&site_settings_clear;</string>
|
||||
<string name="site_settings_no_settings">&site_settings_no_settings;</string>
|
||||
|
||||
<string name="reading_list_added">&reading_list_added3;</string>
|
||||
<string name="reading_list_removed">&reading_list_removed;</string>
|
||||
|
@ -8,37 +8,37 @@ var PermissionsHelper = {
|
||||
"offline-app", "desktop-notification", "plugins", "native-intent"],
|
||||
_permissionStrings: {
|
||||
"password": {
|
||||
label: "password.savePassword",
|
||||
label: "password.logins",
|
||||
allowed: "password.save",
|
||||
denied: "password.dontSave"
|
||||
},
|
||||
"geolocation": {
|
||||
label: "geolocation.shareLocation",
|
||||
label: "geolocation.location",
|
||||
allowed: "geolocation.allow",
|
||||
denied: "geolocation.dontAllow"
|
||||
},
|
||||
"popup": {
|
||||
label: "blockPopups.label",
|
||||
label: "blockPopups.label2",
|
||||
allowed: "popup.show",
|
||||
denied: "popup.dontShow"
|
||||
},
|
||||
"indexedDB": {
|
||||
label: "offlineApps.storeOfflineData",
|
||||
label: "offlineApps.offlineData",
|
||||
allowed: "offlineApps.allow",
|
||||
denied: "offlineApps.dontAllow2"
|
||||
},
|
||||
"offline-app": {
|
||||
label: "offlineApps.storeOfflineData",
|
||||
label: "offlineApps.offlineData",
|
||||
allowed: "offlineApps.allow",
|
||||
denied: "offlineApps.dontAllow2"
|
||||
},
|
||||
"desktop-notification": {
|
||||
label: "desktopNotification.useNotifications",
|
||||
label: "desktopNotification.notifications",
|
||||
allowed: "desktopNotification2.allow",
|
||||
denied: "desktopNotification2.dontAllow"
|
||||
},
|
||||
"plugins": {
|
||||
label: "clickToPlayPlugins.activatePlugins",
|
||||
label: "clickToPlayPlugins.plugins",
|
||||
allowed: "clickToPlayPlugins.activate",
|
||||
denied: "clickToPlayPlugins.dontActivate"
|
||||
},
|
||||
@ -100,15 +100,8 @@ var PermissionsHelper = {
|
||||
// Keep track of permissions, so we know which ones to clear
|
||||
this._currentPermissions = permissions;
|
||||
|
||||
let host;
|
||||
try {
|
||||
host = uri.host;
|
||||
} catch(e) {
|
||||
host = uri.spec;
|
||||
}
|
||||
Messaging.sendRequest({
|
||||
type: "Permissions:Data",
|
||||
host: host,
|
||||
permissions: permissions
|
||||
});
|
||||
break;
|
||||
|
@ -90,9 +90,9 @@ popup.dontShow=Don't show
|
||||
# SafeBrowsing
|
||||
safeBrowsingDoorhanger=This site has been identified as containing malware or a phishing attempt. Be careful.
|
||||
|
||||
# LOCALIZATION NOTE (blockPopups.label): Label that will be used in
|
||||
# LOCALIZATION NOTE (blockPopups.label2): Label that will be used in
|
||||
# site settings dialog.
|
||||
blockPopups.label=Block Popups
|
||||
blockPopups.label2=Popups
|
||||
|
||||
# XPInstall
|
||||
xpinstallPromptWarning2=%S prevented this site (%S) from asking you to install software on your device.
|
||||
@ -113,9 +113,9 @@ identity.identified.title_with_country=%S (%S)
|
||||
geolocation.allow=Share
|
||||
geolocation.dontAllow=Don't share
|
||||
geolocation.ask=Share your location with %S?
|
||||
# LOCALIZATION NOTE (geolocation.shareLocation): Label that will be used in
|
||||
# LOCALIZATION NOTE (geolocation.location): Label that will be used in
|
||||
# site settings dialog.
|
||||
geolocation.shareLocation=Share Location
|
||||
geolocation.shareLocation=Location
|
||||
# LOCALIZATION NOTE (geolocation.dontAskAgain): This label appears next to a
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
geolocation.dontAskAgain=Don't ask again for this site
|
||||
@ -124,9 +124,9 @@ geolocation.dontAskAgain=Don't ask again for this site
|
||||
desktopNotification2.allow=Always
|
||||
desktopNotification2.dontAllow=Never
|
||||
desktopNotification2.ask=Would you like to receive notifications from this site?
|
||||
# LOCALIZATION NOTE (desktopNotification.useNotifications): Label that will be
|
||||
# LOCALIZATION NOTE (desktopNotification.notifications): Label that will be
|
||||
# used in site settings dialog.
|
||||
desktopNotification.useNotifications=Use Notifications
|
||||
desktopNotification.notifications=Notifications
|
||||
|
||||
# Imageblocking
|
||||
imageblocking.downloadedImage=Image unblocked
|
||||
@ -199,13 +199,13 @@ offlineApps.dontAskAgain=Don't ask again for this site
|
||||
offlineApps.allow=Allow
|
||||
offlineApps.dontAllow2=Don't allow
|
||||
|
||||
# LOCALIZATION NOTE (offlineApps.storeOfflineData): Label that will be used in
|
||||
# LOCALIZATION NOTE (offlineApps.offlineData): Label that will be used in
|
||||
# site settings dialog.
|
||||
offlineApps.storeOfflineData=Store Offline Data
|
||||
offlineApps.offlineData=Offline Data
|
||||
|
||||
# LOCALIZATION NOTE (password.savePassword): Label that will be used in
|
||||
# LOCALIZATION NOTE (password.logins): Label that will be used in
|
||||
# site settings dialog.
|
||||
password.savePassword=Save Password
|
||||
password.logins=Logins
|
||||
# LOCALIZATION NOTE (password.save): This should match
|
||||
# saveButton in passwordmgr.properties
|
||||
password.save=Save
|
||||
@ -306,9 +306,9 @@ clickToPlayPlugins.dontActivate=Don't activate
|
||||
# LOCALIZATION NOTE (clickToPlayPlugins.dontAskAgain): This label appears next to a
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
clickToPlayPlugins.dontAskAgain=Don't ask again for this site
|
||||
# LOCALIZATION NOTE (clickToPlayPlugins.playPlugins): Label that
|
||||
# LOCALIZATION NOTE (clickToPlayPlugins.plugins): Label that
|
||||
# will be used in site settings dialog.
|
||||
clickToPlayPlugins.activatePlugins=Activate Plugins
|
||||
clickToPlayPlugins.plugins=Plugins
|
||||
|
||||
# Site settings dialog
|
||||
# LOCALIZATION NOTE (siteSettings.labelToValue): This string will be used to
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pref("datareporting.policy.dataSubmissionEnabled", true);
|
||||
pref("datareporting.policy.dataSubmissionEnabled.v2", true);
|
||||
pref("datareporting.policy.dataSubmissionEnabled.v2", false);
|
||||
pref("datareporting.policy.firstRunTime", "0");
|
||||
pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0");
|
||||
pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 0);
|
||||
|
@ -657,9 +657,9 @@ this.BrowserTestUtils = {
|
||||
});
|
||||
|
||||
let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
|
||||
browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
|
||||
browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
|
||||
dump("\nabout:tabcrashed loaded\n");
|
||||
browser.addEventListener("AboutTabCrashedReady", function onCrash() {
|
||||
browser.removeEventListener("AboutTabCrashedReady", onCrash, false);
|
||||
dump("\nabout:tabcrashed loaded and ready\n");
|
||||
resolve();
|
||||
}, false, true);
|
||||
});
|
||||
|
@ -153,6 +153,10 @@ RemotePages.prototype = {
|
||||
|
||||
this.listener.removeMessageListener(name, callback);
|
||||
},
|
||||
|
||||
portsForBrowser: function(browser) {
|
||||
return [...this.messagePorts].filter(port => port.browser == browser);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -389,7 +393,7 @@ function ChildMessagePort(contentFrame, window) {
|
||||
// Tell the main process to set up its side of the message pipe.
|
||||
this.messageManager.sendAsyncMessage("RemotePage:InitPort", {
|
||||
portID: portID,
|
||||
url: window.location.toString().replace(/\#.*$/, "")
|
||||
url: window.document.documentURI.replace(/[\#|\?].*$/, ""),
|
||||
});
|
||||
}
|
||||
|
||||
@ -491,7 +495,7 @@ var registeredURLs = new Set();
|
||||
|
||||
var observer = (window) => {
|
||||
// Strip the hash from the URL, because it's not part of the origin.
|
||||
let url = window.location.toString().replace(/\#.*$/, "");
|
||||
let url = window.document.documentURI.replace(/[\#|\?].*$/, "");
|
||||
if (!registeredURLs.has(url))
|
||||
return;
|
||||
|
||||
|
@ -387,3 +387,14 @@ add_task(function* send_data2() {
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
add_task(function* get_ports_for_browser() {
|
||||
let pages = new RemotePages(TEST_URL);
|
||||
let port = yield waitForPage(pages);
|
||||
// waitForPage creates a new tab and selects it by default, so
|
||||
// the selected tab should be the one hosting this port.
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let foundPorts = pages.portsForBrowser(browser);
|
||||
is(foundPorts.length, 1, "There should only be one port for this simple page");
|
||||
is(foundPorts[0], port, "Should find the port");
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
7
toolkit/themes/mobile/global/empty.css
Normal file
@ -0,0 +1,7 @@
|
||||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Empty stub for theme CSS
|
||||
*/
|
BIN
toolkit/themes/mobile/global/icons/Error.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
toolkit/themes/mobile/global/media/clicktoplay-bgtexture.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
toolkit/themes/mobile/global/media/error.png
Normal file
After Width: | Height: | Size: 433 B |
BIN
toolkit/themes/mobile/global/media/throbber.png
Normal file
After Width: | Height: | Size: 30 KiB |
@ -0,0 +1,30 @@
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMinYMin meet" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient id="whiteGradientStops">
|
||||
<stop style="stop-color:#fff;stop-opacity:.95" offset="0"/>
|
||||
<stop style="stop-color:#fff;stop-opacity:.75" offset=".45"/>
|
||||
<stop style="stop-color:#fff;stop-opacity:.72" offset=".55"/>
|
||||
<stop style="stop-color:#fff;stop-opacity:.65" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient x1="32" y1="0" x2="32" y2="62" id="whiteGradient" xlink:href="#whiteGradientStops" gradientUnits="userSpaceOnUse"/>
|
||||
<linearGradient id="arrowGradientStops">
|
||||
<stop style="stop-color:#333;stop-opacity:.5" offset="0"/>
|
||||
<stop style="stop-color:#666;stop-opacity:.5" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient x1="32" y1="16" x2="32" y2="48" id="arrowGradient" xlink:href="#arrowGradientStops" gradientUnits="userSpaceOnUse"/>
|
||||
<filter x="-0.15" y="-0.15" width="1.25" height="1.25" color-interpolation-filters="sRGB" id="dropShadow">
|
||||
<feDropShadow dx="0" dy="1" flood-opacity="0.5"/>
|
||||
</filter>
|
||||
<mask id="dropShadowMask">
|
||||
<path style="fill:#fff;" d="M47.285,30.991L23.75,17.24c-0.357-0.208-0.692-0.278-0.969-0.221 C22.32,17.115,22,17.555,22,18.252v27.499c0,1.112,0.797,1.568,1.75,1.011l23.535-13.748C48.238,32.458,48.238,31.547,47.285,30.991 z M0,0v64h64V0H0z M32,60C16.536,60,4,47.464,4,32S16.536,4,32,4s28,12.536,28,28S47.464,60,32,60z"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<path mask="url(#dropShadowMask)" id="playButtonShadow" style="filter:url(#dropShadow);" d="M32,4C16.536,4,4,16.536,4,32s12.536,28,28,28s28-12.536,28-28S47.464,4,32,4z M47.285,33.014 L23.75,46.762C22.797,47.319,22,46.863,22,45.751v-27.5c0-0.697,0.32-1.137,0.781-1.232c0.277-0.058,0.612,0.012,0.969,0.221 l23.535,13.751C48.238,31.546,48.238,32.458,47.285,33.014z"/>
|
||||
<path id="playButtonArrow" style="fill:url(#arrowGradient);" d="M22.781,17.019C22.32,17.114,22,17.555,22,18.251v27.5c0,1.112,0.797,1.568,1.75,1.011 l23.535-13.748c0.953-0.556,0.953-1.467,0-2.023L23.75,17.24C23.393,17.031,23.058,16.961,22.781,17.019z"/>
|
||||
<path id="playButton" style="fill:url(#whiteGradient);" d="M32,4C16.536,4,4,16.536,4,32s12.536,28,28,28s28-12.536,28-28S47.464,4,32,4z M47.285,33.014 L23.75,46.762C22.797,47.319,22,46.863,22,45.751v-27.5c0-0.697,0.32-1.137,0.781-1.232c0.277-0.058,0.612,0.012,0.969,0.221 l23.535,13.751C48.238,31.546,48.238,32.458,47.285,33.014z"/>
|
||||
<path id="playButtonEdgeHighlights" style="fill:white;fill-opacity:.3;" d="M32,4C16.536,4,4,16.536,4,32s12.536,28,28,28s28-12.536,28-28S47.464,4,32,4z M32,59C17.112,59,5,46.888,5,32S17.112,5,32,5s27,12.112,27,27S46.888,59,32,59z M47.789,30.127l-23.534-13.75 C23.826,16.126,23.396,16,22.976,16c-0.135,0-0.27,0.014-0.398,0.041C21.62,16.238,21,17.106,21,18.251v27.5 C21,47.075,21.812,48,22.977,48c0.423,0,0.854-0.126,1.279-0.375L47.79,33.877c0.769-0.449,1.21-1.132,1.21-1.875 S48.559,30.576,47.789,30.127z M47.285,33.014L23.75,46.762C23.474,46.924,23.211,47,22.977,47C22.402,47,22,46.541,22,45.751v-27.5 c0-0.697,0.32-1.137,0.781-1.232L22.976,17c0.233,0,0.498,0.079,0.775,0.24l23.535,13.751 C48.238,31.546,48.238,32.458,47.285,33.014z"/>
|
||||
<path id="playButtonTopEdgeHighlights" style="fill:white;fill-opacity:.8;" d="M32,4C16.536,4,4,16.536,4,32c0,0.167,0.01,0.333,0.013,0.5 C4.28,17.268,16.704,5,32,5c15.296,0,27.72,12.268,27.987,27.5C59.99,32.333,60,32.167,60,32C60,16.536,47.464,4,32,4z M47.285,33.014L23.75,46.762C22.797,47.319,22,46.863,22,45.751v1c0,1.112,0.797,1.568,1.75,1.011l23.535-13.748 c0.697-0.406,0.879-1.003,0.556-1.512C47.723,32.688,47.541,32.864,47.285,33.014z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
56
toolkit/themes/mobile/jar.mn
Normal file
@ -0,0 +1,56 @@
|
||||
# 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/.
|
||||
|
||||
toolkit.jar:
|
||||
% skin global classic/1.0 %skin/classic/global/
|
||||
# These are the CSS files that must exist
|
||||
+ skin/classic/global/autocomplete.css (global/empty.css)
|
||||
+ skin/classic/global/button.css (global/empty.css)
|
||||
+ skin/classic/global/checkbox.css (global/empty.css)
|
||||
+ skin/classic/global/dialog.css (global/empty.css)
|
||||
+ skin/classic/global/dropmarker.css (global/empty.css)
|
||||
+ skin/classic/global/global.css (global/empty.css)
|
||||
+ skin/classic/global/groupbox.css (global/empty.css)
|
||||
+ skin/classic/global/listbox.css (global/empty.css)
|
||||
+ skin/classic/global/menu.css (global/empty.css)
|
||||
+ skin/classic/global/menulist.css (global/empty.css)
|
||||
+ skin/classic/global/numberbox.css (global/empty.css)
|
||||
+ skin/classic/global/popup.css (global/empty.css)
|
||||
+ skin/classic/global/preferences.css (global/empty.css)
|
||||
+ skin/classic/global/progressmeter.css (global/empty.css)
|
||||
+ skin/classic/global/radio.css (global/empty.css)
|
||||
+ skin/classic/global/resizer.css (global/empty.css)
|
||||
+ skin/classic/global/richlistbox.css (global/empty.css)
|
||||
+ skin/classic/global/scale.css (global/empty.css)
|
||||
+ skin/classic/global/scrollbox.css (global/empty.css)
|
||||
+ skin/classic/global/spinbuttons.css (global/empty.css)
|
||||
+ skin/classic/global/splitter.css (global/empty.css)
|
||||
+ skin/classic/global/tabbox.css (global/empty.css)
|
||||
+ skin/classic/global/textbox.css (global/empty.css)
|
||||
+ skin/classic/global/toolbar.css (global/empty.css)
|
||||
+ skin/classic/global/toolbarbutton.css (global/empty.css)
|
||||
+ skin/classic/global/tree.css (global/empty.css)
|
||||
+ skin/classic/global/wizard.css (global/empty.css)
|
||||
+ skin/classic/global/scrollbars.css (global/empty.css)
|
||||
|
||||
skin/classic/global/media/clicktoplay-bgtexture.png (global/media/clicktoplay-bgtexture.png)
|
||||
skin/classic/global/media/error.png (global/media/error.png)
|
||||
skin/classic/global/media/throbber.png (global/media/throbber.png)
|
||||
skin/classic/global/media/videoClickToPlayButton.svg (global/media/videoClickToPlayButton.svg)
|
||||
|
||||
skin/classic/global/icons/Error.png (global/icons/Error.png)
|
||||
|
||||
% skin mozapps classic/1.0 %skin/classic/mozapps/
|
||||
+ skin/classic/mozapps/plugins/pluginProblem.css (mozapps/plugins/pluginProblem.css)
|
||||
|
||||
skin/classic/mozapps/plugins/contentPluginActivate.png (mozapps/plugins/contentPluginActivate.png)
|
||||
skin/classic/mozapps/plugins/contentPluginBlocked.png (mozapps/plugins/contentPluginBlocked.png)
|
||||
skin/classic/mozapps/plugins/contentPluginClose.png (mozapps/plugins/contentPluginClose.png)
|
||||
skin/classic/mozapps/plugins/contentPluginCrashed.png (mozapps/plugins/contentPluginCrashed.png)
|
||||
skin/classic/mozapps/plugins/contentPluginDisabled.png (mozapps/plugins/contentPluginDisabled.png)
|
||||
skin/classic/mozapps/plugins/contentPluginDownload.png (mozapps/plugins/contentPluginDownload.png)
|
||||
skin/classic/mozapps/plugins/contentPluginMissing.png (mozapps/plugins/contentPluginMissing.png)
|
||||
skin/classic/mozapps/plugins/contentPluginStripe.png (mozapps/plugins/contentPluginStripe.png)
|
||||
|
||||
skin/classic/mozapps/passwordmgr/key-16.png (mozapps/passwordmgr/key-16.png)
|
7
toolkit/themes/mobile/moz.build
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
BIN
toolkit/themes/mobile/mozapps/passwordmgr/key-16.png
Normal file
After Width: | Height: | Size: 773 B |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginActivate.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginBlocked.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginClose.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginCrashed.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginDisabled.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginDownload.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginMissing.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
toolkit/themes/mobile/mozapps/plugins/contentPluginStripe.png
Normal file
After Width: | Height: | Size: 460 B |
206
toolkit/themes/mobile/mozapps/plugins/pluginProblem.css
Normal file
@ -0,0 +1,206 @@
|
||||
/* 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/. */
|
||||
|
||||
@namespace html url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
/* These styles affect only the bound element, not other page content. */
|
||||
/* Keep any changes to these styles in sync with plugin-doorhanger.inc.css */
|
||||
.mainBox {
|
||||
font: message-box;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(72,72,72);
|
||||
color: white;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.hoverBox {
|
||||
display: table-cell;
|
||||
box-sizing: border-box;
|
||||
padding: 5px;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
:-moz-handler-vulnerable-updatable .hoverBox:active,
|
||||
:-moz-handler-vulnerable-no-update .hoverBox:active,
|
||||
:-moz-handler-clicktoplay .hoverBox:active {
|
||||
background-color: rgb(65, 65, 65);
|
||||
}
|
||||
|
||||
:-moz-handler-clicktoplay .hoverBox:active .msgTapToPlay,
|
||||
:-moz-handler-clicktoplay .hoverBox:active .msgClickToPlay,
|
||||
:-moz-handler-vulnerable-updatable .hoverBox:active .msgTapToPlay,
|
||||
:-moz-handler-vulnerable-updatable .hoverBox:active .msgClickToPlay,
|
||||
:-moz-handler-vulnerable-no-update .hoverBox:active .msgTapToPlay,
|
||||
:-moz-handler-vulnerable-no-update .hoverBox:active .msgClickToPlay {
|
||||
color: red;
|
||||
}
|
||||
|
||||
:-moz-handler-vulnerable-updatable .hoverBox,
|
||||
:-moz-handler-vulnerable-no-update .hoverBox,
|
||||
:-moz-handler-blocked .hoverBox,
|
||||
:-moz-handler-crashed .hoverBox {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginStripe.png);
|
||||
}
|
||||
|
||||
html|a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
-moz-user-focus: ignore;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
:-moz-type-unsupported .icon,
|
||||
:-moz-type-unsupported-platform .icon {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginMissing.png);
|
||||
}
|
||||
:-moz-type-unsupported .icon[installable] {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginDownload.png);
|
||||
}
|
||||
:-moz-handler-vulnerable-updatable .icon,
|
||||
:-moz-handler-vulnerable-no-update .icon {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginBlocked.png);
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
:-moz-handler-blocked .icon {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginBlocked.png);
|
||||
}
|
||||
:-moz-handler-clicktoplay .icon {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginActivate.png);
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
:-moz-handler-disabled .icon {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginDisabled.png);
|
||||
}
|
||||
:-moz-handler-crashed .icon {
|
||||
background-image: url(chrome://mozapps/skin/plugins/contentPluginCrashed.png);
|
||||
}
|
||||
|
||||
.throbber {
|
||||
padding-left: 16px; /* width of the background image */
|
||||
background: url(chrome://global/skin/icons/loading_16.png) no-repeat;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.msgTapToPlay,
|
||||
.msgClickToPlay {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media not all and (-moz-touch-enabled) {
|
||||
:-moz-handler-clicktoplay .msgTapToPlay {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-touch-enabled) {
|
||||
:-moz-handler-clicktoplay .msgClickToPlay {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.submitStatus div {
|
||||
min-height: 19px; /* height of biggest line (with throbber) */
|
||||
}
|
||||
|
||||
.submitComment {
|
||||
width: 340px;
|
||||
height: 70px;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
resize: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.submitURLOptInBox {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.submitURLOptIn {
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.mainBox[chromedir="rtl"] .submitURLOptIn {
|
||||
margin-left: 0;
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.submitButtonBox {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mainBox[chromedir="rtl"] .submitButton {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.helpIcon {
|
||||
display: inline-block;
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
background: url(chrome://mozapps/skin/plugins/pluginHelp-16.png) no-repeat;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mainBox[chromedir="rtl"] .helpIcon {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-top: 4px;
|
||||
-moz-margin-start: -20px;
|
||||
-moz-margin-end: 4px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
background-image: url("chrome://mozapps/skin/plugins/contentPluginClose.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.closeIcon:hover {
|
||||
background-position: -16px 0;
|
||||
}
|
||||
|
||||
.closeIcon:hover:active {
|
||||
background-position: -32px 0;
|
||||
}
|
||||
|
||||
.action-link {
|
||||
display: inline-block;
|
||||
border-radius: 10px;
|
||||
background-color: rgb(35, 35, 35);
|
||||
padding: 2px 8px;
|
||||
margin-top: 7px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.action-link:active {
|
||||
background-color: rgb(20, 20, 20);
|
||||
}
|
||||
|
||||
:-moz-handler-vulnerable-updatable .action-link {
|
||||
background-color: #a81b0c;
|
||||
}
|
||||
:-moz-handler-vulnerable-updatable .action-link:active {
|
||||
background-color: #801409;
|
||||
}
|
@ -14,13 +14,20 @@
|
||||
# GNOME/Linux linux (toolkit/themes/linux)
|
||||
# non-mac (toolkit/themes/shared/non-mac)
|
||||
# faststripe windows + faststripe (no native theme components)
|
||||
# mobile native UIs that do not use XUL for UI
|
||||
|
||||
toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
|
||||
app = CONFIG['MOZ_BUILD_APP']
|
||||
|
||||
if toolkit == 'cocoa':
|
||||
DIRS += ['osx']
|
||||
elif toolkit in ('gtk2', 'gtk3', 'qt'):
|
||||
DIRS += ['linux']
|
||||
elif app == 'mobile/android':
|
||||
if CONFIG['NIGHTLY_BUILD']:
|
||||
DIRS += ['mobile']
|
||||
else:
|
||||
DIRS += ['windows']
|
||||
else:
|
||||
DIRS += ['windows']
|
||||
|
||||
|