/* 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/. */ /* globals catcher, callBackground, content */ /** 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() { // This gives us the content's copy of XMLHttpRequest, instead of the wrapped // copy that this content script gets: const ContentXMLHttpRequest = content.XMLHttpRequest; catcher.registerHandler((errorObj) => { callBackground("reportError", errorObj); }); const capabilities = {}; function registerListener(name, func) { capabilities[name] = name; document.addEventListener(name, func); } 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; } const req = new ContentXMLHttpRequest(); req.open("POST", "/api/set-login-cookie"); for (const 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); } }; } registerListener("delete-everything", catcher.watchFunction((event) => { // FIXME: reset some data in the add-on }, false)); registerListener("request-login", catcher.watchFunction((event) => { const shotId = event.detail; catcher.watchPromise(callBackground("getAuthInfo", shotId || null).then((info) => { if (info) { sendBackupCookieRequest(info.authHeaders); sendCustomEvent("login-successful", {deviceId: info.deviceId, accountId: info.accountId, isOwner: info.isOwner, backupCookieRequest: true}); } })); })); registerListener("copy-to-clipboard", catcher.watchFunction(event => { catcher.watchPromise(callBackground("copyShotToClipboard", event.detail)); })); registerListener("show-notification", catcher.watchFunction(event => { catcher.watchPromise(callBackground("showNotification", event.detail)); })); // Depending on the script loading order, the site might get the addon-present event, // but probably won't - instead the site will ask for that event after it has loaded registerListener("request-addon-present", catcher.watchFunction(() => { sendCustomEvent("addon-present", capabilities); })); sendCustomEvent("addon-present", capabilities); })(); null;