forked from mirrors/gecko-dev
Merge autoland to mozilla-central a=merge
This commit is contained in:
commit
6ac3e940d9
87 changed files with 1617 additions and 1198 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<blocklist lastupdate="1551250023025" xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
<blocklist lastupdate="1551794995188" xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
<emItems>
|
||||
<emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
|
||||
<prefs/>
|
||||
|
|
@ -2532,6 +2532,30 @@
|
|||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
<emItem blockID="01825fea-8c5c-4d76-bd06-e1019c188056" id="{ba74c7ee-32b1-11e9-ade5-1f2222a4f325}">
|
||||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
<emItem blockID="1d596a69-157f-4743-9465-f86d6452206b" id="Youtube-downloader@Myaddons.com">
|
||||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
<emItem blockID="a30f9f2a-aa68-48b7-88cc-8a582405b385" id="Youtube-video@Myaddons.com">
|
||||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
<emItem blockID="77a32be3-94ce-49c2-b129-fa2562a7f47b" id="/^((\{6745ccb4-833d-497e-b582-d98a5e790e8c\})|(\{cd205ddb-b106-4d2a-a965-5d1c610b5072\})|(\{218ec82e-2839-42da-acaa-e527454f4237\})|(\{7af25a3d-1caf-49f8-8be9-8ae6065db7c5\}))$/">
|
||||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
<emItem blockID="7718be46-8e84-4bc7-a5a9-4c5de18378ee" id="/^((\{5084f455-bc8f-483c-b145-91245bcbfd64\})|(\{bd69d5d0-4b2f-48cb-bab5-dcf1e0f9c63b\}))$/">
|
||||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
<emItem blockID="aca80fb4-760e-4cd4-9fec-649fb38b2947" id="/^((\{ee2d725e-9726-43ac-8040-60ce9ff2831b\})|(\{55417a80-e6f7-4d77-8d73-f59045e5e890\}))$/">
|
||||
<prefs/>
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3"/>
|
||||
</emItem>
|
||||
</emItems>
|
||||
<pluginItems>
|
||||
<pluginItem blockID="p332">
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ var global = this;
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
|
||||
LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
|
||||
LoginFormFactory: "resource://gre/modules/LoginFormFactory.jsm",
|
||||
InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
|
||||
ContextMenuChild: "resource:///actors/ContextMenuChild.jsm",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ tags = fullscreen
|
|||
skip-if = toolkit == "gtk3" || verify # disabled on Linux due to bug 513558
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_contextmenu_input.js]
|
||||
skip-if = toolkit == "gtk3" # disabled on Linux due to bug 513558
|
||||
skip-if = toolkit == "gtk3" || (os == "win" && processor == "aarch64") # disabled on Linux due to bug 513558, aarch64 due to 1533161
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_ctrlTab.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
|
|
|
|||
|
|
@ -199,6 +199,9 @@ const ignorableWhitelist = new Set([
|
|||
|
||||
// Bug 1351669 - obsolete test file
|
||||
"resource://gre/res/test.properties",
|
||||
|
||||
// Bug 1532703
|
||||
"resource://app/localization/en-US/browser/aboutConfig.ftl",
|
||||
]);
|
||||
for (let entry of ignorableWhitelist) {
|
||||
whitelist.add(entry);
|
||||
|
|
|
|||
|
|
@ -200,8 +200,7 @@
|
|||
</rows>
|
||||
</grid>
|
||||
<separator class="thin"/>
|
||||
<hbox flex="1">
|
||||
<spacer flex="1"/>
|
||||
<hbox pack="end">
|
||||
<hbox align="center" pack="end">
|
||||
<label data-l10n-id="fonts-minsize" control="minSize"/>
|
||||
<!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
|
||||
|
|
|
|||
|
|
@ -1657,8 +1657,7 @@ var gMainPane = {
|
|||
this.selectedHandlerListItem = null;
|
||||
|
||||
// Clear the list of entries.
|
||||
while (this._list.childNodes.length > 1)
|
||||
this._list.removeChild(this._list.lastChild);
|
||||
this._list.textContent = "";
|
||||
|
||||
var visibleTypes = this._visibleTypes;
|
||||
|
||||
|
|
|
|||
|
|
@ -249,6 +249,17 @@
|
|||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#cfr-notification-feature-steps li {
|
||||
padding-inline-start: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#cfr-notification-feature-steps li:before {
|
||||
content: "\2022";
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
}
|
||||
|
||||
#cfr-notification-footer-text,
|
||||
#cfr-notification-footer-users,
|
||||
#cfr-notification-footer-learn-more-link {
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@
|
|||
}
|
||||
|
||||
.request-list-item:not(.selected):hover {
|
||||
background-color: var(--theme-selection-background-hover);
|
||||
background-color: var(--table-selection-background-hover);
|
||||
}
|
||||
|
||||
.request-list-item:not(.selected).fromCache > .requests-list-column:not(.requests-list-waterfall) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
--table-text-color: var(--grey-40);
|
||||
--table-splitter-color: rgba(255,255,255,0.15);
|
||||
--table-zebra-background: rgba(255,255,255,0.05);
|
||||
--table-selection-background-hover: rgba(53, 59, 72, 1);
|
||||
|
||||
--timing-blocked-color: rgba(235, 83, 104, 0.8);
|
||||
--timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
|
||||
|
|
@ -21,7 +22,8 @@
|
|||
|
||||
--table-text-color: var(--grey-70);
|
||||
--table-splitter-color: rgba(0,0,0,0.15);
|
||||
--table-zebra-background: rgba(0,0,0,0.05);
|
||||
--table-zebra-background: rgba(247, 247, 247, 0.8);
|
||||
--table-selection-background-hover: rgba(209, 232, 255, 0.8);
|
||||
|
||||
--timing-blocked-color: rgba(235, 83, 104, 0.8);
|
||||
--timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ add_task(async function() {
|
|||
function testResponse(type) {
|
||||
switch (type) {
|
||||
case "br": {
|
||||
is(document.querySelector(".CodeMirror-line").textContent, "X".repeat(64),
|
||||
is(getCodeMirrorValue(monitor), "X".repeat(64),
|
||||
"The text shown in the source editor is incorrect for the brotli request.");
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ add_task(async function() {
|
|||
is(values[0].textContent, queryStringParamValue,
|
||||
"The first query string param value was incorrect.");
|
||||
|
||||
ok(tabpanel.querySelector(".CodeMirror-code").textContent.includes(requestPayload),
|
||||
ok(getCodeMirrorValue(monitor).includes(requestPayload),
|
||||
"The text shown in the source editor is incorrect.");
|
||||
|
||||
if (isJSON) {
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ add_task(async function() {
|
|||
case "xml": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
const text = getCodeMirrorValue(monitor);
|
||||
|
||||
is(text, "<label value='greeting'>Hello XML!</label>",
|
||||
"The text shown in the source editor is incorrect for the xml request.");
|
||||
|
|
@ -194,7 +194,7 @@ add_task(async function() {
|
|||
case "css": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
const text = getCodeMirrorValue(monitor);
|
||||
|
||||
is(text, "body:pre { content: 'Hello CSS!' }",
|
||||
"The text shown in the source editor is incorrect for the css request.");
|
||||
|
|
@ -203,7 +203,7 @@ add_task(async function() {
|
|||
case "js": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
const text = getCodeMirrorValue(monitor);
|
||||
|
||||
is(text, "function() { return 'Hello JS!'; }",
|
||||
"The text shown in the source editor is incorrect for the js request.");
|
||||
|
|
@ -235,7 +235,7 @@ add_task(async function() {
|
|||
case "html": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
const text = getCodeMirrorValue(monitor);
|
||||
|
||||
is(text, "<blink>Not Found</blink>",
|
||||
"The text shown in the source editor is incorrect for the html request.");
|
||||
|
|
@ -258,7 +258,7 @@ add_task(async function() {
|
|||
case "gzip": {
|
||||
checkVisibility("textarea");
|
||||
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
const text = getCodeMirrorValue(monitor);
|
||||
|
||||
is(text, new Array(1000).join("Hello gzip!"),
|
||||
"The text shown in the source editor is incorrect for the gzip request.");
|
||||
|
|
|
|||
|
|
@ -49,9 +49,8 @@ add_task(async function() {
|
|||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
document.querySelector("#response-tab"));
|
||||
await wait;
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
|
||||
ok(text.includes("\u0411\u0440\u0430\u0442\u0430\u043d"),
|
||||
ok(getCodeMirrorValue(monitor).includes("\u0411\u0440\u0430\u0442\u0430\u043d"),
|
||||
"The text shown in the source editor is correct.");
|
||||
|
||||
return teardown(monitor);
|
||||
|
|
|
|||
|
|
@ -67,8 +67,7 @@ add_task(async function() {
|
|||
is(tabpanel.querySelector(".response-image-box") === null, true,
|
||||
"The response image box doesn't have the intended visibility.");
|
||||
|
||||
is(document.querySelector(".CodeMirror-line").textContent,
|
||||
"{ \"greeting\": \"Hello malformed JSON!\" },",
|
||||
is(getCodeMirrorValue(monitor), "{ \"greeting\": \"Hello malformed JSON!\" },",
|
||||
"The text shown in the source editor is incorrect.");
|
||||
|
||||
await teardown(monitor);
|
||||
|
|
|
|||
|
|
@ -55,9 +55,8 @@ add_task(async function() {
|
|||
document.querySelector("#response-tab"));
|
||||
await wait;
|
||||
|
||||
const text = document.querySelector(".CodeMirror-line").textContent;
|
||||
|
||||
ok(text.match(/^<p>/), "The text shown in the source editor is incorrect.");
|
||||
ok(getCodeMirrorValue(monitor).match(/^<p>/),
|
||||
"The text shown in the source editor is incorrect.");
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
|
|
|
|||
|
|
@ -136,13 +136,7 @@ add_task(async function() {
|
|||
|
||||
is(labels.length, 3, "There should be 3 param values displayed in this tabpanel.");
|
||||
|
||||
// Collect code lines and combine into one text for checking
|
||||
let text = "";
|
||||
const lines = [...document.querySelectorAll(".CodeMirror-line")];
|
||||
|
||||
lines.forEach((line) => {
|
||||
text += line.textContent + "\n";
|
||||
});
|
||||
const text = getCodeMirrorValue(monitor);
|
||||
|
||||
ok(text.includes("Content-Disposition: form-data; name=\"text\""),
|
||||
"The text shown in the source editor is incorrect (1.1).");
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ add_task(async function() {
|
|||
is(labels[0].textContent, "a", "The JSON var name was incorrect.");
|
||||
is(values[0].textContent, "1", "The JSON var value was incorrect.");
|
||||
|
||||
ok(tabpanel.querySelector(".CodeMirror-code").textContent.includes('{"a":1}'),
|
||||
ok(getCodeMirrorValue(monitor).includes('{"a":1}'),
|
||||
"The text shown in the source editor is incorrect.");
|
||||
|
||||
return teardown(monitor);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ add_task(async function() {
|
|||
return teardown(monitor);
|
||||
|
||||
function testEditorContent([ fmt, textRe ]) {
|
||||
ok(document.querySelector(".CodeMirror-line").textContent.match(textRe),
|
||||
ok(getCodeMirrorValue(monitor).match(textRe),
|
||||
"The text shown in the source editor for " + fmt + " is correct.");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -767,6 +767,14 @@ async function performRequests(monitor, tab, count) {
|
|||
await wait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for retrieving `.CodeMirror` content
|
||||
*/
|
||||
function getCodeMirrorValue(monitor) {
|
||||
const document = monitor.panelWin.document;
|
||||
return document.querySelector(".CodeMirror").CodeMirror.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for lazy fields to be loaded in a request.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -162,8 +162,8 @@ JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
|
|||
event->mFlags.mCapture = ipc.capture();
|
||||
event->mFlags.mInSystemGroup = ipc.systemGroup();
|
||||
event->mFlags.mAllowUntrustedEvents = ipc.allowUntrusted();
|
||||
if (ipc.hasPassive()) {
|
||||
event->mPassive.Construct(ipc.passive());
|
||||
if (ipc.passive()) {
|
||||
event->mPassive.Construct(ipc.passive().value());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,9 +186,8 @@ JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
|
|||
ipc->capture() = event.mFlags.mCapture;
|
||||
ipc->systemGroup() = event.mFlags.mInSystemGroup;
|
||||
ipc->allowUntrusted() = event.mFlags.mAllowUntrustedEvents;
|
||||
ipc->hasPassive() = event.mPassive.WasPassed();
|
||||
if (event.mPassive.WasPassed()) {
|
||||
ipc->passive() = event.mPassive.Value();
|
||||
ipc->passive() = Some(event.mPassive.Value());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,6 +203,7 @@ JSWindowActorProtocol::FromWebIDLOptions(const nsAString& aName,
|
|||
|
||||
RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName);
|
||||
proto->mAllFrames = aOptions.mAllFrames;
|
||||
proto->mIncludeChrome = aOptions.mIncludeChrome;
|
||||
|
||||
proto->mParent.mModuleURI = aOptions.mParent.mModuleURI;
|
||||
proto->mChild.mModuleURI = aOptions.mChild.mModuleURI;
|
||||
|
|
|
|||
|
|
@ -230,11 +230,7 @@ struct JSWindowActorEventDecl
|
|||
bool capture;
|
||||
bool systemGroup;
|
||||
bool allowUntrusted;
|
||||
|
||||
// FIXME(gross): Optional<bool> is a bit gross to encode here, so instead it's
|
||||
// encoded as two booleans.
|
||||
bool hasPassive;
|
||||
bool passive;
|
||||
bool? passive;
|
||||
};
|
||||
|
||||
struct JSWindowActorInfo
|
||||
|
|
|
|||
|
|
@ -52,6 +52,63 @@ add_task(async function test_getActor() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(async function test_getActor_without_allFrames() {
|
||||
windowActorOptions.allFrames = false;
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
|
||||
async function(browser) {
|
||||
ChromeUtils.registerWindowActor("Test", windowActorOptions);
|
||||
await ContentTask.spawn(
|
||||
browser, {}, async function() {
|
||||
// Create and append an iframe into the window's document.
|
||||
let frame = content.document.createElement("iframe");
|
||||
content.document.body.appendChild(frame);
|
||||
is(content.window.frames.length, 1, "There should be an iframe.");
|
||||
let child = frame.contentWindow.window.getWindowGlobalChild();
|
||||
Assert.throws(() => child.getActor("Test"),
|
||||
/NS_ERROR_NOT_AVAILABLE/, "Should throw if allFrames is false.");
|
||||
});
|
||||
ChromeUtils.unregisterWindowActor("Test");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_getActor_with_allFrames() {
|
||||
windowActorOptions.allFrames = true;
|
||||
ChromeUtils.registerWindowActor("Test", windowActorOptions);
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
|
||||
async function(browser) {
|
||||
await ContentTask.spawn(
|
||||
browser, {}, async function() {
|
||||
// Create and append an iframe into the window's document.
|
||||
let frame = content.document.createElement("iframe");
|
||||
content.document.body.appendChild(frame);
|
||||
is(content.window.frames.length, 1, "There should be an iframe.");
|
||||
let child = frame.contentWindow.window.getWindowGlobalChild();
|
||||
let actorChild = child.getActor("Test");
|
||||
ok(actorChild, "JSWindowActorChild should have value.");
|
||||
});
|
||||
});
|
||||
ChromeUtils.unregisterWindowActor("Test");
|
||||
});
|
||||
|
||||
add_task(async function test_getActor_without_includeChrome() {
|
||||
windowActorOptions.includeChrome = false;
|
||||
let parent = window.docShell.browsingContext.currentWindowGlobal;
|
||||
ChromeUtils.registerWindowActor("Test", windowActorOptions);
|
||||
SimpleTest.doesThrow(() =>
|
||||
parent.getActor("Test"),
|
||||
"Should throw if includeChrome is false.");
|
||||
ChromeUtils.unregisterWindowActor("Test");
|
||||
});
|
||||
|
||||
add_task(async function test_getActor_with_includeChrome() {
|
||||
windowActorOptions.includeChrome = true;
|
||||
ChromeUtils.registerWindowActor("Test", windowActorOptions);
|
||||
let parent = window.docShell.browsingContext.currentWindowGlobal;
|
||||
let actorParent = parent.getActor("Test");
|
||||
ok(actorParent, "JSWindowActorParent should have value.");
|
||||
ChromeUtils.unregisterWindowActor("Test");
|
||||
});
|
||||
|
||||
add_task(async function test_asyncMessage() {
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: URL},
|
||||
async function(browser) {
|
||||
|
|
|
|||
|
|
@ -1310,7 +1310,8 @@ void WebRenderCommandBuilder::DoGroupingForDisplayList(
|
|||
CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
|
||||
|
||||
bool snapped;
|
||||
nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
|
||||
nsRect groupBounds =
|
||||
aWrappingItem->GetUntransformedBounds(aDisplayListBuilder, &snapped);
|
||||
// We don't want to restrict the size of the blob to the building rect of the
|
||||
// display item, since that will change when we scroll and trigger a resize
|
||||
// invalidation of the blob (will be fixed by blob recoordination).
|
||||
|
|
@ -1382,12 +1383,15 @@ void WebRenderCommandBuilder::DoGroupingForDisplayList(
|
|||
group.mImageBounds =
|
||||
IntRect(0, 0, group.mLayerBounds.width, group.mLayerBounds.height);
|
||||
group.mClippedImageBounds = group.mImageBounds;
|
||||
group.mPaintRect =
|
||||
LayerIntRect::FromUnknownRect(
|
||||
ScaleToOutsidePixelsOffset(aWrappingItem->GetPaintRect(), scale.width,
|
||||
scale.height, group.mAppUnitsPerDevPixel,
|
||||
residualOffset))
|
||||
.Intersect(group.mLayerBounds);
|
||||
|
||||
const nsRect& untransformedPaintRect =
|
||||
aWrappingItem->GetUntransformedPaintRect();
|
||||
|
||||
group.mPaintRect = LayerIntRect::FromUnknownRect(
|
||||
ScaleToOutsidePixelsOffset(
|
||||
untransformedPaintRect, scale.width, scale.height,
|
||||
group.mAppUnitsPerDevPixel, residualOffset))
|
||||
.Intersect(group.mLayerBounds);
|
||||
// XXX: Make the paint rect relative to the layer bounds. After we include
|
||||
// mLayerBounds.TopLeft() in the blob image we want to stop doing this
|
||||
// adjustment.
|
||||
|
|
@ -1500,8 +1504,7 @@ bool WebRenderCommandBuilder::ShouldDumpDisplayList(
|
|||
void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
|
||||
nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem,
|
||||
nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
|
||||
wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
|
||||
nsDisplayItem* aOuterItem) {
|
||||
wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources) {
|
||||
if (mDoGrouping) {
|
||||
MOZ_RELEASE_ASSERT(
|
||||
aWrappingItem,
|
||||
|
|
@ -1611,15 +1614,15 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
|
|||
// animated geometry root, so we can combine subsequent items of that
|
||||
// type into the same image.
|
||||
mContainsSVGGroup = mDoGrouping = true;
|
||||
if (aOuterItem &&
|
||||
aOuterItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
|
||||
if (aWrappingItem &&
|
||||
aWrappingItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
|
||||
// Inline <svg> should always have an overflow clip, but it gets put
|
||||
// outside the nsDisplayTransform we create for scaling the svg
|
||||
// viewport. Converting the clip into inner coordinates lets us
|
||||
// restrict the size of the blob images and prevents unnecessary
|
||||
// resizes.
|
||||
nsDisplayTransform* transform =
|
||||
static_cast<nsDisplayTransform*>(aOuterItem);
|
||||
static_cast<nsDisplayTransform*>(aWrappingItem);
|
||||
|
||||
nsRect clippedBounds =
|
||||
transform->GetClippedBounds(aDisplayListBuilder);
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ class WebRenderCommandBuilder {
|
|||
nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem,
|
||||
nsDisplayListBuilder* aDisplayListBuilder,
|
||||
const StackingContextHelper& aSc, wr::DisplayListBuilder& aBuilder,
|
||||
wr::IpcResourceUpdateQueue& aResources,
|
||||
nsDisplayItem* aOuterItem = nullptr);
|
||||
wr::IpcResourceUpdateQueue& aResources);
|
||||
|
||||
// aWrappingItem has to be non-null.
|
||||
void DoGroupingForDisplayList(nsDisplayList* aDisplayList,
|
||||
|
|
|
|||
|
|
@ -2460,19 +2460,7 @@ bool BytecodeEmitter::emitInitializeInstanceFields() {
|
|||
return true;
|
||||
}
|
||||
|
||||
PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
|
||||
PropOpEmitter::ObjKind::Other);
|
||||
if (!poe.prepareForObj()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is guaranteed to run after super(), so we don't need TDZ checks.
|
||||
if (!emitGetName(cx->names().dotThis)) {
|
||||
// [stack] THIS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!poe.emitGet(cx->names().dotInitializers)) {
|
||||
if (!emitGetName(cx->names().dotInitializers)) {
|
||||
// [stack] ARRAY
|
||||
return false;
|
||||
}
|
||||
|
|
@ -8020,23 +8008,14 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
|||
// code (the initializer) for each field. Upon an object's construction,
|
||||
// these lambdas will be called, defining the values.
|
||||
|
||||
PropOpEmitter poe(this, PropOpEmitter::Kind::SimpleAssignment,
|
||||
PropOpEmitter::ObjKind::Other);
|
||||
if (!poe.prepareForObj()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_DUP)) {
|
||||
// [stack] CTOR? OBJ OBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!poe.prepareForRhs()) {
|
||||
NameOpEmitter noe(this, cx->names().dotInitializers,
|
||||
NameOpEmitter::Kind::Initialize);
|
||||
if (!noe.prepareForRhs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitUint32Operand(JSOP_NEWARRAY, numFields)) {
|
||||
// [stack] CTOR? OBJ OBJ ARRAY
|
||||
// [stack] CTOR? OBJ ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -8049,12 +8028,12 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
|||
}
|
||||
|
||||
if (!emitTree(initializer)) {
|
||||
// [stack] CTOR? OBJ OBJ ARRAY LAMBDA
|
||||
// [stack] CTOR? OBJ ARRAY LAMBDA
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldIndex)) {
|
||||
// [stack] CTOR? OBJ OBJ ARRAY
|
||||
// [stack] CTOR? OBJ ARRAY
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -8062,7 +8041,7 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
|||
}
|
||||
}
|
||||
|
||||
if (!poe.emitAssignment(cx->names().dotInitializers)) {
|
||||
if (!noe.emitAssignment()) {
|
||||
// [stack] CTOR? OBJ ARRAY
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,16 @@ class UsedNameTracker {
|
|||
MOZ_MUST_USE bool noteUse(JSContext* cx, JSAtom* name, uint32_t scriptId,
|
||||
uint32_t scopeId);
|
||||
|
||||
MOZ_MUST_USE bool markAsAlwaysClosedOver(JSContext* cx, JSAtom* name,
|
||||
uint32_t scriptId,
|
||||
uint32_t scopeId) {
|
||||
// This marks a variable as always closed over:
|
||||
// UsedNameInfo::noteBoundInScope only checks if scriptId and scopeId are
|
||||
// greater than the current scriptId/scopeId, so do a simple increment to
|
||||
// make that so.
|
||||
return noteUse(cx, name, scriptId + 1, scopeId + 1);
|
||||
}
|
||||
|
||||
struct RewindToken {
|
||||
private:
|
||||
friend class UsedNameTracker;
|
||||
|
|
|
|||
|
|
@ -1829,6 +1829,10 @@ GeneralParser<ParseHandler, Unit>::functionBody(InHandling inHandling,
|
|||
uint32_t startYieldOffset = pc_->lastYieldOffset;
|
||||
#endif
|
||||
|
||||
// One might expect noteUsedName(".initializers") here when parsing a
|
||||
// constructor. See GeneralParser<ParseHandler, Unit>::classDefinition on why
|
||||
// it's not here.
|
||||
|
||||
Node body;
|
||||
if (type == StatementListBody) {
|
||||
bool inheritedStrict = pc_->sc()->strict();
|
||||
|
|
@ -7044,6 +7048,37 @@ GeneralParser<ParseHandler, Unit>::classDefinition(
|
|||
}
|
||||
}
|
||||
|
||||
if (numFieldsWithInitializers > 0) {
|
||||
// .initializers is always closed over by the constructor when there are
|
||||
// fields with initializers. However, there's some strange circumstances
|
||||
// which prevents us from using the normal noteUsedName() system. We
|
||||
// cannot call noteUsedName(".initializers") when parsing the constructor,
|
||||
// because .initializers should be marked as used *only if* there are
|
||||
// fields with initializers. Even if we haven't seen any fields yet,
|
||||
// there may be fields after the constructor.
|
||||
// Consider the following class:
|
||||
//
|
||||
// class C {
|
||||
// constructor() {
|
||||
// // do we noteUsedName(".initializers") here?
|
||||
// }
|
||||
// // ... because there might be some fields down here.
|
||||
// }
|
||||
//
|
||||
// So, instead, at the end of class parsing (where we are now), we do some
|
||||
// tricks to pretend that noteUsedName(".initializers") was called in the
|
||||
// constructor.
|
||||
if (!usedNames_.markAsAlwaysClosedOver(cx_, cx_->names().dotInitializers,
|
||||
pc_->scriptId(),
|
||||
pc_->innermostScope()->id())) {
|
||||
return null();
|
||||
}
|
||||
if (!noteDeclaredName(cx_->names().dotInitializers,
|
||||
DeclarationKind::Const, namePos)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
classEndOffset = pos().end;
|
||||
if (!finishClassConstructor(classStmt, className, classStartOffset,
|
||||
classEndOffset, numFieldsWithInitializers,
|
||||
|
|
@ -7161,6 +7196,9 @@ GeneralParser<ParseHandler, Unit>::synthesizeConstructor(
|
|||
return null();
|
||||
}
|
||||
|
||||
// One might expect a noteUsedName(".initializers") here. See comment in
|
||||
// GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
|
||||
|
||||
bool canSkipLazyClosedOverBindings = handler_.canSkipLazyClosedOverBindings();
|
||||
if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
|
||||
return null();
|
||||
|
|
|
|||
11
js/src/tests/non262/fields/scopes.js
Normal file
11
js/src/tests/non262/fields/scopes.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
(function({
|
||||
k = class i {
|
||||
[_ => i]()
|
||||
{}
|
||||
}
|
||||
} = {}) {
|
||||
var j = 0;
|
||||
})()
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -3572,8 +3572,6 @@ bool JSScript::fullyInitFromEmitter(JSContext* cx, HandleScript script,
|
|||
|
||||
// Initialize script flags from BytecodeEmitter
|
||||
script->setFlag(ImmutableFlags::Strict, bce->sc->strict());
|
||||
script->setFlag(ImmutableFlags::ExplicitUseStrict,
|
||||
bce->sc->hasExplicitUseStrict());
|
||||
script->setFlag(ImmutableFlags::BindingsAccessedDynamically,
|
||||
bce->sc->bindingsAccessedDynamically());
|
||||
script->setFlag(ImmutableFlags::HasSingletons, bce->hasSingletons);
|
||||
|
|
|
|||
|
|
@ -1686,8 +1686,7 @@ class JSScript : public js::gc::TenuredCell {
|
|||
// Code is in strict mode.
|
||||
Strict = 1 << 1,
|
||||
|
||||
// Code has "use strict"; explicitly.
|
||||
ExplicitUseStrict = 1 << 2,
|
||||
// (1 << 2) is unused.
|
||||
|
||||
// True if the script has a non-syntactic scope on its dynamic scope chain.
|
||||
// That is, there are objects about which we know nothing between the
|
||||
|
|
@ -2098,10 +2097,6 @@ class JSScript : public js::gc::TenuredCell {
|
|||
|
||||
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
|
||||
|
||||
bool explicitUseStrict() const {
|
||||
return hasFlag(ImmutableFlags::ExplicitUseStrict);
|
||||
}
|
||||
|
||||
bool hasNonSyntacticScope() const {
|
||||
return hasFlag(ImmutableFlags::HasNonSyntacticScope);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
[test_loader_global_sharing.py]
|
||||
skip-if = !manage_instance || appname == 'fennec'
|
||||
|
||||
[test_preloader_telemetry.py]
|
||||
skip-if = appname == 'fennec'
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ class TestScriptPreloader(MarionetteTestCase):
|
|||
self.assertMuchGreaterThan(misses, child_hits)
|
||||
|
||||
profile = self.marionette.profile_path
|
||||
self.wait_for_file_change(start_time, "{}/startupCache/scriptCache.bin".format(profile))
|
||||
self.wait_for_file_change(start_time, "{}/startupCache/scriptCache-child.bin".format(profile))
|
||||
self.wait_for_file_change(start_time,
|
||||
"{}/startupCache/scriptCache.bin".format(profile))
|
||||
self.wait_for_file_change(start_time,
|
||||
"{}/startupCache/scriptCache-child.bin".format(profile))
|
||||
self.marionette.restart(clean=False, in_app=True)
|
||||
histogram = self.get_histogram("SCRIPT_PRELOADER_REQUESTS")
|
||||
misses = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Miss"], 0)
|
||||
|
|
@ -53,12 +55,11 @@ class TestScriptPreloader(MarionetteTestCase):
|
|||
if os.stat(path).st_mtime > start_time:
|
||||
return
|
||||
if time.time() > expires:
|
||||
raise Error("Never observed file change for {}".format(path))
|
||||
raise Exception("Never observed file change for {}".format(path))
|
||||
time.sleep(1)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def wait_for_observer_notification(self, name):
|
||||
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
||||
return self.marionette.execute_script("""
|
||||
|
|
@ -2787,7 +2787,7 @@ bool nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
|
|||
builder.BeginFrame();
|
||||
nsDisplayList list;
|
||||
nsDisplayTransform* item =
|
||||
MakeDisplayItem<nsDisplayTransform>(&builder, aFrame, &list, nsRect());
|
||||
MakeDisplayItem<nsDisplayTransform>(&builder, aFrame, &list, nsRect(), 0);
|
||||
|
||||
*aTransform = item->GetTransform();
|
||||
item->Destroy(&builder);
|
||||
|
|
|
|||
|
|
@ -2604,8 +2604,7 @@ static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
nsDisplayTransform* item = MakeDisplayItem<nsDisplayTransform>(
|
||||
aBuilder, aFrame, aNonParticipants, aBuilder->GetVisibleRect(),
|
||||
Matrix4x4(), aIndex);
|
||||
aBuilder, aFrame, aNonParticipants, aBuilder->GetVisibleRect(), aIndex);
|
||||
|
||||
if (*aSeparator == nullptr) {
|
||||
*aSeparator = item;
|
||||
|
|
@ -8213,10 +8212,10 @@ nsresult nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) {
|
|||
bool movingInFrameDirection =
|
||||
IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
|
||||
|
||||
done = current->PeekOffsetWord(
|
||||
movingInFrameDirection, wordSelectEatSpace,
|
||||
aPos->mIsKeyboardSelect, &offset, &state,
|
||||
aPos->mTrimSpaces) == FOUND;
|
||||
done =
|
||||
current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
|
||||
aPos->mIsKeyboardSelect, &offset, &state,
|
||||
aPos->mTrimSpaces) == FOUND;
|
||||
|
||||
if (!done) {
|
||||
nsIFrame* nextFrame;
|
||||
|
|
@ -8463,12 +8462,9 @@ nsIFrame::FrameSearchResult nsFrame::PeekOffsetCharacter(
|
|||
return CONTINUE;
|
||||
}
|
||||
|
||||
nsIFrame::FrameSearchResult nsFrame::PeekOffsetWord(bool aForward,
|
||||
bool aWordSelectEatSpace,
|
||||
bool aIsKeyboardSelect,
|
||||
int32_t* aOffset,
|
||||
PeekWordState* aState,
|
||||
bool aTrimSpaces) {
|
||||
nsIFrame::FrameSearchResult nsFrame::PeekOffsetWord(
|
||||
bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
|
||||
int32_t* aOffset, PeekWordState* aState, bool aTrimSpaces) {
|
||||
NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
|
||||
int32_t startOffset = *aOffset;
|
||||
// This isn't text, so truncate the context
|
||||
|
|
@ -10522,9 +10518,8 @@ bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
|
|||
ChildrenHavePerspective(aStyleDisplay) ||
|
||||
aStyleEffects->mMixBlendMode != NS_STYLE_BLEND_NORMAL ||
|
||||
nsSVGIntegrationUtils::UsingEffectsForFrame(this) ||
|
||||
(aIsPositioned &&
|
||||
(aStyleDisplay->IsPositionForcingStackingContext() ||
|
||||
aStylePosition->mZIndex.IsInteger())) ||
|
||||
(aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
|
||||
aStylePosition->mZIndex.IsInteger())) ||
|
||||
(aStyleDisplay->mWillChangeBitField &
|
||||
NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
|
||||
aStyleDisplay->mIsolation != NS_STYLE_ISOLATION_AUTO;
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ void nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
content.AppendToTop(MakeDisplayItem<nsDisplayTransform>(
|
||||
aBuilder, child, &content, content.GetBuildingRect(),
|
||||
aBuilder, child, &content, content.GetBuildingRect(), 0,
|
||||
::ComputePageTransform));
|
||||
|
||||
set.Content()->AppendToTop(&content);
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ void nsSimplePageSequenceFrame::BuildDisplayList(
|
|||
}
|
||||
|
||||
content.AppendToTop(MakeDisplayItem<nsDisplayTransform>(
|
||||
aBuilder, this, &content, content.GetBuildingRect(),
|
||||
aBuilder, this, &content, content.GetBuildingRect(), 0,
|
||||
::ComputePageSequenceTransform));
|
||||
|
||||
aLists.Content()->AppendToTop(&content);
|
||||
|
|
|
|||
|
|
@ -3148,12 +3148,14 @@ bool nsDisplayItem::ForceActiveLayers() {
|
|||
}
|
||||
|
||||
static int32_t ZIndexForFrame(nsIFrame* aFrame) {
|
||||
if (!aFrame->IsAbsPosContainingBlock() && !aFrame->IsFlexOrGridItem())
|
||||
if (!aFrame->IsAbsPosContainingBlock() && !aFrame->IsFlexOrGridItem()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const nsStylePosition* position = aFrame->StylePosition();
|
||||
if (position->mZIndex.IsInteger())
|
||||
if (position->mZIndex.IsInteger()) {
|
||||
return position->mZIndex.AsInteger();
|
||||
}
|
||||
MOZ_ASSERT(position->mZIndex.IsAuto());
|
||||
// sort the auto and 0 elements together
|
||||
return 0;
|
||||
|
|
@ -5668,24 +5670,29 @@ bool nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aList,
|
||||
const nsRect& aListBounds) {
|
||||
if (aList->IsOpaque()) {
|
||||
// Everything within list bounds that's visible is opaque. This is an
|
||||
// optimization to avoid calculating the opaque region.
|
||||
return aListBounds;
|
||||
}
|
||||
|
||||
if (aBuilder->HitTestIsForVisibility()) {
|
||||
// If we care about an accurate opaque region, iterate the display list
|
||||
// and build up a region of opaque bounds.
|
||||
return aList->GetOpaqueRegion(aBuilder);
|
||||
}
|
||||
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const {
|
||||
*aSnap = false;
|
||||
nsRegion result;
|
||||
if (mListPtr->IsOpaque()) {
|
||||
// Everything within GetBounds that's visible is opaque.
|
||||
result = GetBounds(aBuilder, aSnap);
|
||||
} else if (aBuilder->HitTestIsForVisibility()) {
|
||||
// If we care about an accurate opaque region, iterate the display list
|
||||
// and build up a region of opaque bounds.
|
||||
nsDisplayItem* item = mList.GetBottom();
|
||||
while (item) {
|
||||
result.OrWith(item->GetOpaqueRegion(aBuilder, aSnap));
|
||||
item = item->GetAbove();
|
||||
}
|
||||
}
|
||||
*aSnap = false;
|
||||
return result;
|
||||
bool snap;
|
||||
return ::GetOpaqueRegion(aBuilder, GetChildren(), GetBounds(aBuilder, &snap));
|
||||
}
|
||||
|
||||
Maybe<nscolor> nsDisplayWrapList::IsUniform(
|
||||
|
|
@ -5753,11 +5760,7 @@ static LayerState RequiredLayerStateForChildren(
|
|||
|
||||
nsRect nsDisplayWrapList::GetComponentAlphaBounds(
|
||||
nsDisplayListBuilder* aBuilder) const {
|
||||
nsRect bounds;
|
||||
for (nsDisplayItem* i = mListPtr->GetBottom(); i; i = i->GetAbove()) {
|
||||
bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder));
|
||||
}
|
||||
return bounds;
|
||||
return mListPtr->GetComponentAlphaBounds(aBuilder);
|
||||
}
|
||||
|
||||
void nsDisplayWrapList::SetReferenceFrame(const nsIFrame* aFrame) {
|
||||
|
|
@ -7307,35 +7310,62 @@ already_AddRefed<Layer> nsDisplayAsyncZoom::BuildLayer(
|
|||
// nsDisplayTransform Implementation
|
||||
//
|
||||
|
||||
// Write #define UNIFIED_CONTINUATIONS here and in
|
||||
// TransformReferenceBox::Initialize to have the transform property try
|
||||
// to transform content with continuations as one unified block instead of
|
||||
// several smaller ones. This is currently disabled because it doesn't work
|
||||
// correctly, since when the frames are initially being reflowed, their
|
||||
// continuations all compute their bounding rects independently of each other
|
||||
// and consequently get the wrong value. Write #define DEBUG_HIT here to have
|
||||
// the nsDisplayTransform class dump out a bunch of information about hit
|
||||
// detection.
|
||||
#undef UNIFIED_CONTINUATIONS
|
||||
#undef DEBUG_HIT
|
||||
#ifndef DEBUG
|
||||
static_assert(sizeof(nsDisplayTransform) < 512, "nsDisplayTransform has grown");
|
||||
#endif
|
||||
|
||||
nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame, nsDisplayList* aList,
|
||||
const nsRect& aChildrenBuildingRect,
|
||||
uint32_t aIndex)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame),
|
||||
mTransform(Some(Matrix4x4())),
|
||||
mTransformGetter(nullptr),
|
||||
mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
|
||||
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
|
||||
mChildrenBuildingRect(aChildrenBuildingRect),
|
||||
mIndex(aIndex),
|
||||
mIsTransformSeparator(true),
|
||||
mAllowAsyncAnimation(false) {
|
||||
MOZ_COUNT_CTOR(nsDisplayTransform);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
Init(aBuilder, aList);
|
||||
}
|
||||
|
||||
nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame, nsDisplayList* aList,
|
||||
const nsRect& aChildrenBuildingRect,
|
||||
uint32_t aIndex,
|
||||
bool aAllowAsyncAnimation)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame),
|
||||
mTransformGetter(nullptr),
|
||||
mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
|
||||
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
|
||||
mChildrenBuildingRect(aChildrenBuildingRect),
|
||||
mIndex(aIndex),
|
||||
mIsTransformSeparator(false),
|
||||
mAllowAsyncAnimation(aAllowAsyncAnimation) {
|
||||
MOZ_COUNT_CTOR(nsDisplayTransform);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
SetReferenceFrameToAncestor(aBuilder);
|
||||
Init(aBuilder, aList);
|
||||
}
|
||||
|
||||
nsDisplayTransform::nsDisplayTransform(
|
||||
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
|
||||
const nsRect& aChildrenBuildingRect,
|
||||
ComputeTransformFunction aTransformGetter, uint32_t aIndex)
|
||||
const nsRect& aChildrenBuildingRect, uint32_t aIndex,
|
||||
ComputeTransformFunction aTransformGetter)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame),
|
||||
mStoredList(aBuilder, aFrame, aList),
|
||||
mTransformGetter(aTransformGetter),
|
||||
mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
|
||||
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
|
||||
mChildrenBuildingRect(aChildrenBuildingRect),
|
||||
mIndex(aIndex),
|
||||
mIsTransformSeparator(false),
|
||||
mTransformPreserves3DInited(false),
|
||||
mAllowAsyncAnimation(false) {
|
||||
MOZ_COUNT_CTOR(nsDisplayTransform);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
Init(aBuilder);
|
||||
Init(aBuilder, aList);
|
||||
}
|
||||
|
||||
void nsDisplayTransform::SetReferenceFrameToAncestor(
|
||||
|
|
@ -7374,55 +7404,11 @@ void nsDisplayTransform::SetReferenceFrameToAncestor(
|
|||
SetBuildingRect(aBuilder->GetVisibleRect() + mToReferenceFrame);
|
||||
}
|
||||
|
||||
void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder) {
|
||||
void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aChildren) {
|
||||
mShouldFlatten = false;
|
||||
mHasBounds = false;
|
||||
mStoredList.SetClipChain(nullptr, true);
|
||||
mStoredList.SetBuildingRect(mChildrenBuildingRect);
|
||||
}
|
||||
|
||||
nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame, nsDisplayList* aList,
|
||||
const nsRect& aChildrenBuildingRect,
|
||||
uint32_t aIndex,
|
||||
bool aAllowAsyncAnimation)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame),
|
||||
mStoredList(aBuilder, aFrame, aList),
|
||||
mTransformGetter(nullptr),
|
||||
mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
|
||||
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
|
||||
mChildrenBuildingRect(aChildrenBuildingRect),
|
||||
mIndex(aIndex),
|
||||
mIsTransformSeparator(false),
|
||||
mTransformPreserves3DInited(false),
|
||||
mAllowAsyncAnimation(aAllowAsyncAnimation) {
|
||||
MOZ_COUNT_CTOR(nsDisplayTransform);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
SetReferenceFrameToAncestor(aBuilder);
|
||||
Init(aBuilder);
|
||||
UpdateBoundsFor3D(aBuilder);
|
||||
}
|
||||
|
||||
nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame, nsDisplayList* aList,
|
||||
const nsRect& aChildrenBuildingRect,
|
||||
const Matrix4x4& aTransform,
|
||||
uint32_t aIndex)
|
||||
: nsDisplayHitTestInfoItem(aBuilder, aFrame),
|
||||
mStoredList(aBuilder, aFrame, aList),
|
||||
mTransform(Some(aTransform)),
|
||||
mTransformGetter(nullptr),
|
||||
mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
|
||||
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
|
||||
mChildrenBuildingRect(aChildrenBuildingRect),
|
||||
mIndex(aIndex),
|
||||
mIsTransformSeparator(true),
|
||||
mTransformPreserves3DInited(false),
|
||||
mAllowAsyncAnimation(false) {
|
||||
MOZ_COUNT_CTOR(nsDisplayTransform);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
Init(aBuilder);
|
||||
UpdateBoundsFor3D(aBuilder);
|
||||
mChildren.AppendToTop(aChildren);
|
||||
UpdateBounds(aBuilder);
|
||||
}
|
||||
|
||||
bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
|
||||
|
|
@ -7924,14 +7910,13 @@ Matrix4x4 nsDisplayTransform::GetTransformForRendering(
|
|||
const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
|
||||
nsDisplayListBuilder* aBuilder) {
|
||||
MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
|
||||
// XXX: should go back to fix mTransformGetter.
|
||||
if (!mTransformPreserves3DInited) {
|
||||
mTransformPreserves3DInited = true;
|
||||
if (!IsLeafOf3DContext()) {
|
||||
mTransformPreserves3D = GetTransform().GetMatrix();
|
||||
return mTransformPreserves3D;
|
||||
}
|
||||
|
||||
if (!IsLeafOf3DContext()) {
|
||||
return GetTransform().GetMatrix();
|
||||
}
|
||||
|
||||
// XXX: should go back to fix mTransformGetter.
|
||||
if (!mTransformPreserves3D) {
|
||||
const nsIFrame* establisher; // Establisher of the 3D rendering context.
|
||||
for (establisher = mFrame;
|
||||
establisher && establisher->Combines3DTransformWithAncestors();
|
||||
|
|
@ -7945,10 +7930,11 @@ const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
|
|||
float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
uint32_t flags =
|
||||
INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
|
||||
mTransformPreserves3D =
|
||||
GetResultingTransformMatrix(mFrame, offset, scale, flags);
|
||||
mTransformPreserves3D = MakeUnique<Matrix4x4>(
|
||||
GetResultingTransformMatrix(mFrame, offset, scale, flags));
|
||||
}
|
||||
return mTransformPreserves3D;
|
||||
|
||||
return *mTransformPreserves3D;
|
||||
}
|
||||
|
||||
bool nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(
|
||||
|
|
@ -8028,8 +8014,7 @@ bool nsDisplayTransform::CreateWebRenderCommands(
|
|||
LayoutDeviceRect(position, LayoutDeviceSize()));
|
||||
|
||||
aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
|
||||
mStoredList.GetChildren(), &mStoredList, aDisplayListBuilder, sc,
|
||||
aBuilder, aResources, this);
|
||||
GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -8071,8 +8056,8 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(
|
|||
uint32_t flags = FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
|
||||
RefPtr<ContainerLayer> container =
|
||||
aManager->GetLayerBuilder()->BuildContainerLayerFor(
|
||||
aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
|
||||
aContainerParameters, &newTransformMatrix, flags);
|
||||
aBuilder, aManager, mFrame, this, GetChildren(), aContainerParameters,
|
||||
&newTransformMatrix, flags);
|
||||
|
||||
if (!container) {
|
||||
return nullptr;
|
||||
|
|
@ -8152,7 +8137,7 @@ nsDisplayItem::LayerState nsDisplayTransform::GetLayerState(
|
|||
// different animated geometry root, we'll make this an active layer so the
|
||||
// animation can be accelerated.
|
||||
return RequiredLayerStateForChildren(aBuilder, aManager, aParameters,
|
||||
*mStoredList.GetChildren(),
|
||||
*GetChildren(),
|
||||
mAnimatedGeometryRootForChildren);
|
||||
}
|
||||
|
||||
|
|
@ -8174,14 +8159,124 @@ bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder* aBuilder,
|
|||
if (!UntransformPaintRect(aBuilder, &untransformedVisibleRect)) {
|
||||
untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
|
||||
}
|
||||
nsRegion untransformedVisible = untransformedVisibleRect;
|
||||
// Call RecomputeVisiblity instead of ComputeVisibility since
|
||||
// nsDisplayItem::ComputeVisibility should only be called from
|
||||
// nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
|
||||
mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
|
||||
|
||||
bool snap;
|
||||
const nsRect bounds = GetUntransformedBounds(aBuilder, &snap);
|
||||
nsRegion visibleRegion;
|
||||
visibleRegion.And(bounds, untransformedVisibleRect);
|
||||
GetChildren()->ComputeVisibilityForSublist(aBuilder, &visibleRegion,
|
||||
visibleRegion.GetBounds());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsRect nsDisplayTransform::TransformUntransformedBounds(
|
||||
nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
|
||||
bool snap;
|
||||
const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
|
||||
// GetTransform always operates in dev pixels.
|
||||
const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
|
||||
factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds for this transform. The bounds are calculated during
|
||||
* display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
|
||||
*/
|
||||
nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const {
|
||||
*aSnap = false;
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
|
||||
MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
|
||||
|
||||
/* Some transforms can get empty bounds in 2D, but might get transformed again
|
||||
* and get non-empty bounds. A simple example of this would be a 180 degree
|
||||
* rotation getting applied twice.
|
||||
* We should not depend on transforming bounds level by level.
|
||||
*
|
||||
* This function collects the bounds of this transform and stores it in
|
||||
* nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
|
||||
* down and include the bounds of the child transforms.
|
||||
* The bounds are transformed with the accumulated transformation matrix up to
|
||||
* the 3D context root coordinate space.
|
||||
*/
|
||||
nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
|
||||
accTransform.Accumulate(GetTransform().GetMatrix());
|
||||
|
||||
// Do not dive into another 3D context.
|
||||
if (!IsLeafOf3DContext()) {
|
||||
for (nsDisplayItem* i = GetChildren()->GetBottom(); i; i = i->GetAbove()) {
|
||||
i->DoUpdateBoundsPreserves3D(aBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
/* The child transforms that extend 3D context further will have empty bounds,
|
||||
* so the untransformed bounds here is the bounds of all the non-preserve-3d
|
||||
* content under this transform.
|
||||
*/
|
||||
const nsRect rect = TransformUntransformedBounds(
|
||||
aBuilder, accTransform.GetCurrentTransform());
|
||||
aBuilder->AccumulateRect(rect);
|
||||
}
|
||||
|
||||
void nsDisplayTransform::DoUpdateBoundsPreserves3D(
|
||||
nsDisplayListBuilder* aBuilder) {
|
||||
MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
|
||||
IsTransformSeparator());
|
||||
// Updating is not going through to child 3D context.
|
||||
ComputeBounds(aBuilder);
|
||||
}
|
||||
|
||||
void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
|
||||
UpdateUntransformedBounds(aBuilder);
|
||||
|
||||
if (IsTransformSeparator()) {
|
||||
MOZ_ASSERT(GetTransform().IsIdentity());
|
||||
mBounds = mChildBounds;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mFrame->Combines3DTransformWithAncestors()) {
|
||||
if (mFrame->Extend3DContext()) {
|
||||
// The transform establishes a 3D context. |UpdateBoundsFor3D()| will
|
||||
// collect the bounds from the child transforms.
|
||||
UpdateBoundsFor3D(aBuilder);
|
||||
} else {
|
||||
// A stand-alone transform.
|
||||
mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// With nested 3D transforms, the 2D bounds might not be useful.
|
||||
MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors());
|
||||
mBounds = nsRect();
|
||||
}
|
||||
|
||||
void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
|
||||
MOZ_ASSERT(mFrame->Extend3DContext() &&
|
||||
!mFrame->Combines3DTransformWithAncestors() &&
|
||||
!IsTransformSeparator());
|
||||
|
||||
// Always start updating from an establisher of a 3D rendering context.
|
||||
nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
|
||||
nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
|
||||
accTransform.StartRoot();
|
||||
ComputeBounds(aBuilder);
|
||||
mBounds = aBuilder->GetAccumulatedRect();
|
||||
}
|
||||
|
||||
void nsDisplayTransform::UpdateUntransformedBounds(
|
||||
nsDisplayListBuilder* aBuilder) {
|
||||
mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
|
||||
aBuilder, mActiveScrolledRoot);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_HIT
|
||||
# include <time.h>
|
||||
#endif
|
||||
|
|
@ -8191,7 +8286,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
|
|||
const nsRect& aRect, HitTestState* aState,
|
||||
nsTArray<nsIFrame*>* aOutFrames) {
|
||||
if (aState->mInPreserves3D) {
|
||||
mStoredList.HitTest(aBuilder, aRect, aState, aOutFrames);
|
||||
GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -8240,7 +8335,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
|
|||
NSAppUnitsToFloatPixels(aRect.height, factor));
|
||||
|
||||
bool snap;
|
||||
nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
|
||||
nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
|
||||
Rect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
|
||||
NSAppUnitsToFloatPixels(childBounds.y, factor),
|
||||
NSAppUnitsToFloatPixels(childBounds.width, factor),
|
||||
|
|
@ -8266,7 +8361,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
|
|||
uint32_t originalFrameCount = aOutFrames.Length();
|
||||
#endif
|
||||
|
||||
mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
|
||||
GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
|
||||
|
||||
#ifdef DEBUG_HIT
|
||||
if (originalFrameCount != aOutFrames.Length())
|
||||
|
|
@ -8297,64 +8392,6 @@ float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
|
|||
return transformed.z;
|
||||
}
|
||||
|
||||
/* The bounding rectangle for the object is the overflow rectangle translated
|
||||
* by the reference point.
|
||||
*/
|
||||
nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const {
|
||||
*aSnap = false;
|
||||
|
||||
if (mHasBounds) {
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
if (mFrame->Extend3DContext() && !mIsTransformSeparator) {
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
nsRect untransformedBounds = mStoredList.GetBounds(aBuilder, aSnap);
|
||||
// GetTransform always operates in dev pixels.
|
||||
float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
mBounds = nsLayoutUtils::MatrixTransformRect(untransformedBounds,
|
||||
GetTransform(), factor);
|
||||
mHasBounds = true;
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
|
||||
MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
|
||||
|
||||
/* For some cases, the transform would make an empty bounds, but it
|
||||
* may be turned back again to get a non-empty bounds. We should
|
||||
* not depend on transforming bounds level by level.
|
||||
*
|
||||
* Here, it applies accumulated transforms on the leaf frames of the
|
||||
* 3d rendering context, and track and accmulate bounds at
|
||||
* nsDisplayListBuilder.
|
||||
*/
|
||||
nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
|
||||
|
||||
accTransform.Accumulate(GetTransform().GetMatrix());
|
||||
|
||||
if (!IsLeafOf3DContext()) {
|
||||
// Do not dive into another 3D context.
|
||||
mStoredList.DoUpdateBoundsPreserves3D(aBuilder);
|
||||
}
|
||||
|
||||
/* For Preserves3D, it is bounds of only children as leaf frames.
|
||||
* For non-leaf frames, their bounds are accumulated and kept at
|
||||
* nsDisplayListBuilder.
|
||||
*/
|
||||
bool snap;
|
||||
nsRect untransformedBounds = mStoredList.GetBounds(aBuilder, &snap);
|
||||
// GetTransform always operates in dev pixels.
|
||||
float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
nsRect rect = nsLayoutUtils::MatrixTransformRect(
|
||||
untransformedBounds, accTransform.GetCurrentTransform(), factor);
|
||||
|
||||
aBuilder->AccumulateRect(rect);
|
||||
}
|
||||
|
||||
/* The transform is opaque iff the transform consists solely of scales and
|
||||
* translations and if the underlying content is opaque. Thus if the transform
|
||||
* is of the form
|
||||
|
|
@ -8374,24 +8411,40 @@ void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
|
|||
nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const {
|
||||
*aSnap = false;
|
||||
|
||||
nsRect untransformedVisible;
|
||||
if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
const Matrix4x4Flagged& matrix = GetTransform();
|
||||
Matrix matrix2d;
|
||||
if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
nsRegion result;
|
||||
Matrix matrix2d;
|
||||
|
||||
bool tmpSnap;
|
||||
if (matrix.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles() &&
|
||||
mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap)
|
||||
.Contains(untransformedVisible)) {
|
||||
const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
|
||||
const nsRegion opaque = ::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
|
||||
|
||||
if (opaque.Contains(untransformedVisible)) {
|
||||
result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRect nsDisplayTransform::GetComponentAlphaBounds(
|
||||
nsDisplayListBuilder* aBuilder) const {
|
||||
if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
bool snap;
|
||||
return GetBounds(aBuilder, &snap);
|
||||
}
|
||||
|
||||
/* TransformRect takes in as parameters a rectangle (in app space) and returns
|
||||
* the smallest rectangle (in app space) containing the transformed image of
|
||||
* that rectangle. That is, it takes the four corners of the rectangle,
|
||||
|
|
@ -8470,7 +8523,7 @@ bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
|
|||
NSAppUnitsToFloatPixels(aRect.height, factor));
|
||||
|
||||
bool snap;
|
||||
nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
|
||||
nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
|
||||
RectDouble childGfxBounds(
|
||||
NSAppUnitsToFloatPixels(childBounds.x, factor),
|
||||
NSAppUnitsToFloatPixels(childBounds.y, factor),
|
||||
|
|
@ -9415,8 +9468,7 @@ static float ClampStdDeviation(float aStdDeviation) {
|
|||
return std::min(std::max(0.0f, aStdDeviation), 100.0f);
|
||||
}
|
||||
|
||||
bool nsDisplayFilters::CreateWebRenderCSSFilters(
|
||||
WrFiltersHolder& wrFilters) {
|
||||
bool nsDisplayFilters::CreateWebRenderCSSFilters(WrFiltersHolder& wrFilters) {
|
||||
// All CSS filters are supported by WebRender. SVG filters are not fully
|
||||
// supported, those use NS_STYLE_FILTER_URL and are handled separately.
|
||||
const nsTArray<nsStyleFilter>& filters = mFrame->StyleEffects()->mFilters;
|
||||
|
|
@ -9469,9 +9521,10 @@ bool nsDisplayFilters::CreateWebRenderCSSFilters(
|
|||
case NS_STYLE_FILTER_BLUR: {
|
||||
float appUnitsPerDevPixel =
|
||||
mFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
wrFilters.filters.AppendElement(mozilla::wr::FilterOp::Blur(ClampStdDeviation(
|
||||
NSAppUnitsToFloatPixels(filter.GetFilterParameter().GetCoordValue(),
|
||||
appUnitsPerDevPixel))));
|
||||
wrFilters.filters.AppendElement(mozilla::wr::FilterOp::Blur(
|
||||
ClampStdDeviation(NSAppUnitsToFloatPixels(
|
||||
filter.GetFilterParameter().GetCoordValue(),
|
||||
appUnitsPerDevPixel))));
|
||||
break;
|
||||
}
|
||||
case NS_STYLE_FILTER_DROP_SHADOW: {
|
||||
|
|
|
|||
|
|
@ -2271,6 +2271,14 @@ class nsDisplayItem : public nsDisplayItemLink {
|
|||
return nsRect(ToReferenceFrame(), Frame()->GetSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the untransformed bounds of this display item.
|
||||
*/
|
||||
virtual nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const {
|
||||
return GetBounds(aBuilder, aSnap);
|
||||
}
|
||||
|
||||
virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const {
|
||||
*aSnap = false;
|
||||
|
|
@ -2894,6 +2902,10 @@ class nsDisplayItem : public nsDisplayItemLink {
|
|||
|
||||
const nsRect& GetPaintRect() const { return mPaintRect; }
|
||||
|
||||
virtual const nsRect& GetUntransformedPaintRect() const {
|
||||
return GetPaintRect();
|
||||
}
|
||||
|
||||
virtual bool HasHitTestInfo() const { return false; }
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
@ -3260,6 +3272,29 @@ class nsDisplayList {
|
|||
nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
|
||||
nsRect* aBuildingRect = nullptr) const;
|
||||
|
||||
/**
|
||||
* Returns the opaque region of this display list.
|
||||
*/
|
||||
nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder) {
|
||||
nsRegion result;
|
||||
bool snap;
|
||||
for (nsDisplayItem* item = GetBottom(); item; item = item->GetAbove()) {
|
||||
result.OrWith(item->GetOpaqueRegion(aBuilder, &snap));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of the area that needs component alpha.
|
||||
*/
|
||||
nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const {
|
||||
nsRect bounds;
|
||||
for (nsDisplayItem* item = GetBottom(); item; item = item->GetAbove()) {
|
||||
bounds.UnionRect(bounds, item->GetComponentAlphaBounds(aBuilder));
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the topmost display item that returns a non-null frame, and return
|
||||
* the frame.
|
||||
|
|
@ -6419,37 +6454,6 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
|
|||
typedef mozilla::gfx::Matrix4x4Flagged Matrix4x4Flagged;
|
||||
typedef mozilla::gfx::Point3D Point3D;
|
||||
|
||||
/*
|
||||
* Avoid doing UpdateBounds() during construction.
|
||||
*/
|
||||
class StoreList : public nsDisplayWrapList {
|
||||
public:
|
||||
StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||
nsDisplayList* aList)
|
||||
: nsDisplayWrapList(aBuilder, aFrame, aList, true) {}
|
||||
|
||||
~StoreList() override = default;
|
||||
|
||||
void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
|
||||
// For extending 3d rendering context, the bounds would be
|
||||
// updated by DoUpdateBoundsPreserves3D(), not here.
|
||||
if (!mFrame->Extend3DContext()) {
|
||||
nsDisplayWrapList::UpdateBounds(aBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
void ForceUpdateBounds(nsDisplayListBuilder* aBuilder) {
|
||||
nsDisplayWrapList::UpdateBounds(aBuilder);
|
||||
}
|
||||
|
||||
void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
|
||||
for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) {
|
||||
i->DoUpdateBoundsPreserves3D(aBuilder);
|
||||
}
|
||||
nsDisplayWrapList::UpdateBounds(aBuilder);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
enum PrerenderDecision { NoPrerender, FullPrerender, PartialPrerender };
|
||||
|
||||
|
|
@ -6468,14 +6472,16 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
|
|||
*/
|
||||
nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||
nsDisplayList* aList, const nsRect& aChildrenBuildingRect,
|
||||
uint32_t aIndex = 0, bool aAllowAsyncAnimation = false);
|
||||
uint32_t aIndex);
|
||||
|
||||
nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||
nsDisplayList* aList, const nsRect& aChildrenBuildingRect,
|
||||
ComputeTransformFunction aTransformGetter,
|
||||
uint32_t aIndex = 0);
|
||||
uint32_t aIndex, bool aAllowAsyncAnimation);
|
||||
|
||||
nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||
nsDisplayList* aList, const nsRect& aChildrenBuildingRect,
|
||||
const Matrix4x4& aTransform, uint32_t aIndex = 0);
|
||||
uint32_t aIndex,
|
||||
ComputeTransformFunction aTransformGetter);
|
||||
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
~nsDisplayTransform() override { MOZ_COUNT_DTOR(nsDisplayTransform); }
|
||||
|
|
@ -6485,41 +6491,37 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
|
|||
|
||||
void RestoreState() override { mShouldFlatten = false; }
|
||||
|
||||
void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
|
||||
mHasBounds = false;
|
||||
if (IsTransformSeparator()) {
|
||||
mStoredList.ForceUpdateBounds(aBuilder);
|
||||
return;
|
||||
}
|
||||
mStoredList.UpdateBounds(aBuilder);
|
||||
UpdateBoundsFor3D(aBuilder);
|
||||
}
|
||||
void UpdateBounds(nsDisplayListBuilder* aBuilder) override;
|
||||
|
||||
/**
|
||||
* This function updates bounds for items with a frame establishing
|
||||
* 3D rendering context.
|
||||
*/
|
||||
void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder);
|
||||
|
||||
void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override;
|
||||
|
||||
void Destroy(nsDisplayListBuilder* aBuilder) override {
|
||||
mStoredList.GetChildren()->DeleteAll(aBuilder);
|
||||
GetChildren()->DeleteAll(aBuilder);
|
||||
nsDisplayItem::Destroy(aBuilder);
|
||||
}
|
||||
|
||||
nsRect GetComponentAlphaBounds(
|
||||
nsDisplayListBuilder* aBuilder) const override {
|
||||
if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty())
|
||||
return nsRect();
|
||||
bool snap;
|
||||
return GetBounds(aBuilder, &snap);
|
||||
nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;
|
||||
|
||||
RetainedDisplayList* GetChildren() const override { return &mChildren; }
|
||||
|
||||
nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) const override {
|
||||
*aSnap = false;
|
||||
return mChildBounds;
|
||||
}
|
||||
|
||||
RetainedDisplayList* GetChildren() const override {
|
||||
return mStoredList.GetChildren();
|
||||
const nsRect& GetUntransformedPaintRect() const override {
|
||||
return mChildrenBuildingRect;
|
||||
}
|
||||
|
||||
bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
|
||||
|
||||
void SetActiveScrolledRoot(
|
||||
const ActiveScrolledRoot* aActiveScrolledRoot) override {
|
||||
nsDisplayHitTestInfoItem::SetActiveScrolledRoot(aActiveScrolledRoot);
|
||||
mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot);
|
||||
}
|
||||
|
||||
void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||
HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
|
||||
nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
|
||||
|
|
@ -6752,41 +6754,12 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
|
|||
|
||||
void WriteDebugInfo(std::stringstream& aStream) override;
|
||||
|
||||
void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
|
||||
MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
|
||||
IsTransformSeparator());
|
||||
// Updating is not going through to child 3D context.
|
||||
ComputeBounds(aBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function updates bounds for items with a frame establishing
|
||||
* 3D rendering context.
|
||||
*
|
||||
* \see nsDisplayItem::DoUpdateBoundsPreserves3D()
|
||||
*/
|
||||
void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
|
||||
if (!mFrame->Extend3DContext() ||
|
||||
mFrame->Combines3DTransformWithAncestors() || IsTransformSeparator()) {
|
||||
// Not an establisher of a 3D rendering context.
|
||||
return;
|
||||
}
|
||||
// Always start updating from an establisher of a 3D rendering context.
|
||||
|
||||
nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
|
||||
nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
|
||||
accTransform.StartRoot();
|
||||
ComputeBounds(aBuilder);
|
||||
mBounds = aBuilder->GetAccumulatedRect();
|
||||
mHasBounds = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This item is an additional item as the boundary between parent
|
||||
* and child 3D rendering context.
|
||||
* \see nsIFrame::BuildDisplayListForStackingContext().
|
||||
*/
|
||||
bool IsTransformSeparator() { return mIsTransformSeparator; }
|
||||
bool IsTransformSeparator() const { return mIsTransformSeparator; }
|
||||
/**
|
||||
* This item is the boundary between parent and child 3D rendering
|
||||
* context.
|
||||
|
|
@ -6805,33 +6778,33 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
|
|||
mFrame->Combines3DTransformWithAncestors();
|
||||
}
|
||||
|
||||
void RemoveFrame(nsIFrame* aFrame) override {
|
||||
nsDisplayItem::RemoveFrame(aFrame);
|
||||
mStoredList.RemoveFrame(aFrame);
|
||||
}
|
||||
|
||||
private:
|
||||
void ComputeBounds(nsDisplayListBuilder* aBuilder);
|
||||
nsRect TransformUntransformedBounds(nsDisplayListBuilder* aBuilder,
|
||||
const Matrix4x4Flagged& aMatrix) const;
|
||||
void UpdateUntransformedBounds(nsDisplayListBuilder* aBuilder);
|
||||
|
||||
void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
|
||||
void Init(nsDisplayListBuilder* aBuilder);
|
||||
void Init(nsDisplayListBuilder* aBuilder, nsDisplayList* aChildren);
|
||||
|
||||
static Matrix4x4 GetResultingTransformMatrixInternal(
|
||||
const FrameTransformProperties& aProperties, const nsPoint& aOrigin,
|
||||
float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride);
|
||||
|
||||
StoreList mStoredList;
|
||||
mutable mozilla::Maybe<Matrix4x4Flagged> mTransform;
|
||||
mutable mozilla::Maybe<Matrix4x4Flagged> mInverseTransform;
|
||||
// Accumulated transform of ancestors on the preserves-3d chain.
|
||||
Matrix4x4 mTransformPreserves3D;
|
||||
mozilla::UniquePtr<Matrix4x4> mTransformPreserves3D;
|
||||
ComputeTransformFunction mTransformGetter;
|
||||
RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForChildren;
|
||||
RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForScrollMetadata;
|
||||
nsRect mChildrenBuildingRect;
|
||||
uint32_t mIndex;
|
||||
mutable nsRect mBounds;
|
||||
// True for mBounds is valid.
|
||||
mutable bool mHasBounds;
|
||||
mutable RetainedDisplayList mChildren;
|
||||
// The untransformed bounds of |mChildren|.
|
||||
nsRect mChildBounds;
|
||||
// The transformed bounds of this display item.
|
||||
nsRect mBounds;
|
||||
// This item is a separator between 3D rendering contexts, and
|
||||
// mTransform have been presetted by the constructor.
|
||||
// This also forces us not to extend the 3D context. Since we don't create a
|
||||
|
|
@ -6840,8 +6813,6 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
|
|||
// parent context unintendedly if the root of the child preserves3d context
|
||||
// doesn't create a transform item.
|
||||
bool mIsTransformSeparator;
|
||||
// True if mTransformPreserves3D have been initialized.
|
||||
bool mTransformPreserves3DInited;
|
||||
// True if async animation of the transform is allowed.
|
||||
bool mAllowAsyncAnimation;
|
||||
// True if this nsDisplayTransform should get flattened
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=325">
|
||||
<meta name="viewport" content="initial-scale=0.25">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 8228px; width: 9000px;"></div>
|
||||
<div style="height: 80000px; width: 36000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=325">
|
||||
<meta name="viewport" content="initial-scale=0.25">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 8228px; width: 9000px;"></div>
|
||||
<div style="height: 80000px; width: 36000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=125">
|
||||
<meta name="viewport" content="initial-scale=0.5">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 4787px; width: 9000px;"></div>
|
||||
<div style="height: 40000px; width: 18000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=125">
|
||||
<meta name="viewport" content="initial-scale=0.5">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 4787px; width: 9000px;"></div>
|
||||
<div style="height: 40000px; width: 18000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=525">
|
||||
<meta name="viewport" content="initial-scale=0.75">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 13972px; width: 9000px;"></div>
|
||||
<div style="height: 26667px; width: 12000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=525">
|
||||
<meta name="viewport" content="initial-scale=0.75">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 13972px; width: 9000px;"></div>
|
||||
<div style="height: 26667px; width: 12000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=725">
|
||||
<meta name="viewport" content="initial-scale=1.25">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 20000px; width: 9000px;"></div>
|
||||
<div style="height: 16000px; width: 7200px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=725">
|
||||
<meta name="viewport" content="initial-scale=1.25">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 20000px; width: 9000px;"></div>
|
||||
<div style="height: 16000px; width: 7200px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=925">
|
||||
<meta name="viewport" content="initial-scale=1.5">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 26117px; width: 9000px;"></div>
|
||||
<div style="height: 13333px; width: 6000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=925">
|
||||
<meta name="viewport" content="initial-scale=1.5">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 26117px; width: 9000px;"></div>
|
||||
<div style="height: 13333px; width: 6000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=1325">
|
||||
<meta name="viewport" content="initial-scale=2.0">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 37412px; width: 9000px;"></div>
|
||||
<div style="height: 10000px; width: 4500px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=1325">
|
||||
<meta name="viewport" content="initial-scale=2.0">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 37412px; width: 9000px;"></div>
|
||||
<div style="height: 10000px; width: 4500px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=725">
|
||||
<meta name="viewport" content="initial-scale=1.0">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=725">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 20000px; width: 9000px;"></div>
|
||||
|
|
|
|||
|
|
@ -1900,7 +1900,7 @@ skip-if(verify) == 1121748-2.html 1121748-2-ref.html
|
|||
== 1130231-2-button-padding-rtl.html 1130231-2-button-padding-rtl-ref.html
|
||||
# The 1133905-*.html reftests only make sense on platforms where both APZ and
|
||||
# <meta viewport> are enabled.
|
||||
# (Note: bug 1308702 covers these tests' failures on Android)
|
||||
# (Note: bug 1308702 and bug 1527511 cover these tests' failures on Android)
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-1.html 1133905-ref.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-2.html 1133905-ref.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-3.html 1133905-ref.html
|
||||
|
|
@ -1919,12 +1919,13 @@ pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-3-h.html 1133905-ref-h
|
|||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-4-h.html 1133905-ref-h.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-5-h.html 1133905-ref-h.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-6-h.html 1133905-ref-h.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-1-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-2-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-54,0-6) == 1133905-1-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-45,0-4) == 1133905-2-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-3-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-4-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-5-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-6-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-45,0-4) == 1133905-4-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-54,0-6) == 1133905-5-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-54,0-8) == 1133905-6-vh.html 1133905-ref-vh.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) != 1133905-ref-vh.html about:blank # make sure it doesn't render blank
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-1-rtl.html 1133905-ref-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-2-rtl.html 1133905-ref-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-3-rtl.html 1133905-ref-rtl.html
|
||||
|
|
@ -1943,12 +1944,13 @@ pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-3-h-rtl.html 1133905-r
|
|||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-4-h-rtl.html 1133905-ref-h-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-5-h-rtl.html 1133905-ref-h-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-6-h-rtl.html 1133905-ref-h-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-54,0-6) == 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fuzzy-if(Android,0-45,0-4) == 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-3-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fails-if(Android) == 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html # bug 1527511
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fails-if(Android) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html # bug 1527511
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) fails-if(Android) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html # bug 1527511
|
||||
pref(apz.allow_zooming,true) skip-if(!Android) != 1133905-ref-vh-rtl.html about:blank # make sure it doesn't render blank
|
||||
== 1149304-1-transform-change.html 1149304-1-transform-change-ref.html
|
||||
== 1150021-1.xul 1150021-1-ref.xul
|
||||
== 1151145-1.html 1151145-1-ref.html
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ fails-if(Android) skip-if(Android&&AndroidVersion=='18') == poster-2.html poster
|
|||
random-if(Android) == poster-3.html poster-ref-black140x100.html
|
||||
== poster-4.html poster-ref-black140x100.html
|
||||
random-if(Android) == poster-5.html poster-ref-black140x100.html
|
||||
== poster-6.html poster-ref-black140x100.html
|
||||
== poster-7.html poster-ref-red140x100.html
|
||||
fails-if(Android) == poster-6.html poster-ref-black140x100.html
|
||||
fails-if(Android) == poster-7.html poster-ref-red140x100.html
|
||||
fuzzy-if(Android,0-2,0-14000) == poster-8.html poster-ref-black140x100.html
|
||||
random == poster-10.html poster-ref-blue125x100.html
|
||||
random == poster-11.html poster-ref-blue140x100.html
|
||||
random == poster-12.html poster-ref-blue140x100.html
|
||||
== poster-13.html poster-ref-blue400x300.html
|
||||
== poster-15.html poster-ref-green70x30.html
|
||||
random-if(Android) == poster-13.html poster-ref-blue400x300.html
|
||||
random-if(Android) == poster-15.html poster-ref-green70x30.html
|
||||
random-if(winWidget) random-if(cocoaWidget) skip-if(Android) == bug686957.html bug686957-ref.html # bug 922951 for OS X
|
||||
== webm-alpha.html webm-alpha-ref.html
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ function testInheritedProperty(property, info) {
|
|||
// is different (normal vs. 19px).
|
||||
if (property == "line-height")
|
||||
return;
|
||||
// Ongoing debugging in bug 1533392.
|
||||
if (property == "font-family")
|
||||
return;
|
||||
|
||||
const div = kInheritedDiv;
|
||||
const initial = getInitialValue(div, property);
|
||||
|
|
|
|||
|
|
@ -535,9 +535,13 @@ pref("layers.low-precision-opacity", "1.0");
|
|||
// work harder keep scrolling smooth and memory low.
|
||||
pref("layers.max-active", 20);
|
||||
|
||||
// On Fennec we need containerful scrolling to support zooming. Bug 1459312
|
||||
// tracks zooming with containerless scrolling.
|
||||
// Use containerless scrolling in nightly builds on Fennec.
|
||||
// Bug 1137890 tracks letting containerless scrolling ride the trains.
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("layout.scroll.root-frame-containers", 0);
|
||||
#else
|
||||
pref("layout.scroll.root-frame-containers", 1);
|
||||
#endif
|
||||
|
||||
pref("notification.feature.enabled", true);
|
||||
pref("dom.webnotifications.enabled", true);
|
||||
|
|
|
|||
|
|
@ -515,10 +515,10 @@
|
|||
|
||||
<!-- New child services must also be added to the GeckoView AndroidManifest.xml -->
|
||||
<service
|
||||
android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$geckomediaplugin"
|
||||
android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$gmplugin"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:process=":geckomediaplugin"
|
||||
android:process=":gmplugin"
|
||||
android:isolatedProcess="false">
|
||||
</service>
|
||||
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@
|
|||
android:process=":media">
|
||||
</service>
|
||||
<service
|
||||
android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$geckomediaplugin"
|
||||
android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$gmplugin"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:isolatedProcess="false"
|
||||
android:process=":geckomediaplugin">
|
||||
android:process=":gmplugin">
|
||||
</service>
|
||||
<service
|
||||
android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$tab"
|
||||
|
|
@ -69,4 +69,4 @@
|
|||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ public class GeckoServiceChildProcess extends Service {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static final class geckomediaplugin extends GeckoServiceChildProcess {}
|
||||
public static final class gmplugin extends GeckoServiceChildProcess {}
|
||||
|
||||
public static final class tab extends GeckoServiceChildProcess {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4672,6 +4672,7 @@ pref("font.name-list.monospace.x-unicode", "dt-interface user-ucs2.cjk_japan-0")
|
|||
pref("signon.rememberSignons", true);
|
||||
pref("signon.rememberSignons.visibilityToggle", true);
|
||||
pref("signon.autofillForms", true);
|
||||
pref("signon.autofillForms.autocompleteOff", true);
|
||||
pref("signon.autofillForms.http", false);
|
||||
pref("signon.autologin.proxy", false);
|
||||
pref("signon.formlessCapture.enabled", true);
|
||||
|
|
|
|||
|
|
@ -638,22 +638,6 @@ static const StaticFingerprints kPinset_swehackCom = {
|
|||
kPinset_swehackCom_Data
|
||||
};
|
||||
|
||||
static const char* const kPinset_ncsccs_Data[] = {
|
||||
kCOMODO_ECC_Certification_AuthorityFingerprint,
|
||||
kDigiCert_Assured_ID_Root_CAFingerprint,
|
||||
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
|
||||
kBaltimore_CyberTrust_RootFingerprint,
|
||||
kLet_s_Encrypt_Authority_X3Fingerprint,
|
||||
kCOMODO_RSA_Certification_AuthorityFingerprint,
|
||||
kAddTrust_External_RootFingerprint,
|
||||
kDigiCert_Global_Root_CAFingerprint,
|
||||
kLet_s_Encrypt_Authority_X4Fingerprint,
|
||||
};
|
||||
static const StaticFingerprints kPinset_ncsccs = {
|
||||
sizeof(kPinset_ncsccs_Data) / sizeof(const char*),
|
||||
kPinset_ncsccs_Data
|
||||
};
|
||||
|
||||
static const char* const kPinset_tumblr_Data[] = {
|
||||
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
|
||||
kTumblrBackupFingerprint,
|
||||
|
|
@ -677,7 +661,6 @@ struct TransportSecurityPreload {
|
|||
|
||||
/* Sort hostnames for binary search. */
|
||||
static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
||||
{ "0.me.uk", true, false, false, -1, &kPinset_ncsccs },
|
||||
{ "2mdn.net", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "accounts.firefox.com", true, false, true, 4, &kPinset_mozilla_services },
|
||||
{ "accounts.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
|
|
@ -1059,7 +1042,6 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
{ "mx.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
{ "myaccount.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "myactivity.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "ncsccs.com", true, false, false, -1, &kPinset_ncsccs },
|
||||
{ "ni.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
{ "nl.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
{ "no.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
|
|
@ -1096,7 +1078,6 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
{ "security.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "services.mozilla.com", true, false, true, 6, &kPinset_mozilla_services },
|
||||
{ "sg.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
{ "sirburton.com", true, false, false, -1, &kPinset_ncsccs },
|
||||
{ "sites.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "spideroak.com", true, false, false, -1, &kPinset_spideroak },
|
||||
{ "spreadsheets.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
|
|
@ -1113,7 +1094,6 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
{ "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
|
||||
{ "testpilot.firefox.com", false, false, true, 9, &kPinset_mozilla_services },
|
||||
{ "th.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
{ "themathematician.uk", true, false, false, -1, &kPinset_ncsccs },
|
||||
{ "torproject.org", false, false, false, -1, &kPinset_tor },
|
||||
{ "touch.facebook.com", true, false, false, -1, &kPinset_facebook },
|
||||
{ "tr.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
|
|
@ -1167,8 +1147,8 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
{ "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
|
||||
};
|
||||
|
||||
// Pinning Preload List Length = 488;
|
||||
// Pinning Preload List Length = 484;
|
||||
|
||||
static const int32_t kUnknownId = -1;
|
||||
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1559823648419000);
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1560431526439000);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -7,7 +7,6 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from taskgraph.util.taskcluster import (
|
||||
status_task,
|
||||
|
|
@ -54,9 +53,9 @@ def rerun_action(parameters, graph_config, input, task_group_id, task_id):
|
|||
|
||||
status = status_task(task_id)
|
||||
if status not in RERUN_STATES:
|
||||
logger.error(
|
||||
"Refusing to rerun {}: state {} not in {}!".format(label, status, RERUN_STATES)
|
||||
logger.warning(
|
||||
"No need to to rerun {}: state '{}' not in {}!".format(label, status, RERUN_STATES)
|
||||
)
|
||||
sys.exit(1)
|
||||
return
|
||||
rerun_task(task_id)
|
||||
logger.info('Reran {}'.format(label))
|
||||
|
|
|
|||
|
|
@ -128,6 +128,3 @@ skip-if = asan || manage_instance == false || appname == 'fennec' # Bug 1298921
|
|||
|
||||
[test_reftest.py]
|
||||
skip-if = appname == 'fennec' # Bug 1519552
|
||||
|
||||
[test_startup_caches.py]
|
||||
skip-if = appname == 'fennec'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
[transform-flattening-001.html]
|
||||
expected:
|
||||
if webrender: FAIL
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
[RTCPeerConnection-setLocalDescription.html]
|
||||
[Switching role from answerer to offerer after going back to stable state should succeed]
|
||||
expected: FAIL
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531143
|
||||
|
||||
[onsignalingstatechange fires before setLocalDescription resolves]
|
||||
expected: FAIL
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531075
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<title>CSS Transforms: Transforms are flattened if transform-style is flat.</title>
|
||||
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
|
||||
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#3d-rendering-context">
|
||||
<link rel="mismatch" href="about:blank">
|
||||
<!-- Should see a green rectangle, not white -->
|
||||
<div style="transform: rotateX(45deg)">
|
||||
<div style="transform: rotateX(45deg); background: green; width: 100px; height: 100px"></div>
|
||||
</div>
|
||||
|
|
@ -72,29 +72,55 @@
|
|||
})));
|
||||
}, 'Calling createOffer() and setLocalDescription() again after one round of local-offer/remote-answer should succeed');
|
||||
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc.close());
|
||||
promise_test(async t => {
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
|
||||
const pc2 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
const states = [];
|
||||
pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
|
||||
pc1.addEventListener('signalingstatechange', () => states.push(pc1.signalingState));
|
||||
|
||||
return generateDataChannelOffer(pc)
|
||||
.then(offer => pc.setRemoteDescription(offer))
|
||||
.then(() => pc.createAnswer())
|
||||
.then(answer =>
|
||||
pc.setLocalDescription(answer)
|
||||
.then(() => generateVideoReceiveOnlyOffer(pc))
|
||||
.then(offer =>
|
||||
pc.setLocalDescription(offer)
|
||||
.then(() => {
|
||||
assert_equals(pc.signalingState, 'have-local-offer');
|
||||
assert_session_desc_similar(pc.localDescription, offer);
|
||||
assert_session_desc_similar(pc.currentLocalDescription, answer);
|
||||
assert_session_desc_similar(pc.pendingLocalDescription, offer);
|
||||
assert_equals(pc1.localDescription, null);
|
||||
assert_equals(pc1.currentLocalDescription, null);
|
||||
assert_equals(pc1.pendingLocalDescription, null);
|
||||
|
||||
assert_array_equals(states, ['have-remote-offer', 'stable', 'have-local-offer']);
|
||||
})));
|
||||
pc1.createDataChannel('test');
|
||||
const offer = await pc1.createOffer();
|
||||
|
||||
assert_equals(pc1.localDescription, null);
|
||||
assert_equals(pc1.currentLocalDescription, null);
|
||||
assert_equals(pc1.pendingLocalDescription, null);
|
||||
|
||||
await pc1.setLocalDescription(offer);
|
||||
|
||||
assert_session_desc_similar(pc1.localDescription, offer);
|
||||
assert_equals(pc1.currentLocalDescription, null);
|
||||
assert_session_desc_similar(pc1.pendingLocalDescription, offer);
|
||||
|
||||
await pc2.setRemoteDescription(offer);
|
||||
const answer = await pc2.createAnswer();
|
||||
await pc2.setLocalDescription(answer);
|
||||
await pc1.setRemoteDescription(answer);
|
||||
|
||||
assert_equals(pc1.signalingState, 'stable');
|
||||
assert_session_desc_similar(pc1.localDescription, offer);
|
||||
assert_session_desc_similar(pc1.currentLocalDescription, offer);
|
||||
assert_equals(pc1.pendingLocalDescription, null);
|
||||
|
||||
const stream = await getNoiseStream({audio:true});
|
||||
pc2.addTrack(stream.getTracks()[0], stream);
|
||||
|
||||
const reoffer = await pc2.createOffer();
|
||||
await pc2.setLocalDescription(reoffer);
|
||||
await pc1.setRemoteDescription(reoffer);
|
||||
const reanswer = await pc1.createAnswer();
|
||||
await pc1.setLocalDescription(reanswer);
|
||||
|
||||
assert_session_desc_similar(pc1.localDescription, reanswer);
|
||||
assert_session_desc_similar(pc1.currentLocalDescription, reanswer);
|
||||
assert_equals(pc1.pendingLocalDescription, null);
|
||||
}, 'Switching role from answerer to offerer after going back to stable state should succeed');
|
||||
|
||||
promise_test(async t => {
|
||||
|
|
|
|||
122
toolkit/components/passwordmgr/LoginFormFactory.jsm
Normal file
122
toolkit/components/passwordmgr/LoginFormFactory.jsm
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* A factory to generate LoginForm objects that represent a set of login fields
|
||||
* which aren't necessarily marked up with a <form> element.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["LoginFormFactory"];
|
||||
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "FormLikeFactory",
|
||||
"resource://gre/modules/FormLikeFactory.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginHelper",
|
||||
"resource://gre/modules/LoginHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
return LoginHelper.createLogger("LoginFormFactory");
|
||||
});
|
||||
|
||||
|
||||
var LoginFormFactory = {
|
||||
/**
|
||||
* WeakMap of the root element of a LoginForm to the LoginForm representing its fields.
|
||||
*
|
||||
* This is used to be able to lookup an existing LoginForm for a given root element since multiple
|
||||
* calls to LoginFormFactory.createFrom* won't give the exact same object. When batching fills we don't always
|
||||
* want to use the most recent list of elements for a LoginForm since we may end up doing multiple
|
||||
* fills for the same set of elements when a field gets added between arming and running the
|
||||
* DeferredTask.
|
||||
*
|
||||
* @type {WeakMap}
|
||||
*/
|
||||
_loginFormsByRootElement: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Maps all DOM content documents in this content process, including those in
|
||||
* frames, to a WeakSet of LoginForm for the document.
|
||||
*/
|
||||
_loginFormRootElementsByDocument: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Create a LoginForm object from a <form>.
|
||||
*
|
||||
* @param {HTMLFormElement} aForm
|
||||
* @return {LoginForm}
|
||||
* @throws Error if aForm isn't an HTMLFormElement
|
||||
*/
|
||||
createFromForm(aForm) {
|
||||
let formLike = FormLikeFactory.createFromForm(aForm);
|
||||
formLike.action = LoginHelper.getFormActionOrigin(aForm);
|
||||
|
||||
let rootElementsSet = this.getRootElementsWeakSetForDocument(formLike.ownerDocument);
|
||||
rootElementsSet.add(formLike.rootElement);
|
||||
log.debug("adding", formLike.rootElement, "to root elements for", formLike.ownerDocument);
|
||||
|
||||
this._loginFormsByRootElement.set(formLike.rootElement, formLike);
|
||||
return formLike;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a LoginForm object from a password or username field.
|
||||
*
|
||||
* If the field is in a <form>, construct the LoginForm from the form.
|
||||
* Otherwise, create a LoginForm with a rootElement (wrapper) according to
|
||||
* heuristics. Currently all <input> not in a <form> are one LoginForm but this
|
||||
* shouldn't be relied upon as the heuristics may change to detect multiple
|
||||
* "forms" (e.g. registration and login) on one page with a <form>.
|
||||
*
|
||||
* Note that two LoginForms created from the same field won't return the same LoginForm object.
|
||||
* Use the `rootElement` property on the LoginForm as a key instead.
|
||||
*
|
||||
* @param {HTMLInputElement} aField - a password or username field in a document
|
||||
* @return {LoginForm}
|
||||
* @throws Error if aField isn't a password or username field in a document
|
||||
*/
|
||||
createFromField(aField) {
|
||||
if (ChromeUtils.getClassName(aField) !== "HTMLInputElement" ||
|
||||
(aField.type != "password" && !LoginHelper.isUsernameFieldType(aField)) ||
|
||||
!aField.ownerDocument) {
|
||||
throw new Error("createFromField requires a password or username field in a document");
|
||||
}
|
||||
|
||||
if (aField.form) {
|
||||
return this.createFromForm(aField.form);
|
||||
}
|
||||
|
||||
let formLike = FormLikeFactory.createFromField(aField);
|
||||
formLike.action = LoginHelper.getLoginOrigin(aField.ownerDocument.baseURI);
|
||||
log.debug("Created non-form LoginForm for rootElement:", aField.ownerDocument.documentElement);
|
||||
|
||||
let rootElementsSet = this.getRootElementsWeakSetForDocument(formLike.ownerDocument);
|
||||
rootElementsSet.add(formLike.rootElement);
|
||||
log.debug("adding", formLike.rootElement, "to root elements for", formLike.ownerDocument);
|
||||
|
||||
|
||||
this._loginFormsByRootElement.set(formLike.rootElement, formLike);
|
||||
|
||||
return formLike;
|
||||
},
|
||||
|
||||
getRootElementsWeakSetForDocument(aDocument) {
|
||||
let rootElementsSet = this._loginFormRootElementsByDocument.get(aDocument);
|
||||
if (!rootElementsSet) {
|
||||
rootElementsSet = new WeakSet();
|
||||
this._loginFormRootElementsByDocument.set(aDocument, rootElementsSet);
|
||||
}
|
||||
return rootElementsSet;
|
||||
},
|
||||
|
||||
getForRootElement(aRootElement) {
|
||||
return this._loginFormsByRootElement.get(aRootElement);
|
||||
},
|
||||
|
||||
setForRootElement(aRootElement, aLoginForm) {
|
||||
return this._loginFormsByRootElement.set(aRootElement, aLoginForm);
|
||||
},
|
||||
};
|
||||
|
|
@ -40,6 +40,7 @@ var LoginHelper = {
|
|||
|
||||
updateSignonPrefs() {
|
||||
this.autofillForms = Services.prefs.getBoolPref("signon.autofillForms");
|
||||
this.autofillAutocompleteOff = Services.prefs.getBoolPref("signon.autofillForms.autocompleteOff");
|
||||
this.debug = Services.prefs.getBoolPref("signon.debug");
|
||||
this.enabled = Services.prefs.getBoolPref("signon.rememberSignons");
|
||||
this.formlessCaptureEnabled = Services.prefs.getBoolPref("signon.formlessCapture.enabled");
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
|||
ChromeUtils.defineModuleGetter(this, "LoginHelper",
|
||||
"resource://gre/modules/LoginHelper.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginFormFactory",
|
||||
"resource://gre/modules/LoginManagerContent.jsm");
|
||||
"resource://gre/modules/LoginFormFactory.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginManagerContent",
|
||||
"resource://gre/modules/LoginManagerContent.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginAutoCompleteResult",
|
||||
|
|
|
|||
|
|
@ -6,15 +6,12 @@
|
|||
* Module doing most of the content process work for the password manager.
|
||||
*/
|
||||
|
||||
// Disable use-ownerGlobal since FormLike don't have it.
|
||||
// Disable use-ownerGlobal since LoginForm doesn't have it.
|
||||
/* eslint-disable mozilla/use-ownerGlobal */
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"LoginManagerContent",
|
||||
"LoginFormFactory",
|
||||
];
|
||||
var EXPORTED_SYMBOLS = ["LoginManagerContent"];
|
||||
|
||||
const PASSWORD_INPUT_ADDED_COALESCING_THRESHOLD_MS = 1;
|
||||
const AUTOCOMPLETE_AFTER_RIGHT_CLICK_THRESHOLD_MS = 400;
|
||||
|
|
@ -28,6 +25,8 @@ const {PromiseUtils} = ChromeUtils.import("resource://gre/modules/PromiseUtils.j
|
|||
ChromeUtils.defineModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "FormLikeFactory",
|
||||
"resource://gre/modules/FormLikeFactory.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginFormFactory",
|
||||
"resource://gre/modules/LoginFormFactory.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginRecipesContent",
|
||||
"resource://gre/modules/LoginRecipes.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LoginHelper",
|
||||
|
|
@ -152,24 +151,11 @@ var LoginManagerContent = {
|
|||
],
|
||||
|
||||
/**
|
||||
* WeakMap of the root element of a FormLike to the FormLike representing its fields.
|
||||
* WeakMap of the root element of a LoginForm to the DeferredTask to fill its fields.
|
||||
*
|
||||
* This is used to be able to lookup an existing FormLike for a given root element since multiple
|
||||
* calls to LoginFormFactory won't give the exact same object. When batching fills we don't always
|
||||
* want to use the most recent list of elements for a FormLike since we may end up doing multiple
|
||||
* fills for the same set of elements when a field gets added between arming and running the
|
||||
* DeferredTask.
|
||||
*
|
||||
* @type {WeakMap}
|
||||
*/
|
||||
_formLikeByRootElement: new WeakMap(),
|
||||
|
||||
/**
|
||||
* WeakMap of the root element of a FormLike to the DeferredTask to fill its fields.
|
||||
*
|
||||
* This is used to be able to throttle fills for a FormLike since onDOMInputPasswordAdded gets
|
||||
* This is used to be able to throttle fills for a LoginForm since onDOMInputPasswordAdded gets
|
||||
* dispatched for each password field added to a document but we only want to fill once per
|
||||
* FormLike when multiple fields are added at once.
|
||||
* LoginForm when multiple fields are added at once.
|
||||
*
|
||||
* @type {WeakMap}
|
||||
*/
|
||||
|
|
@ -212,8 +198,7 @@ var LoginManagerContent = {
|
|||
return request;
|
||||
},
|
||||
|
||||
_sendRequest(messageManager, requestData,
|
||||
name, messageData) {
|
||||
_sendRequest(messageManager, requestData, name, messageData) {
|
||||
let count;
|
||||
if (!(count = this._managers.get(messageManager))) {
|
||||
this._managers.set(messageManager, 1);
|
||||
|
|
@ -451,12 +436,12 @@ var LoginManagerContent = {
|
|||
let deferredTask = this._deferredPasswordAddedTasksByRootElement.get(formLike.rootElement);
|
||||
if (!deferredTask) {
|
||||
log("Creating a DeferredTask to call _fetchLoginsFromParentAndFillForm soon");
|
||||
this._formLikeByRootElement.set(formLike.rootElement, formLike);
|
||||
LoginFormFactory.setForRootElement(formLike.rootElement, formLike);
|
||||
|
||||
deferredTask = new DeferredTask(() => {
|
||||
// Get the updated formLike instead of the one at the time of creating the DeferredTask via
|
||||
// a closure since it could be stale since FormLike.elements isn't live.
|
||||
let formLike2 = this._formLikeByRootElement.get(formLike.rootElement);
|
||||
// Get the updated LoginForm instead of the one at the time of creating the DeferredTask via
|
||||
// a closure since it could be stale since LoginForm.elements isn't live.
|
||||
let formLike2 = LoginFormFactory.getForRootElement(formLike.rootElement);
|
||||
log("Running deferred processing of onDOMInputPasswordAdded", formLike2);
|
||||
this._deferredPasswordAddedTasksByRootElement.delete(formLike2.rootElement);
|
||||
this._fetchLoginsFromParentAndFillForm(formLike2);
|
||||
|
|
@ -467,10 +452,10 @@ var LoginManagerContent = {
|
|||
|
||||
let window = pwField.ownerGlobal;
|
||||
if (deferredTask.isArmed) {
|
||||
log("DeferredTask is already armed so just updating the FormLike");
|
||||
// We update the FormLike so it (most important .elements) is fresh when the task eventually
|
||||
log("DeferredTask is already armed so just updating the LoginForm");
|
||||
// We update the LoginForm so it (most important .elements) is fresh when the task eventually
|
||||
// runs since changes to the elements could affect our field heuristics.
|
||||
this._formLikeByRootElement.set(formLike.rootElement, formLike);
|
||||
LoginFormFactory.setForRootElement(formLike.rootElement, formLike);
|
||||
} else if (window.document.readyState == "complete") {
|
||||
log("Arming the DeferredTask we just created since document.readyState == 'complete'");
|
||||
deferredTask.arm();
|
||||
|
|
@ -485,7 +470,7 @@ var LoginManagerContent = {
|
|||
/**
|
||||
* Fetch logins from the parent for a given form and then attempt to fill it.
|
||||
*
|
||||
* @param {FormLike} form to fetch the logins for then try autofill.
|
||||
* @param {LoginForm} form to fetch the logins for then try autofill.
|
||||
*/
|
||||
_fetchLoginsFromParentAndFillForm(form) {
|
||||
let window = form.ownerDocument.defaultView;
|
||||
|
|
@ -526,7 +511,6 @@ var LoginManagerContent = {
|
|||
* Keeps track of filled fields and values.
|
||||
*/
|
||||
fillsByRootElement: new WeakMap(),
|
||||
loginFormRootElements: new WeakSet(),
|
||||
};
|
||||
this.loginFormStateByDocument.set(document, loginFormState);
|
||||
}
|
||||
|
|
@ -543,7 +527,7 @@ var LoginManagerContent = {
|
|||
// Returns true if this window or any subframes have insecure login forms.
|
||||
let hasInsecureLoginForms = (thisWindow) => {
|
||||
let doc = thisWindow.document;
|
||||
let rootElsWeakSet = this.stateForDocument(doc).loginFormRootElements;
|
||||
let rootElsWeakSet = LoginFormFactory.getRootElementsWeakSetForDocument(doc);
|
||||
let hasLoginForm = ChromeUtils.nondeterministicGetWeakSetKeys(rootElsWeakSet)
|
||||
.filter(el => el.isConnected).length > 0;
|
||||
return (hasLoginForm && !thisWindow.isSecureContext) ||
|
||||
|
|
@ -726,7 +710,7 @@ var LoginManagerContent = {
|
|||
},
|
||||
|
||||
/**
|
||||
* @param {FormLike} form - the FormLike to look for password fields in.
|
||||
* @param {LoginForm} form - the LoginForm to look for password fields in.
|
||||
* @param {Object} options
|
||||
* @param {bool} [options.skipEmptyFields=false] - Whether to ignore password fields with no value.
|
||||
* Used at capture time since saving empty values isn't
|
||||
|
|
@ -785,7 +769,7 @@ var LoginManagerContent = {
|
|||
* Can handle complex forms by trying to figure out what the
|
||||
* relevant fields are.
|
||||
*
|
||||
* @param {FormLike} form
|
||||
* @param {LoginForm} form
|
||||
* @param {bool} isSubmission
|
||||
* @param {Set} recipes
|
||||
* @return {Array} [usernameField, newPasswordField, oldPasswordField]
|
||||
|
|
@ -797,9 +781,9 @@ var LoginManagerContent = {
|
|||
* change-password field, with oldPasswordField containing the password
|
||||
* that is being changed.
|
||||
*
|
||||
* Note that even though we can create a FormLike from a text field,
|
||||
* Note that even though we can create a LoginForm from a text field,
|
||||
* this method will only return a non-null usernameField if the
|
||||
* FormLike has a password field.
|
||||
* LoginForm has a password field.
|
||||
*/
|
||||
_getFormFields(form, isSubmission, recipes) {
|
||||
var usernameField = null;
|
||||
|
|
@ -811,7 +795,7 @@ var LoginManagerContent = {
|
|||
fieldOverrideRecipe.passwordSelector
|
||||
);
|
||||
if (pwOverrideField) {
|
||||
// The field from the password override may be in a different FormLike.
|
||||
// The field from the password override may be in a different LoginForm.
|
||||
let formLike = LoginFormFactory.createFromField(pwOverrideField);
|
||||
pwFields = [{
|
||||
index: [...formLike.elements].indexOf(pwOverrideField),
|
||||
|
|
@ -937,22 +921,21 @@ var LoginManagerContent = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Trigger capture on any relevant FormLikes due to a navigation alone (not
|
||||
* Trigger capture on any relevant LoginForms due to a navigation alone (not
|
||||
* necessarily due to an actual form submission). This method is used to
|
||||
* capture logins for cases where form submit events are not used.
|
||||
*
|
||||
* To avoid multiple notifications for the same FormLike, this currently
|
||||
* To avoid multiple notifications for the same LoginForm, this currently
|
||||
* avoids capturing when dealing with a real <form> which are ideally already
|
||||
* using a submit event.
|
||||
*
|
||||
* @param {Document} document being navigated
|
||||
*/
|
||||
_onNavigation(aDocument) {
|
||||
let state = this.stateForDocument(aDocument);
|
||||
let rootElsWeakSet = state.loginFormRootElements;
|
||||
let rootElsWeakSet = LoginFormFactory.getRootElementsWeakSetForDocument(aDocument);
|
||||
let weakLoginFormRootElements = ChromeUtils.nondeterministicGetWeakSetKeys(rootElsWeakSet);
|
||||
|
||||
log("_onNavigation: state:", state, "loginFormRootElements approx size:", weakLoginFormRootElements.length,
|
||||
log("_onNavigation: root elements approx size:", weakLoginFormRootElements.length,
|
||||
"document:", aDocument);
|
||||
|
||||
for (let formRoot of weakLoginFormRootElements) {
|
||||
|
|
@ -961,14 +944,14 @@ var LoginManagerContent = {
|
|||
}
|
||||
|
||||
if (ChromeUtils.getClassName(formRoot) === "HTMLFormElement") {
|
||||
// For now only perform capture upon navigation for FormLike's without
|
||||
// For now only perform capture upon navigation for LoginForm's without
|
||||
// a <form> to avoid capture from both a DOMFormBeforeSubmit event and
|
||||
// navigation for the same "form".
|
||||
log("Ignoring navigation for the form root to avoid multiple prompts " +
|
||||
"since it was for a real <form>");
|
||||
continue;
|
||||
}
|
||||
let formLike = this._formLikeByRootElement.get(formRoot);
|
||||
let formLike = LoginFormFactory.getForRootElement(formRoot);
|
||||
this._onFormSubmit(formLike);
|
||||
}
|
||||
},
|
||||
|
|
@ -979,7 +962,7 @@ var LoginManagerContent = {
|
|||
* Looks for a password change in the submitted form, so we can update
|
||||
* our stored password.
|
||||
*
|
||||
* @param {FormLike} form
|
||||
* @param {LoginForm} form
|
||||
*/
|
||||
_onFormSubmit(form) {
|
||||
log("_onFormSubmit", form);
|
||||
|
|
@ -1118,11 +1101,10 @@ var LoginManagerContent = {
|
|||
userTriggered = false,
|
||||
} = {}) {
|
||||
if (ChromeUtils.getClassName(form) === "HTMLFormElement") {
|
||||
throw new Error("_fillForm should only be called with FormLike objects");
|
||||
throw new Error("_fillForm should only be called with LoginForm objects");
|
||||
}
|
||||
|
||||
log("_fillForm", form.elements);
|
||||
let ignoreAutocomplete = true;
|
||||
// Will be set to one of AUTOFILL_RESULT in the `try` block.
|
||||
let autofillResult = -1;
|
||||
const AUTOFILL_RESULT = {
|
||||
|
|
@ -1214,13 +1196,6 @@ var LoginManagerContent = {
|
|||
return;
|
||||
}
|
||||
|
||||
var isAutocompleteOff = false;
|
||||
if (this._isAutocompleteDisabled(form) ||
|
||||
this._isAutocompleteDisabled(usernameField) ||
|
||||
this._isAutocompleteDisabled(passwordField)) {
|
||||
isAutocompleteOff = true;
|
||||
}
|
||||
|
||||
// Discard logins which have username/password values that don't
|
||||
// fit into the fields (as specified by the maxlength attribute).
|
||||
// The user couldn't enter these values anyway, and it helps
|
||||
|
|
@ -1252,9 +1227,11 @@ var LoginManagerContent = {
|
|||
return;
|
||||
}
|
||||
|
||||
const passwordACFieldName = passwordField.getAutocompleteInfo().fieldName;
|
||||
|
||||
// If the password field has the autocomplete value of "new-password"
|
||||
// and we're autofilling without user interaction, there's nothing to do.
|
||||
if (!userTriggered && passwordField.getAutocompleteInfo().fieldName == "new-password") {
|
||||
if (!userTriggered && passwordACFieldName == "new-password") {
|
||||
log("not filling form, password field has the autocomplete new-password value");
|
||||
autofillResult = AUTOFILL_RESULT.PASSWORD_AUTOCOMPLETE_NEW_PASSWORD;
|
||||
return;
|
||||
|
|
@ -1325,8 +1302,8 @@ var LoginManagerContent = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (isAutocompleteOff && !ignoreAutocomplete) {
|
||||
log("Not filling the login because we're respecting autocomplete=off");
|
||||
if (!userTriggered && passwordACFieldName == "off" && !LoginHelper.autofillAutocompleteOff) {
|
||||
log("Not autofilling the login because we're respecting autocomplete=off");
|
||||
autofillResult = AUTOFILL_RESULT.AUTOCOMPLETE_OFF;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1334,7 +1311,7 @@ var LoginManagerContent = {
|
|||
// Fill the form
|
||||
|
||||
if (usernameField) {
|
||||
// Don't modify the username field if it's disabled or readOnly so we preserve its case.
|
||||
// Don't modify the username field if it's disabled or readOnly so we preserve its case.
|
||||
let disabledOrReadOnly = usernameField.disabled || usernameField.readOnly;
|
||||
|
||||
let userNameDiffers = selectedLogin.username != usernameField.value;
|
||||
|
|
@ -1415,21 +1392,21 @@ var LoginManagerContent = {
|
|||
* method doesn't need to since it's only returning a boolean based upon the
|
||||
* recipes used for the last fill (in _fillForm).
|
||||
*
|
||||
* @param {HTMLInputElement} aUsernameField element contained in a FormLike
|
||||
* cached in _formLikeByRootElement.
|
||||
* @param {HTMLInputElement} aUsernameField element contained in a LoginForm
|
||||
* cached in LoginFormFactory.
|
||||
* @returns {Boolean} whether the username and password fields still have the
|
||||
* last-filled values, if previously filled.
|
||||
*/
|
||||
_isLoginAlreadyFilled(aUsernameField) {
|
||||
let formLikeRoot = FormLikeFactory.findRootForField(aUsernameField);
|
||||
// Look for the existing FormLike.
|
||||
let existingFormLike = this._formLikeByRootElement.get(formLikeRoot);
|
||||
if (!existingFormLike) {
|
||||
// Look for the existing LoginForm.
|
||||
let existingLoginForm = LoginFormFactory.getForRootElement(formLikeRoot);
|
||||
if (!existingLoginForm) {
|
||||
throw new Error("_isLoginAlreadyFilled called with a username field with " +
|
||||
"no rootElement FormLike");
|
||||
"no rootElement LoginForm");
|
||||
}
|
||||
|
||||
log("_isLoginAlreadyFilled: existingFormLike", existingFormLike);
|
||||
log("_isLoginAlreadyFilled: existingLoginForm", existingLoginForm);
|
||||
let filledLogin = this.stateForDocument(aUsernameField.ownerDocument).fillsByRootElement.get(formLikeRoot);
|
||||
if (!filledLogin) {
|
||||
return false;
|
||||
|
|
@ -1480,13 +1457,13 @@ var LoginManagerContent = {
|
|||
|
||||
/**
|
||||
* Verify if a field is a valid login form field and
|
||||
* returns some information about it's FormLike.
|
||||
* returns some information about it's LoginForm.
|
||||
*
|
||||
* @param {Element} aField
|
||||
* A form field we want to verify.
|
||||
*
|
||||
* @returns {Object} an object with information about the
|
||||
* FormLike username and password field
|
||||
* LoginForm username and password field
|
||||
* or null if the passed field is invalid.
|
||||
*/
|
||||
getFieldContext(aField) {
|
||||
|
|
@ -1498,8 +1475,7 @@ var LoginManagerContent = {
|
|||
return null;
|
||||
}
|
||||
|
||||
let [usernameField, newPasswordField] =
|
||||
this.getUserNameAndPasswordFields(aField);
|
||||
let [usernameField, newPasswordField] = this.getUserNameAndPasswordFields(aField);
|
||||
|
||||
// If we are not verifying a password field, we want
|
||||
// to use aField as the username field.
|
||||
|
|
@ -1519,70 +1495,3 @@ var LoginManagerContent = {
|
|||
};
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A factory to generate FormLike objects that represent a set of login fields
|
||||
* which aren't necessarily marked up with a <form> element.
|
||||
*/
|
||||
var LoginFormFactory = {
|
||||
/**
|
||||
* Create a LoginForm object from a <form>.
|
||||
*
|
||||
* @param {HTMLFormElement} aForm
|
||||
* @return {LoginForm}
|
||||
* @throws Error if aForm isn't an HTMLFormElement
|
||||
*/
|
||||
createFromForm(aForm) {
|
||||
let formLike = FormLikeFactory.createFromForm(aForm);
|
||||
formLike.action = LoginHelper.getFormActionOrigin(aForm);
|
||||
|
||||
let state = LoginManagerContent.stateForDocument(formLike.ownerDocument);
|
||||
state.loginFormRootElements.add(formLike.rootElement);
|
||||
log("adding", formLike.rootElement, "to loginFormRootElements for", formLike.ownerDocument);
|
||||
|
||||
LoginManagerContent._formLikeByRootElement.set(formLike.rootElement, formLike);
|
||||
return formLike;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a LoginForm object from a password or username field.
|
||||
*
|
||||
* If the field is in a <form>, construct the LoginForm from the form.
|
||||
* Otherwise, create a LoginForm with a rootElement (wrapper) according to
|
||||
* heuristics. Currently all <input> not in a <form> are one LoginForm but this
|
||||
* shouldn't be relied upon as the heuristics may change to detect multiple
|
||||
* "forms" (e.g. registration and login) on one page with a <form>.
|
||||
*
|
||||
* Note that two LoginForms created from the same field won't return the same LoginForm object.
|
||||
* Use the `rootElement` property on the LoginForm as a key instead.
|
||||
*
|
||||
* @param {HTMLInputElement} aField - a password or username field in a document
|
||||
* @return {LoginForm}
|
||||
* @throws Error if aField isn't a password or username field in a document
|
||||
*/
|
||||
createFromField(aField) {
|
||||
if (ChromeUtils.getClassName(aField) !== "HTMLInputElement" ||
|
||||
(aField.type != "password" && !LoginHelper.isUsernameFieldType(aField)) ||
|
||||
!aField.ownerDocument) {
|
||||
throw new Error("createFromField requires a password or username field in a document");
|
||||
}
|
||||
|
||||
if (aField.form) {
|
||||
return this.createFromForm(aField.form);
|
||||
}
|
||||
|
||||
let formLike = FormLikeFactory.createFromField(aField);
|
||||
formLike.action = LoginHelper.getLoginOrigin(aField.ownerDocument.baseURI);
|
||||
log("Created non-form FormLike for rootElement:", aField.ownerDocument.documentElement);
|
||||
|
||||
let state = LoginManagerContent.stateForDocument(formLike.ownerDocument);
|
||||
state.loginFormRootElements.add(formLike.rootElement);
|
||||
log("adding", formLike.rootElement, "to loginFormRootElements for", formLike.ownerDocument);
|
||||
|
||||
|
||||
LoginManagerContent._formLikeByRootElement.set(formLike.rootElement, formLike);
|
||||
|
||||
return formLike;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ EXTRA_JS_MODULES += [
|
|||
'crypto-SDR.js',
|
||||
'InsecurePasswordUtils.jsm',
|
||||
'LoginAutoCompleteResult.jsm',
|
||||
'LoginFormFactory.jsm',
|
||||
'LoginHelper.jsm',
|
||||
'LoginInfo.jsm',
|
||||
'LoginManager.jsm',
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ add_task(async function test() {
|
|||
// Convert the login object to a plain JS object for passing across process boundaries.
|
||||
login = LoginHelper.loginToVanillaObject(login);
|
||||
ContentTask.spawn(tab.linkedBrowser, {login, usernameRequested}, async ({login: addedLogin, usernameRequested: aUsernameRequested}) => {
|
||||
const { LoginManagerContent, LoginFormFactory } = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginFormFactory } = ChromeUtils.import("resource://gre/modules/LoginFormFactory.jsm");
|
||||
const { LoginManagerContent } = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginHelper } = ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");
|
||||
|
||||
let password = content.document.querySelector("#form-basic-password");
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ skip-if = toolkit == 'android' # autocomplete
|
|||
[test_basic_form_3pw_1.html]
|
||||
[test_basic_form_autocomplete.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_basic_form_honor_autocomplete_off.html]
|
||||
scheme = https
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_insecure_form_field_autocomplete.html]
|
||||
skip-if = toolkit == 'android' || os == 'linux' # android:autocomplete., linux: bug 1325778
|
||||
[test_password_field_autocomplete.html]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test login autofill autocomplete when signon.autofillForms.autocompleteOff is false</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: autofilling when autocomplete=off
|
||||
|
||||
<script>
|
||||
let readyPromise = registerRunTests();
|
||||
|
||||
runInParent(function setup() {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Create some logins just for this form, since we'll be deleting them.
|
||||
const nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
assert.ok(nsLoginInfo != null, "nsLoginInfo constructor");
|
||||
|
||||
const login = new nsLoginInfo("https://example.com", "https://autocomplete2", null,
|
||||
"singleuser", "singlepass", "uname", "pword");
|
||||
Services.logins.addLogin(login);
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
<!-- test single logins, with autocomplete=off set -->
|
||||
<form id="form1" action="https://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword" autocomplete="off">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form2" action="https://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname" autocomplete="off">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form3" action="https://autocomplete2" onsubmit="return false;" autocomplete="off">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form4" action="https://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname" autocomplete="off">
|
||||
<input type="password" name="pword" autocomplete="off">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- control -->
|
||||
<form id="form5" action="https://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
/** Test for Login Manager: multiple login autocomplete. **/
|
||||
let {ContentTaskUtils} = SpecialPowers.Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
|
||||
|
||||
// Set the pref before the document loads.
|
||||
SpecialPowers.setBoolPref("signon.autofillForms.autocompleteOff", false);
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
SpecialPowers.clearUserPref("signon.autofillForms.autocompleteOff");
|
||||
});
|
||||
|
||||
// Check for expected username/password in form.
|
||||
function checkFormValues(form, expectedUsername, expectedPassword) {
|
||||
let uname = form.querySelector("[name='uname']");
|
||||
let pword = form.querySelector("[name='pword']");
|
||||
is(uname.value, expectedUsername, `Checking ${form.id} username is: ${expectedUsername}`);
|
||||
is(pword.value, expectedPassword, `Checking ${form.id} password is: ${expectedPassword}`);
|
||||
}
|
||||
|
||||
async function autoCompleteFieldsFromFirstMatch(form) {
|
||||
// trigger autocomplete from the username field
|
||||
await SimpleTest.promiseFocus(form.ownerGlobal);
|
||||
let uname = form.querySelector("[name='uname']");
|
||||
let shownPromise = promiseACShown();
|
||||
uname.focus();
|
||||
await shownPromise;
|
||||
|
||||
let formFilled = promiseFormsProcessed();
|
||||
await synthesizeKey("KEY_ArrowDown"); // open
|
||||
await synthesizeKey("KEY_Enter");
|
||||
await formFilled;
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
ok(readyPromise, "check promise is available");
|
||||
await readyPromise;
|
||||
listenForUnexpectedPopupShown();
|
||||
});
|
||||
|
||||
/* Tests for autofill of single-user forms for when we honor autocomplete=off on password fields */
|
||||
add_task(async function test_form1_honor_password_autocomplete_off() {
|
||||
await SimpleTest.promiseFocus(window);
|
||||
// With the pref toggled off, and with autocomplete=off on the password field,
|
||||
// we expect not to have autofilled this form
|
||||
let form = document.getElementById("form1");
|
||||
ok(form, "found form under test");
|
||||
checkFormValues(form, "", "");
|
||||
|
||||
// ..but it should autocomplete just fine
|
||||
await autoCompleteFieldsFromFirstMatch(form);
|
||||
checkFormValues(form, "singleuser", "singlepass");
|
||||
});
|
||||
|
||||
add_task(async function test_form2_honor_password_autocomplete_off() {
|
||||
await SimpleTest.promiseFocus(window);
|
||||
// With the pref toggled off, and with autocomplete=off on the username field,
|
||||
// we expect to have autofilled this form
|
||||
let form = document.getElementById("form2");
|
||||
ok(form, "found form under test");
|
||||
checkFormValues(form, "singleuser", "singlepass");
|
||||
});
|
||||
|
||||
add_task(async function test_form3_honor_password_autocomplete_off() {
|
||||
await SimpleTest.promiseFocus(window);
|
||||
// With the pref toggled off, and with autocomplete=off on the form,
|
||||
// we expect to have autofilled this form
|
||||
let form = document.getElementById("form3");
|
||||
ok(form, "found form under test");
|
||||
checkFormValues(form, "singleuser", "singlepass");
|
||||
});
|
||||
|
||||
add_task(async function test_form4_honor_password_autocomplete_off() {
|
||||
await SimpleTest.promiseFocus(window);
|
||||
// With the pref toggled off, and autocomplete=off on the username and password field,
|
||||
// we expect not to have autofilled this form
|
||||
let form = document.getElementById("form4");
|
||||
ok(form, "found form under test");
|
||||
checkFormValues(form, "", "");
|
||||
|
||||
// ..but it should autocomplete just fine
|
||||
await autoCompleteFieldsFromFirstMatch(form);
|
||||
checkFormValues(form, "singleuser", "singlepass");
|
||||
});
|
||||
|
||||
add_task(async function test_form5() {
|
||||
await SimpleTest.promiseFocus(window);
|
||||
// (this is a control, w/o autocomplete=off, to ensure the login
|
||||
// that was being suppressed would have been filled in otherwise)
|
||||
let form = document.getElementById("form5");
|
||||
ok(form, "found form under test");
|
||||
checkFormValues(form, "singleuser", "singlepass");
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -10,8 +10,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
const {LoginFormFactory} = SpecialPowers.Cu.import("resource://gre/modules/LoginFormFactory.jsm");
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const { LoginManagerContent } = LMCBackstagePass;
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<body>
|
||||
<script type="application/javascript">
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const { LoginManagerContent } = LMCBackstagePass;
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<body>
|
||||
<script type="application/javascript">
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const { LoginManagerContent } = LMCBackstagePass;
|
||||
|
||||
SimpleTest.requestFlakyTimeout("Testing that a message doesn't arrive");
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
|
||||
|
||||
const {LoginFormFactory} = ChromeUtils.import("resource://gre/modules/LoginFormFactory.jsm");
|
||||
const LMCBackstagePass = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", null);
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const { LoginManagerContent } = LMCBackstagePass;
|
||||
const TESTCASES = [
|
||||
{
|
||||
description: "1 password field outside of a <form>",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {LoginFormFactory} = ChromeUtils.import("resource://gre/modules/LoginFormFactory.jsm");
|
||||
const LMCBackstagePass = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", null);
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const { LoginManagerContent } = LMCBackstagePass;
|
||||
const TESTCASES = [
|
||||
{
|
||||
description: "Empty document",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
|
||||
|
||||
const LMCBackstagePass = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", null);
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const { LoginManagerContent } = LMCBackstagePass;
|
||||
const TESTCASES = [
|
||||
{
|
||||
description: "1 password field outside of a <form>",
|
||||
|
|
|
|||
|
|
@ -331,5 +331,26 @@ function restart(safeMode) {
|
|||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
// Check if any profiles are missing on the disk and if so remove them.
|
||||
// We cannot remove profiles while iterating the list returned from the
|
||||
// profile service, so convert it to a new array first.
|
||||
try {
|
||||
let changed = false;
|
||||
for (let profile of [...ProfileService.profiles]) {
|
||||
if (!profile.rootDir.exists() || !profile.rootDir.isDirectory()) {
|
||||
profile.remove(false);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ProfileService.flush();
|
||||
}
|
||||
} catch (e) {
|
||||
// There shouldn't be any failures to catch here, but just in case let the
|
||||
// UI build itself properly.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
refreshUI();
|
||||
}, {once: true});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
// a block to prevent accidentally leaking globals onto `window`.
|
||||
{
|
||||
class MozWizardPage extends MozXULElement {
|
||||
connectedCallback() {
|
||||
constructor() {
|
||||
super();
|
||||
this.pageIndex = -1;
|
||||
}
|
||||
get pageid() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,23 @@ function startup() {
|
|||
|
||||
gProfileService = C[ToolkitProfileService].getService(I.nsIToolkitProfileService);
|
||||
|
||||
// Check if any profiles are missing on the disk and if so remove them.
|
||||
// We cannot remove profiles while iterating the list returned from the
|
||||
// profile service, so convert it to a new array first.
|
||||
try {
|
||||
for (let profile of [...gProfileService.profiles]) {
|
||||
if (!profile.rootDir.exists() || !profile.rootDir.isDirectory()) {
|
||||
profile.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
// The profile service is always flushed after this dialog completes.
|
||||
} catch (e) {
|
||||
// There shouldn't be any failures to catch here, but just in case let the
|
||||
// UI build itself properly.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
gProfileManagerBundle = document.getElementById("bundle_profileManager");
|
||||
gBrandBundle = document.getElementById("bundle_brand");
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,6 @@
|
|||
"log.js": ["Log"],
|
||||
"logger.jsm": ["Logger"],
|
||||
"logging.js": ["getTestLogger", "initTestLogging"],
|
||||
"LoginManagerContent.jsm": ["LoginManagerContent", "LoginFormFactory"],
|
||||
"LoginRecipes.jsm": ["LoginRecipesContent", "LoginRecipesParent"],
|
||||
"logmanager.js": ["LogManager"],
|
||||
"lz4.js": ["Lz4"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue