Bug 1454029 - Export Screenshots 32.1.0 to Firefox (code excluding translations and Raven update); r=_6a68
MozReview-Commit-ID: GA75ZjrWBjm --HG-- extra : rebase_source : b4e282a57eb9b80f80a7a07dea171fe87a7ebc46
|
@ -109,7 +109,7 @@ const LibraryButton = {
|
|||
const {nextSibling} = libraryViewInsertionPoint;
|
||||
const item = win.document.createElement("toolbarbutton");
|
||||
item.className = "subviewbutton subviewbutton-iconic";
|
||||
item.addEventListener("command", () => win.openTrustedLinkIn(this.PAGE_TO_OPEN, "tab"));
|
||||
item.addEventListener("command", () => win.openWebLinkIn(this.PAGE_TO_OPEN, "tab"));
|
||||
item.id = this.ITEM_ID;
|
||||
const iconURL = this.ICON_URL;
|
||||
item.setAttribute("image", iconURL);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:type>2</em:type>
|
||||
<em:version>30.1.0</em:version>
|
||||
<em:version>32.1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:homepageURL>https://screenshots.firefox.com/</em:homepageURL>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -15,22 +15,22 @@ async function togglePageActionPanel() {
|
|||
}
|
||||
|
||||
function promiseOpenPageActionPanel() {
|
||||
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
const dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
return BrowserTestUtils.waitForCondition(() => {
|
||||
// Wait for the main page action button to become visible. It's hidden for
|
||||
// some URIs, so depending on when this is called, it may not yet be quite
|
||||
// visible. It's up to the caller to make sure it will be visible.
|
||||
info("Waiting for main page action button to have non-0 size");
|
||||
let bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
|
||||
const bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
|
||||
return bounds.width > 0 && bounds.height > 0;
|
||||
}).then(() => {
|
||||
// Wait for the panel to become open, by clicking the button if necessary.
|
||||
info("Waiting for main page action panel to be open");
|
||||
if (BrowserPageActions.panelNode.state == "open") {
|
||||
if (BrowserPageActions.panelNode.state === "open") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
let shownPromise = promisePageActionPanelEvent("popupshown");
|
||||
const shownPromise = promisePageActionPanelEvent("popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
|
||||
return shownPromise;
|
||||
}).then(() => {
|
||||
|
@ -41,9 +41,9 @@ function promiseOpenPageActionPanel() {
|
|||
|
||||
function promisePageActionPanelEvent(eventType) {
|
||||
return new Promise(resolve => {
|
||||
let panel = BrowserPageActions.panelNode;
|
||||
if ((eventType == "popupshown" && panel.state == "open") ||
|
||||
(eventType == "popuphidden" && panel.state == "closed")) {
|
||||
const panel = BrowserPageActions.panelNode;
|
||||
if ((eventType === "popupshown" && panel.state === "open") ||
|
||||
(eventType === "popuphidden" && panel.state === "closed")) {
|
||||
executeSoon(resolve);
|
||||
return;
|
||||
}
|
||||
|
@ -55,12 +55,12 @@ function promisePageActionPanelEvent(eventType) {
|
|||
|
||||
function promisePageActionViewChildrenVisible(panelViewNode) {
|
||||
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
|
||||
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
const dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
return BrowserTestUtils.waitForCondition(() => {
|
||||
let bodyNode = panelViewNode.firstChild;
|
||||
for (let childNode of bodyNode.childNodes) {
|
||||
let bounds = dwu.getBoundsWithoutFlushing(childNode);
|
||||
const bodyNode = panelViewNode.firstChild;
|
||||
for (const childNode of bodyNode.childNodes) {
|
||||
const bounds = dwu.getBoundsWithoutFlushing(childNode);
|
||||
if (bounds.width > 0 && bounds.height > 0) {
|
||||
return true;
|
||||
}
|
||||
|
@ -78,8 +78,8 @@ add_task(async function() {
|
|||
|
||||
// Toggle the page action panel to get it to rebuild itself. An actionable
|
||||
// page must be opened first.
|
||||
let url = "http://example.com/browser_screenshots_ui_check";
|
||||
await BrowserTestUtils.withNewTab(url, async () => {
|
||||
const url = "http://example.com/browser_screenshots_ui_check";
|
||||
await BrowserTestUtils.withNewTab(url, async () => { // eslint-disable-line space-before-function-paren
|
||||
await togglePageActionPanel();
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
|
|
|
@ -11,11 +11,9 @@ this.main = (function() {
|
|||
const manifest = browser.runtime.getManifest();
|
||||
let backend;
|
||||
|
||||
let hasSeenOnboarding;
|
||||
|
||||
browser.storage.local.get(["hasSeenOnboarding"]).then((result) => {
|
||||
hasSeenOnboarding = !!result.hasSeenOnboarding;
|
||||
if (!hasSeenOnboarding) {
|
||||
let hasSeenOnboarding = browser.storage.local.get(["hasSeenOnboarding"]).then((result) => {
|
||||
const onboarded = !!result.hasSeenOnboarding;
|
||||
if (!onboarded) {
|
||||
setIconActive(false, null);
|
||||
// Note that the branded name 'Firefox Screenshots' is not localized:
|
||||
startBackground.photonPageActionPort.postMessage({
|
||||
|
@ -23,6 +21,8 @@ this.main = (function() {
|
|||
title: "Firefox Screenshots"
|
||||
});
|
||||
}
|
||||
hasSeenOnboarding = Promise.resolve(onboarded);
|
||||
return hasSeenOnboarding;
|
||||
}).catch((error) => {
|
||||
log.error("Error getting hasSeenOnboarding:", error);
|
||||
});
|
||||
|
@ -93,34 +93,36 @@ this.main = (function() {
|
|||
|
||||
// This is called by startBackground.js, directly in response to clicks on the Photon page action
|
||||
exports.onClicked = catcher.watchFunction((tab) => {
|
||||
if (shouldOpenMyShots(tab.url)) {
|
||||
if (!hasSeenOnboarding) {
|
||||
catcher.watchPromise(analytics.refreshTelemetryPref().then(() => {
|
||||
sendEvent("goto-onboarding", "selection-button", {incognito: tab.incognito});
|
||||
return forceOnboarding();
|
||||
}));
|
||||
return;
|
||||
}
|
||||
catcher.watchPromise(analytics.refreshTelemetryPref().then(() => {
|
||||
sendEvent("goto-myshots", "about-newtab", {incognito: tab.incognito});
|
||||
}));
|
||||
catcher.watchPromise(
|
||||
auth.authHeaders()
|
||||
.then(() => browser.tabs.update({url: backend + "/shots"})));
|
||||
} else {
|
||||
catcher.watchPromise(
|
||||
toggleSelector(tab)
|
||||
.then(active => {
|
||||
const event = active ? "start-shot" : "cancel-shot";
|
||||
sendEvent(event, "toolbar-button", {incognito: tab.incognito});
|
||||
}, (error) => {
|
||||
if ((!hasSeenOnboarding) && error.popupMessage === "UNSHOOTABLE_PAGE") {
|
||||
sendEvent("goto-onboarding", "selection-button", {incognito: tab.incognito});
|
||||
return forceOnboarding();
|
||||
}
|
||||
throw error;
|
||||
catcher.watchPromise(hasSeenOnboarding.then(onboarded => {
|
||||
if (shouldOpenMyShots(tab.url)) {
|
||||
if (!onboarded) {
|
||||
catcher.watchPromise(analytics.refreshTelemetryPref().then(() => {
|
||||
sendEvent("goto-onboarding", "selection-button", {incognito: tab.incognito});
|
||||
return forceOnboarding();
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
catcher.watchPromise(analytics.refreshTelemetryPref().then(() => {
|
||||
sendEvent("goto-myshots", "about-newtab", {incognito: tab.incognito});
|
||||
}));
|
||||
catcher.watchPromise(
|
||||
auth.authHeaders()
|
||||
.then(() => browser.tabs.update({url: backend + "/shots"})));
|
||||
} else {
|
||||
catcher.watchPromise(
|
||||
toggleSelector(tab)
|
||||
.then(active => {
|
||||
const event = active ? "start-shot" : "cancel-shot";
|
||||
sendEvent(event, "toolbar-button", {incognito: tab.incognito});
|
||||
}, (error) => {
|
||||
if ((!onboarded) && error.popupMessage === "UNSHOOTABLE_PAGE") {
|
||||
sendEvent("goto-onboarding", "selection-button", {incognito: tab.incognito});
|
||||
return forceOnboarding();
|
||||
}
|
||||
throw error;
|
||||
}));
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
function forceOnboarding() {
|
||||
|
@ -273,8 +275,8 @@ this.main = (function() {
|
|||
});
|
||||
|
||||
communication.register("hasSeenOnboarding", () => {
|
||||
hasSeenOnboarding = true;
|
||||
catcher.watchPromise(browser.storage.local.set({hasSeenOnboarding}));
|
||||
hasSeenOnboarding = Promise.resolve(true);
|
||||
catcher.watchPromise(browser.storage.local.set({hasSeenOnboarding: true}));
|
||||
setIconActive(false, null);
|
||||
startBackground.photonPageActionPort.postMessage({
|
||||
type: "setProperties",
|
||||
|
|
|
@ -66,24 +66,26 @@ this.selectorLoader = (function() {
|
|||
const loadingTabs = new Set();
|
||||
|
||||
exports.loadModules = function(tabId, hasSeenOnboarding) {
|
||||
loadingTabs.add(tabId);
|
||||
let promise = downloadOnlyCheck(tabId);
|
||||
if (hasSeenOnboarding) {
|
||||
promise = promise.then(() => {
|
||||
return executeModules(tabId, standardScripts.concat(selectorScripts));
|
||||
catcher.watchPromise(hasSeenOnboarding.then(onboarded => {
|
||||
loadingTabs.add(tabId);
|
||||
let promise = downloadOnlyCheck(tabId);
|
||||
if (onboarded) {
|
||||
promise = promise.then(() => {
|
||||
return executeModules(tabId, standardScripts.concat(selectorScripts));
|
||||
});
|
||||
} else {
|
||||
promise = promise.then(() => {
|
||||
return executeModules(tabId, standardScripts.concat(onboardingScripts).concat(selectorScripts));
|
||||
});
|
||||
}
|
||||
return promise.then((result) => {
|
||||
loadingTabs.delete(tabId);
|
||||
return result;
|
||||
}, (error) => {
|
||||
loadingTabs.delete(tabId);
|
||||
throw error;
|
||||
});
|
||||
} else {
|
||||
promise = promise.then(() => {
|
||||
return executeModules(tabId, standardScripts.concat(onboardingScripts).concat(selectorScripts));
|
||||
});
|
||||
}
|
||||
return promise.then((result) => {
|
||||
loadingTabs.delete(tabId);
|
||||
return result;
|
||||
}, (error) => {
|
||||
loadingTabs.delete(tabId);
|
||||
throw error;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
// TODO: since bootstrap communication is now required, would this function
|
||||
|
|
|
@ -191,13 +191,14 @@ this.takeshot = (function() {
|
|||
files.push({fieldName: "thumbnail", filename: "thumbnail.png", blob: thumbnail});
|
||||
}
|
||||
return createMultipart(
|
||||
{shot: JSON.stringify(shot.asJson())},
|
||||
{shot: JSON.stringify(shot)},
|
||||
|
||||
files
|
||||
);
|
||||
}
|
||||
return {
|
||||
"content-type": "application/json",
|
||||
body: JSON.stringify(shot.asJson())
|
||||
body: JSON.stringify(shot)
|
||||
};
|
||||
|
||||
}).then((submission) => {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
this.shot = (function () {let exports={}; // Note: in this library we can't use any "system" dependencies because this can be used from multiple
|
||||
// environments
|
||||
|
||||
const isNode = typeof process !== "undefined" && Object.prototype.toString.call(process) === "[object process]";
|
||||
const URL = (isNode && require("url").URL) || window.URL;
|
||||
|
||||
/** Throws an error if the condition isn't true. Any extra arguments after the condition
|
||||
are used as console.error() arguments. */
|
||||
function assert(condition, ...args) {
|
||||
|
@ -13,23 +16,17 @@ function assert(condition, ...args) {
|
|||
|
||||
/** True if `url` is a valid URL */
|
||||
function isUrl(url) {
|
||||
// FIXME: this is rather naive, obviously
|
||||
if ((/^about:.{1,8000}$/i).test(url)) {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
|
||||
if (parsed.protocol === "view-source:") {
|
||||
return isUrl(url.substr("view-source:".length));
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
if ((/^file:\/.{0,8000}$/i).test(url)) {
|
||||
return true;
|
||||
}
|
||||
if ((/^data:.*$/i).test(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);
|
||||
}
|
||||
|
||||
function isValidClipImageUrl(url) {
|
||||
|
@ -48,7 +45,7 @@ function assertUrl(url) {
|
|||
}
|
||||
|
||||
function isSecureWebUri(url) {
|
||||
return (/^https?:\/\/[a-z0-9._-]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
|
||||
return isUrl(url) && url.toLowerCase().startsWith("https");
|
||||
}
|
||||
|
||||
function assertOrigin(url) {
|
||||
|
@ -115,39 +112,6 @@ function jsonify(obj, required, optional) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** Resolve url relative to base */
|
||||
function resolveUrl(base, url) {
|
||||
// FIXME: totally ad hoc and probably incorrect, but we can't
|
||||
// use any libraries in this file
|
||||
if (url.search(/^https?:/) !== -1) {
|
||||
// Absolute 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;
|
||||
}
|
||||
// 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
|
||||
return null;
|
||||
}
|
||||
let match = (/.*\//).exec(base)[0];
|
||||
if (match.search(/^https?:\/$/i) === 0) {
|
||||
// Domain without path
|
||||
match = match + "/";
|
||||
}
|
||||
return match + url;
|
||||
}
|
||||
|
||||
/** True if the two objects look alike. Null, undefined, and absent properties
|
||||
are all treated as equivalent. Traverses objects and arrays */
|
||||
function deepEqual(a, b) {
|
||||
|
@ -275,8 +239,8 @@ class AbstractShot {
|
|||
}
|
||||
if (typeof json[attr] === "object" && typeof this[attr] === "object" && this[attr] !== null) {
|
||||
let val = this[attr];
|
||||
if (val.asJson) {
|
||||
val = val.asJson();
|
||||
if (val.toJSON) {
|
||||
val = val.toJSON();
|
||||
}
|
||||
if (!deepEqual(json[attr], val)) {
|
||||
this[attr] = json[attr];
|
||||
|
@ -292,7 +256,7 @@ class AbstractShot {
|
|||
this.delClip(clipId);
|
||||
} else if (!this.getClip(clipId)) {
|
||||
this.setClip(clipId, json.clips[clipId]);
|
||||
} else if (!deepEqual(this.getClip(clipId).asJson(), json.clips[clipId])) {
|
||||
} else if (!deepEqual(this.getClip(clipId).toJSON(), json.clips[clipId])) {
|
||||
this.setClip(clipId, json.clips[clipId]);
|
||||
}
|
||||
}
|
||||
|
@ -301,18 +265,18 @@ class AbstractShot {
|
|||
}
|
||||
|
||||
/** Returns a JSON version of this shot */
|
||||
asJson() {
|
||||
toJSON() {
|
||||
const result = {};
|
||||
for (const attr of this.REGULAR_ATTRS) {
|
||||
let val = this[attr];
|
||||
if (val && val.asJson) {
|
||||
val = val.asJson();
|
||||
if (val && val.toJSON) {
|
||||
val = val.toJSON();
|
||||
}
|
||||
result[attr] = val;
|
||||
}
|
||||
result.clips = {};
|
||||
for (const attr in this._clips) {
|
||||
result.clips[attr] = this._clips[attr].asJson();
|
||||
result.clips[attr] = this._clips[attr].toJSON();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -322,13 +286,13 @@ class AbstractShot {
|
|||
const result = {clips: {}};
|
||||
for (const attr of this.RECALL_ATTRS) {
|
||||
let val = this[attr];
|
||||
if (val && val.asJson) {
|
||||
val = val.asJson();
|
||||
if (val && val.toJSON) {
|
||||
val = val.toJSON();
|
||||
}
|
||||
result[attr] = val;
|
||||
}
|
||||
for (const name of this.clipNames()) {
|
||||
result.clips[name] = this.getClip(name).asJson();
|
||||
result.clips[name] = this.getClip(name).toJSON();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -374,7 +338,8 @@ class AbstractShot {
|
|||
// 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 filenameDate = new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000).toISOString().substring(0, 10);
|
||||
let clipFilename = `Screenshot_${filenameDate} ${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 "[...]"
|
||||
|
@ -497,14 +462,8 @@ class AbstractShot {
|
|||
return this._favicon;
|
||||
}
|
||||
set favicon(val) {
|
||||
// We allow but ignore bad favicon URLs, as they seem somewhat common
|
||||
// We set the favicon with tabs.Tab.faviConUrl, which is a full URL.
|
||||
val = val || null;
|
||||
if (!isUrl(val)) {
|
||||
val = null;
|
||||
}
|
||||
if (val) {
|
||||
val = resolveUrl(this.url, val);
|
||||
}
|
||||
this._favicon = val;
|
||||
}
|
||||
|
||||
|
@ -658,7 +617,7 @@ class _Image {
|
|||
this.alt = json.alt;
|
||||
}
|
||||
|
||||
asJson() {
|
||||
toJSON() {
|
||||
return jsonify(this, ["url"], ["dimensions"]);
|
||||
}
|
||||
}
|
||||
|
@ -689,7 +648,7 @@ class _Clip {
|
|||
return `[Shot Clip id=${this.id} sortOrder=${this.sortOrder} image ${this.image.dimensions.x}x${this.image.dimensions.y}]`;
|
||||
}
|
||||
|
||||
asJson() {
|
||||
toJSON() {
|
||||
return jsonify(this, ["createdDate"], ["sortOrder", "image"]);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ this.clipboard = (function() {
|
|||
element.style.opacity = "0";
|
||||
element.style.width = "1px";
|
||||
element.style.height = "1px";
|
||||
element.style.display = "block";
|
||||
element.addEventListener("load", catcher.watchFunction(() => {
|
||||
try {
|
||||
const doc = element.contentDocument;
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M8 2a4 4 0 0 0-4 4h4V2zm12 0h-4v4h4V2zm8 0v4h4a4 4 0 0 0-4-4zM14 2h-4v4h4V2zm12 0h-4v4h4V2zm2 10h4V8h-4v4zm0 12a4 4 0 0 0 4-4h-4v4zm0-6h4v-4h-4v4zm-.882-4.334a4 4 0 0 0-5.57-.984l-7.67 5.662-3.936-2.76c.031-.193.05-.388.058-.584a4.976 4.976 0 0 0-2-3.978V8H4v2.1a5 5 0 1 0 3.916 8.948l2.484 1.738-2.8 1.964a4.988 4.988 0 1 0 2.3 3.266l17.218-12.35zM5 17.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm0 12a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm10.8-4.858l6.034 4.6a4 4 0 0 0 5.57-.984L19.28 22.2l-3.48 2.442z"/></svg>
|
||||
<svg viewBox="0 0 32 32" width="32" height="32" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M8 2a4 4 0 0 0-4 4h4V2zm12 0h-4v4h4V2zm8 0v4h4a4 4 0 0 0-4-4zM14 2h-4v4h4V2zm12 0h-4v4h4V2zm2 10h4V8h-4v4zm0 12a4 4 0 0 0 4-4h-4v4zm0-6h4v-4h-4v4zm-.882-4.334a4 4 0 0 0-5.57-.984l-7.67 5.662-3.936-2.76c.031-.193.05-.388.058-.584a4.976 4.976 0 0 0-2-3.978V8H4v2.1a5 5 0 1 0 3.916 8.948l2.484 1.738-2.8 1.964a4.988 4.988 0 1 0 2.3 3.266l17.218-12.35zM5 17.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm0 12a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm10.8-4.858l6.034 4.6a4 4 0 0 0 5.57-.984L19.28 22.2l-3.48 2.442z"/></svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 636 B После Ширина: | Высота: | Размер: 657 B |
До Ширина: | Высота: | Размер: 27 KiB После Ширина: | Высота: | Размер: 27 KiB |
До Ширина: | Высота: | Размер: 67 KiB После Ширина: | Высота: | Размер: 40 KiB |
До Ширина: | Высота: | Размер: 57 KiB После Ширина: | Высота: | Размер: 34 KiB |
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Firefox Screenshots",
|
||||
"version": "30.1.0",
|
||||
"version": "32.1.0",
|
||||
"description": "__MSG_addonDescription__",
|
||||
"author": "__MSG_addonAuthorsList__",
|
||||
"homepage_url": "https://github.com/mozilla-services/screenshots",
|
||||
|
@ -30,6 +30,9 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"icons": {
|
||||
"32": "icons/icon-v2.svg"
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
"blank.html",
|
||||
"icons/cancel.svg",
|
||||
|
|
|
@ -24,6 +24,7 @@ this.slides = (function() {
|
|||
iframe.src = browser.extension.getURL("blank.html");
|
||||
iframe.id = "firefox-screenshots-onboarding-iframe";
|
||||
iframe.style.zIndex = "99999999999";
|
||||
iframe.style.display = "block";
|
||||
iframe.style.border = "none";
|
||||
iframe.style.position = "fixed";
|
||||
iframe.style.top = "0";
|
||||
|
|
|
@ -115,7 +115,7 @@ this.shooter = (function() { // eslint-disable-line no-unused-vars
|
|||
}
|
||||
isSaving = null;
|
||||
}, 1000);
|
||||
selectedPos = selectedPos.asJson();
|
||||
selectedPos = selectedPos.toJSON();
|
||||
let captureText = "";
|
||||
if (buildSettings.captureText) {
|
||||
captureText = util.captureEnclosedText(selectedPos);
|
||||
|
@ -152,7 +152,7 @@ this.shooter = (function() { // eslint-disable-line no-unused-vars
|
|||
},
|
||||
selectedPos,
|
||||
shotId: shotObject.id,
|
||||
shot: shotObject.asJson(),
|
||||
shot: shotObject.toJSON(),
|
||||
imageBlob
|
||||
}).then((url) => {
|
||||
return clipboard.copy(url).then((copied) => {
|
||||
|
@ -185,7 +185,7 @@ this.shooter = (function() { // eslint-disable-line no-unused-vars
|
|||
if (!dataUrl) {
|
||||
promise = callBackground(
|
||||
"screenshotPage",
|
||||
selectedPos.asJson(),
|
||||
selectedPos.toJSON(),
|
||||
{
|
||||
scrollX: window.scrollX,
|
||||
scrollY: window.scrollY,
|
||||
|
|
|
@ -173,7 +173,7 @@ this.ui = (function() { // eslint-disable-line no-unused-vars
|
|||
|
||||
unhide() {
|
||||
this.updateElementSize();
|
||||
this.element.style.display = "";
|
||||
this.element.style.display = "block";
|
||||
catcher.watchPromise(callBackground("sendEvent", "internal", "unhide-selection-frame"));
|
||||
if (highContrastCheck(this.element.contentWindow)) {
|
||||
this.element.contentDocument.body.classList.add("hcm");
|
||||
|
@ -222,7 +222,7 @@ this.ui = (function() { // eslint-disable-line no-unused-vars
|
|||
}
|
||||
}
|
||||
if (force && visible) {
|
||||
this.element.style.display = "";
|
||||
this.element.style.display = "block";
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -344,7 +344,7 @@ this.ui = (function() { // eslint-disable-line no-unused-vars
|
|||
unhide() {
|
||||
window.addEventListener("scroll", watchFunction(assertIsTrusted(this.onScroll)));
|
||||
window.addEventListener("resize", this.onResize, true);
|
||||
this.element.style.display = "";
|
||||
this.element.style.display = "block";
|
||||
catcher.watchPromise(callBackground("sendEvent", "internal", "unhide-preselection-frame"));
|
||||
if (highContrastCheck(this.element.contentWindow)) {
|
||||
this.element.contentDocument.body.classList.add("hcm");
|
||||
|
@ -458,7 +458,7 @@ this.ui = (function() { // eslint-disable-line no-unused-vars
|
|||
},
|
||||
|
||||
unhide() {
|
||||
this.element.style.display = "";
|
||||
this.element.style.display = "block";
|
||||
catcher.watchPromise(callBackground("sendEvent", "internal", "unhide-preview-frame"));
|
||||
this.element.focus();
|
||||
},
|
||||
|
@ -635,7 +635,7 @@ this.ui = (function() { // eslint-disable-line no-unused-vars
|
|||
this.bgRight.style.top = `${pos.top}px`;
|
||||
this.bgRight.style.height = `${pos.bottom - pos.top}px`;
|
||||
this.bgRight.style.left = `${pos.right}px`;
|
||||
this.bgRight.style.width = "100%";
|
||||
this.bgRight.style.width = `${document.body.scrollWidth - pos.right}px`;
|
||||
// the download notice is injected into an iframe that matches the document size
|
||||
// in order to reposition it on scroll we need to bind an updated positioning
|
||||
// function to some window events.
|
||||
|
|
|
@ -351,7 +351,7 @@ this.uicontrol = (function() {
|
|||
return new Selection(this.x1, this.y1, this.x2, this.y2);
|
||||
}
|
||||
|
||||
asJson() {
|
||||
toJSON() {
|
||||
return {
|
||||
left: this.left,
|
||||
right: this.right,
|
||||
|
|