--- a/browser/extensions/screenshots/bootstrap.js
+++ b/browser/extensions/screenshots/bootstrap.js
@@ -1,11 +1,9 @@
/* globals ADDON_DISABLE */
-// TODO: re-enable
-/* eslint-disable */
const OLD_ADDON_PREF_NAME = "extensions.jid1-NeEaf3sAHdKHPA@jetpack.deviceIdInfo";
const OLD_ADDON_ID = "jid1-NeEaf3sAHdKHPA@jetpack";
const ADDON_ID = "screenshots@mozilla.org";
const TELEMETRY_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
const PREF_BRANCH = "extensions.screenshots.";
const USER_DISABLE_PREF = "extensions.screenshots.disabled";
const { interfaces: Ci, utils: Cu } = Components;
--- 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>57.0a1</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<em:type>2</em:type>
- <em:version>19.2.0</em:version>
+ <em:version>23.0.0</em:version>
<em:bootstrap>true</em:bootstrap>
<em:homepageURL>https://screenshots.firefox.com/</em:homepageURL>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
</Description>
</RDF>
--- a/browser/extensions/screenshots/moz.build
+++ b/browser/extensions/screenshots/moz.build
@@ -52,16 +52,20 @@ FINAL_TARGET_FILES.features['screenshots
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["bg"] += [
'webextension/_locales/bg/messages.json'
]
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["bn_BD"] += [
'webextension/_locales/bn_BD/messages.json'
]
+FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["bs"] += [
+ 'webextension/_locales/bs/messages.json'
+]
+
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["ca"] += [
'webextension/_locales/ca/messages.json'
]
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["cak"] += [
'webextension/_locales/cak/messages.json'
]
@@ -124,16 +128,20 @@ FINAL_TARGET_FILES.features['screenshots
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["eu"] += [
'webextension/_locales/eu/messages.json'
]
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["fa"] += [
'webextension/_locales/fa/messages.json'
]
+FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["ff"] += [
+ 'webextension/_locales/ff/messages.json'
+]
+
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["fi"] += [
'webextension/_locales/fi/messages.json'
]
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["fr"] += [
'webextension/_locales/fr/messages.json'
]
@@ -196,16 +204,20 @@ FINAL_TARGET_FILES.features['screenshots
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["kk"] += [
'webextension/_locales/kk/messages.json'
]
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["km"] += [
'webextension/_locales/km/messages.json'
]
+FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["kn"] += [
+ 'webextension/_locales/kn/messages.json'
+]
+
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["ko"] += [
'webextension/_locales/ko/messages.json'
]
FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["lij"] += [
'webextension/_locales/lij/messages.json'
]
--- a/browser/extensions/screenshots/test/browser/.eslintrc.yml
+++ b/browser/extensions/screenshots/test/browser/.eslintrc.yml
@@ -1,10 +1,5 @@
env:
node: true
-# TODO: re-enable
-#extends:
-# - plugin:mozilla/browser-test
-
-rules:
- no-unused-vars: off
- no-undef: off
+extends:
+ - plugin:mozilla/browser-test
--- a/browser/extensions/screenshots/test/browser/browser_screenshots_ui_check.js
+++ b/browser/extensions/screenshots/test/browser/browser_screenshots_ui_check.js
@@ -1,9 +1,8 @@
-/* eslint disable */
"use strict";
const BUTTON_ID = "pageAction-panel-screenshots";
function checkElements(expectPresent, l) {
for (let id of l) {
is(!!document.getElementById(id), expectPresent, "element " + id + (expectPresent ? " is" : " is not") + " present");
}
--- a/browser/extensions/screenshots/webextension/background/auth.js
+++ b/browser/extensions/screenshots/webextension/background/auth.js
@@ -195,20 +195,27 @@ this.auth = (function() {
return browser.storage.local.set({registrationInfo}).then(() => {
return true;
});
});
};
communication.register("getAuthInfo", (sender, ownershipCheck) => {
return registrationInfoFetched.then(() => {
+ return exports.authHeaders();
+ }).then((authHeaders) => {
let info = registrationInfo;
if (info.registered) {
return login({ownershipCheck}).then((result) => {
- return {isOwner: result && result.isOwner, deviceId: registrationInfo.deviceId};
+ return {
+ isOwner: result && result.isOwner,
+ deviceId: registrationInfo.deviceId,
+ authHeaders
+ };
});
}
+ info = Object.assign({authHeaders}, info);
return info;
});
});
return exports;
})();
--- a/browser/extensions/screenshots/webextension/background/selectorLoader.js
+++ b/browser/extensions/screenshots/webextension/background/selectorLoader.js
@@ -82,17 +82,17 @@ this.selectorLoader = (function() {
};
function executeModules(tabId, scripts) {
let lastPromise = Promise.resolve(null);
scripts.forEach((file) => {
lastPromise = lastPromise.then(() => {
return browser.tabs.executeScript(tabId, {
file,
- runAt: "document_end"
+ runAt: "document_start"
}).catch((error) => {
log.error("error in script:", file, error);
error.scriptName = file;
throw error;
});
});
});
return lastPromise.then(() => {
--- a/browser/extensions/screenshots/webextension/background/takeshot.js
+++ b/browser/extensions/screenshots/webextension/background/takeshot.js
@@ -147,17 +147,17 @@ this.takeshot = (function() {
body.push(`Content-Disposition: form-data; name="${fileField}"; filename="${fileFilename}"`);
body.push(`Content-Type: ${blob.type}`);
body.push("");
body.push("");
body = body.join("\r\n");
let enc = new TextEncoder("utf-8");
body = enc.encode(body);
body = concatBuffers(body.buffer, blobAsBuffer);
- let tail = "\r\n" + "--" + boundary + "--";
+ let tail = `\r\n--${boundary}--`;
tail = enc.encode(tail);
body = concatBuffers(body, tail.buffer);
return {
"content-type": `multipart/form-data; boundary=${boundary}`,
body
};
});
}
@@ -166,22 +166,22 @@ this.takeshot = (function() {
let headers;
return auth.authHeaders().then((_headers) => {
headers = _headers;
if (blob) {
return createMultipart(
{shot: JSON.stringify(shot.asJson())},
"blob", "screenshot.png", blob
);
- } else {
- return {
- "content-type": "application/json",
- body: JSON.stringify(shot.asJson())
- };
}
+ return {
+ "content-type": "application/json",
+ body: JSON.stringify(shot.asJson())
+ };
+
}).then((submission) => {
headers["content-type"] = submission["content-type"];
sendEvent("upload", "started", {eventValue: Math.floor(submission.body.length / 1000)});
return fetch(shot.jsonUrl, {
method: "PUT",
mode: "cors",
headers,
body: submission.body
--- a/browser/extensions/screenshots/webextension/blobConverters.js
+++ b/browser/extensions/screenshots/webextension/blobConverters.js
@@ -1,9 +1,9 @@
-this.blobConverters = (function () {
+this.blobConverters = (function() {
let exports = {};
exports.dataUrlToBlob = function(url) {
const binary = atob(url.split(',', 2)[1]);
let contentType = exports.getTypeFromDataUrl(url);
if (contentType != "image/png" && contentType != "image/jpeg") {
contentType = "image/png";
}
--- a/browser/extensions/screenshots/webextension/build/inlineSelectionCss.js
+++ b/browser/extensions/screenshots/webextension/build/inlineSelectionCss.js
@@ -252,16 +252,19 @@ window.inlineSelectionCss = `
.hover-highlight::before {
border: 2px dashed rgba(255, 255, 255, 0.4);
bottom: 0;
content: "";
left: 0;
position: absolute;
right: 0;
top: 0; }
+ body.hcm .hover-highlight {
+ background-color: white;
+ opacity: 0.2; }
.mover-target.direction-topLeft {
cursor: nwse-resize;
height: 60px;
left: -30px;
top: -30px;
width: 60px; }
@@ -353,38 +356,47 @@ window.inlineSelectionCss = `
.direction-bottom .mover,
.direction-bottomLeft .mover {
bottom: -1px; }
.bghighlight {
background-color: rgba(0, 0, 0, 0.7);
position: absolute;
z-index: 9999999999; }
+ body.hcm .bghighlight {
+ background-color: black;
+ opacity: 0.7; }
.preview-overlay {
align-items: center;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
height: 100%;
justify-content: center;
left: 0;
margin: 0;
padding: 0;
position: fixed;
top: 0;
width: 100%;
z-index: 9999999999; }
+ body.hcm .preview-overlay {
+ background-color: black;
+ opacity: 0.7; }
.highlight {
border-radius: 2px;
border: 2px dashed rgba(255, 255, 255, 0.8);
box-sizing: border-box;
cursor: move;
position: absolute;
z-index: 9999999999; }
+ body.hcm .highlight {
+ border: 2px dashed white;
+ opacity: 1.0; }
.highlight-buttons {
display: flex;
align-items: center;
justify-content: center;
bottom: -55px;
position: absolute;
z-index: 6; }
--- a/browser/extensions/screenshots/webextension/build/onboardingCss.js
+++ b/browser/extensions/screenshots/webextension/build/onboardingCss.js
@@ -252,16 +252,22 @@ body {
background-size: 24px 24px; }
#next {
background-image: url("MOZ_EXTENSION/icons/back.svg");
transform: rotate(180deg); }
.active-slide-1 #next {
background-image: url("MOZ_EXTENSION/icons/back-highlight.svg"); }
+[dir='rtl'] #next {
+ transform: rotate(0deg); }
+
+[dir='rtl'] #prev {
+ transform: rotate(180deg); }
+
#skip {
background: none;
border: 0;
color: #fff;
font-size: 16px;
left: 50%;
margin-left: -330px;
margin-top: 257px;
--- a/browser/extensions/screenshots/webextension/build/shot.js
+++ b/browser/extensions/screenshots/webextension/build/shot.js
@@ -24,17 +24,17 @@ function isUrl(url) {
return true;
}
if ((/^chrome:.{0,8000}/i).test(url)) {
return true;
}
if ((/^view-source:/i).test(url)) {
return isUrl(url.substr("view-source:".length));
}
- return (/^https?:\/\/[a-z0-9.-]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
+ return (/^https?:\/\/[a-z0-9.-_]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
}
function isValidClipImageUrl(url) {
return isUrl(url) && !(url.indexOf(')') > -1);
}
function assertUrl(url) {
if (!url) {
@@ -43,17 +43,17 @@ function assertUrl(url) {
if (!isUrl(url)) {
let exc = new Error("Not a URL");
exc.scheme = url.split(":")[0];
throw exc;
}
}
function isSecureWebUri(url) {
- return (/^https?:\/\/[a-z0-9.-]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
+ return (/^https?:\/\/[a-z0-9.-_]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
}
function assertOrigin(url) {
assertUrl(url);
if (url.search(/^https?:/i) != -1) {
let match = (/^https?:\/\/[^/:]{1,4000}\/?$/i).exec(url);
if (!match) {
throw new Error("Bad origin, might include path");
@@ -124,17 +124,17 @@ function resolveUrl(base, url) {
return url;
}
if (url.indexOf("//") === 0) {
// Protocol-relative URL
return (/^https?:/i).exec(base)[0] + url;
}
if (url.indexOf("/") === 0) {
// Domain-relative URL
- return (/^https?:\/\/[a-z0-9.-]{1,4000}/i).exec(base)[0] + url;
+ return (/^https?:\/\/[a-z0-9.-_]{1,4000}/i).exec(base)[0] + url;
}
// Otherwise, a full relative URL
while (url.indexOf("./") === 0) {
url = url.substr(2);
}
if (!base) {
// It's not an absolute URL, and we don't have a base URL, so we have
// to throw away the URL
@@ -204,17 +204,17 @@ function makeRandomId() {
}
return id;
}
class AbstractShot {
constructor(backend, id, attrs) {
attrs = attrs || {};
- assert((/^[a-zA-Z0-9]{1,4000}\/[a-z0-9.-]{1,4000}$/).test(id), "Bad ID (should be alphanumeric):", JSON.stringify(id));
+ assert((/^[a-zA-Z0-9]{1,4000}\/[a-z0-9.-_]{1,4000}$/).test(id), "Bad ID (should be alphanumeric):", JSON.stringify(id));
this._backend = backend;
this._id = id;
this.origin = attrs.origin || null;
this.fullUrl = attrs.fullUrl || null;
if ((!attrs.fullUrl) && attrs.url) {
console.warn("Received deprecated attribute .url");
this.fullUrl = attrs.url;
}
@@ -360,17 +360,18 @@ class AbstractShot {
assertOrigin(val);
}
this._origin = val || undefined;
}
get filename() {
let filenameTitle = this.title;
let date = new Date(this.createdDate);
- filenameTitle = filenameTitle.replace(/[:\\<>/!@&?"*.|\n\r\t]/g, " ");
+ // eslint-disable-next-line no-control-regex
+ filenameTitle = filenameTitle.replace(/[:\\<>/!@&?"*.|\x00-\x1F]/g, " ");
filenameTitle = filenameTitle.replace(/\s{1,4000}/g, " ");
let clipFilename = `Screenshot-${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${filenameTitle}`;
const clipFilenameBytesSize = clipFilename.length * 2; // JS STrings are UTF-16
if (clipFilenameBytesSize > 251) { // 255 bytes (Usual filesystems max) - 4 for the ".png" file extension string
const excedingchars = (clipFilenameBytesSize - 246) / 2; // 251 - 5 for ellipsis "[...]"
clipFilename = clipFilename.substring(0, clipFilename.length - excedingchars);
clipFilename = clipFilename + '[...]';
}
--- a/browser/extensions/screenshots/webextension/domainFromUrl.js
+++ b/browser/extensions/screenshots/webextension/domainFromUrl.js
@@ -9,20 +9,20 @@ this.domainFromUrl = (function() {
return function urlDomainForId(location) { // eslint-disable-line no-unused-vars
let domain = location.hostname;
if (!domain) {
domain = location.origin.split(":")[0];
if (!domain) {
domain = "unknown";
}
}
- if (domain.search(/^[a-z0-9.-]{1,1000}$/i) === -1) {
+ if (domain.search(/^[a-z0-9.-_]{1,1000}$/i) === -1) {
// Probably a unicode domain; we could use punycode but it wouldn't decode
// well in the URL anyway. Instead we'll punt.
- domain = domain.replace(/[^a-z0-9.-]/ig, "");
+ domain = domain.replace(/[^a-z0-9.-_]/ig, "");
if (!domain) {
domain = "site";
}
}
return domain;
};
})();
--- 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": "19.2.0",
+ "version": "23.0.0",
"description": "__MSG_addonDescription__",
"author": "__MSG_addonAuthorsList__",
"homepage_url": "https://github.com/mozilla-services/screenshots",
"applications": {
"gecko": {
"id": "screenshots@mozilla.org",
"strict_min_version": "57.0a1"
}
--- a/browser/extensions/screenshots/webextension/selector/ui.js
+++ b/browser/extensions/screenshots/webextension/selector/ui.js
@@ -71,16 +71,31 @@ this.ui = (function() { // eslint-disabl
let els = doc.querySelectorAll("[data-l10n-id]");
for (let el of els) {
let id = el.getAttribute("data-l10n-id");
let text = browser.i18n.getMessage(id);
el.textContent = text;
}
}
+ function highContrastCheck(win) {
+ let result, doc, el;
+ doc = win.document;
+ el = doc.createElement("div");
+ el.style.backgroundImage = "url('#')";
+ el.style.display = "none";
+ doc.body.appendChild(el);
+ let computed = win.getComputedStyle(el);
+ // When Windows is in High Contrast mode, Firefox replaces background
+ // image URLs with the string "none".
+ result = computed && computed.backgroundImage === "none";
+ doc.body.removeChild(el);
+ return result;
+ }
+
function initializeIframe() {
let el = document.createElement("iframe");
el.src = browser.extension.getURL("blank.html");
el.style.zIndex = "99999999999";
el.style.border = "none";
el.style.top = "0";
el.style.left = "0";
el.style.margin = "0";
@@ -135,16 +150,19 @@ this.ui = (function() { // eslint-disabl
hide() {
this.element.style.display = "none";
this.stopSizeWatch();
},
unhide() {
this.updateElementSize();
this.element.style.display = "";
+ if (highContrastCheck(this.element.contentWindow)) {
+ this.element.contentDocument.body.classList.add("hcm");
+ }
this.initSizeWatch();
this.element.focus();
},
updateElementSize(force) {
// Note: if someone sizes down the page, then the iframe will keep the
// document from naturally shrinking. We use force to temporarily hide
// the element so that we can tell if the document shrinks
@@ -285,16 +303,19 @@ this.ui = (function() { // eslint-disabl
this.element.style.display = "none";
}
},
unhide() {
window.addEventListener("scroll", watchFunction(assertIsTrusted(this.onScroll)));
window.addEventListener("resize", this.onResize, true);
this.element.style.display = "";
+ if (highContrastCheck(this.element.contentWindow)) {
+ this.element.contentDocument.body.classList.add("hcm");
+ }
this.element.focus();
},
onScroll() {
exports.HoverBox.hide();
},
getElementFromPoint(x, y) {
--- a/browser/extensions/screenshots/webextension/selector/uicontrol.js
+++ b/browser/extensions/screenshots/webextension/selector/uicontrol.js
@@ -156,45 +156,46 @@ this.uicontrol = (function() {
window.scrollX, window.scrollY,
window.scrollX + window.innerWidth, window.scrollY + window.innerHeight);
captureType = 'visible';
setState("previewing");
},
onClickFullPage: () => {
sendEvent("capture-full-page", "selection-button");
captureType = "fullPage";
- let width = Math.max(
- document.body.clientWidth,
- document.documentElement.clientWidth,
- document.body.scrollWidth,
- document.documentElement.scrollWidth);
+ let width = getDocumentWidth();
if (width > MAX_PAGE_WIDTH) {
captureType = "fullPageTruncated";
}
width = Math.min(width, MAX_PAGE_WIDTH);
- let height = Math.max(
- document.body.clientHeight,
- document.documentElement.clientHeight,
- document.body.scrollHeight,
- document.documentElement.scrollHeight);
+ let height = getDocumentHeight();
if (height > MAX_PAGE_HEIGHT) {
captureType = "fullPageTruncated";
}
height = Math.min(height, MAX_PAGE_HEIGHT);
selectedPos = new Selection(
0, 0,
width, height);
setState("previewing");
},
onSavePreview: () => {
sendEvent(`save-${captureType.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}`, "save-preview-button");
shooter.takeShot(captureType, selectedPos, dataUrl);
},
onDownloadPreview: () => {
sendEvent(`download-${captureType.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}`, "download-preview-button");
+
+ // Downloaded shots don't have dimension limits
+ if (captureType === "fullPageTruncated") {
+ captureType = "fullPage";
+ selectedPos = new Selection(
+ 0, 0,
+ getDocumentWidth(), getDocumentHeight());
+ }
+
shooter.downloadShot(selectedPos);
}
};
/** Holds all the objects that handle events for each state: */
let stateHandlers = {};
function getState() {
@@ -456,16 +457,21 @@ this.uicontrol = (function() {
let attemptExtend = false;
let node = el;
while (node) {
rect = Selection.getBoundingClientRect(node);
if (!rect) {
rect = lastRect;
break;
}
+ if (rect.width < MIN_DETECT_WIDTH || rect.height < MIN_DETECT_HEIGHT) {
+ // Avoid infinite loop for elements with zero or nearly zero height,
+ // like non-clearfixed float parents with or without borders.
+ break;
+ }
if (rect.width > MAX_DETECT_WIDTH || rect.height > MAX_DETECT_HEIGHT) {
// Then the last rectangle is better
rect = lastRect;
attemptExtend = true;
break;
}
if (rect.width >= MIN_DETECT_WIDTH && rect.height >= MIN_DETECT_HEIGHT) {
if (!doNotAutoselectTags[node.tagName]) {
@@ -811,38 +817,42 @@ this.uicontrol = (function() {
stateHandlers.cancel = {
start() {
ui.iframe.hide();
ui.Box.remove();
}
};
- let documentWidth = Math.max(
- document.body && document.body.clientWidth,
- document.documentElement.clientWidth,
- document.body && document.body.scrollWidth,
- document.documentElement.scrollWidth);
- let documentHeight = Math.max(
- document.body && document.body.clientHeight,
- document.documentElement.clientHeight,
- document.body && document.body.scrollHeight,
- document.documentElement.scrollHeight);
+ function getDocumentWidth() {
+ return Math.max(
+ document.body && document.body.clientWidth,
+ document.documentElement.clientWidth,
+ document.body && document.body.scrollWidth,
+ document.documentElement.scrollWidth);
+ }
+ function getDocumentHeight() {
+ return Math.max(
+ document.body && document.body.clientHeight,
+ document.documentElement.clientHeight,
+ document.body && document.body.scrollHeight,
+ document.documentElement.scrollHeight);
+ }
function scrollIfByEdge(pageX, pageY) {
let top = window.scrollY;
let bottom = top + window.innerHeight;
let left = window.scrollX;
let right = left + window.innerWidth;
- if (pageY + SCROLL_BY_EDGE >= bottom && bottom < documentHeight) {
+ if (pageY + SCROLL_BY_EDGE >= bottom && bottom < getDocumentHeight()) {
window.scrollBy(0, SCROLL_BY_EDGE);
} else if (pageY - SCROLL_BY_EDGE <= top) {
window.scrollBy(0, -SCROLL_BY_EDGE);
}
- if (pageX + SCROLL_BY_EDGE >= right && right < documentWidth) {
+ if (pageX + SCROLL_BY_EDGE >= right && right < getDocumentWidth()) {
window.scrollBy(SCROLL_BY_EDGE, 0);
} else if (pageX - SCROLL_BY_EDGE <= left) {
window.scrollBy(-SCROLL_BY_EDGE, 0);
}
}
/** *********************************************
* Selection communication
--- a/browser/extensions/screenshots/webextension/sitehelper.js
+++ b/browser/extensions/screenshots/webextension/sitehelper.js
@@ -1,37 +1,75 @@
/* globals catcher, callBackground */
/** This is a content script added to all screenshots.firefox.com pages, and allows the site to
communicate with the add-on */
"use strict";
this.sitehelper = (function() {
+ let ContentXMLHttpRequest = XMLHttpRequest;
+ // This gives us the content's copy of XMLHttpRequest, instead of the wrapped
+ // copy that this content script gets:
+ if (location.origin === "https://screenshots.firefox.com" ||
+ location.origin === "http://localhost:10080") {
+ // Note http://localhost:10080 is the default development server
+ // This code should always run, unless this content script is
+ // somehow run in a bad/malicious context
+ ContentXMLHttpRequest = window.wrappedJSObject.XMLHttpRequest;
+ }
+
catcher.registerHandler((errorObj) => {
callBackground("reportError", errorObj);
});
function sendCustomEvent(name, detail) {
if (typeof detail == "object") {
// Note sending an object can lead to security problems, while a string
// is safe to transfer:
detail = JSON.stringify(detail);
}
document.dispatchEvent(new CustomEvent(name, {detail}));
}
+ /** Set the cookie, even if third-party cookies are disabled in this browser
+ (when they are disabled, login from the background page won't set cookies) */
+ function sendBackupCookieRequest(authHeaders) {
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1295660
+ // This bug would allow us to access window.content.XMLHttpRequest, and get
+ // a safer (not overridable by content) version of the object.
+
+ // This is a very minimal attempt to verify that the XMLHttpRequest object we got
+ // is legitimate. It is not a good test.
+ if (Object.toString.apply(ContentXMLHttpRequest) != "function XMLHttpRequest() {\n [native code]\n}") {
+ console.warn("Insecure copy of XMLHttpRequest");
+ return;
+ }
+ let req = new ContentXMLHttpRequest();
+ req.open("POST", "/api/set-login-cookie");
+ for (let name in authHeaders) {
+ req.setRequestHeader(name, authHeaders[name]);
+ }
+ req.send("");
+ req.onload = () => {
+ if (req.status != 200) {
+ console.warn("Attempt to set Screenshots cookie via /api/set-login-cookie failed:", req.status, req.statusText, req.responseText);
+ }
+ };
+ }
+
document.addEventListener("delete-everything", catcher.watchFunction((event) => {
// FIXME: reset some data in the add-on
}, false));
document.addEventListener("request-login", catcher.watchFunction((event) => {
let shotId = event.detail;
catcher.watchPromise(callBackground("getAuthInfo", shotId || null).then((info) => {
+ sendBackupCookieRequest(info.authHeaders);
sendCustomEvent("login-successful", {deviceId: info.deviceId, isOwner: info.isOwner});
}));
}));
document.addEventListener("request-onboarding", catcher.watchFunction((event) => {
callBackground("requestOnboarding");
}));