Bug 1381132 - Export Screenshots 10.6.0 to Firefox; r?Mossop
MozReview-Commit-ID: 8tfKRXYZVnT
--- a/browser/extensions/screenshots/install.rdf
+++ b/browser/extensions/screenshots/install.rdf
@@ -7,14 +7,14 @@
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
<em:minVersion>51.0a1</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<em:type>2</em:type>
- <em:version>10.5.0</em:version>
+ <em:version>10.6.0</em:version>
<em:bootstrap>true</em:bootstrap>
<em:homepageURL>https://pageshot.net/</em:homepageURL>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
</Description>
</RDF>
--- a/browser/extensions/screenshots/moz.build
+++ b/browser/extensions/screenshots/moz.build
@@ -7,16 +7,17 @@
FINAL_TARGET_FILES.features['screenshots@mozilla.org'] += [
'bootstrap.js',
'install.rdf'
]
# This file list is automatically generated by Screenshots' export scripts.
# AUTOMATIC INSERTION START
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"] += [
+ 'webextension/assertIsBlankDocumentUrl.js',
'webextension/assertIsTrusted.js',
'webextension/blank.html',
'webextension/catcher.js',
'webextension/clipboard.js',
'webextension/domainFromUrl.js',
'webextension/log.js',
'webextension/makeUuid.js',
'webextension/manifest.json',
new file mode 100644
--- /dev/null
+++ b/browser/extensions/screenshots/webextension/assertIsBlankDocumentUrl.js
@@ -0,0 +1,12 @@
+/** For use inside an iframe onload function, throws an Error if iframe src is not blank.html
+
+ Should be applied *inside* catcher.watchFunction
+*/
+this.assertIsBlankDocumentUrl = function assertIsBlankDocumentUrl(documentUrl) {
+ if (documentUrl !== browser.extension.getURL("blank.html")) {
+ let exc = new Error('iframe URL does not match expected blank.html');
+ exc.foundURL = documentUrl;
+ throw exc;
+ }
+}
+null;
--- a/browser/extensions/screenshots/webextension/background/selectorLoader.js
+++ b/browser/extensions/screenshots/webextension/background/selectorLoader.js
@@ -9,16 +9,17 @@ this.selectorLoader = (function() {
// These modules are loaded in order, first standardScripts, then optionally onboardingScripts, and then selectorScripts
// The order is important due to dependencies
const standardScripts = [
"build/buildSettings.js",
"log.js",
"catcher.js",
"assertIsTrusted.js",
+ "assertIsBlankDocumentUrl.js",
"background/selectorLoader.js",
"selector/callBackground.js",
"selector/util.js"
];
const selectorScripts = [
"clipboard.js",
"makeUuid.js",
--- a/browser/extensions/screenshots/webextension/clipboard.js
+++ b/browser/extensions/screenshots/webextension/clipboard.js
@@ -1,23 +1,42 @@
-/* globals catcher */
+/* globals catcher, assertIsBlankDocumentUrl */
"use strict";
this.clipboard = (function() {
let exports = {};
exports.copy = function(text) {
- let el = document.createElement("textarea");
- document.body.appendChild(el);
- el.value = text;
- el.select();
- const copied = document.execCommand("copy");
- document.body.removeChild(el);
- if (!copied) {
- catcher.unhandled(new Error("Clipboard copy failed"));
- }
- return copied;
+ return new Promise((resolve, reject) => {
+ let element = document.createElement("iframe");
+ element.src = browser.extension.getURL("blank.html");
+ // We can't actually hide the iframe while copying, but we can make
+ // it close to invisible:
+ element.style.opacity = "0";
+ element.style.width = "1px";
+ element.style.height = "1px";
+ element.onload = catcher.watchFunction(() => {
+ try {
+ element.onload = null;
+ let doc = element.contentDocument;
+ assertIsBlankDocumentUrl(doc.URL);
+ let el = doc.createElement("textarea");
+ doc.body.appendChild(el);
+ el.value = text;
+ el.select();
+ const copied = doc.execCommand("copy");
+ if (!copied) {
+ catcher.unhandled(new Error("Clipboard copy failed"));
+ }
+ doc.body.removeChild(el);
+ resolve(copied);
+ } finally {
+ document.body.removeChild(element);
+ }
+ });
+ document.body.appendChild(element);
+ });
};
return exports;
})();
null;
--- a/browser/extensions/screenshots/webextension/manifest.json
+++ b/browser/extensions/screenshots/webextension/manifest.json
@@ -1,12 +1,12 @@
{
"manifest_version": 2,
"name": "Firefox Screenshots",
- "version": "10.5.0",
+ "version": "10.6.0",
"description": "__MSG_addonDescription__",
"author": "__MSG_addonAuthorsList__",
"homepage_url": "https://github.com/mozilla-services/screenshots",
"applications": {
"gecko": {
"id": "screenshots@mozilla.org"
}
},
--- a/browser/extensions/screenshots/webextension/onboarding/slides.js
+++ b/browser/extensions/screenshots/webextension/onboarding/slides.js
@@ -1,9 +1,9 @@
-/* globals log, catcher, onboardingHtml, onboardingCss, util, shooter, callBackground, assertIsTrusted */
+/* globals log, catcher, onboardingHtml, onboardingCss, util, shooter, callBackground, assertIsTrusted, assertIsBlankDocumentUrl */
"use strict";
this.slides = (function() {
let exports = {};
const { watchFunction } = catcher;
@@ -31,21 +31,23 @@ this.slides = (function() {
iframe.style.margin = "0";
iframe.scrolling = "no";
updateIframeSize();
let html = onboardingHtml.replace('<style></style>', `<style>${onboardingCss}</style>`);
html = html.replace(/MOZ_EXTENSION([^\"]+)/g, (match, filename) => {
return browser.extension.getURL(filename);
});
iframe.onload = catcher.watchFunction(() => {
+ iframe.onload = null;
+ doc = iframe.contentDocument;
+ assertIsBlankDocumentUrl(doc.URL);
let parsedDom = (new DOMParser()).parseFromString(
html,
"text/html"
);
- doc = iframe.contentDocument;
doc.replaceChild(
doc.adoptNode(parsedDom.documentElement),
doc.documentElement
);
doc.addEventListener("keyup", onKeyUp);
doc.documentElement.dir = browser.i18n.getMessage("@@bidi_dir");
doc.documentElement.lang = browser.i18n.getMessage("@@ui_locale");
localizeText(doc);
--- a/browser/extensions/screenshots/webextension/selector/shooter.js
+++ b/browser/extensions/screenshots/webextension/selector/shooter.js
@@ -117,18 +117,19 @@ this.shooter = (function() { // eslint-d
scrollY: window.scrollY,
innerHeight: window.innerHeight,
innerWidth: window.innerWidth
},
selectedPos,
shotId: shotObject.id,
shot: shotObject.asJson()
}).then((url) => {
- const copied = clipboard.copy(url);
- return callBackground("openShot", { url, copied });
+ return clipboard.copy(url).then((copied) => {
+ return callBackground("openShot", { url, copied });
+ });
}, (error) => {
if ('popupMessage' in error && (error.popupMessage == "REQUEST_ERROR" || error.popupMessage == 'CONNECTION_ERROR')) {
// The error has been signaled to the user, but unlike other errors (or
// success) we should not abort the selection
deactivateAfterFinish = false;
return;
}
if (error.name != "BackgroundError") {
--- a/browser/extensions/screenshots/webextension/selector/ui.js
+++ b/browser/extensions/screenshots/webextension/selector/ui.js
@@ -1,9 +1,9 @@
-/* globals log, util, catcher, inlineSelectionCss, callBackground, assertIsTrusted */
+/* globals log, util, catcher, inlineSelectionCss, callBackground, assertIsTrusted, assertIsBlankDocumentUrl */
"use strict";
this.ui = (function() { // eslint-disable-line no-unused-vars
let exports = {};
const SAVE_BUTTON_HEIGHT = 50;
const { watchFunction } = catcher;
@@ -88,17 +88,19 @@ this.ui = (function() { // eslint-disabl
this.element.style.border = "none";
this.element.style.position = "absolute";
this.element.style.top = "0";
this.element.style.left = "0";
this.element.style.margin = "0";
this.element.scrolling = "no";
this.updateElementSize();
this.element.onload = watchFunction(() => {
+ this.element.onload = null;
this.document = this.element.contentDocument;
+ assertIsBlankDocumentUrl(this.document.URL);
this.document.documentElement.innerHTML = `
<head>
<style>${substitutedCss}</style>
<title></title>
</head>
<body></body>`;
installHandlerOnDocument(this.document);
if (this.addClassName) {
@@ -158,30 +160,30 @@ this.ui = (function() { // eslint-disabl
if (force && visible) {
this.element.style.display = "";
}
},
initSizeWatch() {
this.stopSizeWatch();
this.sizeTracking.timer = setInterval(watchFunction(this.updateElementSize.bind(this)), 2000);
- window.addEventListener("resize", this.onResize, true);
+ window.addEventListener("resize", watchFunction(assertIsTrusted(this.onResize)), true);
},
stopSizeWatch() {
if (this.sizeTracking.timer) {
clearTimeout(this.sizeTracking.timer);
this.sizeTracking.timer = null;
}
if (this.sizeTracking.windowDelayer) {
clearTimeout(this.sizeTracking.windowDelayer);
this.sizeTracking.windowDelayer = null;
}
this.sizeTracking.lastHeight = this.sizeTracking.lastWidth = null;
- window.removeEventListener("resize", this.onResize, true);
+ window.removeEventListener("resize", watchFunction(assertIsTrusted(this.onResize)), true);
},
getElementFromPoint(x, y) {
this.element.style.pointerEvents = "none";
let el;
try {
el = document.elementFromPoint(x, y);
} finally {
@@ -192,17 +194,17 @@ this.ui = (function() { // eslint-disabl
remove() {
this.stopSizeWatch();
util.removeNode(this.element);
this.element = this.document = null;
}
};
- iframeSelection.onResize = watchFunction(onResize.bind(iframeSelection));
+ iframeSelection.onResize = watchFunction(assertIsTrusted(onResize.bind(iframeSelection)));
let iframePreSelection = exports.iframePreSelection = {
element: null,
document: null,
sizeTracking: {
windowDelayer: null
},
display(installHandlerOnDocument, standardOverlayCallbacks) {
@@ -215,17 +217,19 @@ this.ui = (function() { // eslint-disabl
this.element.style.border = "none";
this.element.style.position = "fixed";
this.element.style.top = "0";
this.element.style.left = "0";
this.element.style.margin = "0";
this.element.scrolling = "no";
this.updateElementSize();
this.element.onload = watchFunction(() => {
+ this.element.onload = null;
this.document = this.element.contentDocument;
+ assertIsBlankDocumentUrl(this.document.URL)
this.document.documentElement.innerHTML = `
<head>
<style>${substitutedCss}</style>
<title></title>
</head>
<body>
<div class="preview-overlay">
<div class="fixed-container">
@@ -277,26 +281,26 @@ this.ui = (function() { // eslint-disabl
// time-delay
return;
}
this.element.style.height = window.innerHeight + "px";
this.element.style.width = window.innerWidth + "px";
},
hide() {
- window.removeEventListener("scroll", this.onScroll);
+ window.removeEventListener("scroll", watchFunction(assertIsTrusted(this.onScroll)));
window.removeEventListener("resize", this.onResize, true);
if (this.element) {
this.element.style.display = "none";
}
},
unhide() {
this.updateElementSize();
- window.addEventListener("scroll", this.onScroll);
+ window.addEventListener("scroll", watchFunction(assertIsTrusted(this.onScroll)));
window.addEventListener("resize", this.onResize, true);
this.element.style.display = "";
this.element.focus();
},
onScroll() {
exports.HoverBox.hide();
},
--- a/browser/extensions/screenshots/webextension/selector/uicontrol.js
+++ b/browser/extensions/screenshots/webextension/selector/uicontrol.js
@@ -864,35 +864,35 @@ this.uicontrol = (function() {
* Event handlers
*/
let primedDocumentHandlers = new Map();
let registeredDocumentHandlers = []
function addHandlers() {
["mouseup", "mousedown", "mousemove", "click"].forEach((eventName) => {
- let fn = watchFunction((function(eventName, event) {
+ let fn = watchFunction(assertIsTrusted((function(eventName, event) {
if (typeof event.button == "number" && event.button !== 0) {
// Not a left click
return undefined;
}
if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey) {
// Modified click of key
return undefined;
}
let state = getState();
let handler = stateHandlers[state];
if (handler[eventName]) {
return handler[eventName](event);
}
return undefined;
- }).bind(null, eventName));
+ }).bind(null, eventName)));
primedDocumentHandlers.set(eventName, fn);
});
- primedDocumentHandlers.set("keyup", keyupHandler);
+ primedDocumentHandlers.set("keyup", watchFunction(assertIsTrusted(keyupHandler)));
window.addEventListener('beforeunload', beforeunloadHandler);
}
let mousedownSetOnDocument = false;
function installHandlersOnDocument(docObj) {
for (let [eventName, handler] of primedDocumentHandlers) {
let watchHandler = watchFunction(handler);