mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1741736 - Add overlay to Screenshots component implementation. r=sfoster,fluent-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D132832
This commit is contained in:
parent
468748a0bd
commit
ce3333f502
@ -7,6 +7,14 @@
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ScreenshotsComponentChild"];
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ScreenshotsOverlayChild: "resource:///modules/ScreenshotsOverlayChild.jsm",
|
||||
});
|
||||
|
||||
class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
@ -22,6 +30,13 @@ class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to cancel the screenshot to the parent process
|
||||
*/
|
||||
requestCancelScreenshot() {
|
||||
this.sendAsyncMessage("Screenshots:CancelScreenshot", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves when the document is ready to have an overlay injected into it.
|
||||
*
|
||||
@ -64,16 +79,35 @@ class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
* @returns {Boolean} true when document is ready and the overlay is shown
|
||||
* otherwise false
|
||||
*/
|
||||
async startScreenshotsOverlay(details = {}) {
|
||||
async startScreenshotsOverlay() {
|
||||
try {
|
||||
await this.documentIsReady();
|
||||
} catch (ex) {
|
||||
console.warn(`ScreenshotsComponentChild: ${ex.message}`);
|
||||
return false;
|
||||
}
|
||||
await this.documentIsReady();
|
||||
let overlay =
|
||||
this._overlay ||
|
||||
(this._overlay = new ScreenshotsOverlayChild.AnonymousContentOverlay(
|
||||
this.document,
|
||||
this
|
||||
));
|
||||
this.document.addEventListener("keydown", this.handler);
|
||||
overlay.initialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to handle the escape key press to cancel screenshots overlay
|
||||
* @param event The keydown event
|
||||
*/
|
||||
handler = event => {
|
||||
if (event.key === "Escape") {
|
||||
this.requestCancelScreenshot();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the screenshots overlay.
|
||||
*
|
||||
@ -81,7 +115,8 @@ class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||
* true when the overlay has been removed otherwise false
|
||||
*/
|
||||
endScreenshotsOverlay() {
|
||||
// this function will be implemented soon
|
||||
this.document.removeEventListener("keydown", this.handler);
|
||||
this._overlay?.tearDown();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -172,6 +172,7 @@
|
||||
norolluponanchor="true"
|
||||
consumeoutsideclicks="never"
|
||||
level="parent"
|
||||
noautohide="true"
|
||||
tabspecific="true">
|
||||
<screenshots-buttons></screenshots-buttons>
|
||||
</panel>
|
||||
|
@ -666,6 +666,9 @@ let JSWINDOWACTORS = {
|
||||
},
|
||||
|
||||
ScreenshotsComponent: {
|
||||
parent: {
|
||||
moduleURI: "resource:///modules/ScreenshotsUtils.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource:///actors/ScreenshotsComponentChild.jsm",
|
||||
},
|
||||
|
291
browser/components/screenshots/ScreenshotsOverlayChild.jsm
Normal file
291
browser/components/screenshots/ScreenshotsOverlayChild.jsm
Normal file
@ -0,0 +1,291 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* The Screenshots overlay is inserted into the document's
|
||||
* canvasFrame anonymous content container (see dom/webidl/Document.webidl).
|
||||
*
|
||||
* This container gets cleared automatically when the document navigates.
|
||||
*
|
||||
* Since the overlay markup is inserted in the canvasFrame using
|
||||
* insertAnonymousContent, this means that it can be modified using the API
|
||||
* described in AnonymousContent.webidl.
|
||||
*
|
||||
* Any mutation of this content must be via the AnonymousContent API.
|
||||
* This is similar in design to [devtools' highlighters](https://firefox-source-docs.mozilla.org/devtools/tools/highlighters.html#inserting-content-in-the-page),
|
||||
* though as Screenshots doesnt need to work on XUL documents, or allow multiple kinds of
|
||||
* highlight/overlay our case is a little simpler.
|
||||
*
|
||||
* To retrieve the AnonymousContent instance, use the `content` getter.
|
||||
*/
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ScreenshotsOverlayChild"];
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "overlayLocalization", () => {
|
||||
return new Localization(["browser/screenshotsOverlay.ftl"], true);
|
||||
});
|
||||
|
||||
const STYLESHEET_URL =
|
||||
"chrome://browser/content/screenshots/overlay/overlay.css";
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
class AnonymousContentOverlay {
|
||||
constructor(contentDocument, screenshotsChild) {
|
||||
this.listeners = new Map();
|
||||
this.elements = new Map();
|
||||
|
||||
this.screenshotsChild = screenshotsChild;
|
||||
|
||||
this.contentDocument = contentDocument;
|
||||
// aliased for easier diffs/maintenance of the event management code borrowed from devtools highlighters
|
||||
this.pageListenerTarget = contentDocument.ownerGlobal;
|
||||
|
||||
this.overlayFragment = null;
|
||||
|
||||
this.overlayId = "screenshots-overlay-container";
|
||||
this._initialized = false;
|
||||
}
|
||||
get content() {
|
||||
if (!this._content || Cu.isDeadWrapper(this._content)) {
|
||||
return null;
|
||||
}
|
||||
return this._content;
|
||||
}
|
||||
async initialize() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
let document = this.contentDocument;
|
||||
let window = document.ownerGlobal;
|
||||
|
||||
// Inject stylesheet
|
||||
if (!this.overlayFragment) {
|
||||
try {
|
||||
window.windowUtils.loadSheetUsingURIString(
|
||||
STYLESHEET_URL,
|
||||
window.windowUtils.AGENT_SHEET
|
||||
);
|
||||
} catch {
|
||||
// The method fails if the url is already loaded.
|
||||
}
|
||||
}
|
||||
|
||||
// Inject markup for the overlay UI
|
||||
this.overlayFragment = this.overlayFragment
|
||||
? this.overlayFragment
|
||||
: this.buildOverlay();
|
||||
|
||||
this._content = document.insertAnonymousContent(
|
||||
this.overlayFragment.children[0]
|
||||
);
|
||||
|
||||
this.addEventListenerForElement(
|
||||
"screenshots-cancel-button",
|
||||
"click",
|
||||
(event, targetId) => {
|
||||
this.screenshotsChild.requestCancelScreenshot();
|
||||
}
|
||||
);
|
||||
|
||||
// TODO:
|
||||
// * Define & hook up drag handles for custom region selection
|
||||
|
||||
this._initialized = true;
|
||||
}
|
||||
|
||||
tearDown() {
|
||||
if (this._content) {
|
||||
this._removeAllListeners();
|
||||
try {
|
||||
this.contentDocument.removeAnonymousContent(this._content);
|
||||
} catch (e) {
|
||||
// If the current window isn't the one the content was inserted into, this
|
||||
// will fail, but that's fine.
|
||||
}
|
||||
}
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
createElem(elemName, props = {}, className = "") {
|
||||
let elem = this.contentDocument.createElementNS(HTML_NS, elemName);
|
||||
if (props) {
|
||||
for (let [name, value] of Object.entries(props)) {
|
||||
elem[name] = value;
|
||||
}
|
||||
}
|
||||
if (className) {
|
||||
elem.className = className;
|
||||
}
|
||||
// register this element so we can target events at it
|
||||
if (elem.id) {
|
||||
this.elements.set(elem.id, elem);
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
buildOverlay() {
|
||||
let [cancel, instrustions] = overlayLocalization.formatMessagesSync([
|
||||
{ id: "screenshots-overlay-cancel-button" },
|
||||
{ id: "screenshots-overlay-instructions" },
|
||||
]);
|
||||
|
||||
const htmlString = `
|
||||
<div id="${this.overlayId}">
|
||||
<div class="fixed-container">
|
||||
<div class="face-container">
|
||||
<div class="eye left"><div class="eyeball"></div></div>
|
||||
<div class="eye right"><div class="eyeball"></div></div>
|
||||
<div class="face"></div>
|
||||
</div>
|
||||
<div class="preview-instructions" data-l10n-id="screenshots-instructions">${instrustions.value}</div>
|
||||
<div class="cancel-shot" id="screenshots-cancel-button" data-l10n-id="screenshots-overlay-cancel-button">${cancel.value}</div>
|
||||
</div>
|
||||
</div`;
|
||||
|
||||
const parser = new this.contentDocument.ownerGlobal.DOMParser();
|
||||
const tmpDoc = parser.parseFromString(htmlString, "text/html");
|
||||
const fragment = this.contentDocument.createDocumentFragment();
|
||||
|
||||
fragment.appendChild(tmpDoc.body.children[0]);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
// The event tooling is borrowed directly from devtools' highlighters (CanvasFrameAnonymousContentHelper)
|
||||
/**
|
||||
* Add an event listener to one of the elements inserted in the canvasFrame
|
||||
* native anonymous container.
|
||||
* Like other methods in this helper, this requires the ID of the element to
|
||||
* be passed in.
|
||||
*
|
||||
* Note that if the content page navigates, the event listeners won't be
|
||||
* added again.
|
||||
*
|
||||
* Also note that unlike traditional DOM events, the events handled by
|
||||
* listeners added here will propagate through the document only through
|
||||
* bubbling phase, so the useCapture parameter isn't supported.
|
||||
* It is possible however to call e.stopPropagation() to stop the bubbling.
|
||||
*
|
||||
* IMPORTANT: the chrome-only canvasFrame insertion API takes great care of
|
||||
* not leaking references to inserted elements to chrome JS code. That's
|
||||
* because otherwise, chrome JS code could freely modify native anon elements
|
||||
* inside the canvasFrame and probably change things that are assumed not to
|
||||
* change by the C++ code managing this frame.
|
||||
* See https://wiki.mozilla.org/DevTools/Highlighter#The_AnonymousContent_API
|
||||
* Unfortunately, the inserted nodes are still available via
|
||||
* event.originalTarget, and that's what the event handler here uses to check
|
||||
* that the event actually occured on the right element, but that also means
|
||||
* consumers of this code would be able to access the inserted elements.
|
||||
* Therefore, the originalTarget property will be nullified before the event
|
||||
* is passed to your handler.
|
||||
*
|
||||
* IMPL DETAIL: A single event listener is added per event types only, at
|
||||
* browser level and if the event originalTarget is found to have the provided
|
||||
* ID, the callback is executed (and then IDs of parent nodes of the
|
||||
* originalTarget are checked too).
|
||||
*
|
||||
* @param {String} id
|
||||
* @param {String} type
|
||||
* @param {Function} handler
|
||||
*/
|
||||
addEventListenerForElement(id, type, handler) {
|
||||
if (typeof id !== "string") {
|
||||
throw new Error(
|
||||
"Expected a string ID in addEventListenerForElement but got: " + id
|
||||
);
|
||||
}
|
||||
|
||||
// If no one is listening for this type of event yet, add one listener.
|
||||
if (!this.listeners.has(type)) {
|
||||
const target = this.pageListenerTarget;
|
||||
target.addEventListener(type, this, true);
|
||||
// Each type entry in the map is a map of ids:handlers.
|
||||
this.listeners.set(type, new Map());
|
||||
}
|
||||
|
||||
const listeners = this.listeners.get(type);
|
||||
listeners.set(id, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event listener from one of the elements inserted in the
|
||||
* canvasFrame native anonymous container.
|
||||
* @param {String} id
|
||||
* @param {String} type
|
||||
*/
|
||||
removeEventListenerForElement(id, type) {
|
||||
const listeners = this.listeners.get(type);
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
listeners.delete(id);
|
||||
|
||||
// If no one is listening for event type anymore, remove the listener.
|
||||
if (!listeners.size) {
|
||||
const target = this.pageListenerTarget;
|
||||
target.removeEventListener(type, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
const listeners = this.listeners.get(event.type);
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the originalTarget property to avoid exposing references to native
|
||||
// anonymous elements. See addEventListenerForElement's comment.
|
||||
let isPropagationStopped = false;
|
||||
const eventProxy = new Proxy(event, {
|
||||
get: (obj, name) => {
|
||||
if (name === "originalTarget") {
|
||||
return null;
|
||||
} else if (name === "stopPropagation") {
|
||||
return () => {
|
||||
isPropagationStopped = true;
|
||||
};
|
||||
}
|
||||
return obj[name];
|
||||
},
|
||||
});
|
||||
|
||||
// Start at originalTarget, bubble through ancestors and call handlers when
|
||||
// needed.
|
||||
let node = event.originalTarget;
|
||||
while (node) {
|
||||
let nodeId = node.id;
|
||||
if (nodeId) {
|
||||
const handler = listeners.get(node.id);
|
||||
if (handler) {
|
||||
handler(eventProxy, nodeId);
|
||||
if (isPropagationStopped) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nodeId == this.overlayId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
_removeAllListeners() {
|
||||
if (this.pageListenerTarget) {
|
||||
const target = this.pageListenerTarget;
|
||||
for (const [type] of this.listeners) {
|
||||
target.removeEventListener(type, this, true);
|
||||
}
|
||||
}
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
var ScreenshotsOverlayChild = {
|
||||
AnonymousContentOverlay,
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ScreenshotsUtils"];
|
||||
var EXPORTED_SYMBOLS = ["ScreenshotsUtils", "ScreenshotsComponentParent"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
@ -12,6 +12,16 @@ const PanelPosition = "bottomright topright";
|
||||
const PanelOffsetX = -33;
|
||||
const PanelOffsetY = -8;
|
||||
|
||||
class ScreenshotsComponentParent extends JSWindowActorParent {
|
||||
receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case "Screenshots:CancelScreenshot":
|
||||
let browser = message.target.browsingContext.topFrameElement;
|
||||
ScreenshotsUtils.closePanel(browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ScreenshotsUtils = {
|
||||
initialized: false,
|
||||
initialize() {
|
||||
@ -57,9 +67,8 @@ var ScreenshotsUtils = {
|
||||
}
|
||||
break;
|
||||
case "screenshots-take-screenshot":
|
||||
// need to toggle because panel button was clicked
|
||||
// and we need to hide the buttons
|
||||
this.togglePreview(browser);
|
||||
// need to close the preview because screenshot was taken
|
||||
this.closePanel(browser);
|
||||
|
||||
// init UI as a tab dialog box
|
||||
let dialogBox = gBrowser.getTabDialogBox(browser);
|
||||
@ -103,8 +112,31 @@ var ScreenshotsUtils = {
|
||||
return actor;
|
||||
},
|
||||
/**
|
||||
* If the buttons panel exists and the panel is open we will hipe the panel
|
||||
* popup and hide the screenshot overlay.
|
||||
* Open the panel buttons and call child actor to open the overlay
|
||||
* @param browser The current browser
|
||||
*/
|
||||
openPanel(browser) {
|
||||
let actor = this.getActor(browser);
|
||||
actor.sendQuery("Screenshots:ShowOverlay");
|
||||
this.createOrDisplayButtons(browser);
|
||||
},
|
||||
/**
|
||||
* Close the panel and call child actor to close the overlay
|
||||
* @param browser The current browser
|
||||
*/
|
||||
closePanel(browser) {
|
||||
let buttonsPanel = browser.ownerDocument.querySelector(
|
||||
"#screenshotsPagePanel"
|
||||
);
|
||||
if (buttonsPanel && buttonsPanel.state !== "closed") {
|
||||
buttonsPanel.hidePopup();
|
||||
}
|
||||
let actor = this.getActor(browser);
|
||||
actor.sendQuery("Screenshots:HideOverlay");
|
||||
},
|
||||
/**
|
||||
* If the buttons panel exists and is open we will hide both the panel
|
||||
* popup and the overlay.
|
||||
* Otherwise create or display the buttons.
|
||||
* @param browser The current browser.
|
||||
*/
|
||||
@ -117,6 +149,8 @@ var ScreenshotsUtils = {
|
||||
let actor = this.getActor(browser);
|
||||
return actor.sendQuery("Screenshots:HideOverlay");
|
||||
}
|
||||
let actor = this.getActor(browser);
|
||||
actor.sendQuery("Screenshots:ShowOverlay");
|
||||
return this.createOrDisplayButtons(browser);
|
||||
},
|
||||
/**
|
||||
@ -173,10 +207,9 @@ var ScreenshotsUtils = {
|
||||
template.replaceWith(clone);
|
||||
buttonsPanel = doc.querySelector("#screenshotsPagePanel");
|
||||
}
|
||||
|
||||
let anchor = doc.querySelector("#navigator-toolbox");
|
||||
buttonsPanel.openPopup(anchor, PanelPosition, PanelOffsetX, PanelOffsetY);
|
||||
let actor = this.getActor(browser);
|
||||
return actor.sendQuery("Screenshots:ShowOverlay");
|
||||
},
|
||||
/**
|
||||
* Gets the full page bounds from the screenshots child actor.
|
||||
|
@ -16,3 +16,7 @@ browser.jar:
|
||||
content/browser/screenshots/screenshots-buttons.css (screenshots-buttons.css)
|
||||
content/browser/screenshots/screenshots.css (content/screenshots.css)
|
||||
content/browser/screenshots/screenshots.html (content/screenshots.html)
|
||||
content/browser/screenshots/overlay/ (overlay/**)
|
||||
|
||||
|
||||
% content screenshots-overlay %overlay/
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
"ScreenshotsOverlayChild.jsm",
|
||||
"ScreenshotsUtils.jsm",
|
||||
]
|
||||
|
||||
|
126
browser/components/screenshots/overlay/overlay.css
Normal file
126
browser/components/screenshots/overlay/overlay.css
Normal file
@ -0,0 +1,126 @@
|
||||
/* 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/. */
|
||||
|
||||
:-moz-native-anonymous #screenshots-overlay-container {
|
||||
/*
|
||||
Content CSS applying to the html element can impact the overlay.
|
||||
To avoid that, possible cases have been set to initial.
|
||||
*/
|
||||
text-transform: initial;
|
||||
text-indent: initial;
|
||||
letter-spacing: initial;
|
||||
word-spacing: initial;
|
||||
color: initial;
|
||||
direction: initial;
|
||||
writing-mode: initial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlay content is position: fixed as we need to allow for the possiblily
|
||||
* of the document scrolling or changing size while the overlay is visible
|
||||
*/
|
||||
:-moz-native-anonymous #screenshots-overlay-container {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
pointer-events: auto;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous #screenshots-overlay-container[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous #screenshots-overlay-container[dragging] {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous #screenshots-cancel-button {
|
||||
background-color: transparent;
|
||||
width: fit-content;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border-radius: 3px;
|
||||
border: 1px #9b9b9b solid;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "segoe ui", "helvetica neue", helvetica, ubuntu, roboto, noto, arial, sans-serif;
|
||||
font-size: 16px;
|
||||
margin-top: 40px;
|
||||
padding: 10px 25px;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .fixed-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
inset-inline-start: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .face-container {
|
||||
position: relative;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .face {
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
display: block;
|
||||
background-image: url("chrome://browser/content/screenshots/icon-welcome-face-without-eyes.svg");
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .eye {
|
||||
background-color: #fff;
|
||||
width: 10px;
|
||||
height: 14px;
|
||||
position: absolute;
|
||||
border-radius: 100%;
|
||||
overflow: hidden;
|
||||
inset-inline-start: 16px;
|
||||
top: 19px;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .eyeball {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color: #000;
|
||||
border-radius: 50%;
|
||||
inset-inline-start: 2px;
|
||||
top: 4px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .left {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .right {
|
||||
margin-inline-start: 20px;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .preview-instructions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: pulse 125mm cubic-bezier(0.07, 0.95, 0, 1);
|
||||
color: #fff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "segoe ui", "helvetica neue", helvetica, ubuntu, roboto, noto, arial, sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
width: 400px;
|
||||
user-select: none;
|
||||
}
|
@ -7,6 +7,7 @@ prefs =
|
||||
extensions.screenshots.disabled=false
|
||||
screenshots.browser.component.enabled=true
|
||||
|
||||
[browser_screenshots_test_escape.js]
|
||||
[browser_screenshots_test_full_page.js]
|
||||
skip-if = (!debug && os == 'win' && os_version == '6.1') # Bug 1746281
|
||||
[browser_screenshots_test_toggle_pref.js]
|
||||
|
@ -0,0 +1,52 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_fullpageScreenshot() {
|
||||
CustomizableUI.addWidgetToArea(
|
||||
"screenshot-button",
|
||||
CustomizableUI.AREA_NAVBAR
|
||||
);
|
||||
let screenshotBtn = document.getElementById("screenshot-button");
|
||||
Assert.ok(screenshotBtn, "The screenshots button was added to the nav bar");
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: TEST_PAGE,
|
||||
},
|
||||
async browser => {
|
||||
let helper = new ScreenshotsHelper(browser);
|
||||
let contentInfo = await helper.getContentDimensions();
|
||||
ok(contentInfo, "Got dimensions back from the content");
|
||||
|
||||
// click toolbar button so panel shows
|
||||
helper.triggerUIFromToolbar();
|
||||
|
||||
let panel = gBrowser.selectedBrowser.ownerDocument.querySelector(
|
||||
"#screenshotsPagePanel"
|
||||
);
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
panel,
|
||||
{ attributes: true },
|
||||
() => {
|
||||
return BrowserTestUtils.is_visible(panel);
|
||||
}
|
||||
);
|
||||
ok(BrowserTestUtils.is_visible(panel), "Panel buttons are visible");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Escape");
|
||||
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
panel,
|
||||
{ attributes: true },
|
||||
() => {
|
||||
return BrowserTestUtils.is_hidden(panel);
|
||||
}
|
||||
);
|
||||
|
||||
ok(BrowserTestUtils.is_hidden(panel), "Panel buttons are hidden");
|
||||
}
|
||||
);
|
||||
});
|
6
browser/locales/en-US/browser/screenshotsOverlay.ftl
Normal file
6
browser/locales/en-US/browser/screenshotsOverlay.ftl
Normal file
@ -0,0 +1,6 @@
|
||||
# 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/.
|
||||
|
||||
screenshots-overlay-cancel-button = Cancel
|
||||
screenshots-overlay-instructions = Drag or click on the page to select a region. Press ESC to cancel.
|
Loading…
Reference in New Issue
Block a user