Bug 1824886 - Rewrite AnonymousContent to use a shadow tree. r=smaug,TYLin,sfoster,devtools-reviewers,nchevobbe

Subtle things:

 * We now have shadow trees in NAC, inception! Only a couple lines of
   code in the style system had to be changed to match :host rules and
   so properly.

 * Had to make highlighters.css contentaccessible, because otherwise we
   can't load it from the shadow tree. I don't think it's a big deal.

 * I removed some of the code from highlighters.css that claimed that
   stuff inherited from the html element. That's just no longer true.

 * Had to switch from `setAttribute("style", ...)` to `.style = ...;`.
   This is needed because CSSOM from chrome code bypasses CSP (as
   AnonymousContent did), but setAttribute() doesn't, see bug 1424474.

Differential Revision: https://phabricator.services.mozilla.com/D173998
This commit is contained in:
Emilio Cobos Álvarez 2023-07-09 11:34:05 +00:00
Родитель c3b2365bfe
Коммит bd1b363ff9
59 изменённых файлов: 863 добавлений и 1299 удалений

Просмотреть файл

@ -16,12 +16,6 @@ let whitelist = [
sourceName: /devtools\/content\/debugger\/src\/components\/([A-z\/]+).css/i,
isFromDevTools: true,
},
// Highlighter CSS uses a UA-only pseudo-class, see bug 985597.
{
sourceName: /highlighters\.css$/i,
errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i,
isFromDevTools: true,
},
// UA-only media features.
{
sourceName: /\b(autocomplete-item)\.css$/,
@ -68,11 +62,6 @@ let whitelist = [
/Unknown property text-size-adjust\. {2}Declaration dropped\./i,
isFromDevTools: false,
},
{
sourceName: /overlay\.css$/i,
errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i,
isFromDevTools: false,
},
];
if (!Services.prefs.getBoolPref("layout.css.color-mix.enabled")) {
@ -147,6 +136,7 @@ let propNameWhitelist = [
// These variables are used in a shorthand, but the CSS parser deletes the values
// when expanding the shorthands. See https://github.com/w3c/csswg-drafts/issues/2515
{ propName: "--bezier-diagonal-color", isFromDevTools: true },
{ propName: "--highlighter-font-family", isFromDevTools: true },
// This variable is used from CSS embedded in JS in adjustableTitle.js
{ propName: "--icon-url", isFromDevTools: false },

Просмотреть файл

@ -110,6 +110,7 @@ class AnonymousContentOverlay {
}
return this._content;
}
async initialize() {
if (this._initialized) {
return;
@ -118,24 +119,12 @@ class AnonymousContentOverlay {
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.buildOverlay();
}
this._content = document.insertAnonymousContent(
this.overlayFragment.children[0]
);
this._content = document.insertAnonymousContent();
this._content.root.appendChild(this.overlayFragment.cloneNode(true));
this.addEventListeners();
const hoverElementBox = new HoverElementBox(
@ -304,6 +293,7 @@ class AnonymousContentOverlay {
const htmlString = `
<div id="screenshots-component">
<link rel="stylesheet" href="${STYLESHEET_URL}">
<div id="${this.overlayId}">
<div id="${this.previewId}">
<div class="fixed-container">
@ -1047,18 +1037,22 @@ class AnonLayer {
this.content = content;
}
_element(id) {
return this.content.root.getElementById(id);
}
/**
* Show element with id this.id
*/
show() {
this.content.removeAttributeForElement(this.id, "style");
this._element(this.id).style = "";
}
/**
* Hide element with id this.id
*/
hide() {
this.content.setAttributeForElement(this.id, "style", "display:none;");
this._element(this.id).style.display = "none";
}
}
@ -1097,11 +1091,9 @@ class HoverElementBox extends AnonLayer {
let width =
rect.left + rect.width > maxWidth ? maxWidth - rect.left : rect.width;
this.content.setAttributeForElement(
this.id,
"style",
`top:${top}px;left:${left}px;height:${height}px;width:${width}px;`
);
this._element(
this.id
).style = `top:${top}px;left:${left}px;height:${height}px;width:${width}px;`;
}
}
@ -1304,38 +1296,17 @@ class HoverElementBox extends AnonLayer {
* get the screenshots elements as the elements from a given point
*/
setPointerEventsNone() {
this.content.setAttributeForElement(
"screenshots-component",
"style",
"pointer-events:none;"
);
let temp = this.content.getAttributeForElement(
"screenshots-overlay-container",
"style"
);
this.content.setAttributeForElement(
"screenshots-overlay-container",
"style",
temp + "pointer-events:none;"
);
this._element("screenshots-component").style.pointerEvents = "none";
this._element("screenshots-overlay-container").style.pointerEvents = "none";
}
/**
* Return the pointer events to the original state because we found the element
*/
resetPointerEvents() {
this.content.setAttributeForElement("screenshots-component", "style", "");
this._element("screenshots-component").style.pointerEvents = "";
let temp = this.content.getAttributeForElement(
"screenshots-overlay-container",
"style"
);
this.content.setAttributeForElement(
"screenshots-overlay-container",
"style",
temp.replace("pointer-events:none;", "")
);
this._element("screenshots-overlay-container").style.pointerEvents = "";
}
}
@ -1599,35 +1570,21 @@ class SelectionBox extends AnonLayer {
* Draw the selected region for screenshotting
*/
show() {
this.content.setAttributeForElement(
"highlight",
"style",
`top:${this.top}px;left:${this.left}px;height:${this.height}px;width:${this.width}px;`
);
this.content.setAttributeForElement(
"bgTop",
"style",
`top:0px;height:${this.top}px;left:0px;width:100%;`
);
this.content.setAttributeForElement(
"bgBottom",
"style",
`top:${this.bottom}px;height:calc(100% - ${this.bottom}px);left:0px;width:100%;`
);
this.content.setAttributeForElement(
"bgLeft",
"style",
`top:${this.top}px;height:${this.height}px;left:0px;width:${this.left}px;`
);
this.content.setAttributeForElement(
"bgRight",
"style",
`top:${this.top}px;height:${this.height}px;left:${this.right}px;width:calc(100% - ${this.right}px);`
);
this._element(
"highlight"
).style = `top:${this.top}px;left:${this.left}px;height:${this.height}px;width:${this.width}px;`;
this._element(
"bgTop"
).style = `top:0px;height:${this.top}px;left:0px;width:100%;`;
this._element(
"bgBottom"
).style = `top:${this.bottom}px;height:calc(100% - ${this.bottom}px);left:0px;width:100%;`;
this._element(
"bgLeft"
).style = `top:${this.top}px;height:${this.height}px;left:0px;width:${this.left}px;`;
this._element(
"bgRight"
).style = `top:${this.top}px;height:${this.height}px;left:${this.right}px;width:calc(100% - ${this.right}px);`;
}
/**
@ -1647,11 +1604,9 @@ class SelectionBox extends AnonLayer {
* Hide the selected region
*/
hide() {
this.content.setAttributeForElement("highlight", "style", "display:none;");
this.content.setAttributeForElement("bgTop", "style", "display:none;");
this.content.setAttributeForElement("bgBottom", "style", "display:none;");
this.content.setAttributeForElement("bgLeft", "style", "display:none;");
this.content.setAttributeForElement("bgRight", "style", "display:none;");
for (let id of ["highlight", "bgTop", "bgBottom", "bgLeft", "bgRight"]) {
this._element(id).style = `display:none;`;
}
}
/**
@ -1826,11 +1781,7 @@ class ButtonsLayer extends AnonLayer {
leftOrRight = `left:${boxLeft}px;`;
}
this.content.setAttributeForElement(
"buttons",
"style",
`top:${top}px;${leftOrRight}`
);
this._element("buttons").style = `top:${top}px;${leftOrRight}`;
}
}
@ -1850,8 +1801,8 @@ class PreviewLayer extends AnonLayer {
const xpos = Math.floor((10 * (clientX - width / 2)) / width);
const ypos = Math.floor((10 * (clientY - height / 2)) / height);
const move = `transform:translate(${xpos}px, ${ypos}px);`;
this.content.setAttributeForElement("left-eye", "style", move);
this.content.setAttributeForElement("right-eye", "style", move);
this._element("left-eye").style = move;
this._element("right-eye").style = move;
}
}
@ -2135,11 +2086,9 @@ class ScreenshotsContainerLayer extends AnonLayer {
* Draw the screenshots container
*/
drawScreenshotsContainer() {
this.content.setAttributeForElement(
this.id,
"style",
`top:0;left:0;width:${this.#width}px;height:${this.#height}px;`
);
this._element(this.id).style = `top:0;left:0;width:${
this.#width
}px;height:${this.#height}px;`;
}
get hoverElementBoxRect() {

Просмотреть файл

@ -2,7 +2,7 @@
* 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-component {
#screenshots-component {
--in-content-page-background: #fff;
--in-content-button-text-color: rgb(21, 20, 26);
@ -29,7 +29,7 @@
}
@media (prefers-color-scheme: dark) {
:-moz-native-anonymous #screenshots-component {
#screenshots-component {
--in-content-page-background: #42414d;
--in-content-button-text-color: rgb(251,251,254);
@ -45,7 +45,7 @@
}
@media (forced-colors: active), (prefers-contrast) {
:-moz-native-anonymous #screenshots-component {
#screenshots-component {
--in-content-page-background: Canvas;
--in-content-button-text-color: ButtonText;
@ -72,7 +72,7 @@
}
}
:-moz-native-anonymous #screenshots-component {
#screenshots-component {
width: 100%;
height: 100%;
overflow: clip;
@ -85,7 +85,7 @@
* 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 {
#screenshots-overlay-container {
/*
Content CSS applying to the html element can impact the overlay.
To avoid that, possible cases have been set to initial.
@ -103,7 +103,7 @@
cursor: crosshair;
}
:-moz-native-anonymous #preview-container {
#preview-container {
background-color: rgba(0, 0, 0, 0.7);
position: fixed;
top: 0;
@ -112,7 +112,7 @@
height: 100%;
}
:-moz-native-anonymous #selection-container {
#selection-container {
overflow: clip;
position: absolute;
top: 0;
@ -121,15 +121,15 @@
height: 100%;
}
:-moz-native-anonymous #screenshots-overlay-container[hidden] {
#screenshots-overlay-container[hidden] {
display: none;
}
:-moz-native-anonymous #screenshots-overlay-container[dragging] {
#screenshots-overlay-container[dragging] {
cursor: grabbing;
}
:-moz-native-anonymous #buttons {
#buttons {
position: absolute;
margin: 10px 0;
display: flex;
@ -140,7 +140,7 @@
padding: 4px;
}
:-moz-native-anonymous .screenshots-button {
.screenshots-button {
display: flex;
align-items: center;
justify-content: center;
@ -167,43 +167,43 @@
background-color: var(--in-content-button-background);
}
:-moz-native-anonymous .screenshots-button:focus-visible,
:-moz-native-anonymous #screenshots-cancel-button:focus-visible {
.screenshots-button:focus-visible,
#screenshots-cancel-button:focus-visible {
outline: 2px solid var(--in-content-focus-outline-color);
outline-offset: 2px;
}
:-moz-native-anonymous .screenshots-button:hover {
.screenshots-button:hover {
background-color: var(--in-content-button-background-hover);
border-color: var(--in-content-button-border-color-hover);
color: var(--in-content-button-text-color-hover);
}
:-moz-native-anonymous .screenshots-button:active {
.screenshots-button:active {
background-color: var(--in-content-button-background-active);
border-color: var(--in-content-button-border-color-active);
color: var(--in-content-button-text-color-active);
}
:-moz-native-anonymous .primary {
.primary {
background-color: var(--in-content-primary-button-background);
border-color: var(--in-content-primary-button-border-color);
color: var(--in-content-primary-button-text-color);
}
:-moz-native-anonymous .primary:hover {
.primary:hover {
background-color: var(--in-content-primary-button-background-hover);
border-color: var(--in-content-primary-button-border-color-hover);
color: var(--in-content-primary-button-text-color-hover);
}
:-moz-native-anonymous .primary:active {
.primary:active {
background-color: var(--in-content-primary-button-background-active);
border-color: var(--in-content-primary-button-border-color-active);
color: var(--in-content-primary-button-text-color-active);
}
:-moz-native-anonymous #screenshots-cancel-button {
#screenshots-cancel-button {
background-color: transparent;
margin-top: 40px;
width: fit-content;
@ -211,43 +211,43 @@
color: #fff;
}
:-moz-native-anonymous #screenshots-cancel-button:hover {
#screenshots-cancel-button:hover {
background-color: #fff;
color: #000;
}
@media (forced-colors: active), (prefers-contrast) {
:-moz-native-anonymous #screenshots-cancel-button {
#screenshots-cancel-button {
border-color: ButtonBorder;
color: CanvasText;
}
}
:-moz-native-anonymous .screenshots-button > img {
.screenshots-button > img {
-moz-context-properties: fill;
fill: currentColor;
width: 16px;
height: 16px;
}
:-moz-native-anonymous #cancel > img {
#cancel > img {
content: url("chrome://global/skin/icons/close.svg");
}
:-moz-native-anonymous #copy > img {
#copy > img {
content: url("chrome://global/skin/icons/edit-copy.svg");
}
:-moz-native-anonymous #download > img {
#download > img {
content: url("chrome://browser/skin/downloads/downloads.svg");
}
:-moz-native-anonymous #download > img,
:-moz-native-anonymous #copy > img {
#download > img,
#copy > img {
margin-inline-end: 5px;
}
:-moz-native-anonymous .fixed-container {
.fixed-container {
align-items: center;
display: flex;
flex-direction: column;
@ -261,20 +261,20 @@
width: 100%;
}
:-moz-native-anonymous .face-container {
.face-container {
position: relative;
width: 64px;
height: 64px;
}
:-moz-native-anonymous .face {
.face {
width: 62px;
height: 62px;
display: block;
background-image: url("chrome://browser/content/screenshots/icon-welcome-face-without-eyes.svg");
}
:-moz-native-anonymous .eye {
.eye {
background-color: #fff;
width: 10px;
height: 14px;
@ -285,7 +285,7 @@
top: 19px;
}
:-moz-native-anonymous .eyeball {
.eyeball {
position: absolute;
width: 6px;
height: 6px;
@ -296,19 +296,19 @@
z-index: 10;
}
:-moz-native-anonymous .left {
.left {
margin-inline-start: 0;
}
:-moz-native-anonymous .right {
.right {
margin-inline-start: 20px;
}
:-moz-native-anonymous .preview-instructions {
.preview-instructions {
display: flex;
align-items: center;
justify-content: center;
animation: pulse 125mm cubic-bezier(0.07, 0.95, 0, 1);
animation: pulse 125ms 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;
@ -319,12 +319,12 @@
}
@media (forced-colors: active), (prefers-contrast) {
:-moz-native-anonymous .preview-instructions {
.preview-instructions {
color: CanvasText;
}
}
:-moz-native-anonymous #hover-highlight {
#hover-highlight {
animation: fade-in 125ms forwards cubic-bezier(0.07, 0.95, 0, 1);
background: rgba(255, 255, 255, 0.2);
border-radius: 1px;
@ -333,7 +333,7 @@
z-index: 11;
}
:-moz-native-anonymous #hover-highlight::before {
#hover-highlight::before {
border: 2px dashed rgba(255, 255, 255, 0.4);
bottom: 0;
content: "";
@ -343,13 +343,13 @@
top: 0;
}
:-moz-native-anonymous .bghighlight {
.bghighlight {
background-color: rgba(0, 0, 0, 0.7);
position: absolute;
overflow: clip;
}
:-moz-native-anonymous .highlight {
.highlight {
border-radius: 1px;
border: 2px dashed rgba(255, 255, 255, 0.8);
box-sizing: border-box;
@ -359,7 +359,7 @@
z-index: 2;
}
:-moz-native-anonymous .mover-target {
.mover-target {
display: flex;
align-items: center;
justify-content: center;
@ -368,7 +368,7 @@
pointer-events: auto;
}
:-moz-native-anonymous .mover-target.direction-topLeft {
.mover-target.direction-topLeft {
cursor: nwse-resize;
height: 60px;
left: -30px;
@ -376,7 +376,7 @@
width: 60px;
}
:-moz-native-anonymous .mover-target.direction-top {
.mover-target.direction-top {
cursor: ns-resize;
height: 60px;
inset-inline-start: 0;
@ -385,7 +385,7 @@
z-index: 4;
}
:-moz-native-anonymous .mover-target.direction-topRight {
.mover-target.direction-topRight {
cursor: nesw-resize;
height: 60px;
right: -30px;
@ -393,7 +393,7 @@
width: 60px;
}
:-moz-native-anonymous .mover-target.direction-left {
.mover-target.direction-left {
cursor: ew-resize;
height: 100%;
left: -30px;
@ -402,7 +402,7 @@
z-index: 4;
}
:-moz-native-anonymous .mover-target.direction-right {
.mover-target.direction-right {
cursor: ew-resize;
height: 100%;
right: -30px;
@ -411,7 +411,7 @@
z-index: 4;
}
:-moz-native-anonymous .mover-target.direction-bottomLeft {
.mover-target.direction-bottomLeft {
bottom: -30px;
cursor: nesw-resize;
height: 60px;
@ -419,7 +419,7 @@
width: 60px;
}
:-moz-native-anonymous .mover-target.direction-bottom {
.mover-target.direction-bottom {
bottom: -30px;
cursor: ns-resize;
height: 60px;
@ -428,7 +428,7 @@
z-index: 4;
}
:-moz-native-anonymous .mover-target.direction-bottomRight {
.mover-target.direction-bottomRight {
bottom: -30px;
cursor: nwse-resize;
height: 60px;
@ -436,11 +436,11 @@
width: 60px;
}
:-moz-native-anonymous .mover-target:hover .mover {
.mover-target:hover .mover {
transform: scale(1.05);
}
:-moz-native-anonymous .mover {
.mover {
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.5);
@ -451,31 +451,31 @@
width: 16px;
}
:-moz-native-anonymous .small-selection .mover {
.small-selection .mover {
height: 10px;
width: 10px;
}
:-moz-native-anonymous .direction-topLeft .mover,
:-moz-native-anonymous .direction-left .mover,
:-moz-native-anonymous .direction-bottomLeft .mover {
.direction-topLeft .mover,
.direction-left .mover,
.direction-bottomLeft .mover {
left: -1px;
}
:-moz-native-anonymous .direction-topLeft .mover,
:-moz-native-anonymous .direction-top .mover,
:-moz-native-anonymous .direction-topRight .mover {
.direction-topLeft .mover,
.direction-top .mover,
.direction-topRight .mover {
top: -1px;
}
:-moz-native-anonymous .direction-topRight .mover,
:-moz-native-anonymous .direction-right .mover,
:-moz-native-anonymous .direction-bottomRight .mover {
.direction-topRight .mover,
.direction-right .mover,
.direction-bottomRight .mover {
right: -1px;
}
:-moz-native-anonymous .direction-bottomRight .mover,
:-moz-native-anonymous .direction-bottom .mover,
:-moz-native-anonymous .direction-bottomLeft .mover {
.direction-bottomRight .mover,
.direction-bottom .mover,
.direction-bottomLeft .mover {
bottom: -1px;
}

Просмотреть файл

@ -44,7 +44,7 @@ class ManifestIssue extends PureComponent {
case MANIFEST_ISSUE_LEVELS.ERROR:
default:
return {
src: "chrome://devtools/skin/images/error-small.svg",
src: "resource://devtools-shared-images/error-small.svg",
localizationId: "icon-error",
};
}

Просмотреть файл

@ -39,7 +39,7 @@ exports[`ManifestIssue renders the expected snapshot for an error 1`] = `
>
<img
className="manifest-issue__icon manifest-issue__icon--error"
src="chrome://devtools/skin/images/error-small.svg"
src="resource://devtools-shared-images/error-small.svg"
/>
</Localized>
<span>

Просмотреть файл

@ -158,7 +158,7 @@ html[dir="rtl"] .img.more-tabs {
}
.img.resume {
mask-image: url(chrome://devtools/content/shared/images/resume.svg);
mask-image: url(resource://devtools-shared-images/resume.svg);
}
.img.search {
@ -182,7 +182,7 @@ html[dir="rtl"] .img.more-tabs {
}
.img.stepOver {
mask-image: url(chrome://devtools/content/shared/images/stepOver.svg);
mask-image: url(resource://devtools-shared-images/stepOver.svg);
}
.img.tab {

Просмотреть файл

@ -15,9 +15,11 @@ const TEST_LEVELS = [2, 1, 0.5];
// Returns the expected style attribute value to check for on the highlighter's elements
// node, for the values given.
const expectedStyle = (w, h, z) =>
(z !== 1 ? `transform-origin:top left; transform:scale(${1 / z}); ` : "") +
`position:absolute; width:${w * z}px;height:${h * z}px; ` +
"overflow:hidden";
(z !== 1
? `transform-origin: left top 0px; transform: scale(${1 / z}); `
: "") +
`position: absolute; width: ${w * z}px; height: ${h * z}px; ` +
"overflow: hidden;";
add_task(async function () {
const { inspector, highlighterTestFront } = await openInspectorForURL(

Просмотреть файл

@ -814,6 +814,11 @@ function waitForStyleEditor(toolbox, href) {
// A helper that resolves the promise once it receives an editor that
// matches the expected href. Returns false if the editor was not correct.
const gotEditor = editor => {
if (!editor) {
info("Editor went away after selected?");
return false;
}
const currentHref = editor.styleSheet.href;
if (!href || (href && currentHref.endsWith(href))) {
info("Stylesheet editor selected");

Просмотреть файл

@ -147,7 +147,6 @@ devtools.jar:
skin/images/alert-tiny.svg (themes/images/alert-tiny.svg)
skin/images/arrow-dropdown-12.svg (themes/images/arrow-dropdown-12.svg)
skin/images/error.svg (themes/images/error.svg)
skin/images/error-small.svg (themes/images/error-small.svg)
skin/images/error-tiny.svg (themes/images/error-tiny.svg)
skin/images/info.svg (themes/images/info.svg)
skin/images/info-small.svg (themes/images/info-small.svg)

Просмотреть файл

@ -336,7 +336,7 @@
}
.security-state-broken {
background-image: url(chrome://devtools/skin/images/error-small.svg);
background-image: url(resource://devtools-shared-images/error-small.svg);
width: 16px;
fill: var(--theme-icon-error-color);
}

Просмотреть файл

@ -42,7 +42,7 @@
height: 12px;
content: "";
vertical-align: -2px;
background-image: url(chrome://devtools/skin/images/error-small.svg);
background-image: url(resource://devtools-shared-images/error-small.svg);
background-position: center;
background-repeat: no-repeat;
-moz-context-properties: fill;

Просмотреть файл

@ -750,5 +750,5 @@ select.playback-rate-selector.devtools-button:not(:empty, :disabled, .checked):h
}
.animation-element-picker::before {
background-image: url("chrome://devtools/content/shared/images/command-pick.svg");
background-image: url("resource://devtools-shared-images/command-pick.svg");
}

Просмотреть файл

@ -473,7 +473,7 @@
}
.toolbox-error::before {
background-image: url("chrome://devtools/skin/images/error-small.svg");
background-image: url("resource://devtools-shared-images/error-small.svg");
fill: var(--theme-icon-error-color) !important;
}
@ -488,7 +488,7 @@
}
#command-button-pick::before {
background-image: url("chrome://devtools/content/shared/images/command-pick.svg");
background-image: url("resource://devtools-shared-images/command-pick.svg");
}
#command-button-pick.accessibility::before {
@ -496,7 +496,7 @@
}
#command-button-pick.remote-fenix::before {
background-image: url("chrome://devtools/content/shared/images/command-pick-remote-touch.svg");
background-image: url("resource://devtools-shared-images/command-pick-remote-touch.svg");
}
/* Command button images */

Просмотреть файл

@ -296,7 +296,7 @@
.message.error > .icon {
color: var(--theme-icon-error-color);
background-image: url(chrome://devtools/skin/images/error-small.svg);
background-image: url(resource://devtools-shared-images/error-small.svg);
}
.message.warn > .icon {

Просмотреть файл

@ -1801,7 +1801,10 @@ class CssGridHighlighter extends AutoRefreshHighlighter {
setIgnoreLayoutChanges(true);
// Set z-index.
this.markup.content.setStyle("z-index", this.options.zIndex);
this.markup.content.root.firstElementChild.style.setProperty(
"z-index",
this.options.zIndex
);
const root = this.getElement("root");
const cells = this.getElement("cells");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,9 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
"highlighters.css",
)

Просмотреть файл

@ -6,6 +6,7 @@
DIRS += [
"utils",
"css",
]
DevToolsModules(

Просмотреть файл

@ -205,7 +205,8 @@ class Infobar {
*/
getTextContent(id) {
const anonymousContent = this.markup.content;
return anonymousContent.getTextContentForElement(`${this.prefix}${id}`);
return anonymousContent.root.getElementById(`${this.prefix}${id}`)
.textContent;
}
/**

Просмотреть файл

@ -8,7 +8,6 @@ const {
getCurrentZoom,
getWindowDimensions,
getViewportDimensions,
loadSheet,
} = require("resource://devtools/shared/layout/utils.js");
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
@ -45,7 +44,8 @@ exports.removePseudoClassLock = (...args) =>
const SVG_NS = "http://www.w3.org/2000/svg";
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const STYLESHEET_URI = "resource://devtools/server/actors/highlighters.css";
const STYLESHEET_URI =
"resource://devtools-highlighter-styles/highlighters.css";
const _tokens = Symbol("classList/tokens");
@ -239,14 +239,6 @@ CanvasFrameAnonymousContentHelper.prototype = {
this.anonymousContentWindow = this.highlighterEnv.window;
this.pageListenerTarget = this.highlighterEnv.pageListenerTarget;
// For now highlighters.css is injected in content as a ua sheet because
// we no longer support scoped style sheets (see bug 1345702).
// If it did, highlighters.css would be injected as an anonymous content
// node using CanvasFrameAnonymousContentHelper instead.
loadSheet(this.anonymousContentWindow, STYLESHEET_URI);
const node = this.nodeBuilder();
// It was stated that hidden documents don't accept
// `insertAnonymousContent` calls yet. That doesn't seems the case anymore,
// at least on desktop. Therefore, removing the code that was dealing with
@ -257,7 +249,6 @@ CanvasFrameAnonymousContentHelper.prototype = {
// to ensure the anonymous content will be rendered (see Bug 1580394).
const forceSynchronousLayoutUpdate = !this.waitForDocumentToLoad;
this._content = this.anonymousContentDocument.insertAnonymousContent(
node,
forceSynchronousLayoutUpdate
);
} catch (e) {
@ -278,13 +269,18 @@ CanvasFrameAnonymousContentHelper.prototype = {
{ once: true }
);
});
this._content =
this.anonymousContentDocument.insertAnonymousContent(node);
this._content = this.anonymousContentDocument.insertAnonymousContent();
} else {
throw e;
}
}
const link = this.anonymousContentDocument.createElement("link");
link.href = STYLESHEET_URI;
link.rel = "stylesheet";
this._content.root.appendChild(link);
this._content.root.appendChild(this.nodeBuilder());
this._initialized();
},
@ -312,36 +308,42 @@ CanvasFrameAnonymousContentHelper.prototype = {
}
},
_getNodeById(id) {
return this.content?.root.getElementById(id);
},
getComputedStylePropertyValue(id, property) {
return (
this.content && this.content.getComputedStylePropertyValue(id, property)
);
const node = this._getNodeById(id);
if (!node) {
return null;
}
return this.anonymousContentWindow
.getComputedStyle(node)
.getPropertyValue(property);
},
getTextContentForElement(id) {
return this.content && this.content.getTextContentForElement(id);
return this._getNodeById(id)?.textContent;
},
setTextContentForElement(id, text) {
if (this.content) {
this.content.setTextContentForElement(id, text);
const node = this._getNodeById(id);
if (!node) {
return;
}
node.textContent = text;
},
setAttributeForElement(id, name, value) {
if (this.content) {
this.content.setAttributeForElement(id, name, value);
}
this._getNodeById(id)?.setAttribute(name, value);
},
getAttributeForElement(id, name) {
return this.content && this.content.getAttributeForElement(id, name);
return this._getNodeById(id)?.getAttribute(name);
},
removeAttributeForElement(id, name) {
if (this.content) {
this.content.removeAttributeForElement(id, name);
}
this._getNodeById(id)?.removeAttribute(name);
},
hasAttributeForElement(id, name) {
@ -349,7 +351,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
},
getCanvasContext(id, type = "2d") {
return this.content && this.content.getCanvasContext(id, type);
return this._getNodeById(id)?.getContext(type);
},
/**
@ -544,7 +546,8 @@ CanvasFrameAnonymousContentHelper.prototype = {
const zoom = getCurrentZoom(node);
// Hide the root element and force the reflow in order to get the proper window's
// dimensions without increasing them.
this.setAttributeForElement(id, "style", "display: none");
const root = this._getNodeById(id);
root.style.display = "none";
node.offsetWidth;
let { width, height } = getWindowDimensions(boundaryWindow);
@ -556,9 +559,8 @@ CanvasFrameAnonymousContentHelper.prototype = {
height *= zoom;
}
value += `position:absolute; width:${width}px;height:${height}px; overflow:hidden`;
this.setAttributeForElement(id, "style", value);
value += `position:absolute; width:${width}px;height:${height}px; overflow:hidden;`;
root.style = value;
},
/**

Просмотреть файл

@ -37,7 +37,6 @@ DevToolsModules(
"errordocs.js",
"frame.js",
"heap-snapshot-file.js",
"highlighters.css",
"highlighters.js",
"layout.js",
"manifest.js",

Просмотреть файл

@ -663,7 +663,17 @@ class StyleSheetsManager extends EventEmitter {
this._targetActor.window.document,
true
);
return styleSheets.indexOf(styleSheet);
let i = 0;
for (const sheet of styleSheets) {
if (!this._shouldListSheet(sheet)) {
continue;
}
if (sheet == styleSheet) {
return i;
}
i++;
}
return -1;
}
/**
@ -815,13 +825,18 @@ class StyleSheetsManager extends EventEmitter {
* @returns {Boolean}
*/
_shouldListSheet(styleSheet) {
// Special case about:PreferenceStyleSheet, as it is generated on the
// fly and the URI is not registered with the about: handler.
// Special case about:PreferenceStyleSheet, as it is generated on the fly
// and the URI is not registered with the about: handler.
// https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
if (styleSheet.href?.toLowerCase() === "about:preferencestylesheet") {
const href = styleSheet.href?.toLowerCase();
if (href === "about:preferencestylesheet") {
return false;
}
// FIXME(bug 1826538): Make accessiblecaret.css and similar UA-widget
// sheets system sheets, then remove this special-case.
if (href === "resource://content-accessible/accessiblecaret.css") {
return false;
}
return true;
}

Просмотреть файл

@ -3,13 +3,11 @@
<!--
Test that css-logic calculates CSS specificity properly
-->
<head>
<meta charset="utf-8">
<title>Test css-logic specificity</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
</head>
<meta charset="utf-8">
<title>Test css-logic specificity</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<body style="background:blue;">
<script type="application/javascript">
<script>
"use strict";
window.onload = function() {
@ -37,7 +35,7 @@ Test that css-logic calculates CSS specificity properly
function createDocument() {
let text = TEST_DATA.map(i=>i.text).join(",");
text = '<style type="text/css">' + text + " {color:red;}</style>";
text = '<style>' + text + " {color:red;}</style>";
// eslint-disable-next-line no-unsanitized/property
document.body.innerHTML = text;
}
@ -52,11 +50,18 @@ Test that css-logic calculates CSS specificity properly
const cssLogic = new CssLogic();
cssLogic.highlight(document.body);
const cssSheet = cssLogic.sheets[0];
// There could be more stylesheets due to e.g, accessiblecaret, so find the
// right one.
info(`Sheets: ${cssLogic.sheets.map(s => s.href).join(", ")}`);
const cssSheet = cssLogic.sheets.find(s => s.href == location.href);
const cssRule = cssSheet.domSheet.cssRules[0];
const selectors = CssLogic.getSelectors(cssRule);
info("Iterating over the test selectors");
is(selectors.length, TEST_DATA.length, "Should be the right rule");
info("Iterating over the test selectors: " + selectors.join(", "));
for (let i = 0; i < selectors.length; i++) {
const selectorText = selectors[i];
info("Testing selector " + selectorText);

Просмотреть файл

@ -32,16 +32,16 @@ window.onload = async function() {
const id = elementID => `${highlighter.ID_CLASS_PREFIX}${elementID}`;
function isHidden(elementID) {
const attr = anonymousContent.getAttributeForElement(id(elementID), "hidden");
const attr = anonymousContent.root.getElementById(id(elementID)).getAttribute("hidden");
return typeof attr === "string" && attr == "true";
}
function getReason() {
return anonymousContent.getTextContentForElement(id("reason"));
return anonymousContent.root.getElementById(id("reason")).textContent;
}
function isOverlayShown() {
const attr = anonymousContent.getAttributeForElement(id("root"), "overlay");
const attr = anonymousContent.root.getElementById(id("root")).getAttribute("overlay");
return typeof attr === "string" && attr == "true";
}

Просмотреть файл

@ -125,6 +125,18 @@ add_task(async function () {
await testNestedResourceUpdateFeature();
});
function pushAvailableResource(availableResources) {
// TODO(bug 1826538): Find a better way of dealing with these.
return function (resources) {
for (const resource of resources) {
if (resource.href?.startsWith("resource://")) {
continue;
}
availableResources.push(resource);
}
};
}
async function testResourceAvailableFeature() {
info("Check resource available feature of the ResourceCommand");
@ -143,7 +155,7 @@ async function testResourceAvailableFeature() {
info("Check whether ResourceCommand gets existing stylesheet");
const availableResources = [];
await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
onAvailable: resources => availableResources.push(...resources),
onAvailable: pushAvailableResource(availableResources),
});
is(
@ -238,7 +250,7 @@ async function testResourceUpdateFeature() {
const availableResources = [];
const updates = [];
await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
onAvailable: resources => availableResources.push(...resources),
onAvailable: pushAvailableResource(availableResources),
onUpdated: newUpdates => updates.push(...newUpdates),
});
is(
@ -376,7 +388,7 @@ async function testNestedResourceUpdateFeature() {
const availableResources = [];
const updates = [];
await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
onAvailable: resources => availableResources.push(...resources),
onAvailable: pushAvailableResource(availableResources),
onUpdated: newUpdates => updates.push(...newUpdates),
});
is(

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 538 B

После

Ширина:  |  Высота:  |  Размер: 538 B

Просмотреть файл

@ -0,0 +1,13 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
"command-pick-remote-touch.svg",
"command-pick.svg",
"error-small.svg",
"resume.svg",
"stepOver.svg",
)

Просмотреть файл

@ -5,18 +5,11 @@
devtools.jar:
% content devtools %content/
content/shared/webextension-fallback.html (webextension-fallback.html)
# Those images are in devtools/shared (and not in devtools/client/debugger/images like
# the other debugger images) because they are used in both the debugger UI and in the
# PausedDebuggerOverlay (which is in devtools/server).
content/shared/images/resume.svg (images/resume.svg)
content/shared/images/stepOver.svg (images/stepOver.svg)
# These images are in devtools/shared (and not in devtools/client/themes/images/) because
# they're also used in the RemoteNodePickerNotice (which is in devtools/server).
content/shared/images/command-pick.svg (images/command-pick.svg)
content/shared/images/command-pick-remote-touch.svg (images/command-pick-remote-touch.svg)
% resource devtools %modules/devtools/
% resource devtools-client-jsonview resource://devtools/client/jsonview/ contentaccessible=yes
% resource devtools-client-shared resource://devtools/client/shared/ contentaccessible=yes
% resource devtools-highlighter-styles resource://devtools/server/actors/highlighters/css/ contentaccessible=yes
% resource devtools-shared-images resource://devtools/shared/images/ contentaccessible=true
% content devtools-jsonview-styles %modules/devtools/client/jsonview/css/ contentaccessible=yes
# The typical approach would be to list all the resource files in this manifest
# for installation. Instead of doing this, use the DevToolsModules syntax via

Просмотреть файл

@ -12,6 +12,7 @@ DIRS += [
"compatibility",
"discovery",
"heapsnapshot",
"images",
"inspector",
"jsbeautify",
"layout",

Просмотреть файл

@ -5,216 +5,41 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AnonymousContent.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/AnonymousContentBinding.h"
#include "nsComputedDOMStyle.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIFrame.h"
#include "nsStyledElement.h"
#include "HTMLCanvasElement.h"
namespace mozilla::dom {
// Ref counting and cycle collection
NS_IMPL_CYCLE_COLLECTION(AnonymousContent, mContentNode)
NS_IMPL_CYCLE_COLLECTION(AnonymousContent, mHost, mRoot)
AnonymousContent::AnonymousContent(already_AddRefed<Element> aContentNode)
: mContentNode(aContentNode) {
MOZ_ASSERT(mContentNode);
already_AddRefed<AnonymousContent> AnonymousContent::Create(Document& aDoc) {
RefPtr<Element> host = aDoc.CreateHTMLElement(nsGkAtoms::div);
if (!host) {
return nullptr;
}
host->SetAttr(kNameSpaceID_None, nsGkAtoms::role, u"presentation"_ns, false);
RefPtr<ShadowRoot> root = host->AttachShadowWithoutNameChecks(
ShadowRootMode::Closed, Element::DelegatesFocus::No);
root->SetIsUAWidget();
return do_AddRef(new AnonymousContent(host.forget(), root.forget()));
}
AnonymousContent::AnonymousContent(already_AddRefed<Element> aHost,
already_AddRefed<ShadowRoot> aRoot)
: mHost(aHost), mRoot(aRoot) {
MOZ_ASSERT(mHost);
MOZ_ASSERT(mRoot);
}
AnonymousContent::~AnonymousContent() = default;
void AnonymousContent::SetTextContentForElement(const nsAString& aElementId,
const nsAString& aText,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
element->SetTextContent(aText, aRv);
}
void AnonymousContent::GetTextContentForElement(const nsAString& aElementId,
DOMString& aText,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
element->GetTextContent(aText, aRv);
}
void AnonymousContent::SetAttributeForElement(const nsAString& aElementId,
const nsAString& aName,
const nsAString& aValue,
nsIPrincipal* aSubjectPrincipal,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
element->SetAttribute(aName, aValue, aSubjectPrincipal, aRv);
}
void AnonymousContent::GetAttributeForElement(const nsAString& aElementId,
const nsAString& aName,
DOMString& aValue,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
element->GetAttribute(aName, aValue);
}
void AnonymousContent::RemoveAttributeForElement(const nsAString& aElementId,
const nsAString& aName,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
element->RemoveAttribute(aName, aRv);
}
already_AddRefed<nsISupports> AnonymousContent::GetCanvasContext(
const nsAString& aElementId, const nsAString& aContextId,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
if (!element->IsHTMLElement(nsGkAtoms::canvas)) {
return nullptr;
}
nsCOMPtr<nsISupports> context;
HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(element);
canvas->GetContext(aContextId, getter_AddRefs(context));
return context.forget();
}
already_AddRefed<Animation> AnonymousContent::SetAnimationForElement(
JSContext* aContext, const nsAString& aElementId,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
return element->Animate(aContext, aKeyframes, aOptions, aRv);
}
void AnonymousContent::SetCutoutRectsForElement(
const nsAString& aElementId, const Sequence<OwningNonNull<DOMRect>>& aRects,
ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
nsRegion cutOutRegion;
for (const auto& r : aRects) {
CSSRect rect(r->X(), r->Y(), r->Width(), r->Height());
cutOutRegion.OrWith(CSSRect::ToAppUnits(rect));
}
element->SetProperty(nsGkAtoms::cutoutregion, new nsRegion(cutOutRegion),
nsINode::DeleteProperty<nsRegion>);
nsIFrame* frame = element->GetPrimaryFrame();
if (frame) {
frame->SchedulePaint();
}
}
Element* AnonymousContent::GetElementById(const nsAString& aElementId) {
// This can be made faster in the future if needed.
RefPtr<nsAtom> elementId = NS_Atomize(aElementId);
for (nsIContent* node = mContentNode; node;
node = node->GetNextNode(mContentNode)) {
if (!node->IsElement()) {
continue;
}
nsAtom* id = node->AsElement()->GetID();
if (id && id == elementId) {
return node->AsElement();
}
}
return nullptr;
}
bool AnonymousContent::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector) {
return AnonymousContent_Binding::Wrap(aCx, this, aGivenProto, aReflector);
}
void AnonymousContent::GetComputedStylePropertyValue(
const nsAString& aElementId, const nsACString& aPropertyName,
nsACString& aResult, ErrorResult& aRv) {
Element* element = GetElementById(aElementId);
if (!element) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
if (!element->OwnerDoc()->GetPresShell()) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
RefPtr<nsComputedDOMStyle> cs = new nsComputedDOMStyle(
element, PseudoStyleType::NotPseudo, element->OwnerDoc(),
nsComputedDOMStyle::StyleType::All);
cs->GetPropertyValue(aPropertyName, aResult);
}
void AnonymousContent::GetTargetIdForEvent(Event& aEvent, DOMString& aResult) {
nsCOMPtr<Element> el = do_QueryInterface(aEvent.GetOriginalTarget());
if (el && el->IsInNativeAnonymousSubtree() && mContentNode->Contains(el)) {
aResult.SetKnownLiveAtom(el->GetID(), DOMString::eTreatNullAsNull);
return;
}
aResult.SetNull();
}
void AnonymousContent::SetStyle(const nsACString& aProperty,
const nsACString& aValue, ErrorResult& aRv) {
if (!mContentNode->IsHTMLElement()) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
nsGenericHTMLElement* element = nsGenericHTMLElement::FromNode(mContentNode);
nsCOMPtr<nsICSSDeclaration> declaration = element->Style();
declaration->SetProperty(aProperty, aValue, ""_ns, IgnoreErrors());
}
} // namespace mozilla::dom

Просмотреть файл

@ -7,25 +7,15 @@
#ifndef mozilla_dom_AnonymousContent_h
#define mozilla_dom_AnonymousContent_h
#include "mozilla/AlreadyAddRefed.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
class nsIPrincipal;
namespace mozilla::dom {
namespace mozilla {
class ErrorResult;
namespace dom {
class Animation;
class DOMRect;
class DOMString;
class Element;
class Event;
template <typename T>
class Sequence;
class UnrestrictedDoubleOrAnonymousKeyframeAnimationOptions;
class UnrestrictedDoubleOrKeyframeAnimationOptions;
class Document;
class ShadowRoot;
class AnonymousContent final {
public:
@ -33,61 +23,23 @@ class AnonymousContent final {
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnonymousContent)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnonymousContent)
explicit AnonymousContent(already_AddRefed<Element> aContentNode);
Element& ContentNode() { return *mContentNode; }
static already_AddRefed<AnonymousContent> Create(Document&);
Element* Host() const { return mHost.get(); }
ShadowRoot* Root() const { return mRoot.get(); }
Element* GetElementById(const nsAString& aElementId);
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
// WebIDL methods
void SetTextContentForElement(const nsAString& aElementId,
const nsAString& aText, ErrorResult& aRv);
void GetTextContentForElement(const nsAString& aElementId, DOMString& aText,
ErrorResult& aRv);
void SetAttributeForElement(const nsAString& aElementId,
const nsAString& aName, const nsAString& aValue,
nsIPrincipal* aSubjectPrincipal,
ErrorResult& aRv);
void GetAttributeForElement(const nsAString& aElementId,
const nsAString& aName, DOMString& aValue,
ErrorResult& aRv);
void RemoveAttributeForElement(const nsAString& aElementId,
const nsAString& aName, ErrorResult& aRv);
already_AddRefed<nsISupports> GetCanvasContext(const nsAString& aElementId,
const nsAString& aContextId,
ErrorResult& aRv);
already_AddRefed<Animation> SetAnimationForElement(
JSContext* aContext, const nsAString& aElementId,
JS::Handle<JSObject*> aKeyframes,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError);
void SetCutoutRectsForElement(const nsAString& aElementId,
const Sequence<OwningNonNull<DOMRect>>& aRects,
ErrorResult& aError);
void GetComputedStylePropertyValue(const nsAString& aElementId,
const nsACString& aPropertyName,
nsACString& aResult, ErrorResult& aRv);
void GetTargetIdForEvent(Event& aEvent, DOMString& aResult);
void SetStyle(const nsACString& aProperty, const nsACString& aValue,
ErrorResult& aRv);
private:
~AnonymousContent();
RefPtr<Element> mContentNode;
explicit AnonymousContent(already_AddRefed<Element> aHost,
already_AddRefed<ShadowRoot> aRoot);
RefPtr<Element> mHost;
RefPtr<ShadowRoot> mRoot;
};
} // namespace dom
} // namespace mozilla
} // namespace mozilla::dom
#endif // mozilla_dom_AnonymousContent_h

Просмотреть файл

@ -8293,14 +8293,8 @@ static Element* GetCustomContentContainer(PresShell* aPresShell) {
}
already_AddRefed<AnonymousContent> Document::InsertAnonymousContent(
Element& aElement, bool aForce, ErrorResult& aRv) {
// Clone the node to avoid returning a direct reference.
nsCOMPtr<nsINode> clone = aElement.CloneNode(true, aRv);
if (aRv.Failed()) {
return nullptr;
}
PresShell* shell = GetPresShell();
bool aForce, ErrorResult& aRv) {
RefPtr<PresShell> shell = GetPresShell();
if (aForce && !GetCustomContentContainer(shell)) {
FlushPendingNotifications(FlushType::Layout);
shell = GetPresShell();
@ -8308,14 +8302,23 @@ already_AddRefed<AnonymousContent> Document::InsertAnonymousContent(
nsAutoScriptBlocker scriptBlocker;
auto anonContent =
MakeRefPtr<AnonymousContent>(clone.forget().downcast<Element>());
RefPtr<AnonymousContent> anonContent = AnonymousContent::Create(*this);
if (!anonContent) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
mAnonymousContents.AppendElement(anonContent);
if (Element* container = GetCustomContentContainer(shell)) {
container->AppendChildTo(&anonContent->ContentNode(), true, IgnoreErrors());
shell->GetCanvasFrame()->ShowCustomContentContainer();
if (RefPtr<Element> container = GetCustomContentContainer(shell)) {
// If the container is empty and we have other anon content we should be
// about to show all the other anonymous content nodes.
if (container->HasChildren() || mAnonymousContents.Length() == 1) {
container->AppendChildTo(anonContent->Host(), true, IgnoreErrors());
if (auto* canvasFrame = shell->GetCanvasFrame()) {
canvasFrame->ShowCustomContentContainer();
}
}
}
return anonContent.forget();
@ -8327,7 +8330,7 @@ static void RemoveAnonContentFromCanvas(AnonymousContent& aAnonContent,
if (!container) {
return;
}
container->RemoveChild(aAnonContent.ContentNode(), IgnoreErrors());
container->RemoveChild(*aAnonContent.Host(), IgnoreErrors());
}
void Document::RemoveAnonymousContent(AnonymousContent& aContent) {

Просмотреть файл

@ -1345,9 +1345,9 @@ class Document : public nsINode,
*/
nsresult GetSrcdocData(nsAString& aSrcdocData);
already_AddRefed<AnonymousContent> InsertAnonymousContent(
Element& aElement, bool aForce, ErrorResult& aError);
void RemoveAnonymousContent(AnonymousContent& aContent);
already_AddRefed<AnonymousContent> InsertAnonymousContent(bool aForce,
ErrorResult&);
void RemoveAnonymousContent(AnonymousContent&);
/**
* If aNode is a descendant of anonymous content inserted by
* InsertAnonymousContent, this method returns the root element of the

Просмотреть файл

@ -267,7 +267,6 @@ skip-if =
skip-if = headless # Bug 1405867
[test_anonymousContent_insert.html]
[test_anonymousContent_manipulate_content.html]
[test_anonymousContent_set_style.html]
[test_anonymousContent_style_csp.html]
[test_appname_override.html]
[test_async_setTimeout_stack.html]

Просмотреть файл

@ -1,18 +1,13 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Test the chrome-only AnonymousContent API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Test the chrome-only AnonymousContent API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1020244">Mozilla Bug 1020244</a>
<script type="application/javascript">
<script>
// Testing the presence of the chrome-only API
ok(!document.insertAnonymousContent,
"Content document shouldn't have access to insertAnonymousContent");
@ -25,32 +20,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
ok(chromeDocument.removeAnonymousContent,
"Chrome document should have access to removeAnonymousContent");
// Testing invalid inputs
let invalidNodes = [null, undefined, false, 1, "string"];
for (let node of invalidNodes) {
let didThrow = false;
try {
chromeDocument.insertAnonymousContent(node);
} catch (e) {
didThrow = true;
}
ok(didThrow, "Passing an invalid node to insertAnonymousContent should throw");
}
// Testing the API of the returned object
let anonymousContent = chromeDocument.insertAnonymousContent();
ok(anonymousContent, "AnonymousContent object returned");
let div = document.createElement("div");
div.textContent = "this is a test element";
let anonymousContent = chromeDocument.insertAnonymousContent(div);
ok(anonymousContent, "AnonymousContent object returned");
let members = ["getTextContentForElement", "setTextContentForElement",
"getAttributeForElement", "setAttributeForElement",
"removeAttributeForElement", "getCanvasContext",
"setAnimationForElement", "setStyle"];
for (let member of members) {
ok(member in anonymousContent, "AnonymousContent object defines " + member);
}
anonymousContent.root.appendChild(div);
chromeDocument.removeAnonymousContent(anonymousContent);
</script>
</body>
</html>
</script>

Просмотреть файл

@ -1,25 +1,22 @@
<!DOCTYPE HTML>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1020244 -->
<head>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Make sure anonymous content still works after a reflow (after the canvasframe has been reconstructed)</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Make sure anonymous content still works after a reflow (after the canvasframe has been reconstructed)</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<div>
<div id="test-element" style="color:red;">text content</div>
</div>
<script type="application/javascript">
<script>
info("Inserting anonymous content into the document frame");
let chromeDocument = SpecialPowers.wrap(document);
let anonymousContent = chromeDocument.insertAnonymousContent();
let testElement = document.querySelector("div");
let anonymousContent = chromeDocument.insertAnonymousContent(testElement);
anonymousContent.root.appendChild(testElement.cloneNode(true));
info("Modifying the style of an element in the anonymous content");
let style = anonymousContent.setAttributeForElement("test-element",
"style", "color:green;");
anonymousContent.root.getElementById("test-element").style.color = "green";
info("Toggling the display style on the document element to force reframing");
// Note that we force sync reflows to make sure the canvasframe is recreated
@ -30,11 +27,9 @@
forceFlush = document.documentElement.offsetHeight;
info("Checking that the anonymous content can be retrieved still");
style = anonymousContent.getAttributeForElement("test-element", "style");
is(style, "color:green;", "The anonymous content still exists after reflow");
let color = anonymousContent.root.getElementById("test-element").style.color;
is(color, "green", "The anonymous content still exists after reflow");
info("Removing the anonymous content");
chromeDocument.removeAnonymousContent(anonymousContent);
</script>
</body>
</html>

Просмотреть файл

@ -3,57 +3,46 @@
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1212477
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1212477 - Needs a way to access to &lt;canvas&gt;'s context (2d, webgl) from Anonymous Content API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1212477">Mozilla Bug 1212477</a>
<div>
<div id="id" class="test">text content</div>
<canvas id="canvas2d"></canvas>
<canvas id="canvas-webgl"></canvas>
<canvas id="canvas-foo"></canvas>
</div>
<script type="application/javascript">
let chromeDocument = SpecialPowers.wrap(document);
let testElement = document.querySelector("div");
<meta charset="utf-8">
<title>Test for Bug 1212477 - Needs a way to access to &lt;canvas&gt;'s context (2d, webgl) from Anonymous Content API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1212477">Mozilla Bug 1212477</a>
<div>
<div id="id" class="test">text content</div>
<canvas id="canvas2d"></canvas>
<canvas id="canvas-webgl"></canvas>
<canvas id="canvas-foo"></canvas>
</div>
<script>
let chromeDocument = SpecialPowers.wrap(document);
let testElement = document.querySelector("div");
let anonymousContent = chromeDocument.insertAnonymousContent(testElement);
let anonymousContent = chromeDocument.insertAnonymousContent();
anonymousContent.root.appendChild(testElement.cloneNode(true));
is(anonymousContent.getCanvasContext("id", "2d"), null,
"Context is null for non-canvas elements");
let canvas2d = anonymousContent.root.getElementById("canvas2d");
let context2d = canvas2d.getContext("2d");
let context2d = anonymousContent.getCanvasContext("canvas2d", "2d");
is(context2d.toString(), "[object CanvasRenderingContext2D]",
"2D Context is returned properly");
is(context2d.toString(), "[object CanvasRenderingContext2D]",
"2D Context is returned properly");
is(context2d.canvas, null, "context's canvas property");
is(context2d.canvas, null,
"context's canvas property is null in anonymous content");
is(anonymousContent.root.getElementById("canvas-foo").getContext("foo"),
null,
"Context is null for unknown context type");
is (anonymousContent.getCanvasContext("canvas-foo", "foo"), null,
"Context is null for unknown context type");
const normalWebGL = document.createElement('canvas').getContext('webgl');
if (normalWebGL) {
let canvasWebGL = anonymousContent.root.getElementById("canvas-webgl");
let webgl = canvasWebGL.getContext("webgl");
SimpleTest.doesThrow(
() => anonymousContent.getCanvasContext("foo", "2d"),
"NS_ERROR_NOT_AVAILABLE",
"Get a context using unexisting id should throw"
);
is(webgl.toString(), "[object WebGLRenderingContext]",
"WebGL Context is returned properly");
const normalWebGL = document.createElement('canvas').getContext('webgl');
if (normalWebGL) {
let webgl = anonymousContent.getCanvasContext("canvas-webgl", "webgl");
is(webgl.toString(), "[object WebGLRenderingContext]",
"WebGL Context is returned properly");
is(webgl.canvas, null,
"WebGL context's canvas property is null in anonymous content");
}
chromeDocument.removeAnonymousContent(anonymousContent);
</script>
</body>
</html>
is(webgl.canvas, null,
"WebGL context's canvas property is null in anonymous content");
}
chromeDocument.removeAnonymousContent(anonymousContent);
</script>

Просмотреть файл

@ -3,18 +3,15 @@
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Insert content using the AnonymousContent API, several times, and don't remove it</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Insert content using the AnonymousContent API, several times, and don't remove it</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1020244">Mozilla Bug 1020244</a>
<div>
<div id="id" class="test">text content</div>
</div>
<script type="application/javascript">
<script>
const INSERTED_NB = 5;
// Insert the same content several times
@ -23,7 +20,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
let anonymousContents = [];
for (let i = 0; i < INSERTED_NB; i ++) {
let content = chromeDocument.insertAnonymousContent(testElement);
let content = chromeDocument.insertAnonymousContent();
content.root.appendChild(testElement.cloneNode(true));
// Adding an expando pointing to the document to make sure this doesn't
// cause leaks.
content.dummyExpando = testElement.ownerDocument;
@ -32,14 +30,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
// Make sure that modifying one of the inserted elements does not modify the
// other ones.
anonymousContents[0].setAttributeForElement("id", "class", "updated");
anonymousContents[0].root.getElementById("id").className = "updated";
for (let i = 1; i < INSERTED_NB; i ++) {
is(anonymousContents[i].getAttributeForElement("id", "class"),
"test",
"Element " + i + " didn't change when element 0 was changed");
is(
anonymousContents[i].root.getElementById("id").className,
"test",
"Element " + i + " didn't change when element 0 was changed"
);
}
// Do not remove inserted elements on purpose to test for potential leaks too.
</script>
</body>
</html>
</script>

Просмотреть файл

@ -3,64 +3,33 @@
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Manipulate content created with the AnonymousContent API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<meta charset="utf-8">
<title>Test for Bug 1020244 - Manipulate content created with the AnonymousContent API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1020244">Mozilla Bug 1020244</a>
<div>
<div id="test-element" class="test-class" test="test">text content</div>
</div>
<script type="application/javascript">
<script>
// Insert content
let chromeDocument = SpecialPowers.wrap(document);
let testElement = document.querySelector("div");
let anonymousContent = chromeDocument.insertAnonymousContent(testElement);
let anonymousContent = chromeDocument.insertAnonymousContent();
anonymousContent.root.appendChild(testElement.cloneNode(true));
// Test getting/setting text content.
is(anonymousContent.getTextContentForElement("test-element"),
is(anonymousContent.root.getElementById("test-element").textContent,
"text content", "Textcontent for the test element is correct");
anonymousContent.setTextContentForElement("test-element",
"updated text content");
is(anonymousContent.getTextContentForElement("test-element"),
let anonTestElement = anonymousContent.root.getElementById("test-element");
anonTestElement.textContent =
"updated text content";
is(anonTestElement.textContent,
"updated text content",
"Textcontent for the test element is correct after update");
// Test that modifying the original DOM element doesn't change the inserted
// element.
testElement.removeAttribute("test");
is(anonymousContent.getAttributeForElement("test-element", "test"),
"test",
"Removing attributes on the original DOM node does not change the inserted node");
testElement.setAttribute("test", "test-updated");
is(anonymousContent.getAttributeForElement("test-element", "test"),
"test",
"Setting attributes on the original DOM node does not change the inserted node");
// Test getting/setting/removing attributes on the inserted element and test
// that this doesn't change the original DOM element.
is(anonymousContent.getAttributeForElement("test-element", "class"),
"test-class", "Class attribute for the test element is correct");
anonymousContent.setAttributeForElement("test-element", "class",
"updated-test-class");
is(anonymousContent.getAttributeForElement("test-element", "class"),
"updated-test-class",
"Class attribute for the test element is correct after update");
ok(testElement.getAttribute("class") !== "updated-test-class",
"Class attribute change on the inserted node does not change the original DOM node");
anonymousContent.removeAttributeForElement("test-element", "class");
is(anonymousContent.getAttributeForElement("test-element", "class"), null,
"Class attribute for the test element was removed");
let anim = anonymousContent.setAnimationForElement("test-element", [
let anim = anonTestElement.animate([
{ transform: 'translateY(0px)' },
{ transform: 'translateY(-300px)' }
], 2000);
@ -69,6 +38,4 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1020244
is(anim.playState, "idle", "Animation should have stopped immediately");
chromeDocument.removeAnonymousContent(anonymousContent);
</script>
</body>
</html>
</script>

Просмотреть файл

@ -1,35 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1571650
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1571650 - Test AnonymousContent.setStyle() API</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1571650">Mozilla Bug 1571650</a>
<script type="application/javascript">
const chromeDocument = SpecialPowers.wrap(document);
info("Set z-index to anonymous content");
const div = document.createElement("div");
div.setAttribute("class", "set-style-test");
const anonymousContent = chromeDocument.insertAnonymousContent(div);
anonymousContent.setStyle("z-index", 3);
info("Test the element which became to anonymous");
const mozCustomContentContainerEl =
[...SpecialPowers.InspectorUtils.getChildrenForNode(document.documentElement, true, false)]
.find(n => n.classList && n.classList.contains("moz-custom-content-container"));
const targetEl = mozCustomContentContainerEl.querySelector(".set-style-test");
ok(targetEl, "Element which became to anonymous is found");
is(targetEl.style.zIndex, "3", "z-index is correct");
chromeDocument.removeAnonymousContent(anonymousContent);
</script>
</body>
</html>

Просмотреть файл

@ -1,28 +1,23 @@
<!DOCTYPE HTML>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1020244 -->
<head>
<meta charset="utf-8">
<title>Test for Bug 1185351 - Make sure that we don't enforce CSP on styles for AnonymousContent</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<meta charset="utf-8">
<title>Test for Bug 1185351 - Make sure that we don't enforce CSP on styles for AnonymousContent</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<div>
<div id="test-element" style="color:red;">text content</div>
</div>
<script type="application/javascript">
<script>
let chromeDocument = SpecialPowers.wrap(document);
let testElement = document.querySelector("div");
let anonymousContent = chromeDocument.insertAnonymousContent(testElement);
let anonymousContent = chromeDocument.insertAnonymousContent();
anonymousContent.root.appendChild(testElement.cloneNode(true));
let style = anonymousContent.setAttributeForElement("test-element",
"style", "color:green;");
style = anonymousContent.getAttributeForElement("test-element", "style");
is(style, "color:green;", "The anonymous content exists with CSP");
let el = anonymousContent.root.getElementById("test-element");
el.style = `color: green;`;
let style = el.getAttribute("style");
is(style, "color: green;", "The anonymous content exists with CSP");
chromeDocument.removeAnonymousContent(anonymousContent);
</script>
</body>
</html>

Просмотреть файл

@ -7,94 +7,11 @@
/*
* This file declares the AnonymousContent interface which is used to
* manipulate content that has been inserted into the document's canvasFrame
* anonymous container.
* See Document.insertAnonymousContent.
*
* This API never returns a reference to the actual inserted DOM node on
* purpose. This is to make sure the content cannot be randomly changed and the
* DOM cannot be traversed from the node, so that Gecko can remain in control of
* the inserted content.
* anonymous container. See Document.insertAnonymousContent.
* Users of this API must never remove the host of the shadow root.
*/
[ChromeOnly, Exposed=Window]
interface AnonymousContent {
/**
* Get the text content of an element inside this custom anonymous content.
*/
[Throws]
DOMString getTextContentForElement(DOMString elementId);
/**
* Set the text content of an element inside this custom anonymous content.
*/
[Throws]
undefined setTextContentForElement(DOMString elementId, DOMString text);
/**
* Get the value of an attribute of an element inside this custom anonymous
* content.
*/
[Throws]
DOMString? getAttributeForElement(DOMString elementId,
DOMString attributeName);
/**
* Set the value of an attribute of an element inside this custom anonymous
* content.
*/
[NeedsSubjectPrincipal=NonSystem, Throws]
undefined setAttributeForElement(DOMString elementId,
DOMString attributeName,
DOMString value);
/**
* Remove an attribute from an element inside this custom anonymous content.
*/
[Throws]
undefined removeAttributeForElement(DOMString elementId,
DOMString attributeName);
/**
* Get the canvas' context for the element specified if it's a <canvas>
* node, `null` otherwise.
*/
[Throws]
nsISupports? getCanvasContext(DOMString elementId,
DOMString contextId);
[Throws]
Animation setAnimationForElement(DOMString elementId,
object? keyframes,
optional UnrestrictedDoubleOrKeyframeAnimationOptions
options = {});
/**
* Accepts a list of (possibly overlapping) DOMRects which describe a shape
* in CSS pixels relative to the element's border box. This shape will be
* excluded from the element's background color rendering. The element will
* not render any background images once this method has been called.
*/
[Throws]
undefined setCutoutRectsForElement(DOMString elementId,
sequence<DOMRect> rects);
/**
* Get the computed value of a property on an element inside this custom
* anonymous content.
*/
[Throws]
UTF8String? getComputedStylePropertyValue(DOMString elementId,
UTF8String propertyName);
/**
* If event's original target is in the anonymous content, this returns the id
* attribute value of the target.
*/
DOMString? getTargetIdForEvent(Event event);
/**
* Set given style to this AnonymousContent.
*/
[Throws]
undefined setStyle(UTF8String property, UTF8String value);
readonly attribute ShadowRoot root;
};

Просмотреть файл

@ -506,20 +506,14 @@ partial interface Document {
* Chrome document anonymous content management.
* This is a Chrome-only API that allows inserting fixed positioned anonymous
* content on top of the current page displayed in the document.
* The supplied content is cloned and inserted into the document's CanvasFrame.
* Note that this only works for HTML documents.
*/
partial interface Document {
/**
* Deep-clones the provided element and inserts it into the CanvasFrame.
* Returns an AnonymousContent instance that can be used to manipulate the
* inserted element.
*
* If aForce is true, tries to update layout to be able to insert the element
* synchronously.
*/
[ChromeOnly, NewObject, Throws]
AnonymousContent insertAnonymousContent(Element aElement, optional boolean aForce = false);
AnonymousContent insertAnonymousContent(optional boolean aForce = false);
/**
* Removes the element inserted into the CanvasFrame given an AnonymousContent

Просмотреть файл

@ -7,15 +7,19 @@
#include "AccessibleCaret.h"
#include "AccessibleCaretLogger.h"
#include "mozilla/Assertions.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ToString.h"
#include "nsCanvasFrame.h"
#include "nsCaret.h"
#include "nsCSSFrameConstructor.h"
#include "nsDOMTokenList.h"
#include "nsGenericHTMLElement.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "nsPlaceholderFrame.h"
@ -33,9 +37,8 @@ using namespace dom;
NS_IMPL_ISUPPORTS(AccessibleCaret::DummyTouchListener, nsIDOMEventListener)
const nsLiteralString AccessibleCaret::sTextOverlayElementId =
u"text-overlay"_ns;
const nsLiteralString AccessibleCaret::sCaretImageElementId = u"image"_ns;
static constexpr auto kTextOverlayElementId = u"text-overlay"_ns;
static constexpr auto kCaretImageElementId = u"image"_ns;
#define AC_PROCESS_ENUM_TO_STREAM(e) \
case (e): \
@ -75,7 +78,6 @@ AccessibleCaret::AccessibleCaret(PresShell* aPresShell)
: mPresShell(aPresShell) {
// Check all resources required.
if (mPresShell) {
MOZ_ASSERT(RootFrame());
MOZ_ASSERT(mPresShell->GetDocument());
InjectCaretElement(mPresShell->GetDocument());
}
@ -87,12 +89,20 @@ AccessibleCaret::~AccessibleCaret() {
}
}
dom::Element* AccessibleCaret::TextOverlayElement() const {
return mCaretElementHolder->Root()->GetElementById(kTextOverlayElementId);
}
dom::Element* AccessibleCaret::CaretImageElement() const {
return mCaretElementHolder->Root()->GetElementById(kCaretImageElementId);
}
void AccessibleCaret::SetAppearance(Appearance aAppearance) {
if (mAppearance == aAppearance) {
return;
}
ErrorResult rv;
IgnoredErrorResult rv;
CaretElement().ClassList()->Remove(AppearanceString(mAppearance), rv);
MOZ_ASSERT(!rv.Failed(), "Remove old appearance failed!");
@ -171,7 +181,6 @@ void AccessibleCaret::EnsureApzAware() {
// if that's the case we register a dummy listener if there isn't one on
// the element already.
if (!CaretElement().IsApzAware()) {
// FIXME(emilio): Is this needed anymore?
CaretElement().AddEventListener(u"touchstart"_ns, mDummyTouchListener,
false);
}
@ -183,43 +192,53 @@ bool AccessibleCaret::IsInPositionFixedSubtree() const {
}
void AccessibleCaret::InjectCaretElement(Document* aDocument) {
IgnoredErrorResult rv;
RefPtr<Element> element = CreateCaretElement(aDocument);
mCaretElementHolder =
aDocument->InsertAnonymousContent(*element, /* aForce = */ false, rv);
aDocument->InsertAnonymousContent(/* aForce = */ false, IgnoreErrors());
MOZ_RELEASE_ASSERT(mCaretElementHolder, "We must have anonymous content!");
MOZ_ASSERT(!rv.Failed(), "Insert anonymous content should not fail!");
MOZ_ASSERT(mCaretElementHolder, "We must have anonymous content!");
// InsertAnonymousContent will clone the element to make an AnonymousContent.
// Since event listeners are not being cloned when cloning a node, we need to
// add the listener here.
CreateCaretElement();
EnsureApzAware();
}
already_AddRefed<Element> AccessibleCaret::CreateCaretElement(
Document* aDocument) const {
void AccessibleCaret::CreateCaretElement() const {
// Content structure of AccessibleCaret
// <div class="moz-accessiblecaret"> <- CaretElement()
// <div id="text-overlay"> <- TextOverlayElement()
// <div id="image"> <- CaretImageElement()
// <#shadow-root>
// <link rel="stylesheet" href="accessiblecaret.css">
// <div id="text-overlay"> <- TextOverlayElement()
// <div id="image"> <- CaretImageElement()
ErrorResult rv;
RefPtr<Element> parent = aDocument->CreateHTMLElement(nsGkAtoms::div);
parent->ClassList()->Add(u"moz-accessiblecaret"_ns, rv);
parent->ClassList()->Add(u"none"_ns, rv);
constexpr bool kNotify = false;
auto CreateAndAppendChildElement =
[aDocument, &parent](const nsLiteralString& aElementId) {
RefPtr<Element> child = aDocument->CreateHTMLElement(nsGkAtoms::div);
child->SetAttr(kNameSpaceID_None, nsGkAtoms::id, aElementId, true);
parent->AppendChildTo(child, false, IgnoreErrors());
};
Element& host = CaretElement();
host.SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
u"moz-accessiblecaret none"_ns, kNotify);
CreateAndAppendChildElement(sTextOverlayElementId);
CreateAndAppendChildElement(sCaretImageElementId);
ShadowRoot* root = mCaretElementHolder->Root();
Document* doc = host.OwnerDoc();
{
RefPtr<NodeInfo> linkNodeInfo = doc->NodeInfoManager()->GetNodeInfo(
nsGkAtoms::link, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
RefPtr<nsGenericHTMLElement> link =
NS_NewHTMLLinkElement(linkNodeInfo.forget());
if (NS_WARN_IF(!link)) {
return;
}
link->SetAttr(nsGkAtoms::rel, u"stylesheet"_ns, IgnoreErrors());
link->SetAttr(nsGkAtoms::href,
u"resource://content-accessible/accessiblecaret.css"_ns,
IgnoreErrors());
root->AppendChildTo(link, kNotify, IgnoreErrors());
}
return parent.forget();
auto CreateAndAppendChildElement = [&](const nsLiteralString& aElementId) {
RefPtr<Element> child = doc->CreateHTMLElement(nsGkAtoms::div);
child->SetAttr(kNameSpaceID_None, nsGkAtoms::id, aElementId, kNotify);
mCaretElementHolder->Root()->AppendChildTo(child, kNotify, IgnoreErrors());
};
CreateAndAppendChildElement(kTextOverlayElementId);
CreateAndAppendChildElement(kCaretImageElementId);
}
void AccessibleCaret::RemoveCaretElement(Document* aDocument) {

Просмотреть файл

@ -130,9 +130,7 @@ class AccessibleCaret {
// Element for 'Intersects' test. This is the container of the caret image
// and text-overlay elements. See CreateCaretElement() for the content
// structure.
dom::Element& CaretElement() const {
return mCaretElementHolder->ContentNode();
}
dom::Element& CaretElement() const { return *mCaretElementHolder->Host(); }
// Ensures that the caret element is made "APZ aware" so that the APZ code
// doesn't scroll the page when the user is trying to drag the caret.
@ -150,14 +148,10 @@ class AccessibleCaret {
float GetZoomLevel();
// Element which contains the text overly for the 'Contains' test.
dom::Element* TextOverlayElement() const {
return mCaretElementHolder->GetElementById(sTextOverlayElementId);
}
dom::Element* TextOverlayElement() const;
// Element which contains the caret image for 'Contains' test.
dom::Element* CaretImageElement() const {
return mCaretElementHolder->GetElementById(sCaretImageElementId);
}
dom::Element* CaretImageElement() const;
nsIFrame* RootFrame() const;
@ -166,7 +160,7 @@ class AccessibleCaret {
// Transform Appearance to CSS id used in ua.css.
static nsAutoString AppearanceString(Appearance aAppearance);
already_AddRefed<dom::Element> CreateCaretElement(dom::Document*) const;
void CreateCaretElement() const;
// Inject caret element into custom content container.
void InjectCaretElement(dom::Document*);
@ -223,11 +217,6 @@ class AccessibleCaret {
// A no-op touch-start listener which prevents APZ from panning when dragging
// the caret.
RefPtr<DummyTouchListener> mDummyTouchListener{new DummyTouchListener()};
// Static class variables
static const nsLiteralString sTextOverlayElementId;
static const nsLiteralString sCaretImageElementId;
}; // class AccessibleCaret
std::ostream& operator<<(std::ostream& aStream,

Просмотреть файл

@ -332,11 +332,7 @@ AccessibleCaretEventHub::AccessibleCaretEventHub(PresShell* aPresShell)
: mPresShell(aPresShell) {}
void AccessibleCaretEventHub::Init() {
if (mInitialized && mManager) {
mManager->OnFrameReconstruction();
}
if (mInitialized || !mPresShell || !mPresShell->GetCanvasFrame()) {
if (mInitialized || !mPresShell) {
return;
}

Просмотреть файл

@ -798,11 +798,6 @@ void AccessibleCaretManager::OnKeyboardEvent() {
}
}
void AccessibleCaretManager::OnFrameReconstruction() {
mCarets.GetFirst()->EnsureApzAware();
mCarets.GetSecond()->EnsureApzAware();
}
void AccessibleCaretManager::SetLastInputSource(uint16_t aInputSource) {
mLastInputSource = aInputSource;
}

Просмотреть файл

@ -115,10 +115,6 @@ class AccessibleCaretManager {
MOZ_CAN_RUN_SCRIPT
virtual void OnKeyboardEvent();
// The canvas frame holding the accessible caret anonymous content elements
// was reconstructed, resulting in the content elements getting cloned.
virtual void OnFrameReconstruction();
// Update the manager with the last input source that was observed. This
// is used in part to determine if the carets should be shown or hidden.
void SetLastInputSource(uint16_t aInputSource);

Просмотреть файл

@ -903,11 +903,12 @@ void PresShell::Init(nsPresContext* aPresContext, nsViewManager* aViewManager) {
// Add the preference style sheet.
UpdatePreferenceStyles();
bool accessibleCaretEnabled =
const bool accessibleCaretEnabled =
AccessibleCaretEnabled(mDocument->GetDocShell());
if (accessibleCaretEnabled) {
// Need to happen before nsFrameSelection has been set up.
mAccessibleCaretEventHub = new AccessibleCaretEventHub(this);
mAccessibleCaretEventHub->Init();
}
mSelection = new nsFrameSelection(this, nullptr, accessibleCaretEnabled);

Просмотреть файл

@ -731,7 +731,7 @@ static bool HasVisibleAnonymousContents(Document* aDoc) {
// For now we assume that if it has a frame, it is visible. We might be able
// to refine this further by adding complexity if it turns out this
// condition results in a lot of false positives.
if (ac->ContentNode().GetPrimaryFrame()) {
if (ac->Host()->GetPrimaryFrame()) {
return true;
}
}

Просмотреть файл

@ -74,6 +74,39 @@ void nsCanvasFrame::HideCustomContentContainer() {
}
}
// Do this off a script-runner because some anon content might load CSS which we
// don't want to deal with while doing frame construction.
void InsertAnonymousContentInContainer(Document& aDoc, Element& aContainer) {
if (!aContainer.IsInComposedDoc() || aDoc.GetAnonymousContents().IsEmpty()) {
return;
}
for (RefPtr<AnonymousContent>& anonContent : aDoc.GetAnonymousContents()) {
if (nsCOMPtr<nsINode> parent = anonContent->Host()->GetParentNode()) {
// Parent had better be an old custom content container already
// removed from a reframe. Forget about it since we're about to get
// inserted in a new one.
//
// TODO(emilio): Maybe we should extend PostDestroyData and do this
// stuff there instead, or something...
MOZ_ASSERT(parent != &aContainer);
MOZ_ASSERT(parent->IsElement());
MOZ_ASSERT(parent->AsElement()->IsRootOfNativeAnonymousSubtree());
MOZ_ASSERT(!parent->IsInComposedDoc());
MOZ_ASSERT(!parent->GetParentNode());
parent->RemoveChildNode(anonContent->Host(), true);
}
aContainer.AppendChildTo(anonContent->Host(), true, IgnoreErrors());
}
// Flush frames now. This is really sadly needed, but otherwise stylesheets
// inserted by the above DOM changes might not be processed in time for layout
// to run.
// FIXME(emilio): This is because we have a script-running checkpoint just
// after ProcessPendingRestyles but before DoReflow. That seems wrong! Ideally
// the whole layout / styling pass should be atomic.
aDoc.FlushPendingNotifications(FlushType::Frames);
}
nsresult nsCanvasFrame::CreateAnonymousContent(
nsTArray<ContentInfo>& aElements) {
MOZ_ASSERT(!mCustomContentContainer);
@ -82,19 +115,7 @@ nsresult nsCanvasFrame::CreateAnonymousContent(
return NS_OK;
}
nsCOMPtr<Document> doc = mContent->OwnerDoc();
RefPtr<AccessibleCaretEventHub> eventHub =
PresShell()->GetAccessibleCaretEventHub();
// This will go through InsertAnonymousContent and such, and we don't really
// want it to end up inserting into our content container.
//
// FIXME(emilio): The fact that this enters into InsertAnonymousContent is a
// bit nasty, can we avoid it, maybe doing this off a scriptrunner?
if (eventHub) {
eventHub->Init();
}
Document* doc = mContent->OwnerDoc();
// Create the custom content container.
mCustomContentContainer = doc->CreateHTMLElement(nsGkAtoms::div);
@ -129,27 +150,12 @@ nsresult nsCanvasFrame::CreateAnonymousContent(
// Only create a frame for mCustomContentContainer if it has some children.
if (doc->GetAnonymousContents().IsEmpty()) {
HideCustomContentContainer();
}
for (RefPtr<AnonymousContent>& anonContent : doc->GetAnonymousContents()) {
if (nsCOMPtr<nsINode> parent = anonContent->ContentNode().GetParentNode()) {
// Parent had better be an old custom content container already removed
// from a reframe. Forget about it since we're about to get inserted in a
// new one.
//
// TODO(emilio): Maybe we should extend PostDestroyData and do this stuff
// there instead, or something...
MOZ_ASSERT(parent != mCustomContentContainer);
MOZ_ASSERT(parent->IsElement());
MOZ_ASSERT(parent->AsElement()->IsRootOfNativeAnonymousSubtree());
MOZ_ASSERT(!parent->IsInComposedDoc());
MOZ_ASSERT(!parent->GetParentNode());
parent->RemoveChildNode(&anonContent->ContentNode(), false);
}
mCustomContentContainer->AppendChildTo(&anonContent->ContentNode(), false,
IgnoreErrors());
} else {
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
"InsertAnonymousContentInContainer",
[doc = RefPtr{doc}, container = RefPtr{mCustomContentContainer.get()}] {
InsertAnonymousContentInContainer(*doc, *container);
}));
}
// Create a popupgroup element for system privileged non-XUL documents to

Просмотреть файл

@ -41,6 +41,7 @@
#include "mozilla/ViewportUtils.h"
#include "nsCSSRendering.h"
#include "nsCSSRenderingGradients.h"
#include "nsCaseTreatment.h"
#include "nsRefreshDriver.h"
#include "nsRegion.h"
#include "nsStyleStructInlines.h"
@ -1383,8 +1384,9 @@ void nsDisplayListBuilder::MarkFramesForDisplayList(
nsIContent* content = e->GetContent();
if (content && content->IsInNativeAnonymousSubtree() &&
content->IsElement()) {
auto* classList = content->AsElement()->ClassList();
if (classList->Contains(u"moz-accessiblecaret"_ns)) {
const nsAttrValue* classes = content->AsElement()->GetClasses();
if (classes &&
classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
continue;
}
}

Просмотреть файл

@ -904,15 +904,22 @@ nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
}
static void RecordUseCountersIfNeeded(Document* aDoc,
const StyleUseCounters* aCounters) {
if (!aDoc || !aCounters) {
const StyleSheet& aSheet) {
if (!aDoc) {
return;
}
const StyleUseCounters* docCounters = aDoc->GetStyleUseCounters();
if (!docCounters) {
return;
}
Servo_UseCounters_Merge(docCounters, aCounters);
if (aSheet.URLData()->ChromeRulesEnabled()) {
return;
}
auto* sheetCounters = aSheet.GetStyleUseCounters();
if (!sheetCounters) {
return;
}
Servo_UseCounters_Merge(docCounters, sheetCounters);
aDoc->MaybeWarnAboutZoom();
}
@ -959,7 +966,7 @@ std::tuple<RefPtr<StyleSheet>, Loader::SheetState> Loader::CreateSheet(
if (cacheResult.mCompleteValue) {
sheet = cacheResult.mCompleteValue->Clone(nullptr, nullptr);
mDocument->SetDidHitCompleteSheetCache();
RecordUseCountersIfNeeded(mDocument, sheet->GetStyleUseCounters());
RecordUseCountersIfNeeded(mDocument, *sheet);
mLoadsPerformed.PutEntry(key);
} else {
MOZ_ASSERT(cacheResult.mLoadingOrPendingValue);
@ -1555,7 +1562,7 @@ Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
}
void Loader::NotifyObservers(SheetLoadData& aData, nsresult aStatus) {
RecordUseCountersIfNeeded(mDocument, aData.mSheet->GetStyleUseCounters());
RecordUseCountersIfNeeded(mDocument, *aData.mSheet);
RefPtr loadDispatcher = aData.PrepareLoadEventIfNeeded();
if (aData.mURI) {
mLoadsPerformed.PutEntry(SheetLoadDataHashKey(aData));

Просмотреть файл

@ -764,7 +764,7 @@ void StyleSheet::ReplaceSync(const nsACString& aText, ErrorResult& aRv) {
RefPtr<const StyleStylesheetContents> rawContent =
Servo_StyleSheet_FromUTF8Bytes(
loader, this,
/* load_data = */ nullptr, &aText, mParsingMode, Inner().mURLData,
/* load_data = */ nullptr, &aText, mParsingMode, URLData(),
/* line_number_offset = */ 0,
mConstructorDocument->GetCompatibilityMode(),
/* reusable_sheets = */ nullptr,
@ -1204,9 +1204,11 @@ RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
auto allowImportRules = SelfOrAncestorIsConstructed()
? StyleAllowImportRules::No
: StyleAllowImportRules::Yes;
URLExtraData* urlData = URLData();
const bool shouldRecordCounters =
aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters();
if (!AllowParallelParse(aLoader, Inner().mURLData)) {
aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters() &&
!urlData->ChromeRulesEnabled();
if (!AllowParallelParse(aLoader, urlData)) {
UniquePtr<StyleUseCounters> counters;
if (shouldRecordCounters) {
counters.reset(Servo_UseCounters_Create());
@ -1214,7 +1216,7 @@ RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
RefPtr<StyleStylesheetContents> contents =
Servo_StyleSheet_FromUTF8Bytes(
&aLoader, this, &aLoadData, &aBytes, mParsingMode, Inner().mURLData,
&aLoader, this, &aLoadData, &aBytes, mParsingMode, urlData,
aLoadData.mLineNumber, aLoadData.mCompatMode,
/* reusable_sheets = */ nullptr, counters.get(), allowImportRules,
StyleSanitizationKind::None,
@ -1224,7 +1226,7 @@ RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
} else {
auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, &aLoadData);
Servo_StyleSheet_FromUTF8BytesAsync(
holder, Inner().mURLData, &aBytes, mParsingMode, aLoadData.mLineNumber,
holder, urlData, &aBytes, mParsingMode, aLoadData.mLineNumber,
aLoadData.mCompatMode, shouldRecordCounters, allowImportRules);
}
@ -1256,8 +1258,11 @@ void StyleSheet::ParseSheetSync(
return eCompatibility_FullStandards;
}();
SetURLExtraData();
URLExtraData* urlData = URLData();
const StyleUseCounters* useCounters =
aLoader && aLoader->GetDocument()
aLoader && aLoader->GetDocument() && !urlData->ChromeRulesEnabled()
? aLoader->GetDocument()->GetStyleUseCounters()
: nullptr;
@ -1265,12 +1270,11 @@ void StyleSheet::ParseSheetSync(
? StyleAllowImportRules::No
: StyleAllowImportRules::Yes;
SetURLExtraData();
Inner().mContents =
Servo_StyleSheet_FromUTF8Bytes(
aLoader, this, aLoadData, &aBytes, mParsingMode, Inner().mURLData,
aLineNumber, compatMode, aReusableSheets, useCounters,
allowImportRules, StyleSanitizationKind::None,
aLoader, this, aLoadData, &aBytes, mParsingMode, urlData, aLineNumber,
compatMode, aReusableSheets, useCounters, allowImportRules,
StyleSanitizationKind::None,
/* sanitized_output = */ nullptr)
.Consume();
@ -1481,7 +1485,7 @@ void StyleSheet::SetSharedContents(const StyleLockedCssRules* aSharedRules) {
SetURLExtraData();
Inner().mContents =
Servo_StyleSheet_FromSharedData(Inner().mURLData, aSharedRules).Consume();
Servo_StyleSheet_FromSharedData(URLData(), aSharedRules).Consume();
// Don't call FinishParse(), since that tries to set source map URLs,
// which we don't have.

Просмотреть файл

@ -274,6 +274,7 @@ RESOURCE_FILES += [
CONTENT_ACCESSIBLE_FILES += [
"ImageDocument.css",
"res/accessiblecaret.css",
"res/details.css",
"res/plaintext.css",
"res/searchfield-cancel.svg",

Просмотреть файл

@ -0,0 +1,108 @@
/* 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/. */
:host {
/* Add transition effect to make caret size changing smoother. */
transition-property: width, height, margin-left;
position: absolute;
z-index: 2147483647;
}
#image,
#text-overlay {
width: 100%;
/* Override this property in moz-custom-content-container to make dummy touch
* listener work. */
pointer-events: auto;
}
#image {
background-position: center top;
background-size: 100%;
background-repeat: no-repeat;
background-origin: content-box;
}
:host(.normal) #image {
background-image: image-set(
url("resource://gre-resources/accessiblecaret-normal@1x.png"),
url("resource://gre-resources/accessiblecaret-normal@1.5x.png") 1.5x,
url("resource://gre-resources/accessiblecaret-normal@2x.png") 2x,
url("resource://gre-resources/accessiblecaret-normal@2.25x.png") 2.25x
);
}
:host(.left) #image,
:host(.left) #text-overlay {
margin-left: -39%;
}
:host(.left) > #image {
background-image: image-set(
url("resource://gre-resources/accessiblecaret-tilt-left@1x.png"),
url("resource://gre-resources/accessiblecaret-tilt-left@1.5x.png") 1.5x,
url("resource://gre-resources/accessiblecaret-tilt-left@2x.png") 2x,
url("resource://gre-resources/accessiblecaret-tilt-left@2.25x.png") 2.25x
);
}
:host(.right) #image,
:host(.right) #text-overlay {
margin-left: 41%;
}
:host(.right) #image {
background-image: image-set(
url("resource://gre-resources/accessiblecaret-tilt-right@1x.png"),
url("resource://gre-resources/accessiblecaret-tilt-right@1.5x.png") 1.5x,
url("resource://gre-resources/accessiblecaret-tilt-right@2x.png") 2x,
url("resource://gre-resources/accessiblecaret-tilt-right@2.25x.png") 2.25x
);
}
:host(.none) {
display: none;
}
:host(.hidden) {
visibility: hidden;
}
@media (-moz-platform: android) {
#image,
#text-overlay {
/* border: 0.1px solid red; */ /* Uncomment border to see the touch target. */
padding-left: 59%; /* Enlarge the touch area. ((48-22)/2)px / 22px ~= 59% */
padding-right: 59%; /* Enlarge the touch area. */
margin-left: -59%;
}
#image {
padding-bottom: 59%; /* Enlarge the touch area. */
}
:host(.normal) #image {
background-image: url("chrome://geckoview/skin/images/accessiblecaret-normal.svg");
}
:host(.left) #image,
:host(.left) #text-overlay {
margin-left: -109%;
}
:host(.left) #image {
background-image: url("chrome://geckoview/skin/images/accessiblecaret-tilt-left.svg");
}
:host(.right) #image,
:host(.right) #text-overlay {
margin-left: -12%;
}
:host(.right) #image {
background-image: url("chrome://geckoview/skin/images/accessiblecaret-tilt-right.svg");
}
}

Просмотреть файл

@ -443,108 +443,9 @@ parsererror|sourcetext {
font-size: 12pt;
}
div:-moz-native-anonymous.moz-accessiblecaret {
/* Add transition effect to make caret size changing smoother. */
transition-property: width, height, margin-left;
position: absolute;
z-index: 2147483647;
}
div:-moz-native-anonymous.moz-accessiblecaret > :is(#text-overlay, #image) {
width: 100%;
/* Override this property in moz-custom-content-container to make dummy touch
* listener work. */
pointer-events: auto;
}
div:-moz-native-anonymous.moz-accessiblecaret > #image {
background-position: center top;
background-size: 100%;
background-repeat: no-repeat;
background-origin: content-box;
}
div:-moz-native-anonymous.moz-accessiblecaret.normal > #image {
background-image: image-set(
url("resource://gre-resources/accessiblecaret-normal@1x.png"),
url("resource://gre-resources/accessiblecaret-normal@1.5x.png") 1.5x,
url("resource://gre-resources/accessiblecaret-normal@2x.png") 2x,
url("resource://gre-resources/accessiblecaret-normal@2.25x.png") 2.25x
);
}
div:-moz-native-anonymous.moz-accessiblecaret.left > :is(#text-overlay, #image) {
margin-left: -39%;
}
div:-moz-native-anonymous.moz-accessiblecaret.left > #image {
background-image: image-set(
url("resource://gre-resources/accessiblecaret-tilt-left@1x.png"),
url("resource://gre-resources/accessiblecaret-tilt-left@1.5x.png") 1.5x,
url("resource://gre-resources/accessiblecaret-tilt-left@2x.png") 2x,
url("resource://gre-resources/accessiblecaret-tilt-left@2.25x.png") 2.25x
);
}
div:-moz-native-anonymous.moz-accessiblecaret.right > :is(#text-overlay, #image) {
margin-left: 41%;
}
div:-moz-native-anonymous.moz-accessiblecaret.right > #image {
background-image: image-set(
url("resource://gre-resources/accessiblecaret-tilt-right@1x.png"),
url("resource://gre-resources/accessiblecaret-tilt-right@1.5x.png") 1.5x,
url("resource://gre-resources/accessiblecaret-tilt-right@2x.png") 2x,
url("resource://gre-resources/accessiblecaret-tilt-right@2.25x.png") 2.25x
);
}
div:-moz-native-anonymous.moz-accessiblecaret.none {
display: none;
}
div:-moz-native-anonymous.moz-accessiblecaret.hidden {
visibility: hidden;
}
@media (-moz-platform: android) {
div:-moz-native-anonymous.moz-accessiblecaret > :is(#text-overlay, #image) {
/* border: 0.1px solid red; */ /* Uncomment border to see the touch target. */
padding-left: 59%; /* Enlarge the touch area. ((48-22)/2)px / 22px ~= 59% */
padding-right: 59%; /* Enlarge the touch area. */
margin-left: -59%;
}
div:-moz-native-anonymous.moz-accessiblecaret > #image {
padding-bottom: 59%; /* Enlarge the touch area. */
}
div:-moz-native-anonymous.moz-accessiblecaret.normal > #image {
background-image: url("chrome://geckoview/skin/images/accessiblecaret-normal.svg");
}
div:-moz-native-anonymous.moz-accessiblecaret.left > :is(#text-overlay, #image) {
margin-left: -109%;
}
div:-moz-native-anonymous.moz-accessiblecaret.left > #image {
background-image: url("chrome://geckoview/skin/images/accessiblecaret-tilt-left.svg");
}
div:-moz-native-anonymous.moz-accessiblecaret.right > :is(#text-overlay, #image) {
margin-left: -12%;
}
div:-moz-native-anonymous.moz-accessiblecaret.right > #image {
background-image: url("chrome://geckoview/skin/images/accessiblecaret-tilt-right.svg");
}
}
/* Custom content container in the CanvasFrame, positioned on top of everything
everything else, not reacting to pointer events. */
div:-moz-native-anonymous.moz-custom-content-container {
.moz-custom-content-container:-moz-native-anonymous {
pointer-events: none;
user-select: none;
-moz-top-layer: top;
@ -553,4 +454,6 @@ div:-moz-native-anonymous.moz-custom-content-container {
left: 0;
width: 100%;
height: 100%;
/* Initial direction depends on the document, make sure to reset it */
direction: ltr;
}

Просмотреть файл

@ -33,10 +33,11 @@ function startTest() {
div.id = "ac-child";
div.textContent = " This div text should be green.";
bq.appendChild(div);
var ac = document.insertAnonymousContent(bq);
var ac = document.insertAnonymousContent();
ac.root.appendChild(bq);
document.body.offsetWidth;
is(ac.getComputedStylePropertyValue("ac-child", "color"), "rgb(0, 128, 0)",
is(getComputedStyle(div).color, "rgb(0, 128, 0)",
"color before reframing");
// reframe the root
@ -44,10 +45,10 @@ function startTest() {
document.body.offsetWidth;
// restyle the div
ac.setAttributeForElement("ac-child", "class", "abc");
div.className = "abc";
document.body.offsetWidth;
is(ac.getComputedStylePropertyValue("ac-child", "color"), "rgb(0, 128, 0)",
is(getComputedStyle(div).color, "rgb(0, 128, 0)",
"color after reframing");
SimpleTest.finish();
}

Просмотреть файл

@ -6,20 +6,18 @@
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script>
onload = function() {
try {
let doc = SpecialPowers.wrap(document);
let div = doc.createElement('div');
div.id = "test-id";
ok(!!doc.insertAnonymousContent,
"Must have the insertAnonymousContent API");
let content = doc.insertAnonymousContent(div);
ok(!!content, "Must have anon content");
isnot(content.getComputedStylePropertyValue("test-id", "color"),
getComputedStyle(document.documentElement).color,
"Custom anon content shouldn't inherit from the root element");
} catch(e) {
ok(false, "Threw: " + e);
}
let doc = SpecialPowers.wrap(document);
let div = doc.createElement('div');
div.id = "test-id";
ok(!!doc.insertAnonymousContent,
"Must have the insertAnonymousContent API");
let content = doc.insertAnonymousContent();
ok(!!content, "Must have anon content");
content.root.appendChild(div);
let color = SpecialPowers.wrap(window).getComputedStyle(div).color;
ok(!!color, "Should be able to get a color");
isnot(color, getComputedStyle(document.documentElement).color,
"Custom anon content shouldn't inherit from the root element");
SimpleTest.finish();
};
SimpleTest.waitForExplicitFinish();