merge fx-team to mozilla-central on a CLOSED TREE

This commit is contained in:
Carsten "Tomcat" Book 2015-11-23 11:52:13 +01:00
commit 7e51d297a6
94 changed files with 1907 additions and 858 deletions

View File

@ -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 => {

View File

@ -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');
});

View File

@ -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,

View File

@ -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

View File

@ -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();
});

View File

@ -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));
});

View File

@ -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();
});

View File

@ -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));
});

View File

@ -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();
}
);

View File

@ -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();

View File

@ -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>

View File

@ -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() {

View File

@ -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);
]]>

View File

@ -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");

View File

@ -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");

View File

@ -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.

View File

@ -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

View File

@ -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);
}
},

View File

@ -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");
}
}
};

View File

@ -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

View File

@ -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);

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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");

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});
});

View File

@ -562,3 +562,4 @@ function popPrefs() {
SpecialPowers.popPrefEnv(resolve);
});
}

View File

@ -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

View File

@ -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'];

View File

@ -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");

View File

@ -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");

View File

@ -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'];

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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';

View File

@ -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;

View File

@ -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;

View File

@ -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 = {

View File

@ -17,7 +17,7 @@ EXTRA_JS_MODULES += [
'CastingApps.jsm',
'Chat.jsm',
'ContentClick.jsm',
'ContentCrashReporters.jsm',
'ContentCrashHandlers.jsm',
'ContentLinkHandler.jsm',
'ContentObservers.jsm',
'ContentSearch.jsm',

View File

@ -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");

View File

@ -10,7 +10,4 @@ DevToolsModules(
'snapshot-list-item.js',
'toolbar.js',
'tree-item.js',
'tree.js',
)
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']

View File

@ -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);

View File

@ -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);

View 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']

View File

@ -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:
//

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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, {

View File

@ -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, {

View File

@ -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, {

View File

@ -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, {

View File

@ -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");

View File

@ -8,6 +8,7 @@ BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
DIRS += [
'components',
'redux',
'vendor',
'widgets',

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}
}
});
}
});
}

View File

@ -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. -->

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
});

View File

@ -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;

View File

@ -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();
});

View 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
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -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

View 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)

View 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']

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

View 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;
}

View File

@ -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']